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