xref: /aoo41x/main/sc/source/core/tool/address.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 #include "address.hxx"
32 #include "global.hxx"
33 #include "compiler.hxx"
34 #include "document.hxx"
35 #include "externalrefmgr.hxx"
36 
37 #include "globstr.hrc"
38 #include <sal/alloca.h>
39 
40 #include <com/sun/star/frame/XModel.hpp>
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/sheet/ExternalLinkInfo.hpp>
43 #include <com/sun/star/sheet/ExternalLinkType.hpp>
44 #include <sfx2/objsh.hxx>
45 #include <tools/urlobj.hxx>
46 using namespace ::com::sun::star;
47 
48 ////////////////////////////////////////////////////////////////////////////
49 const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 );
50 
51 ScAddress::Details::Details ( const ScDocument* pDoc,
52                               const ScAddress & rAddr ) :
53     eConv( pDoc->GetAddressConvention() ),
54     nRow( rAddr.Row() ),
55     nCol( rAddr.Col() )
56 {
57 }
58 
59 //UNUSED2009-05 void ScAddress::Details::SetPos ( const ScDocument* pDoc,
60 //UNUSED2009-05                                   const ScAddress & rAddr )
61 //UNUSED2009-05 {
62 //UNUSED2009-05     nRow  = rAddr.Row();
63 //UNUSED2009-05     nCol  = rAddr.Col();
64 //UNUSED2009-05     eConv = pDoc->GetAddressConvention();
65 //UNUSED2009-05 }
66 
67 ////////////////////////////////////////////////////////////////////////////
68 
69 #include <iostream>
70 
71 /**
72  * Parse from the opening single quote to the closing single quote.  Inside
73  * the quotes, a single quote character is encoded by double single-quote
74  * characters.
75  *
76  * @param p pointer to the first character to begin parsing.
77  * @param rName (reference) parsed name within the quotes.  If the name is
78  *              empty, either the parsing failed or it's an empty quote.
79  *
80  * @return pointer to the character immediately after the closing single
81  *         quote.
82  */
83 static const sal_Unicode* lcl_ParseQuotedName( const sal_Unicode* p, String& rName )
84 {
85     rName.Erase();
86     if (*p != '\'')
87         return p;
88 
89     const sal_Unicode* pStart = p;
90     sal_Unicode cPrev = 0;
91     for (++p; *p; ++p)
92     {
93         if (*p == '\'')
94         {
95             if (cPrev == '\'')
96             {
97                 // double single-quote equals one single quote.
98                 rName += *p;
99                 cPrev = 0;
100                 continue;
101             }
102         }
103         else if (cPrev == '\'')
104             // We are past the closing quote.  We're done!
105             return p;
106         else
107             rName += *p;
108         cPrev = *p;
109     }
110     rName.Erase();
111     return pStart;
112 }
113 
114 static long int
115 sal_Unicode_strtol ( const sal_Unicode*  p,
116                      const sal_Unicode** pEnd )
117 {
118     long int accum = 0, prev = 0;
119     bool is_neg = false;
120 
121     if( *p == '-' )
122     {
123         is_neg = true;
124         p++;
125     }
126     else if( *p == '+' )
127         p++;
128 
129     while (CharClass::isAsciiDigit( *p ))
130     {
131         accum = accum * 10 + *p - '0';
132         if( accum < prev )
133         {
134             *pEnd = NULL;
135             return 0;
136         }
137         prev = accum;
138         p++;
139     }
140 
141     *pEnd = p;
142     return is_neg ? -accum : accum;
143 }
144 
145 const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p )
146 {
147     if ( p )
148     {
149         while(  *p == ' ' )
150             ++p;
151     }
152     return p;
153 }
154 
155 /** Determines the number of sheets an external reference spans and sets
156     rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
157     bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
158     cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
159     is set to rEndTabName.
160     @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
161              result in the identical file ID. Else <TRUE/>.
162  */
163 static bool lcl_ScRange_External_TabSpan(
164         ScRange & rRange,
165         sal_uInt16 & rFlags,
166         ScAddress::ExternalInfo* pExtInfo,
167         const String & rExternDocName,
168         const String & rStartTabName,
169         const String & rEndTabName,
170         ScDocument* pDoc )
171 {
172     if (!rExternDocName.Len())
173         return !pExtInfo || !pExtInfo->mbExternal;
174 
175     ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
176     if (pRefMgr->isOwnDocument( rExternDocName))
177         return !pExtInfo || !pExtInfo->mbExternal;
178 
179     sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName);
180 
181     if (pExtInfo)
182     {
183         if (pExtInfo->mbExternal)
184         {
185             if (pExtInfo->mnFileId != nFileId)
186                 return false;
187         }
188         else
189         {
190             pExtInfo->mbExternal = true;
191             pExtInfo->maTabName = rStartTabName;
192             pExtInfo->mnFileId = nFileId;
193         }
194     }
195 
196     if (!rEndTabName.Len() || rStartTabName == rEndTabName)
197     {
198         rRange.aEnd.SetTab( rRange.aStart.Tab());
199         return true;
200     }
201 
202     SCsTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName);
203     if (nSpan == -1)
204         rFlags &= ~(SCA_VALID_TAB | SCA_VALID_TAB2);
205     else if (nSpan == 0)
206         rFlags &= ~SCA_VALID_TAB2;
207     else if (nSpan >= 1)
208         rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1);
209     else // (nSpan < -1)
210     {
211         rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1);
212         if (pExtInfo)
213             pExtInfo->maTabName = rEndTabName;
214     }
215     return true;
216 }
217 
218 /** Returns NULL if the string should be a sheet name, but is invalid.
219     Returns a pointer to the first character after the sheet name, if there was
220     any, else pointer to start.
221     @param pMsoxlQuoteStop
222         Starting _within_ a quoted name, but still may be 3D; quoted name stops
223         at pMsoxlQuoteStop
224  */
225 static const sal_Unicode *
226 lcl_XL_ParseSheetRef( const sal_Unicode* start,
227                       String& rExternTabName,
228                       bool allow_3d,
229                       const sal_Unicode* pMsoxlQuoteStop )
230 {
231     String aTabName;
232     const sal_Unicode *p = start;
233 
234     // XL only seems to use single quotes for sheet names.
235     if (pMsoxlQuoteStop)
236     {
237         const sal_Unicode* pCurrentStart = p;
238         while (p < pMsoxlQuoteStop)
239         {
240             if (*p == '\'')
241             {
242                 // We pre-analyzed the quoting, no checks needed here.
243                 if (*++p == '\'')
244                 {
245                     aTabName.Append( pCurrentStart,
246                             sal::static_int_cast<xub_StrLen>( p - pCurrentStart));
247                     pCurrentStart = ++p;
248                 }
249             }
250             else if (*p == ':')
251             {
252                 break;  // while
253             }
254             else
255                 ++p;
256         }
257         if (pCurrentStart < p)
258             aTabName.Append( pCurrentStart, sal::static_int_cast<xub_StrLen>( p - pCurrentStart));
259         if (!aTabName.Len())
260             return NULL;
261         if (p == pMsoxlQuoteStop)
262             ++p;    // position on ! of ...'!...
263         if( *p != '!' && ( !allow_3d || *p != ':' ) )
264             return (!allow_3d && *p == ':') ? p : start;
265     }
266     else if( *p == '\'')
267     {
268         p = lcl_ParseQuotedName(p, aTabName);
269         if (!aTabName.Len())
270             return NULL;
271     }
272     else
273     {
274         bool only_digits = sal_True;
275 
276         /*
277          * Valid: Normal!a1
278          * Valid: x.y!a1
279          * Invalid: .y!a1
280          *
281          * Some names starting with digits are actually valid, but
282          * unparse quoted. Things are quite tricky: most sheet names
283          * starting with a digit are ok, but not those starting with
284          * "[0-9]*\." or "[0-9]+[eE]".
285          *
286          * Valid: 42!a1
287          * Valid: 4x!a1
288          * Invalid: 1.!a1
289          * Invalid: 1e!a1
290          */
291         while( 1 )
292         {
293             const sal_Unicode uc = *p;
294             if( CharClass::isAsciiAlpha( uc ) || uc == '_' )
295             {
296                 if( only_digits && p != start &&
297                    (uc == 'e' || uc == 'E' ) )
298                 {
299                     p = start;
300                     break;
301                 }
302                 only_digits = sal_False;
303                 p++;
304             }
305             else if( CharClass::isAsciiDigit( uc ))
306             {
307                 p++;
308             }
309             else if( uc == '.' )
310             {
311                 if( only_digits ) // Valid, except after only digits.
312                 {
313                     p = start;
314                     break;
315                 }
316                 p++;
317             }
318             else if (uc > 127)
319             {
320                 // non ASCII character is allowed.
321                 ++p;
322             }
323             else
324                 break;
325         }
326 
327         if( *p != '!' && ( !allow_3d || *p != ':' ) )
328             return (!allow_3d && *p == ':') ? p : start;
329 
330         aTabName.Append( start, sal::static_int_cast<xub_StrLen>( p - start ) );
331     }
332 
333     rExternTabName = aTabName;
334     return p;
335 }
336 
337 
338 const sal_Unicode* ScRange::Parse_XL_Header(
339         const sal_Unicode* p,
340         const ScDocument* pDoc,
341         String& rExternDocName,
342         String& rStartTabName,
343         String& rEndTabName,
344         sal_uInt16& nFlags,
345         bool bOnlyAcceptSingle,
346         const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
347 {
348     const sal_Unicode* startTabs, *start = p;
349     sal_uInt16 nSaveFlags = nFlags;
350 
351     // Is this an external reference ?
352     rStartTabName.Erase();
353     rEndTabName.Erase();
354     rExternDocName.Erase();
355     const sal_Unicode* pMsoxlQuoteStop = NULL;
356     if (*p == '[')
357     {
358         ++p;
359         // Only single quotes are correct, and a double single quote escapes a
360         // single quote text inside the quoted text.
361         if (*p == '\'')
362         {
363             p = lcl_ParseQuotedName(p, rExternDocName);
364             if (!*p || *p != ']' || !rExternDocName.Len())
365             {
366                 rExternDocName.Erase();
367                 return start;
368             }
369         }
370         else
371         {
372             // non-quoted file name.
373             p = ScGlobal::UnicodeStrChr( start+1, ']' );
374             if( p == NULL )
375                 return start;
376             rExternDocName.Append( start+1, sal::static_int_cast<xub_StrLen>( p-(start+1) ) );
377         }
378         ++p;
379 
380         if (pExternalLinks && pExternalLinks->hasElements())
381         {
382             // A numeric "document name" is an index into the sequence.
383             if (CharClass::isAsciiNumeric( rExternDocName))
384             {
385                 sal_Int32 i = rExternDocName.ToInt32();
386                 if (i < 0 || i >= pExternalLinks->getLength())
387                     return start;
388                 const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i];
389                 switch (rInfo.Type)
390                 {
391                     case sheet::ExternalLinkType::DOCUMENT :
392                         {
393                             rtl::OUString aStr;
394                             if (!(rInfo.Data >>= aStr))
395                             {
396                                 DBG_ERROR1( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i);
397                                 return NULL;
398                             }
399                             rExternDocName = aStr;
400                         }
401                         break;
402                     case sheet::ExternalLinkType::SELF :
403                         return start;   // ???
404                     case sheet::ExternalLinkType::SPECIAL :
405                         // silently return nothing (do not assert), caller has to handle this
406                         return NULL;
407                     default:
408                         DBG_ERROR2( "ScRange::Parse_XL_Header: unhandled ExternalLinkType %d for index %d",
409                                 rInfo.Type, i);
410                         return NULL;
411                 }
412             }
413         }
414         rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, pDoc->GetDocumentShell());
415     }
416     else if (*p == '\'')
417     {
418         // Sickness in Excel's ODF msoxl namespace:
419         // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7  or
420         // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
421         // But, 'Sheet1'!B3 would also be a valid!
422         // Excel does not allow [ and ] characters in sheet names though.
423         p = lcl_ParseQuotedName(p, rExternDocName);
424         if (!*p || *p != '!')
425         {
426             rExternDocName.Erase();
427             return start;
428         }
429         if (rExternDocName.Len())
430         {
431             xub_StrLen nOpen = rExternDocName.Search( '[');
432             if (nOpen == STRING_NOTFOUND)
433                 rExternDocName.Erase();
434             else
435             {
436                 xub_StrLen nClose = rExternDocName.Search( ']', nOpen+1);
437                 if (nClose == STRING_NOTFOUND)
438                     rExternDocName.Erase();
439                 else
440                 {
441                     rExternDocName.Erase( nClose);
442                     rExternDocName.Erase( nOpen, 1);
443                     pMsoxlQuoteStop = p - 1;    // the ' quote char
444                     // There may be embedded escaped quotes, just matching the
445                     // doc name's length may not work.
446                     for (p = start; *p != '['; ++p)
447                         ;
448                     for ( ; *p != ']'; ++p)
449                         ;
450                     ++p;
451                 }
452             }
453         }
454         if (!rExternDocName.Len())
455             p = start;
456     }
457 
458     startTabs = p;
459     p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop);
460     if( NULL == p )
461         return start;       // invalid tab
462     if (bOnlyAcceptSingle && *p == ':')
463         return NULL;        // 3D
464     if( p != startTabs )
465     {
466         nFlags |= SCA_VALID_TAB | SCA_TAB_3D | SCA_TAB_ABSOLUTE;
467         if( *p == ':' ) // 3d ref
468         {
469             p = lcl_XL_ParseSheetRef( p+1, rEndTabName, false, pMsoxlQuoteStop);
470             if( p == NULL )
471             {
472                 nFlags = nSaveFlags;
473                 return start; // invalid tab
474             }
475             nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE;
476         }
477         else
478         {
479             // If only one sheet is given, the full reference is still valid,
480             // only the second 3D flag is not set.
481             nFlags |= SCA_VALID_TAB2 | SCA_TAB2_ABSOLUTE;
482             aEnd.SetTab( aStart.Tab() );
483         }
484 
485         if( *p++ != '!' )
486         {
487             nFlags = nSaveFlags;
488             return start;   // syntax error
489         }
490         else
491             p = lcl_eatWhiteSpace( p );
492     }
493     else
494     {
495         nFlags |= SCA_VALID_TAB | SCA_VALID_TAB2;
496         // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
497     }
498 
499     if (rExternDocName.Len())
500     {
501         ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
502         pRefMgr->convertToAbsName( rExternDocName);
503     }
504     else
505     {
506         // Internal reference.
507         if (!rStartTabName.Len())
508         {
509             nFlags = nSaveFlags;
510             return start;
511         }
512 
513         SCTAB nTab;
514         if (!pDoc->GetTable(rStartTabName, nTab))
515         {
516             // invalid table name.
517             nFlags &= ~SCA_VALID_TAB;
518             nTab = -1;
519         }
520 
521         aStart.SetTab(nTab);
522         aEnd.SetTab(nTab);
523 
524         if (rEndTabName.Len())
525         {
526             if (!pDoc->GetTable(rEndTabName, nTab))
527             {
528                 // invalid table name.
529                 nFlags &= ~SCA_VALID_TAB2;
530                 nTab = -1;
531             }
532 
533             aEnd.SetTab(nTab);
534         }
535     }
536     return p;
537 }
538 
539 
540 static const sal_Unicode*
541 lcl_r1c1_get_col( const sal_Unicode* p,
542                   const ScAddress::Details& rDetails,
543                   ScAddress* pAddr, sal_uInt16* nFlags )
544 {
545     const sal_Unicode *pEnd;
546     long int n;
547     bool isRelative;
548 
549     if( p[0] == '\0' )
550         return NULL;
551 
552     p++;
553     if( (isRelative = (*p == '[') ) != false )
554         p++;
555     n = sal_Unicode_strtol( p, &pEnd );
556     if( NULL == pEnd )
557         return NULL;
558 
559     if( p == pEnd ) // C is a relative ref with offset 0
560     {
561         if( isRelative )
562             return NULL;
563         n = rDetails.nCol;
564     }
565     else if( isRelative )
566     {
567         if( *pEnd != ']' )
568             return NULL;
569         n += rDetails.nCol;
570         pEnd++;
571     }
572     else
573     {
574         *nFlags |= SCA_COL_ABSOLUTE;
575         n--;
576     }
577 
578     if( n < 0 || n >= MAXCOLCOUNT )
579         return NULL;
580     pAddr->SetCol( static_cast<SCCOL>( n ) );
581     *nFlags |= SCA_VALID_COL;
582 
583     return pEnd;
584 }
585 static inline const sal_Unicode*
586 lcl_r1c1_get_row( const sal_Unicode* p,
587                   const ScAddress::Details& rDetails,
588                   ScAddress* pAddr, sal_uInt16* nFlags )
589 {
590     const sal_Unicode *pEnd;
591     long int n;
592     bool isRelative;
593 
594     if( p[0] == '\0' )
595         return NULL;
596 
597     p++;
598     if( (isRelative = (*p == '[') ) != false )
599         p++;
600     n = sal_Unicode_strtol( p, &pEnd );
601     if( NULL == pEnd )
602         return NULL;
603 
604     if( p == pEnd ) // R is a relative ref with offset 0
605     {
606         if( isRelative )
607             return NULL;
608         n = rDetails.nRow;
609     }
610     else if( isRelative )
611     {
612         if( *pEnd != ']' )
613             return NULL;
614         n += rDetails.nRow;
615         pEnd++;
616     }
617     else
618     {
619         *nFlags |= SCA_ROW_ABSOLUTE;
620         n--;
621     }
622 
623     if( n < 0 || n >= MAXROWCOUNT )
624         return NULL;
625     pAddr->SetRow( static_cast<SCROW>( n ) );
626     *nFlags |= SCA_VALID_ROW;
627 
628     return pEnd;
629 }
630 
631 static sal_uInt16
632 lcl_ScRange_Parse_XL_R1C1( ScRange& r,
633                            const sal_Unicode* p,
634                            ScDocument* pDoc,
635                            const ScAddress::Details& rDetails,
636                            bool bOnlyAcceptSingle,
637                            ScAddress::ExternalInfo* pExtInfo )
638 {
639     const sal_Unicode* pTmp = NULL;
640     String aExternDocName, aStartTabName, aEndTabName;
641     sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB;
642     // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
643     sal_uInt16 nFlags2 = SCA_VALID_TAB;
644 
645 #if 0
646     {
647         ByteString  aStr(p, RTL_TEXTENCODING_UTF8);
648         aStr.Append(static_cast< char >(0));
649         std::cerr << "parse::XL::R1C1 \'" << aStr.GetBuffer() << '\'' << std::endl;
650     }
651 #endif
652     p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
653             aEndTabName, nFlags, bOnlyAcceptSingle, NULL );
654 
655     if (aExternDocName.Len() > 0)
656         lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
657                 aStartTabName, aEndTabName, pDoc);
658 
659     if( NULL == p )
660         return 0;
661 
662     if( *p == 'R' || *p == 'r' )
663     {
664         if( NULL == (p = lcl_r1c1_get_row( p, rDetails, &r.aStart, &nFlags )) )
665             goto failed;
666 
667         if( *p != 'C' && *p != 'c' )    // full row R#
668         {
669             if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) ||
670                 NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )))
671             {
672                 // Only the initial row number is given, or the second row
673                 // number is invalid. Fallback to just the initial R
674                 nFlags |= (nFlags << 4);
675                 r.aEnd.SetRow( r.aStart.Row() );
676             }
677             else
678             {
679                 // Full row range successfully parsed.
680                 nFlags |= (nFlags2 << 4);
681                 p = pTmp;
682             }
683 
684             if (p && p[0] != 0)
685             {
686                 // any trailing invalid character must invalidate the whole address.
687                 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
688                             SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
689                 return nFlags;
690             }
691 
692             nFlags |=
693                 SCA_VALID_COL | SCA_VALID_COL2 |
694                 SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
695             r.aStart.SetCol( 0 );
696             r.aEnd.SetCol( MAXCOL );
697 
698             return bOnlyAcceptSingle ? 0 : nFlags;
699         }
700         else if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
701             goto failed;
702 
703         if( p[0] != ':' ||
704             (p[1] != 'R' && p[1] != 'r') ||
705             NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )) ||
706             (*pTmp != 'C' && *pTmp != 'c') ||
707             NULL == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 )))
708         {
709             // single cell reference
710 
711             if (p && p[0] != 0)
712             {
713                 // any trailing invalid character must invalidate the whole address.
714                 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
715                 return nFlags;
716             }
717 
718             return bOnlyAcceptSingle ? nFlags : 0;
719         }
720         p = pTmp;
721 
722         // double reference
723 
724         if (p && p[0] != 0)
725         {
726             // any trailing invalid character must invalidate the whole range.
727             nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
728                         SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
729             return nFlags;
730         }
731 
732         nFlags |= (nFlags2 << 4);
733         return bOnlyAcceptSingle ? 0 : nFlags;
734     }
735     else if( *p == 'C' || *p == 'c' )   // full col C#
736     {
737         if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
738             goto failed;
739 
740         if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') ||
741             NULL == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, &nFlags2 )))
742         {    // Fallback to just the initial C
743             nFlags |= (nFlags << 4);
744             r.aEnd.SetCol( r.aStart.Col() );
745         }
746         else
747         {
748             nFlags |= (nFlags2 << 4);
749             p = pTmp;
750         }
751 
752         if (p && p[0] != 0)
753         {
754             // any trailing invalid character must invalidate the whole address.
755             nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
756                         SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
757             return nFlags;
758         }
759 
760         nFlags |=
761             SCA_VALID_ROW | SCA_VALID_ROW2 |
762             SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
763         r.aStart.SetRow( 0 );
764         r.aEnd.SetRow( MAXROW );
765 
766         return bOnlyAcceptSingle ? 0 : nFlags;
767     }
768 
769 failed :
770     return 0;
771 }
772 
773 static inline const sal_Unicode*
774 lcl_a1_get_col( const sal_Unicode* p, ScAddress* pAddr, sal_uInt16* nFlags )
775 {
776     SCCOL nCol;
777 
778     if( *p == '$' )
779         *nFlags |= SCA_COL_ABSOLUTE, p++;
780 
781     if( !CharClass::isAsciiAlpha( *p ) )
782         return NULL;
783 
784     nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
785     while (nCol <= MAXCOL && CharClass::isAsciiAlpha(*p))
786         nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
787     if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) )
788         return NULL;
789 
790     *nFlags |= SCA_VALID_COL;
791     pAddr->SetCol( nCol );
792 
793     return p;
794 }
795 
796 static inline const sal_Unicode*
797 lcl_a1_get_row( const sal_Unicode* p, ScAddress* pAddr, sal_uInt16* nFlags )
798 {
799     const sal_Unicode *pEnd;
800     long int n;
801 
802     if( *p == '$' )
803         *nFlags |= SCA_ROW_ABSOLUTE, p++;
804 
805     n = sal_Unicode_strtol( p, &pEnd ) - 1;
806     if( NULL == pEnd || p == pEnd || n < 0 || n > MAXROW )
807         return NULL;
808 
809     *nFlags |= SCA_VALID_ROW;
810     pAddr->SetRow( static_cast<SCROW>(n) );
811 
812     return pEnd;
813 }
814 
815 static sal_uInt16
816 lcl_ScRange_Parse_XL_A1( ScRange& r,
817                          const sal_Unicode* p,
818                          ScDocument* pDoc,
819                          bool bOnlyAcceptSingle,
820                          ScAddress::ExternalInfo* pExtInfo,
821                          const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
822 {
823     const sal_Unicode* tmp1, *tmp2;
824     String aExternDocName, aStartTabName, aEndTabName; // for external link table
825     sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB;
826 
827 #if 0
828     {
829         ByteString  aStr(p, RTL_TEXTENCODING_UTF8);
830         aStr.Append(static_cast< char >(0));
831         std::cerr << "parse::XL::A1 \'" << aStr.GetBuffer() << '\'' << std::endl;
832     }
833 #endif
834     p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
835             aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks );
836 
837     if (aExternDocName.Len() > 0)
838         lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
839                 aStartTabName, aEndTabName, pDoc);
840 
841     if( NULL == p )
842         return 0;
843 
844     tmp1 = lcl_a1_get_col( p, &r.aStart, &nFlags );
845     if( tmp1 == NULL )          // Is it a row only reference 3:5
846     {
847         if( bOnlyAcceptSingle ) // by definition full row refs are ranges
848             return 0;
849 
850         tmp1 = lcl_a1_get_row( p, &r.aStart, &nFlags );
851 
852         tmp1 = lcl_eatWhiteSpace( tmp1 );
853         if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
854             return 0;
855 
856         tmp1 = lcl_eatWhiteSpace( tmp1 );
857         tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
858         if( !tmp2 )
859             return 0;
860 
861         r.aStart.SetCol( 0 ); r.aEnd.SetCol( MAXCOL );
862         nFlags |=
863             SCA_VALID_COL | SCA_VALID_COL2 |
864             SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
865         nFlags |= (nFlags2 << 4);
866         return nFlags;
867     }
868 
869     tmp2 = lcl_a1_get_row( tmp1, &r.aStart, &nFlags );
870     if( tmp2 == NULL )          // check for col only reference F:H
871     {
872         if( bOnlyAcceptSingle ) // by definition full col refs are ranges
873             return 0;
874 
875         tmp1 = lcl_eatWhiteSpace( tmp1 );
876         if( *tmp1++ != ':' )    // Even a singleton requires ':' (eg F:F)
877             return 0;
878 
879         tmp1 = lcl_eatWhiteSpace( tmp1 );
880         tmp2 = lcl_a1_get_col( tmp1, &r.aEnd, &nFlags2 );
881         if( !tmp2 )
882             return 0;
883 
884         r.aStart.SetRow( 0 ); r.aEnd.SetRow( MAXROW );
885         nFlags |=
886             SCA_VALID_ROW | SCA_VALID_ROW2 |
887             SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
888         nFlags |= (nFlags2 << 4);
889         return nFlags;
890     }
891 
892     // prepare as if it's a singleton, in case we want to fall back */
893     r.aEnd.SetCol( r.aStart.Col() );
894     r.aEnd.SetRow( r.aStart.Row() );    // don't overwrite sheet number as parsed in Parse_XL_Header()
895 
896     if ( bOnlyAcceptSingle )
897     {
898         if ( *tmp2 == 0 )
899             return nFlags;
900         else
901         {
902             // any trailing invalid character must invalidate the address.
903             nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
904             return nFlags;
905         }
906     }
907 
908     tmp2 = lcl_eatWhiteSpace( tmp2 );
909     if( *tmp2 != ':' )
910     {
911         // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
912         // not. Any trailing invalid character invalidates the range.
913         if (*tmp2 == 0 && (nFlags & SCA_TAB2_3D))
914         {
915             if (nFlags & SCA_COL_ABSOLUTE)
916                 nFlags |= SCA_COL2_ABSOLUTE;
917             if (nFlags & SCA_ROW_ABSOLUTE)
918                 nFlags |= SCA_ROW2_ABSOLUTE;
919         }
920         else
921             nFlags &= ~(SCA_VALID |
922                     SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
923                     SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
924         return nFlags;
925     }
926 
927     p = tmp2;
928     p = lcl_eatWhiteSpace( p+1 );
929     tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 );
930     if( !tmp1 ) // strange, but valid singleton
931         return nFlags;
932 
933     tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
934     if( !tmp2 ) // strange, but valid singleton
935         return nFlags;
936 
937     if ( *tmp2 != 0 )
938     {
939         // any trailing invalid character must invalidate the range.
940         nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
941                     SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
942         return nFlags;
943     }
944 
945     nFlags |= (nFlags2 << 4);
946     return nFlags;
947 }
948 
949 /**
950     @param pRange   pointer to range where rAddr effectively is *pRange->aEnd,
951                     used in conjunction with pExtInfo to determine the tab span
952                     of a 3D reference.
953  */
954 static sal_uInt16
955 lcl_ScAddress_Parse_OOo( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
956                          ScAddress::ExternalInfo* pExtInfo = NULL, ScRange* pRange = NULL )
957 {
958     sal_uInt16  nRes = 0;
959     String  aDocName;       // der pure Dokumentenname
960     String  aTab;
961     bool    bExtDoc = false;
962     bool    bExtDocInherited = false;
963     const ScAddress aCurPos(rAddr);
964 
965     // Lets see if this is a reference to something in an external file.  A
966     // document name is always quoted and has a trailing #.
967     if (*p == '\'')
968     {
969         const sal_Unicode* pStart = p;
970         p = lcl_ParseQuotedName(p, aDocName);
971         if (*p++ == SC_COMPILER_FILE_TAB_SEP)
972             bExtDoc = true;
973         else
974             // This is not a document name.  Perhaps a quoted relative table
975             // name.
976             p = pStart;
977     }
978     else if (pExtInfo && pExtInfo->mbExternal)
979     {
980         // This is an external reference.
981         bExtDoc = bExtDocInherited = true;
982     }
983 
984     SCCOL   nCol = 0;
985     SCROW   nRow = 0;
986     SCTAB   nTab = 0;
987     sal_uInt16  nBits = SCA_VALID_TAB;
988     const sal_Unicode* q;
989     if ( ScGlobal::FindUnquoted( p, '.') )
990     {
991         nRes |= SCA_TAB_3D;
992         if ( bExtDoc )
993             nRes |= SCA_TAB_ABSOLUTE;
994         if (*p == '$')
995             nRes |= SCA_TAB_ABSOLUTE, p++;
996 
997         if (*p == '\'')
998         {
999             // Tokens that start at ' can have anything in them until a final
1000             // ' but '' marks an escaped '.  We've earlier guaranteed that a
1001             // string containing '' will be surrounded by '.
1002             p = lcl_ParseQuotedName(p, aTab);
1003         }
1004         else
1005         {
1006             while (*p)
1007             {
1008                 if( *p == '.')
1009                     break;
1010 
1011                 if( *p == '\'' )
1012                 {
1013                     p++; break;
1014                 }
1015                 aTab += *p++;
1016             }
1017         }
1018         if( *p++ != '.' )
1019             nBits = 0;
1020 
1021         if (!bExtDoc && (!pDoc || !pDoc->GetTable( aTab, nTab )))
1022             nBits = 0;
1023     }
1024     else
1025     {
1026         if (bExtDoc && !bExtDocInherited)
1027             return nRes;        // After a document a sheet must follow.
1028         nTab = rAddr.Tab();
1029     }
1030     nRes |= nBits;
1031 
1032     q = p;
1033     if (*p)
1034     {
1035         nBits = SCA_VALID_COL;
1036         if (*p == '$')
1037             nBits |= SCA_COL_ABSOLUTE, p++;
1038 
1039         if (CharClass::isAsciiAlpha( *p ))
1040         {
1041             nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
1042             while (nCol < MAXCOL && CharClass::isAsciiAlpha(*p))
1043                 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
1044         }
1045         else
1046             nBits = 0;
1047 
1048         if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) )
1049             nBits = 0;
1050         nRes |= nBits;
1051         if( !nBits )
1052             p = q;
1053     }
1054 
1055     q = p;
1056     if (*p)
1057     {
1058         nBits = SCA_VALID_ROW;
1059         if (*p == '$')
1060             nBits |= SCA_ROW_ABSOLUTE, p++;
1061         if( !CharClass::isAsciiDigit( *p ) )
1062         {
1063             nBits = 0;
1064             nRow = SCROW(-1);
1065         }
1066         else
1067         {
1068             String aTmp( p );
1069             long n = aTmp.ToInt32() - 1;
1070             while (CharClass::isAsciiDigit( *p ))
1071                 p++;
1072             if( n < 0 || n > MAXROW )
1073                 nBits = 0;
1074             nRow = static_cast<SCROW>(n);
1075         }
1076         nRes |= nBits;
1077         if( !nBits )
1078             p = q;
1079     }
1080 
1081     rAddr.Set( nCol, nRow, nTab );
1082 
1083     if (!*p && bExtDoc)
1084     {
1085         if (!pDoc)
1086             nRes = 0;
1087         else
1088         {
1089             ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
1090 
1091             // Need document name if inherited.
1092             if (bExtDocInherited)
1093             {
1094                 const String* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId);
1095                 if (pFileName)
1096                     aDocName = *pFileName;
1097                 else
1098                     nRes = 0;
1099             }
1100             pRefMgr->convertToAbsName(aDocName);
1101 
1102             if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
1103             {
1104                 if (!pDoc->GetTable( aTab, nTab ))
1105                     nRes = 0;
1106                 else
1107                 {
1108                     rAddr.SetTab( nTab);
1109                     nRes |= SCA_VALID_TAB;
1110                 }
1111             }
1112             else
1113             {
1114                 if (!pExtInfo)
1115                     nRes = 0;
1116                 else
1117                 {
1118                     if (!pExtInfo->mbExternal)
1119                     {
1120                         sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
1121 
1122                         pExtInfo->mbExternal = true;
1123                         pExtInfo->maTabName = aTab;
1124                         pExtInfo->mnFileId = nFileId;
1125 
1126                         if (pRefMgr->getSingleRefToken(nFileId, aTab,
1127                                     ScAddress(nCol, nRow, 0), NULL,
1128                                     &nTab).get())
1129                         {
1130                             rAddr.SetTab( nTab);
1131                             nRes |= SCA_VALID_TAB;
1132                         }
1133                         else
1134                             nRes = 0;
1135                     }
1136                     else
1137                     {
1138                         // This is a call for the second part of the reference,
1139                         // we must have the range to adapt tab span.
1140                         if (!pRange)
1141                             nRes = 0;
1142                         else
1143                         {
1144                             sal_uInt16 nFlags = nRes | SCA_VALID_TAB2;
1145                             if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
1146                                         pExtInfo, aDocName,
1147                                         pExtInfo->maTabName, aTab, pDoc))
1148                                 nRes &= ~SCA_VALID_TAB;
1149                             else
1150                             {
1151                                 if (nFlags & SCA_VALID_TAB2)
1152                                 {
1153                                     rAddr.SetTab( pRange->aEnd.Tab());
1154                                     nRes |= SCA_VALID_TAB;
1155                                 }
1156                                 else
1157                                     nRes &= ~SCA_VALID_TAB;
1158                             }
1159                         }
1160                     }
1161                 }
1162             }
1163         }
1164     }
1165 
1166     if ( !(nRes & SCA_VALID_ROW) && (nRes & SCA_VALID_COL)
1167             && !( (nRes & SCA_TAB_3D) && (nRes & SCA_VALID_TAB)) )
1168     {   // no Row, no Tab, but Col => DM (...), B (...) et al
1169         nRes = 0;
1170     }
1171     if( !*p )
1172     {
1173         sal_uInt16 nMask = nRes & ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1174         if( nMask == ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ) )
1175             nRes |= SCA_VALID;
1176     }
1177     else
1178         nRes = 0;
1179     return nRes;
1180 }
1181 
1182 static sal_uInt16
1183 lcl_ScAddress_Parse ( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
1184                       const ScAddress::Details& rDetails,
1185                       ScAddress::ExternalInfo* pExtInfo = NULL,
1186                       const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL )
1187 {
1188     if( !*p )
1189         return 0;
1190 
1191     switch (rDetails.eConv)
1192     {
1193     default :
1194     case formula::FormulaGrammar::CONV_OOO:
1195         {
1196             return lcl_ScAddress_Parse_OOo( p, pDoc, rAddr, pExtInfo, NULL );
1197         }
1198 
1199     case formula::FormulaGrammar::CONV_XL_A1:
1200     case formula::FormulaGrammar::CONV_XL_OOX:
1201         {
1202             ScRange r = rAddr;
1203             sal_uInt16 nFlags = lcl_ScRange_Parse_XL_A1( r, p, pDoc, true, pExtInfo,
1204                     (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1205             rAddr = r.aStart;
1206             return nFlags;
1207         }
1208     case formula::FormulaGrammar::CONV_XL_R1C1:
1209         {
1210             ScRange r = rAddr;
1211             sal_uInt16 nFlags = lcl_ScRange_Parse_XL_R1C1( r, p, pDoc, rDetails, true, pExtInfo );
1212             rAddr = r.aStart;
1213             return nFlags;
1214         }
1215     }
1216 }
1217 
1218 
1219 bool ConvertSingleRef( ScDocument* pDoc, const String& rRefString,
1220                        SCTAB nDefTab, ScRefAddress& rRefAddress,
1221                        const ScAddress::Details& rDetails,
1222                        ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1223 {
1224     bool bRet = false;
1225     if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND))
1226     {
1227         ScAddress aAddr( 0, 0, nDefTab );
1228         sal_uInt16 nRes = aAddr.Parse( rRefString, pDoc, rDetails, pExtInfo);
1229         if ( nRes & SCA_VALID )
1230         {
1231             rRefAddress.Set( aAddr,
1232                     ((nRes & SCA_COL_ABSOLUTE) == 0),
1233                     ((nRes & SCA_ROW_ABSOLUTE) == 0),
1234                     ((nRes & SCA_TAB_ABSOLUTE) == 0));
1235             bRet = true;
1236         }
1237     }
1238     return bRet;
1239 }
1240 
1241 
1242 bool ConvertDoubleRef( ScDocument* pDoc, const String& rRefString, SCTAB nDefTab,
1243                        ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1244                        const ScAddress::Details& rDetails,
1245                        ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1246 {
1247     bool bRet = false;
1248     if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND))
1249     {
1250         ScRange aRange( ScAddress( 0, 0, nDefTab));
1251         sal_uInt16 nRes = aRange.Parse( rRefString, pDoc, rDetails, pExtInfo);
1252         if ( nRes & SCA_VALID )
1253         {
1254             rStartRefAddress.Set( aRange.aStart,
1255                     ((nRes & SCA_COL_ABSOLUTE) == 0),
1256                     ((nRes & SCA_ROW_ABSOLUTE) == 0),
1257                     ((nRes & SCA_TAB_ABSOLUTE) == 0));
1258             rEndRefAddress.Set( aRange.aEnd,
1259                     ((nRes & SCA_COL2_ABSOLUTE) == 0),
1260                     ((nRes & SCA_ROW2_ABSOLUTE) == 0),
1261                     ((nRes & SCA_TAB2_ABSOLUTE) == 0));
1262             bRet = true;
1263         }
1264     }
1265     return bRet;
1266 }
1267 
1268 
1269 sal_uInt16 ScAddress::Parse( const String& r, ScDocument* pDoc,
1270                          const Details& rDetails,
1271                          ExternalInfo* pExtInfo,
1272                          const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
1273 {
1274     return lcl_ScAddress_Parse( r.GetBuffer(), pDoc, *this, rDetails, pExtInfo, pExternalLinks );
1275 }
1276 
1277 
1278 bool ScRange::Intersects( const ScRange& r ) const
1279 {
1280     return !(
1281         Min( aEnd.Col(), r.aEnd.Col() ) < Max( aStart.Col(), r.aStart.Col() )
1282      || Min( aEnd.Row(), r.aEnd.Row() ) < Max( aStart.Row(), r.aStart.Row() )
1283      || Min( aEnd.Tab(), r.aEnd.Tab() ) < Max( aStart.Tab(), r.aStart.Tab() )
1284         );
1285 }
1286 
1287 
1288 void ScRange::Justify()
1289 {
1290     SCCOL nTempCol;
1291     if ( aEnd.Col() < (nTempCol = aStart.Col()) )
1292     {
1293         aStart.SetCol(aEnd.Col()); aEnd.SetCol(nTempCol);
1294     }
1295     SCROW nTempRow;
1296     if ( aEnd.Row() < (nTempRow = aStart.Row()) )
1297     {
1298         aStart.SetRow(aEnd.Row()); aEnd.SetRow(nTempRow);
1299     }
1300     SCTAB nTempTab;
1301     if ( aEnd.Tab() < (nTempTab = aStart.Tab()) )
1302     {
1303         aStart.SetTab(aEnd.Tab()); aEnd.SetTab(nTempTab);
1304     }
1305 }
1306 
1307 void ScRange::ExtendTo( const ScRange& rRange )
1308 {
1309     DBG_ASSERT( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1310     if( IsValid() )
1311     {
1312         aStart.SetCol( ::std::min( aStart.Col(), rRange.aStart.Col() ) );
1313         aStart.SetRow( ::std::min( aStart.Row(), rRange.aStart.Row() ) );
1314         aStart.SetTab( ::std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1315         aEnd.SetCol(   ::std::max( aEnd.Col(),   rRange.aEnd.Col() ) );
1316         aEnd.SetRow(   ::std::max( aEnd.Row(),   rRange.aEnd.Row() ) );
1317         aEnd.SetTab(   ::std::max( aEnd.Tab(),   rRange.aEnd.Tab() ) );
1318     }
1319     else
1320         *this = rRange;
1321 }
1322 
1323 static sal_uInt16
1324 lcl_ScRange_Parse_OOo( ScRange &aRange, const String& r, ScDocument* pDoc, ScAddress::ExternalInfo* pExtInfo = NULL )
1325 {
1326     sal_uInt16 nRes1 = 0, nRes2 = 0;
1327     xub_StrLen nPos = ScGlobal::FindUnquoted( r, ':');
1328     if (nPos != STRING_NOTFOUND)
1329     {
1330         String aTmp( r );
1331         sal_Unicode* p = aTmp.GetBufferAccess();
1332         p[ nPos ] = 0;
1333         if( (nRes1 = lcl_ScAddress_Parse_OOo( p, pDoc, aRange.aStart, pExtInfo, NULL ) ) != 0 )
1334         {
1335             aRange.aEnd = aRange.aStart;  // sheet must be initialized identical to first sheet
1336             if ( (nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, pDoc, aRange.aEnd, pExtInfo, &aRange ) ) != 0 )
1337             {
1338                 // PutInOrder / Justify
1339                 sal_uInt16 nMask, nBits1, nBits2;
1340                 SCCOL nTempCol;
1341                 if ( aRange.aEnd.Col() < (nTempCol = aRange.aStart.Col()) )
1342                 {
1343                     aRange.aStart.SetCol(aRange.aEnd.Col()); aRange.aEnd.SetCol(nTempCol);
1344                     nMask = (SCA_VALID_COL | SCA_COL_ABSOLUTE);
1345                     nBits1 = nRes1 & nMask;
1346                     nBits2 = nRes2 & nMask;
1347                     nRes1 = (nRes1 & ~nMask) | nBits2;
1348                     nRes2 = (nRes2 & ~nMask) | nBits1;
1349                 }
1350                 SCROW nTempRow;
1351                 if ( aRange.aEnd.Row() < (nTempRow = aRange.aStart.Row()) )
1352                 {
1353                     aRange.aStart.SetRow(aRange.aEnd.Row()); aRange.aEnd.SetRow(nTempRow);
1354                     nMask = (SCA_VALID_ROW | SCA_ROW_ABSOLUTE);
1355                     nBits1 = nRes1 & nMask;
1356                     nBits2 = nRes2 & nMask;
1357                     nRes1 = (nRes1 & ~nMask) | nBits2;
1358                     nRes2 = (nRes2 & ~nMask) | nBits1;
1359                 }
1360                 SCTAB nTempTab;
1361                 if ( aRange.aEnd.Tab() < (nTempTab = aRange.aStart.Tab()) )
1362                 {
1363                     aRange.aStart.SetTab(aRange.aEnd.Tab()); aRange.aEnd.SetTab(nTempTab);
1364                     nMask = (SCA_VALID_TAB | SCA_TAB_ABSOLUTE | SCA_TAB_3D);
1365                     nBits1 = nRes1 & nMask;
1366                     nBits2 = nRes2 & nMask;
1367                     nRes1 = (nRes1 & ~nMask) | nBits2;
1368                     nRes2 = (nRes2 & ~nMask) | nBits1;
1369                 }
1370                 if ( ((nRes1 & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1371                         == ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1372                         && !(nRes2 & SCA_TAB_3D) )
1373                     nRes2 |= SCA_TAB_ABSOLUTE;
1374             }
1375             else
1376                 nRes1 = 0;      // #38840# keine Tokens aus halben Sachen
1377         }
1378     }
1379     nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID )
1380           | nRes1
1381           | ( ( nRes2 & 0x070F ) << 4 );
1382     return nRes1;
1383 }
1384 
1385 sal_uInt16 ScRange::Parse( const String& r, ScDocument* pDoc,
1386                        const ScAddress::Details& rDetails,
1387                        ScAddress::ExternalInfo* pExtInfo,
1388                        const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks )
1389 {
1390     if ( r.Len() <= 0 )
1391         return 0;
1392 
1393     switch (rDetails.eConv)
1394     {
1395     default :
1396     case formula::FormulaGrammar::CONV_OOO:
1397         return lcl_ScRange_Parse_OOo( *this, r, pDoc, pExtInfo );
1398 
1399     case formula::FormulaGrammar::CONV_XL_A1:
1400     case formula::FormulaGrammar::CONV_XL_OOX:
1401         return lcl_ScRange_Parse_XL_A1( *this, r.GetBuffer(), pDoc, false, pExtInfo,
1402                 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1403 
1404     case formula::FormulaGrammar::CONV_XL_R1C1:
1405         return lcl_ScRange_Parse_XL_R1C1( *this, r.GetBuffer(), pDoc, rDetails, false, pExtInfo );
1406     }
1407 }
1408 
1409 
1410 // Accept a full range, or an address
1411 sal_uInt16 ScRange::ParseAny( const String& r, ScDocument* pDoc,
1412                           const ScAddress::Details& rDetails )
1413 {
1414     sal_uInt16 nRet = Parse( r, pDoc, rDetails );
1415     const sal_uInt16 nValid = SCA_VALID | SCA_VALID_COL2 | SCA_VALID_ROW2 |
1416         SCA_VALID_TAB2;
1417 
1418     if ( (nRet & nValid) != nValid )
1419     {
1420         ScAddress aAdr;
1421         nRet = aAdr.Parse( r, pDoc, rDetails );
1422         if ( nRet & SCA_VALID )
1423             aStart = aEnd = aAdr;
1424     }
1425     return nRet;
1426 }
1427 
1428 // Parse only full row references
1429 sal_uInt16 ScRange::ParseCols( const String& rStr, ScDocument* pDoc,
1430                            const ScAddress::Details& rDetails )
1431 {
1432     const sal_Unicode* p = rStr.GetBuffer();
1433     sal_uInt16 nRes = 0, ignored = 0;
1434 
1435     if( NULL == p )
1436         return 0;
1437 
1438     pDoc = NULL; // make compiler shutup we may need this later
1439 
1440     switch (rDetails.eConv)
1441     {
1442     default :
1443     case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1444     case formula::FormulaGrammar::CONV_XL_A1:
1445     case formula::FormulaGrammar::CONV_XL_OOX:
1446         if (NULL != (p = lcl_a1_get_col( p, &aStart, &ignored ) ) )
1447         {
1448             if( p[0] == ':')
1449             {
1450                 if( NULL != (p = lcl_a1_get_col( p+1, &aEnd, &ignored )))
1451                 {
1452                     nRes = SCA_VALID_COL;
1453                 }
1454             }
1455             else
1456             {
1457                 aEnd = aStart;
1458                 nRes = SCA_VALID_COL;
1459             }
1460         }
1461         break;
1462 
1463     case formula::FormulaGrammar::CONV_XL_R1C1:
1464         if ((p[0] == 'C' || p[0] != 'c') &&
1465             NULL != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored )))
1466         {
1467             if( p[0] == ':')
1468             {
1469                 if( (p[1] == 'C' || p[1] == 'c') &&
1470                     NULL != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored )))
1471                 {
1472                     nRes = SCA_VALID_COL;
1473                 }
1474             }
1475             else
1476             {
1477                 aEnd = aStart;
1478                 nRes = SCA_VALID_COL;
1479             }
1480         }
1481         break;
1482     }
1483 
1484     return (p != NULL && *p == '\0') ? nRes : 0;
1485 }
1486 
1487 // Parse only full row references
1488 sal_uInt16 ScRange::ParseRows( const String& rStr, ScDocument* pDoc,
1489                            const ScAddress::Details& rDetails )
1490 {
1491     const sal_Unicode* p = rStr.GetBuffer();
1492     sal_uInt16 nRes = 0, ignored = 0;
1493 
1494     if( NULL == p )
1495         return 0;
1496 
1497     pDoc = NULL; // make compiler shutup we may need this later
1498 
1499     switch (rDetails.eConv)
1500     {
1501     default :
1502     case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1503     case formula::FormulaGrammar::CONV_XL_A1:
1504     case formula::FormulaGrammar::CONV_XL_OOX:
1505         if (NULL != (p = lcl_a1_get_row( p, &aStart, &ignored ) ) )
1506         {
1507             if( p[0] == ':')
1508             {
1509                 if( NULL != (p = lcl_a1_get_row( p+1, &aEnd, &ignored )))
1510                 {
1511                     nRes = SCA_VALID_COL;
1512                 }
1513             }
1514             else
1515             {
1516                 aEnd = aStart;
1517                 nRes = SCA_VALID_COL;
1518             }
1519         }
1520         break;
1521 
1522     case formula::FormulaGrammar::CONV_XL_R1C1:
1523         if ((p[0] == 'R' || p[0] != 'r') &&
1524             NULL != (p = lcl_r1c1_get_row( p, rDetails, &aStart, &ignored )))
1525         {
1526             if( p[0] == ':')
1527             {
1528                 if( (p[1] == 'R' || p[1] == 'r') &&
1529                     NULL != (p = lcl_r1c1_get_row( p+1, rDetails, &aEnd, &ignored )))
1530                 {
1531                     nRes = SCA_VALID_COL;
1532                 }
1533             }
1534             else
1535             {
1536                 aEnd = aStart;
1537                 nRes = SCA_VALID_COL;
1538             }
1539         }
1540         break;
1541     }
1542 
1543     return (p != NULL && *p == '\0') ? nRes : 0;
1544 }
1545 
1546 static inline void
1547 lcl_a1_append_c ( String &r, int nCol, bool bIsAbs )
1548 {
1549     if( bIsAbs )
1550         r += '$';
1551     ScColToAlpha( r, sal::static_int_cast<SCCOL>(nCol) );
1552 }
1553 
1554 static inline void
1555 lcl_a1_append_r ( String &r, int nRow, bool bIsAbs )
1556 {
1557     if ( bIsAbs )
1558         r += '$';
1559     r += String::CreateFromInt32( nRow+1 );
1560 }
1561 
1562 static inline void
1563 lcl_r1c1_append_c ( String &r, int nCol, bool bIsAbs,
1564                     const ScAddress::Details& rDetails )
1565 {
1566     r += 'C';
1567     if (bIsAbs)
1568     {
1569         r += String::CreateFromInt32( nCol + 1 );
1570     }
1571     else
1572     {
1573         nCol -= rDetails.nCol;
1574         if (nCol != 0) {
1575             r += '[';
1576             r += String::CreateFromInt32( nCol );
1577             r += ']';
1578         }
1579     }
1580 }
1581 static inline void
1582 lcl_r1c1_append_r ( String &r, int nRow, bool bIsAbs,
1583                     const ScAddress::Details& rDetails )
1584 {
1585     r += 'R';
1586     if (bIsAbs)
1587     {
1588         r += String::CreateFromInt32( nRow + 1 );
1589     }
1590     else
1591     {
1592         nRow -= rDetails.nRow;
1593         if (nRow != 0) {
1594             r += '[';
1595             r += String::CreateFromInt32( nRow );
1596             r += ']';
1597         }
1598     }
1599 }
1600 
1601 static String
1602 getFileNameFromDoc( const ScDocument* pDoc )
1603 {
1604     // TODO : er points at ScGlobal::GetAbsDocName()
1605     // as a better template.  Look into it
1606     String sFileName;
1607     SfxObjectShell* pShell;
1608 
1609     if( NULL != pDoc &&
1610         NULL != (pShell = pDoc->GetDocumentShell() ) )
1611     {
1612         uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY );
1613         if( xModel.is() )
1614         {
1615             if( xModel->getURL().getLength() )
1616             {
1617                 INetURLObject aURL( xModel->getURL() );
1618                 sFileName = aURL.GetLastName();
1619             }
1620             else
1621                 sFileName = pShell->GetTitle();
1622         }
1623     }
1624 #if 0
1625         {
1626             ByteString  aStr( sFileName, RTL_TEXTENCODING_UTF8 );
1627             aStr.Append(static_cast< char >(0));
1628             std::cerr << "docname \'" << aStr.GetBuffer() << '\'' << std::endl;
1629         }
1630 #endif
1631     return sFileName;
1632 }
1633 
1634 void ScAddress::Format( String& r, sal_uInt16 nFlags, ScDocument* pDoc,
1635                         const Details& rDetails) const
1636 {
1637     r.Erase();
1638     if( nFlags & SCA_VALID )
1639         nFlags |= ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1640     if( pDoc && (nFlags & SCA_VALID_TAB ) )
1641     {
1642         if ( nTab >= pDoc->GetTableCount() )
1643         {
1644             r = ScGlobal::GetRscString( STR_NOREF_STR );
1645             return;
1646         }
1647 //      if( nFlags & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ) )
1648         if( nFlags & SCA_TAB_3D )
1649         {
1650             String aTabName, aDocName;
1651             pDoc->GetName( nTab, aTabName );
1652             // External Reference, same as in ScCompiler::MakeTabStr()
1653             if( aTabName.GetChar(0) == '\'' )
1654             {   // "'Doc'#Tab"
1655                 xub_StrLen nPos = ScCompiler::GetDocTabPos( aTabName);
1656                 if (nPos != STRING_NOTFOUND)
1657                 {
1658                     aDocName = aTabName.Copy( 0, nPos + 1 );
1659                     aTabName.Erase( 0, nPos + 1 );
1660                 }
1661             }
1662             else if( nFlags & SCA_FORCE_DOC )
1663             {
1664                 // VBA has an 'external' flag that forces the addition of the
1665                 // tab name _and_ the doc name.  The VBA code would be
1666                 // needlessly complicated if it constructed an actual external
1667                 // reference so we add this somewhat cheesy kludge to force the
1668                 // addition of the document name even for non-external references
1669                 aDocName = getFileNameFromDoc( pDoc );
1670             }
1671             ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
1672 
1673             switch( rDetails.eConv )
1674             {
1675             default :
1676             case formula::FormulaGrammar::CONV_OOO:
1677                 r += aDocName;
1678                 if( nFlags & SCA_TAB_ABSOLUTE )
1679                     r += '$';
1680                 r += aTabName;
1681                 r += '.';
1682                 break;
1683 
1684             case formula::FormulaGrammar::CONV_XL_A1:
1685             case formula::FormulaGrammar::CONV_XL_R1C1:
1686             case formula::FormulaGrammar::CONV_XL_OOX:
1687                 if (aDocName.Len() > 0)
1688                 {
1689                     r += '[';
1690                     r += aDocName;
1691                     r += ']';
1692                 }
1693                 r += aTabName;
1694                 r += '!';
1695                 break;
1696             }
1697         }
1698     }
1699     switch( rDetails.eConv )
1700     {
1701     default :
1702     case formula::FormulaGrammar::CONV_OOO:
1703     case formula::FormulaGrammar::CONV_XL_A1:
1704     case formula::FormulaGrammar::CONV_XL_OOX:
1705         if( nFlags & SCA_VALID_COL )
1706             lcl_a1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE );
1707         if( nFlags & SCA_VALID_ROW )
1708             lcl_a1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE );
1709         break;
1710 
1711     case formula::FormulaGrammar::CONV_XL_R1C1:
1712         if( nFlags & SCA_VALID_ROW )
1713             lcl_r1c1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE, rDetails );
1714         if( nFlags & SCA_VALID_COL )
1715             lcl_r1c1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE, rDetails );
1716         break;
1717     }
1718 }
1719 
1720 static void
1721 lcl_Split_DocTab( const ScDocument* pDoc,  SCTAB nTab,
1722                   const ScAddress::Details& rDetails,
1723                   sal_uInt16 nFlags,
1724                   String& rTabName, String& rDocName )
1725 {
1726     pDoc->GetName( nTab, rTabName );
1727     rDocName.Erase();
1728 #if 0
1729     {
1730         ByteString  aStr(rTabName, RTL_TEXTENCODING_UTF8);
1731         aStr.Append(static_cast< char >(0));
1732         std::cerr << "tabname \'" << aStr.GetBuffer() << '\'' << std::endl;
1733     }
1734 #endif
1735     // External reference, same as in ScCompiler::MakeTabStr()
1736     if ( rTabName.GetChar(0) == '\'' )
1737     {   // "'Doc'#Tab"
1738         xub_StrLen nPos = ScCompiler::GetDocTabPos( rTabName);
1739         if (nPos != STRING_NOTFOUND)
1740         {
1741             rDocName = rTabName.Copy( 0, nPos + 1 );
1742             rTabName.Erase( 0, nPos + 1 );
1743         }
1744     }
1745     else if( nFlags & SCA_FORCE_DOC )
1746     {
1747         // VBA has an 'external' flag that forces the addition of the
1748         // tab name _and_ the doc name.  The VBA code would be
1749         // needlessly complicated if it constructed an actual external
1750         // reference so we add this somewhat cheesy kludge to force the
1751         // addition of the document name even for non-external references
1752         rDocName = getFileNameFromDoc( pDoc );
1753     }
1754     ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
1755 }
1756 
1757 static void
1758 lcl_ScRange_Format_XL_Header( String& r, const ScRange& rRange,
1759                               sal_uInt16 nFlags, ScDocument* pDoc,
1760                               const ScAddress::Details& rDetails )
1761 {
1762     if( nFlags & SCA_TAB_3D )
1763     {
1764         String aTabName, aDocName;
1765         lcl_Split_DocTab( pDoc, rRange.aStart.Tab(), rDetails, nFlags,
1766                           aTabName, aDocName );
1767         if( aDocName.Len() > 0 )
1768         {
1769             r += '[';
1770             r += aDocName;
1771             r += ']';
1772         }
1773         r += aTabName;
1774 
1775         if( nFlags & SCA_TAB2_3D )
1776         {
1777             lcl_Split_DocTab( pDoc, rRange.aEnd.Tab(), rDetails, nFlags,
1778                               aTabName, aDocName );
1779             r += ':';
1780             r += aTabName;
1781         }
1782         r += '!';
1783     }
1784 }
1785 
1786 void ScRange::Format( String& r, sal_uInt16 nFlags, ScDocument* pDoc,
1787                       const ScAddress::Details& rDetails ) const
1788 {
1789     r.Erase();
1790     if( !( nFlags & SCA_VALID ) )
1791     {
1792         r = ScGlobal::GetRscString( STR_NOREF_STR );
1793         return;
1794     }
1795 
1796 #define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask)))
1797     switch( rDetails.eConv ) {
1798     default :
1799     case formula::FormulaGrammar::CONV_OOO: {
1800         sal_Bool bOneTab = (aStart.Tab() == aEnd.Tab());
1801         if ( !bOneTab )
1802             nFlags |= SCA_TAB_3D;
1803         aStart.Format( r, nFlags, pDoc, rDetails );
1804         if( aStart != aEnd ||
1805             absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1806             absrel_differ( nFlags, SCA_ROW_ABSOLUTE ))
1807         {
1808             String aName;
1809             nFlags = ( nFlags & SCA_VALID ) | ( ( nFlags >> 4 ) & 0x070F );
1810             if ( bOneTab )
1811                 pDoc = NULL;
1812             else
1813                 nFlags |= SCA_TAB_3D;
1814             aEnd.Format( aName, nFlags, pDoc, rDetails );
1815             r += ':';
1816             r += aName;
1817         }
1818     }
1819     break;
1820 
1821     case formula::FormulaGrammar::CONV_XL_A1:
1822     case formula::FormulaGrammar::CONV_XL_OOX:
1823         lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1824         if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1825         {
1826             // Full col refs always require 2 rows (2:2)
1827             lcl_a1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE );
1828             r += ':';
1829             lcl_a1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE );
1830         }
1831         else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1832         {
1833             // Full row refs always require 2 cols (A:A)
1834             lcl_a1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE );
1835             r += ':';
1836             lcl_a1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE );
1837         }
1838         else
1839         {
1840             lcl_a1_append_c ( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE );
1841             lcl_a1_append_r ( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE );
1842             if( aStart.Col() != aEnd.Col() ||
1843                 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1844                 aStart.Row() != aEnd.Row() ||
1845                 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1846                 r += ':';
1847                 lcl_a1_append_c ( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE );
1848                 lcl_a1_append_r ( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE );
1849             }
1850         }
1851     break;
1852 
1853     case formula::FormulaGrammar::CONV_XL_R1C1:
1854         lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
1855         if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
1856         {
1857             lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails );
1858             if( aStart.Row() != aEnd.Row() ||
1859                 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1860                 r += ':';
1861                 lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails );
1862             }
1863         }
1864         else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
1865         {
1866             lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails );
1867             if( aStart.Col() != aEnd.Col() ||
1868                 absrel_differ( nFlags, SCA_COL_ABSOLUTE )) {
1869                 r += ':';
1870                 lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails );
1871             }
1872         }
1873         else
1874         {
1875             lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails );
1876             lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails );
1877             if( aStart.Col() != aEnd.Col() ||
1878                 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
1879                 aStart.Row() != aEnd.Row() ||
1880                 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
1881                 r += ':';
1882                 lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails );
1883                 lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails );
1884             }
1885         }
1886     }
1887 #undef  absrel_differ
1888 }
1889 
1890 bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1891 {
1892     SCsTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB+1;
1893     dx = Col() + dx;
1894     dy = Row() + dy;
1895     dz = Tab() + dz;
1896     sal_Bool bValid = sal_True;
1897     if( dx < 0 )
1898         dx = 0, bValid = sal_False;
1899     else if( dx > MAXCOL )
1900         dx = MAXCOL, bValid =sal_False;
1901     if( dy < 0 )
1902         dy = 0, bValid = sal_False;
1903     else if( dy > MAXROW )
1904         dy = MAXROW, bValid =sal_False;
1905     if( dz < 0 )
1906         dz = 0, bValid = sal_False;
1907     else if( dz >= nMaxTab )
1908         dz = nMaxTab-1, bValid =sal_False;
1909     Set( dx, dy, dz );
1910     return bValid;
1911 }
1912 
1913 
1914 bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
1915 {
1916     // Einfahces &, damit beides ausgefuehrt wird!!
1917     return aStart.Move( dx, dy, dz, pDoc ) & aEnd.Move( dx, dy, dz, pDoc );
1918 }
1919 
1920 
1921 String ScAddress::GetColRowString( bool bAbsolute,
1922                                    const Details& rDetails ) const
1923 {
1924     String aString;
1925 
1926     switch( rDetails.eConv )
1927     {
1928     default :
1929     case formula::FormulaGrammar::CONV_OOO:
1930     case formula::FormulaGrammar::CONV_XL_A1:
1931     case formula::FormulaGrammar::CONV_XL_OOX:
1932     if (bAbsolute)
1933         aString.Append( '$' );
1934 
1935     ScColToAlpha( aString, nCol);
1936 
1937     if ( bAbsolute )
1938         aString.Append( '$' );
1939 
1940     aString += String::CreateFromInt32(nRow+1);
1941         break;
1942 
1943     case formula::FormulaGrammar::CONV_XL_R1C1:
1944         lcl_r1c1_append_r ( aString, nRow, bAbsolute, rDetails );
1945         lcl_r1c1_append_c ( aString, nCol, bAbsolute, rDetails );
1946         break;
1947     }
1948 
1949     return aString;
1950 }
1951 
1952 
1953 String ScRefAddress::GetRefString( ScDocument* pDoc, SCTAB nActTab,
1954                                    const ScAddress::Details& rDetails ) const
1955 {
1956     if ( !pDoc )
1957         return EMPTY_STRING;
1958     if ( Tab()+1 > pDoc->GetTableCount() )
1959         return ScGlobal::GetRscString( STR_NOREF_STR );
1960 
1961     String aString;
1962     sal_uInt16 nFlags = SCA_VALID;
1963     if ( nActTab != Tab() )
1964     {
1965         nFlags |= SCA_TAB_3D;
1966         if ( !bRelTab )
1967             nFlags |= SCA_TAB_ABSOLUTE;
1968     }
1969     if ( !bRelCol )
1970         nFlags |= SCA_COL_ABSOLUTE;
1971     if ( !bRelRow )
1972         nFlags |= SCA_ROW_ABSOLUTE;
1973 
1974     aAdr.Format( aString, nFlags, pDoc, rDetails );
1975 
1976     return aString;
1977 }
1978 
1979 //------------------------------------------------------------------------
1980 
1981 void ScColToAlpha( rtl::OUStringBuffer& rBuf, SCCOL nCol )
1982 {
1983     if (nCol < 26*26)
1984     {
1985         if (nCol < 26)
1986             rBuf.append( static_cast<sal_Unicode>( 'A' +
1987                         static_cast<sal_uInt16>(nCol)));
1988         else
1989         {
1990             rBuf.append( static_cast<sal_Unicode>( 'A' +
1991                         (static_cast<sal_uInt16>(nCol) / 26) - 1));
1992             rBuf.append( static_cast<sal_Unicode>( 'A' +
1993                         (static_cast<sal_uInt16>(nCol) % 26)));
1994         }
1995     }
1996     else
1997     {
1998         String aStr;
1999         while (nCol >= 26)
2000         {
2001             SCCOL nC = nCol % 26;
2002             aStr += static_cast<sal_Unicode>( 'A' +
2003                     static_cast<sal_uInt16>(nC));
2004             nCol = sal::static_int_cast<SCCOL>( nCol - nC );
2005             nCol = nCol / 26 - 1;
2006         }
2007         aStr += static_cast<sal_Unicode>( 'A' +
2008                 static_cast<sal_uInt16>(nCol));
2009         aStr.Reverse();
2010         rBuf.append( aStr);
2011     }
2012 }
2013 
2014 
2015 bool AlphaToCol( SCCOL& rCol, const String& rStr)
2016 {
2017     SCCOL nResult = 0;
2018     xub_StrLen nStop = rStr.Len();
2019     xub_StrLen nPos = 0;
2020     sal_Unicode c;
2021     while (nResult <= MAXCOL && nPos < nStop && (c = rStr.GetChar( nPos)) != 0 &&
2022             CharClass::isAsciiAlpha(c))
2023     {
2024         if (nPos > 0)
2025             nResult = (nResult + 1) * 26;
2026         nResult += ScGlobal::ToUpperAlpha(c) - 'A';
2027         ++nPos;
2028     }
2029     bool bOk = (ValidCol(nResult) && nPos > 0);
2030     if (bOk)
2031         rCol = nResult;
2032     return bOk;
2033 }
2034