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 #ifdef _MSC_VER 27 #pragma optimize("",off) 28 #endif 29 30 //------------------------------------------------------------------ 31 32 33 34 // INCLUDE --------------------------------------------------------- 35 36 #include <sfx2/sfxsids.hrc> 37 #include <sfx2/app.hxx> 38 #include <svl/itemset.hxx> 39 #include <svl/stritem.hxx> 40 #include <sfx2/docfile.hxx> 41 #include <sfx2/docfilt.hxx> 42 #include <sfx2/fcontnr.hxx> 43 #include <sfx2/linkmgr.hxx> 44 #include <tools/urlobj.hxx> 45 #include <unotools/transliterationwrapper.hxx> 46 47 #include "tablink.hxx" 48 49 #include "scextopt.hxx" 50 #include "table.hxx" 51 #include "document.hxx" 52 #include "docsh.hxx" 53 #include "globstr.hrc" 54 #include "undoblk.hxx" 55 #include "undotab.hxx" 56 #include "global.hxx" 57 #include "hints.hxx" 58 #include "cell.hxx" 59 #include "dociter.hxx" 60 #include "formula/opcode.hxx" 61 62 struct TableLink_Impl 63 { 64 ScDocShell* m_pDocSh; 65 Window* m_pOldParent; 66 Link m_aEndEditLink; 67 68 TableLink_Impl() : m_pDocSh( NULL ), m_pOldParent( NULL ) {} 69 }; 70 71 TYPEINIT1(ScTableLink, ::sfx2::SvBaseLink); 72 73 //------------------------------------------------------------------------ 74 75 ScTableLink::ScTableLink(ScDocShell* pDocSh, const String& rFile, 76 const String& rFilter, const String& rOpt, 77 sal_uLong nRefresh ): 78 ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL,FORMAT_FILE), 79 ScRefreshTimer( nRefresh ), 80 pImpl( new TableLink_Impl ), 81 aFileName(rFile), 82 aFilterName(rFilter), 83 aOptions(rOpt), 84 bInCreate( sal_False ), 85 bInEdit( sal_False ), 86 bAddUndo( sal_True ), 87 bDoPaint( sal_True ) 88 { 89 pImpl->m_pDocSh = pDocSh; 90 } 91 92 ScTableLink::ScTableLink(SfxObjectShell* pShell, const String& rFile, 93 const String& rFilter, const String& rOpt, 94 sal_uLong nRefresh ): 95 ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL,FORMAT_FILE), 96 ScRefreshTimer( nRefresh ), 97 pImpl( new TableLink_Impl ), 98 aFileName(rFile), 99 aFilterName(rFilter), 100 aOptions(rOpt), 101 bInCreate( sal_False ), 102 bInEdit( sal_False ), 103 bAddUndo( sal_True ), 104 bDoPaint( sal_True ) 105 { 106 pImpl->m_pDocSh = static_cast< ScDocShell* >( pShell ); 107 SetRefreshHandler( LINK( this, ScTableLink, RefreshHdl ) ); 108 SetRefreshControl( pImpl->m_pDocSh->GetDocument()->GetRefreshTimerControlAddress() ); 109 } 110 111 __EXPORT ScTableLink::~ScTableLink() 112 { 113 // Verbindung aufheben 114 115 StopRefreshTimer(); 116 String aEmpty; 117 ScDocument* pDoc = pImpl->m_pDocSh->GetDocument(); 118 SCTAB nCount = pDoc->GetTableCount(); 119 for (SCTAB nTab=0; nTab<nCount; nTab++) 120 if (pDoc->IsLinked(nTab) && pDoc->GetLinkDoc(nTab)==aFileName) 121 pDoc->SetLink( nTab, SC_LINK_NONE, aEmpty, aEmpty, aEmpty, aEmpty, 0 ); 122 delete pImpl; 123 } 124 125 void __EXPORT ScTableLink::Edit( Window* pParent, const Link& rEndEditHdl ) 126 { 127 // DefModalDialogParent setzen, weil evtl. aus der DocShell beim ConvertFrom 128 // ein Optionen-Dialog kommt... 129 130 pImpl->m_aEndEditLink = rEndEditHdl; 131 pImpl->m_pOldParent = Application::GetDefDialogParent(); 132 if (pParent) 133 Application::SetDefDialogParent(pParent); 134 135 bInEdit = sal_True; 136 SvBaseLink::Edit( pParent, LINK( this, ScTableLink, TableEndEditHdl ) ); 137 } 138 139 void __EXPORT ScTableLink::DataChanged( const String&, 140 const ::com::sun::star::uno::Any& ) 141 { 142 sfx2::LinkManager* pLinkManager=pImpl->m_pDocSh->GetDocument()->GetLinkManager(); 143 if (pLinkManager!=NULL) 144 { 145 String aFile; 146 String aFilter; 147 pLinkManager->GetDisplayNames( this,0,&aFile,NULL,&aFilter); 148 149 // the file dialog returns the filter name with the application prefix 150 // -> remove prefix 151 ScDocumentLoader::RemoveAppPrefix( aFilter ); 152 153 if (!bInCreate) 154 Refresh( aFile, aFilter, NULL, GetRefreshDelay() ); // don't load twice 155 } 156 } 157 158 void __EXPORT ScTableLink::Closed() 159 { 160 // Verknuepfung loeschen: Undo 161 ScDocument* pDoc = pImpl->m_pDocSh->GetDocument(); 162 sal_Bool bUndo (pDoc->IsUndoEnabled()); 163 164 if (bAddUndo && bUndo) 165 { 166 pImpl->m_pDocSh->GetUndoManager()->AddUndoAction( 167 new ScUndoRemoveLink( pImpl->m_pDocSh, aFileName ) ); 168 169 bAddUndo = sal_False; // nur einmal 170 } 171 172 // Verbindung wird im dtor aufgehoben 173 174 SvBaseLink::Closed(); 175 } 176 177 sal_Bool ScTableLink::IsUsed() const 178 { 179 return pImpl->m_pDocSh->GetDocument()->HasLink( aFileName, aFilterName, aOptions ); 180 } 181 182 sal_Bool ScTableLink::Refresh(const String& rNewFile, const String& rNewFilter, 183 const String* pNewOptions, sal_uLong nNewRefresh ) 184 { 185 // Dokument laden 186 187 if (!rNewFile.Len() || !rNewFilter.Len()) 188 return sal_False; 189 190 String aNewUrl( ScGlobal::GetAbsDocName( rNewFile, pImpl->m_pDocSh ) ); 191 sal_Bool bNewUrlName = (aNewUrl != aFileName); 192 193 const SfxFilter* pFilter = pImpl->m_pDocSh->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter); 194 if (!pFilter) 195 return sal_False; 196 197 ScDocument* pDoc = pImpl->m_pDocSh->GetDocument(); 198 pDoc->SetInLinkUpdate( sal_True ); 199 200 sal_Bool bUndo(pDoc->IsUndoEnabled()); 201 202 // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen 203 if ( rNewFilter != aFilterName ) 204 aOptions.Erase(); 205 if ( pNewOptions ) // Optionen hart angegeben? 206 aOptions = *pNewOptions; 207 208 // ItemSet immer anlegen, damit die DocShell die Optionen setzen kann 209 SfxItemSet* pSet = new SfxAllItemSet( SFX_APP()->GetPool() ); 210 if ( aOptions.Len() ) 211 pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, aOptions ) ); 212 213 SfxMedium* pMed = new SfxMedium(aNewUrl, STREAM_STD_READ, sal_False, pFilter, pSet); 214 215 if ( bInEdit ) // only if using the edit dialog, 216 pMed->UseInteractionHandler( sal_True ); // enable the filter options dialog 217 218 // aRef->DoClose() will be called explicitly, but it is still more safe to use SfxObjectShellLock here 219 ScDocShell* pSrcShell = new ScDocShell(SFX_CREATE_MODE_INTERNAL); 220 SfxObjectShellLock aRef = pSrcShell; 221 pSrcShell->DoLoad(pMed); 222 223 // Optionen koennten gesetzt worden sein 224 String aNewOpt = ScDocumentLoader::GetOptions(*pMed); 225 if (!aNewOpt.Len()) 226 aNewOpt = aOptions; 227 228 // Undo... 229 230 ScDocument* pUndoDoc = NULL; 231 sal_Bool bFirst = sal_True; 232 if (bAddUndo && bUndo) 233 pUndoDoc = new ScDocument( SCDOCMODE_UNDO ); 234 235 // Tabellen kopieren 236 237 ScDocShellModificator aModificator( *pImpl->m_pDocSh ); 238 239 sal_Bool bNotFound = sal_False; 240 ScDocument* pSrcDoc = pSrcShell->GetDocument(); 241 242 // #74835# from text filters that don't set the table name, 243 // use the one table regardless of link table name 244 sal_Bool bAutoTab = (pSrcDoc->GetTableCount() == 1) && 245 ScDocShell::HasAutomaticTableName( rNewFilter ); 246 247 SCTAB nCount = pDoc->GetTableCount(); 248 for (SCTAB nTab=0; nTab<nCount; nTab++) 249 { 250 sal_uInt8 nMode = pDoc->GetLinkMode(nTab); 251 if (nMode && pDoc->GetLinkDoc(nTab)==aFileName) 252 { 253 String aTabName = pDoc->GetLinkTab(nTab); 254 255 // Undo 256 257 if (bAddUndo && bUndo) 258 { 259 if (bFirst) 260 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True ); 261 else 262 pUndoDoc->AddUndoTab( nTab, nTab, sal_True, sal_True ); 263 bFirst = sal_False; 264 ScRange aRange(0,0,nTab,MAXCOL,MAXROW,nTab); 265 pDoc->CopyToDocument(aRange, IDF_ALL, sal_False, pUndoDoc); 266 pUndoDoc->TransferDrawPage( pDoc, nTab, nTab ); 267 pUndoDoc->SetLink( nTab, nMode, aFileName, aFilterName, 268 aOptions, aTabName, GetRefreshDelay() ); 269 pUndoDoc->SetTabBgColor( nTab, pDoc->GetTabBgColor(nTab) ); 270 } 271 272 // Tabellenname einer ExtDocRef anpassen 273 274 if ( bNewUrlName && nMode == SC_LINK_VALUE ) 275 { 276 String aName; 277 pDoc->GetName( nTab, aName ); 278 if ( ScGlobal::GetpTransliteration()->isEqual( 279 ScGlobal::GetDocTabName( aFileName, aTabName ), aName ) ) 280 { 281 pDoc->RenameTab( nTab, 282 ScGlobal::GetDocTabName( aNewUrl, aTabName ), 283 sal_False, sal_True ); // kein RefUpdate, kein ValidTabName 284 } 285 } 286 287 // kopieren 288 289 SCTAB nSrcTab = 0; 290 bool bFound = false; 291 /* #i71497# check if external document is loaded successfully, 292 otherwise we may find the empty default sheet "Sheet1" in 293 pSrcDoc, even if the document does not exist. */ 294 if( pMed->GetError() == 0 ) 295 { 296 // no sheet name -> use first sheet 297 if ( aTabName.Len() && !bAutoTab ) 298 bFound = pSrcDoc->GetTable( aTabName, nSrcTab ); 299 else 300 bFound = true; 301 } 302 303 if (bFound) 304 pDoc->TransferTab( pSrcDoc, nSrcTab, nTab, sal_False, // nicht neu einfuegen 305 (nMode == SC_LINK_VALUE) ); // nur Werte? 306 else 307 { 308 pDoc->DeleteAreaTab( 0,0,MAXCOL,MAXROW, nTab, IDF_ALL ); 309 310 bool bShowError = true; 311 if ( nMode == SC_LINK_VALUE ) 312 { 313 // #139464# Value link (used with external references in formulas): 314 // Look for formulas that reference the sheet, and put errors in the referenced cells. 315 316 ScRangeList aErrorCells; // cells on the linked sheets that need error values 317 318 ScCellIterator aCellIter( pDoc, 0,0,0, MAXCOL,MAXROW,MAXTAB ); // all sheets 319 ScBaseCell* pCell = aCellIter.GetFirst(); 320 while (pCell) 321 { 322 if (pCell->GetCellType() == CELLTYPE_FORMULA) 323 { 324 ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); 325 326 ScDetectiveRefIter aRefIter( pFCell ); 327 ScRange aRefRange; 328 while ( aRefIter.GetNextRef( aRefRange ) ) 329 { 330 if ( aRefRange.aStart.Tab() <= nTab && aRefRange.aEnd.Tab() >= nTab ) 331 { 332 // use first cell of range references (don't fill potentially large ranges) 333 334 aErrorCells.Join( ScRange( aRefRange.aStart ) ); 335 } 336 } 337 } 338 pCell = aCellIter.GetNext(); 339 } 340 341 sal_uLong nRanges = aErrorCells.Count(); 342 if ( nRanges ) // found any? 343 { 344 ScTokenArray aTokenArr; 345 aTokenArr.AddOpCode( ocNotAvail ); 346 aTokenArr.AddOpCode( ocOpen ); 347 aTokenArr.AddOpCode( ocClose ); 348 aTokenArr.AddOpCode( ocStop ); 349 350 for (sal_uLong nPos=0; nPos<nRanges; nPos++) 351 { 352 const ScRange* pRange = aErrorCells.GetObject(nPos); 353 SCCOL nStartCol = pRange->aStart.Col(); 354 SCROW nStartRow = pRange->aStart.Row(); 355 SCCOL nEndCol = pRange->aEnd.Col(); 356 SCROW nEndRow = pRange->aEnd.Row(); 357 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++) 358 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) 359 { 360 ScAddress aDestPos( nCol, nRow, nTab ); 361 ScFormulaCell* pNewCell = new ScFormulaCell( pDoc, aDestPos, &aTokenArr ); 362 pDoc->PutCell( aDestPos, pNewCell ); 363 } 364 } 365 366 bShowError = false; 367 } 368 // if no references were found, insert error message (don't leave the sheet empty) 369 } 370 371 if ( bShowError ) 372 { 373 // Normal link or no references: put error message on sheet. 374 375 pDoc->SetString( 0,0,nTab, ScGlobal::GetRscString(STR_LINKERROR) ); 376 pDoc->SetString( 0,1,nTab, ScGlobal::GetRscString(STR_LINKERRORFILE) ); 377 pDoc->SetString( 1,1,nTab, aNewUrl ); 378 pDoc->SetString( 0,2,nTab, ScGlobal::GetRscString(STR_LINKERRORTAB) ); 379 pDoc->SetString( 1,2,nTab, aTabName ); 380 } 381 382 bNotFound = sal_True; 383 } 384 385 if ( bNewUrlName || rNewFilter != aFilterName || 386 aNewOpt != aOptions || pNewOptions || 387 nNewRefresh != GetRefreshDelay() ) 388 pDoc->SetLink( nTab, nMode, aNewUrl, rNewFilter, aNewOpt, 389 aTabName, nNewRefresh ); 390 } 391 } 392 393 // neue Einstellungen merken 394 395 if ( bNewUrlName ) 396 aFileName = aNewUrl; 397 if ( rNewFilter != aFilterName ) 398 aFilterName = rNewFilter; 399 if ( aNewOpt != aOptions ) 400 aOptions = aNewOpt; 401 402 // aufraeumen 403 404 // pSrcShell->DoClose(); 405 aRef->DoClose(); 406 407 // Undo 408 409 if (bAddUndo && bUndo) 410 pImpl->m_pDocSh->GetUndoManager()->AddUndoAction( 411 new ScUndoRefreshLink( pImpl->m_pDocSh, pUndoDoc ) ); 412 413 // Paint (koennen mehrere Tabellen sein) 414 415 if (bDoPaint) 416 { 417 pImpl->m_pDocSh->PostPaint( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB), 418 PAINT_GRID | PAINT_TOP | PAINT_LEFT | PAINT_EXTRAS ); 419 aModificator.SetDocumentModified(); 420 } 421 422 if (bNotFound) 423 { 424 //! Fehler ausgeben ? 425 } 426 427 pDoc->SetInLinkUpdate( sal_False ); 428 429 // notify Uno objects (for XRefreshListener) 430 //! also notify Uno objects if file name was changed! 431 ScLinkRefreshedHint aHint; 432 aHint.SetSheetLink( aFileName ); 433 pDoc->BroadcastUno( aHint ); 434 435 return sal_True; 436 } 437 438 IMPL_LINK( ScTableLink, RefreshHdl, ScTableLink*, EMPTYARG ) 439 { 440 long nRes = Refresh( aFileName, aFilterName, NULL, GetRefreshDelay() ) != 0; 441 return nRes; 442 } 443 444 IMPL_LINK( ScTableLink, TableEndEditHdl, ::sfx2::SvBaseLink*, pLink ) 445 { 446 if ( pImpl->m_aEndEditLink.IsSet() ) 447 pImpl->m_aEndEditLink.Call( pLink ); 448 bInEdit = sal_False; 449 Application::SetDefDialogParent( pImpl->m_pOldParent ); 450 return 0; 451 } 452 453 // === ScDocumentLoader ================================================== 454 455 String ScDocumentLoader::GetOptions( SfxMedium& rMedium ) // static 456 { 457 SfxItemSet* pSet = rMedium.GetItemSet(); 458 const SfxPoolItem* pItem; 459 if ( pSet && SFX_ITEM_SET == pSet->GetItemState( SID_FILE_FILTEROPTIONS, sal_True, &pItem ) ) 460 return ((const SfxStringItem*)pItem)->GetValue(); 461 462 return EMPTY_STRING; 463 } 464 465 sal_Bool ScDocumentLoader::GetFilterName( const String& rFileName, 466 String& rFilter, String& rOptions, 467 sal_Bool bWithContent, sal_Bool bWithInteraction ) // static 468 { 469 TypeId aScType = TYPE(ScDocShell); 470 SfxObjectShell* pDocSh = SfxObjectShell::GetFirst( &aScType ); 471 while ( pDocSh ) 472 { 473 if ( pDocSh->HasName() ) 474 { 475 SfxMedium* pMed = pDocSh->GetMedium(); 476 if ( rFileName == pMed->GetName() ) 477 { 478 rFilter = pMed->GetFilter()->GetFilterName(); 479 rOptions = GetOptions(*pMed); 480 return sal_True; 481 } 482 } 483 pDocSh = SfxObjectShell::GetNext( *pDocSh, &aScType ); 484 } 485 486 INetURLObject aUrl( rFileName ); 487 INetProtocol eProt = aUrl.GetProtocol(); 488 if ( eProt == INET_PROT_NOT_VALID ) // invalid URL? 489 return sal_False; // abort without creating a medium 490 491 // Filter-Detection 492 493 const SfxFilter* pSfxFilter = NULL; 494 SfxMedium* pMedium = new SfxMedium( rFileName, STREAM_STD_READ, sal_False ); 495 if ( pMedium->GetError() == ERRCODE_NONE ) 496 { 497 if ( bWithInteraction ) 498 pMedium->UseInteractionHandler(sal_True); // #i73992# no longer called from GuessFilter 499 500 SfxFilterMatcher aMatcher( String::CreateFromAscii("scalc") ); 501 if( bWithContent ) 502 aMatcher.GuessFilter( *pMedium, &pSfxFilter ); 503 else 504 aMatcher.GuessFilterIgnoringContent( *pMedium, &pSfxFilter ); 505 } 506 507 sal_Bool bOK = sal_False; 508 if ( pMedium->GetError() == ERRCODE_NONE ) 509 { 510 if ( pSfxFilter ) 511 rFilter = pSfxFilter->GetFilterName(); 512 else 513 rFilter = ScDocShell::GetOwnFilterName(); // sonst Calc-Datei 514 bOK = (rFilter.Len()>0); 515 } 516 517 delete pMedium; 518 return bOK; 519 } 520 521 void ScDocumentLoader::RemoveAppPrefix( String& rFilterName ) // static 522 { 523 String aAppPrefix = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM( STRING_SCAPP )); 524 aAppPrefix.AppendAscii(RTL_CONSTASCII_STRINGPARAM( ": " )); 525 xub_StrLen nPreLen = aAppPrefix.Len(); 526 if ( rFilterName.Copy(0,nPreLen) == aAppPrefix ) 527 rFilterName.Erase(0,nPreLen); 528 } 529 530 ScDocumentLoader::ScDocumentLoader( const String& rFileName, 531 String& rFilterName, String& rOptions, 532 sal_uInt32 nRekCnt, sal_Bool bWithInteraction ) : 533 pDocShell(0), 534 pMedium(0) 535 { 536 if ( !rFilterName.Len() ) 537 GetFilterName( rFileName, rFilterName, rOptions, sal_True, bWithInteraction ); 538 539 const SfxFilter* pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( rFilterName ); 540 541 // ItemSet immer anlegen, damit die DocShell die Optionen setzen kann 542 SfxItemSet* pSet = new SfxAllItemSet( SFX_APP()->GetPool() ); 543 if ( rOptions.Len() ) 544 pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, rOptions ) ); 545 546 pMedium = new SfxMedium( rFileName, STREAM_STD_READ, sal_False, pFilter, pSet ); 547 if ( pMedium->GetError() != ERRCODE_NONE ) 548 return ; 549 550 if ( bWithInteraction ) 551 pMedium->UseInteractionHandler( sal_True ); // to enable the filter options dialog 552 553 pDocShell = new ScDocShell( SFX_CREATE_MODE_INTERNAL ); 554 aRef = pDocShell; 555 556 ScDocument* pDoc = pDocShell->GetDocument(); 557 if( pDoc ) 558 { 559 ScExtDocOptions* pExtDocOpt = pDoc->GetExtDocOptions(); 560 if( !pExtDocOpt ) 561 { 562 pExtDocOpt = new ScExtDocOptions; 563 pDoc->SetExtDocOptions( pExtDocOpt ); 564 } 565 pExtDocOpt->GetDocSettings().mnLinkCnt = nRekCnt; 566 } 567 568 pDocShell->DoLoad( pMedium ); 569 570 String aNew = GetOptions(*pMedium); // Optionen werden beim Laden per Dialog gesetzt 571 if (aNew.Len() && aNew != rOptions) 572 rOptions = aNew; 573 } 574 575 ScDocumentLoader::~ScDocumentLoader() 576 { 577 /* if ( pDocShell ) 578 pDocShell->DoClose(); 579 */ 580 if ( aRef.Is() ) 581 aRef->DoClose(); 582 else if ( pMedium ) 583 delete pMedium; 584 } 585 586 void ScDocumentLoader::ReleaseDocRef() 587 { 588 if ( aRef.Is() ) 589 { 590 // release reference without calling DoClose - caller must 591 // have another reference to the doc and call DoClose later 592 593 pDocShell = NULL; 594 pMedium = NULL; 595 aRef.Clear(); 596 } 597 } 598 599 ScDocument* ScDocumentLoader::GetDocument() 600 { 601 return pDocShell ? pDocShell->GetDocument() : 0; 602 } 603 604 sal_Bool ScDocumentLoader::IsError() const 605 { 606 if ( pDocShell && pMedium ) 607 return pMedium->GetError() != ERRCODE_NONE; 608 else 609 return sal_True; 610 } 611 612 String ScDocumentLoader::GetTitle() const 613 { 614 if ( pDocShell ) 615 return pDocShell->GetTitle(); 616 else 617 return EMPTY_STRING; 618 } 619 620