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