/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" #ifdef _MSC_VER #pragma optimize("",off) #endif //------------------------------------------------------------------ // INCLUDE --------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include "tablink.hxx" #include "scextopt.hxx" #include "table.hxx" #include "document.hxx" #include "docsh.hxx" #include "globstr.hrc" #include "undoblk.hxx" #include "undotab.hxx" #include "global.hxx" #include "hints.hxx" #include "cell.hxx" #include "dociter.hxx" #include "formula/opcode.hxx" struct TableLink_Impl { ScDocShell* m_pDocSh; Window* m_pOldParent; Link m_aEndEditLink; TableLink_Impl() : m_pDocSh( NULL ), m_pOldParent( NULL ) {} }; TYPEINIT1(ScTableLink, ::sfx2::SvBaseLink); //------------------------------------------------------------------------ ScTableLink::ScTableLink(ScDocShell* pDocSh, const String& rFile, const String& rFilter, const String& rOpt, sal_uLong nRefresh ): ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL,FORMAT_FILE), ScRefreshTimer( nRefresh ), pImpl( new TableLink_Impl ), aFileName(rFile), aFilterName(rFilter), aOptions(rOpt), bInCreate( sal_False ), bInEdit( sal_False ), bAddUndo( sal_True ), bDoPaint( sal_True ) { pImpl->m_pDocSh = pDocSh; } ScTableLink::ScTableLink(SfxObjectShell* pShell, const String& rFile, const String& rFilter, const String& rOpt, sal_uLong nRefresh ): ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL,FORMAT_FILE), ScRefreshTimer( nRefresh ), pImpl( new TableLink_Impl ), aFileName(rFile), aFilterName(rFilter), aOptions(rOpt), bInCreate( sal_False ), bInEdit( sal_False ), bAddUndo( sal_True ), bDoPaint( sal_True ) { pImpl->m_pDocSh = static_cast< ScDocShell* >( pShell ); SetRefreshHandler( LINK( this, ScTableLink, RefreshHdl ) ); SetRefreshControl( pImpl->m_pDocSh->GetDocument()->GetRefreshTimerControlAddress() ); } __EXPORT ScTableLink::~ScTableLink() { // Verbindung aufheben StopRefreshTimer(); String aEmpty; ScDocument* pDoc = pImpl->m_pDocSh->GetDocument(); SCTAB nCount = pDoc->GetTableCount(); for (SCTAB nTab=0; nTabIsLinked(nTab) && pDoc->GetLinkDoc(nTab)==aFileName) pDoc->SetLink( nTab, SC_LINK_NONE, aEmpty, aEmpty, aEmpty, aEmpty, 0 ); delete pImpl; } void __EXPORT ScTableLink::Edit( Window* pParent, const Link& rEndEditHdl ) { // DefModalDialogParent setzen, weil evtl. aus der DocShell beim ConvertFrom // ein Optionen-Dialog kommt... pImpl->m_aEndEditLink = rEndEditHdl; pImpl->m_pOldParent = Application::GetDefDialogParent(); if (pParent) Application::SetDefDialogParent(pParent); bInEdit = sal_True; SvBaseLink::Edit( pParent, LINK( this, ScTableLink, TableEndEditHdl ) ); } void __EXPORT ScTableLink::DataChanged( const String&, const ::com::sun::star::uno::Any& ) { sfx2::LinkManager* pLinkManager=pImpl->m_pDocSh->GetDocument()->GetLinkManager(); if (pLinkManager!=NULL) { String aFile; String aFilter; pLinkManager->GetDisplayNames( this,0,&aFile,NULL,&aFilter); // the file dialog returns the filter name with the application prefix // -> remove prefix ScDocumentLoader::RemoveAppPrefix( aFilter ); if (!bInCreate) Refresh( aFile, aFilter, NULL, GetRefreshDelay() ); // don't load twice } } void __EXPORT ScTableLink::Closed() { // Verknuepfung loeschen: Undo ScDocument* pDoc = pImpl->m_pDocSh->GetDocument(); sal_Bool bUndo (pDoc->IsUndoEnabled()); if (bAddUndo && bUndo) { pImpl->m_pDocSh->GetUndoManager()->AddUndoAction( new ScUndoRemoveLink( pImpl->m_pDocSh, aFileName ) ); bAddUndo = sal_False; // nur einmal } // Verbindung wird im dtor aufgehoben SvBaseLink::Closed(); } sal_Bool ScTableLink::IsUsed() const { return pImpl->m_pDocSh->GetDocument()->HasLink( aFileName, aFilterName, aOptions ); } sal_Bool ScTableLink::Refresh(const String& rNewFile, const String& rNewFilter, const String* pNewOptions, sal_uLong nNewRefresh ) { // Dokument laden if (!rNewFile.Len() || !rNewFilter.Len()) return sal_False; String aNewUrl( ScGlobal::GetAbsDocName( rNewFile, pImpl->m_pDocSh ) ); sal_Bool bNewUrlName = (aNewUrl != aFileName); const SfxFilter* pFilter = pImpl->m_pDocSh->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter); if (!pFilter) return sal_False; ScDocument* pDoc = pImpl->m_pDocSh->GetDocument(); pDoc->SetInLinkUpdate( sal_True ); sal_Bool bUndo(pDoc->IsUndoEnabled()); // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen if ( rNewFilter != aFilterName ) aOptions.Erase(); if ( pNewOptions ) // Optionen hart angegeben? aOptions = *pNewOptions; // ItemSet immer anlegen, damit die DocShell die Optionen setzen kann SfxItemSet* pSet = new SfxAllItemSet( SFX_APP()->GetPool() ); if ( aOptions.Len() ) pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, aOptions ) ); SfxMedium* pMed = new SfxMedium(aNewUrl, STREAM_STD_READ, sal_False, pFilter, pSet); if ( bInEdit ) // only if using the edit dialog, pMed->UseInteractionHandler( sal_True ); // enable the filter options dialog // aRef->DoClose() will be called explicitly, but it is still more safe to use SfxObjectShellLock here ScDocShell* pSrcShell = new ScDocShell(SFX_CREATE_MODE_INTERNAL); SfxObjectShellLock aRef = pSrcShell; pSrcShell->DoLoad(pMed); // Optionen koennten gesetzt worden sein String aNewOpt = ScDocumentLoader::GetOptions(*pMed); if (!aNewOpt.Len()) aNewOpt = aOptions; // Undo... ScDocument* pUndoDoc = NULL; sal_Bool bFirst = sal_True; if (bAddUndo && bUndo) pUndoDoc = new ScDocument( SCDOCMODE_UNDO ); // Tabellen kopieren ScDocShellModificator aModificator( *pImpl->m_pDocSh ); sal_Bool bNotFound = sal_False; ScDocument* pSrcDoc = pSrcShell->GetDocument(); // #74835# from text filters that don't set the table name, // use the one table regardless of link table name sal_Bool bAutoTab = (pSrcDoc->GetTableCount() == 1) && ScDocShell::HasAutomaticTableName( rNewFilter ); SCTAB nCount = pDoc->GetTableCount(); for (SCTAB nTab=0; nTabGetLinkMode(nTab); if (nMode && pDoc->GetLinkDoc(nTab)==aFileName) { String aTabName = pDoc->GetLinkTab(nTab); // Undo if (bAddUndo && bUndo) { if (bFirst) pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True ); else pUndoDoc->AddUndoTab( nTab, nTab, sal_True, sal_True ); bFirst = sal_False; ScRange aRange(0,0,nTab,MAXCOL,MAXROW,nTab); pDoc->CopyToDocument(aRange, IDF_ALL, sal_False, pUndoDoc); pUndoDoc->TransferDrawPage( pDoc, nTab, nTab ); pUndoDoc->SetLink( nTab, nMode, aFileName, aFilterName, aOptions, aTabName, GetRefreshDelay() ); pUndoDoc->SetTabBgColor( nTab, pDoc->GetTabBgColor(nTab) ); } // Tabellenname einer ExtDocRef anpassen if ( bNewUrlName && nMode == SC_LINK_VALUE ) { String aName; pDoc->GetName( nTab, aName ); if ( ScGlobal::GetpTransliteration()->isEqual( ScGlobal::GetDocTabName( aFileName, aTabName ), aName ) ) { pDoc->RenameTab( nTab, ScGlobal::GetDocTabName( aNewUrl, aTabName ), sal_False, sal_True ); // kein RefUpdate, kein ValidTabName } } // kopieren SCTAB nSrcTab = 0; bool bFound = false; /* #i71497# check if external document is loaded successfully, otherwise we may find the empty default sheet "Sheet1" in pSrcDoc, even if the document does not exist. */ if( pMed->GetError() == 0 ) { // no sheet name -> use first sheet if ( aTabName.Len() && !bAutoTab ) bFound = pSrcDoc->GetTable( aTabName, nSrcTab ); else bFound = true; } if (bFound) pDoc->TransferTab( pSrcDoc, nSrcTab, nTab, sal_False, // nicht neu einfuegen (nMode == SC_LINK_VALUE) ); // nur Werte? else { pDoc->DeleteAreaTab( 0,0,MAXCOL,MAXROW, nTab, IDF_ALL ); bool bShowError = true; if ( nMode == SC_LINK_VALUE ) { // #139464# Value link (used with external references in formulas): // Look for formulas that reference the sheet, and put errors in the referenced cells. ScRangeList aErrorCells; // cells on the linked sheets that need error values ScCellIterator aCellIter( pDoc, 0,0,0, MAXCOL,MAXROW,MAXTAB ); // all sheets ScBaseCell* pCell = aCellIter.GetFirst(); while (pCell) { if (pCell->GetCellType() == CELLTYPE_FORMULA) { ScFormulaCell* pFCell = static_cast(pCell); ScDetectiveRefIter aRefIter( pFCell ); ScRange aRefRange; while ( aRefIter.GetNextRef( aRefRange ) ) { if ( aRefRange.aStart.Tab() <= nTab && aRefRange.aEnd.Tab() >= nTab ) { // use first cell of range references (don't fill potentially large ranges) aErrorCells.Join( ScRange( aRefRange.aStart ) ); } } } pCell = aCellIter.GetNext(); } sal_uLong nRanges = aErrorCells.Count(); if ( nRanges ) // found any? { ScTokenArray aTokenArr; aTokenArr.AddOpCode( ocNotAvail ); aTokenArr.AddOpCode( ocOpen ); aTokenArr.AddOpCode( ocClose ); aTokenArr.AddOpCode( ocStop ); for (sal_uLong nPos=0; nPosaStart.Col(); SCROW nStartRow = pRange->aStart.Row(); SCCOL nEndCol = pRange->aEnd.Col(); SCROW nEndRow = pRange->aEnd.Row(); for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++) for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) { ScAddress aDestPos( nCol, nRow, nTab ); ScFormulaCell* pNewCell = new ScFormulaCell( pDoc, aDestPos, &aTokenArr ); pDoc->PutCell( aDestPos, pNewCell ); } } bShowError = false; } // if no references were found, insert error message (don't leave the sheet empty) } if ( bShowError ) { // Normal link or no references: put error message on sheet. pDoc->SetString( 0,0,nTab, ScGlobal::GetRscString(STR_LINKERROR) ); pDoc->SetString( 0,1,nTab, ScGlobal::GetRscString(STR_LINKERRORFILE) ); pDoc->SetString( 1,1,nTab, aNewUrl ); pDoc->SetString( 0,2,nTab, ScGlobal::GetRscString(STR_LINKERRORTAB) ); pDoc->SetString( 1,2,nTab, aTabName ); } bNotFound = sal_True; } if ( bNewUrlName || rNewFilter != aFilterName || aNewOpt != aOptions || pNewOptions || nNewRefresh != GetRefreshDelay() ) pDoc->SetLink( nTab, nMode, aNewUrl, rNewFilter, aNewOpt, aTabName, nNewRefresh ); } } // neue Einstellungen merken if ( bNewUrlName ) aFileName = aNewUrl; if ( rNewFilter != aFilterName ) aFilterName = rNewFilter; if ( aNewOpt != aOptions ) aOptions = aNewOpt; // aufraeumen // pSrcShell->DoClose(); aRef->DoClose(); // Undo if (bAddUndo && bUndo) pImpl->m_pDocSh->GetUndoManager()->AddUndoAction( new ScUndoRefreshLink( pImpl->m_pDocSh, pUndoDoc ) ); // Paint (koennen mehrere Tabellen sein) if (bDoPaint) { pImpl->m_pDocSh->PostPaint( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB), PAINT_GRID | PAINT_TOP | PAINT_LEFT | PAINT_EXTRAS ); aModificator.SetDocumentModified(); } if (bNotFound) { //! Fehler ausgeben ? } pDoc->SetInLinkUpdate( sal_False ); // notify Uno objects (for XRefreshListener) //! also notify Uno objects if file name was changed! ScLinkRefreshedHint aHint; aHint.SetSheetLink( aFileName ); pDoc->BroadcastUno( aHint ); return sal_True; } IMPL_LINK( ScTableLink, RefreshHdl, ScTableLink*, EMPTYARG ) { long nRes = Refresh( aFileName, aFilterName, NULL, GetRefreshDelay() ) != 0; return nRes; } IMPL_LINK( ScTableLink, TableEndEditHdl, ::sfx2::SvBaseLink*, pLink ) { if ( pImpl->m_aEndEditLink.IsSet() ) pImpl->m_aEndEditLink.Call( pLink ); bInEdit = sal_False; Application::SetDefDialogParent( pImpl->m_pOldParent ); return 0; } // === ScDocumentLoader ================================================== String ScDocumentLoader::GetOptions( SfxMedium& rMedium ) // static { SfxItemSet* pSet = rMedium.GetItemSet(); const SfxPoolItem* pItem; if ( pSet && SFX_ITEM_SET == pSet->GetItemState( SID_FILE_FILTEROPTIONS, sal_True, &pItem ) ) return ((const SfxStringItem*)pItem)->GetValue(); return EMPTY_STRING; } sal_Bool ScDocumentLoader::GetFilterName( const String& rFileName, String& rFilter, String& rOptions, sal_Bool bWithContent, sal_Bool bWithInteraction ) // static { TypeId aScType = TYPE(ScDocShell); SfxObjectShell* pDocSh = SfxObjectShell::GetFirst( &aScType ); while ( pDocSh ) { if ( pDocSh->HasName() ) { SfxMedium* pMed = pDocSh->GetMedium(); if ( rFileName == pMed->GetName() ) { rFilter = pMed->GetFilter()->GetFilterName(); rOptions = GetOptions(*pMed); return sal_True; } } pDocSh = SfxObjectShell::GetNext( *pDocSh, &aScType ); } INetURLObject aUrl( rFileName ); INetProtocol eProt = aUrl.GetProtocol(); if ( eProt == INET_PROT_NOT_VALID ) // invalid URL? return sal_False; // abort without creating a medium // Filter-Detection const SfxFilter* pSfxFilter = NULL; SfxMedium* pMedium = new SfxMedium( rFileName, STREAM_STD_READ, sal_False ); if ( pMedium->GetError() == ERRCODE_NONE ) { if ( bWithInteraction ) pMedium->UseInteractionHandler(sal_True); // #i73992# no longer called from GuessFilter SfxFilterMatcher aMatcher( String::CreateFromAscii("scalc") ); if( bWithContent ) aMatcher.GuessFilter( *pMedium, &pSfxFilter ); else aMatcher.GuessFilterIgnoringContent( *pMedium, &pSfxFilter ); } sal_Bool bOK = sal_False; if ( pMedium->GetError() == ERRCODE_NONE ) { if ( pSfxFilter ) rFilter = pSfxFilter->GetFilterName(); else rFilter = ScDocShell::GetOwnFilterName(); // sonst Calc-Datei bOK = (rFilter.Len()>0); } delete pMedium; return bOK; } void ScDocumentLoader::RemoveAppPrefix( String& rFilterName ) // static { String aAppPrefix = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM( STRING_SCAPP )); aAppPrefix.AppendAscii(RTL_CONSTASCII_STRINGPARAM( ": " )); xub_StrLen nPreLen = aAppPrefix.Len(); if ( rFilterName.Copy(0,nPreLen) == aAppPrefix ) rFilterName.Erase(0,nPreLen); } ScDocumentLoader::ScDocumentLoader( const String& rFileName, String& rFilterName, String& rOptions, sal_uInt32 nRekCnt, sal_Bool bWithInteraction ) : pDocShell(0), pMedium(0) { if ( !rFilterName.Len() ) GetFilterName( rFileName, rFilterName, rOptions, sal_True, bWithInteraction ); const SfxFilter* pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( rFilterName ); // ItemSet immer anlegen, damit die DocShell die Optionen setzen kann SfxItemSet* pSet = new SfxAllItemSet( SFX_APP()->GetPool() ); if ( rOptions.Len() ) pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, rOptions ) ); pMedium = new SfxMedium( rFileName, STREAM_STD_READ, sal_False, pFilter, pSet ); if ( pMedium->GetError() != ERRCODE_NONE ) return ; if ( bWithInteraction ) pMedium->UseInteractionHandler( sal_True ); // to enable the filter options dialog pDocShell = new ScDocShell( SFX_CREATE_MODE_INTERNAL ); aRef = pDocShell; ScDocument* pDoc = pDocShell->GetDocument(); if( pDoc ) { ScExtDocOptions* pExtDocOpt = pDoc->GetExtDocOptions(); if( !pExtDocOpt ) { pExtDocOpt = new ScExtDocOptions; pDoc->SetExtDocOptions( pExtDocOpt ); } pExtDocOpt->GetDocSettings().mnLinkCnt = nRekCnt; } pDocShell->DoLoad( pMedium ); String aNew = GetOptions(*pMedium); // Optionen werden beim Laden per Dialog gesetzt if (aNew.Len() && aNew != rOptions) rOptions = aNew; } ScDocumentLoader::~ScDocumentLoader() { /* if ( pDocShell ) pDocShell->DoClose(); */ if ( aRef.Is() ) aRef->DoClose(); else if ( pMedium ) delete pMedium; } void ScDocumentLoader::ReleaseDocRef() { if ( aRef.Is() ) { // release reference without calling DoClose - caller must // have another reference to the doc and call DoClose later pDocShell = NULL; pMedium = NULL; aRef.Clear(); } } ScDocument* ScDocumentLoader::GetDocument() { return pDocShell ? pDocShell->GetDocument() : 0; } sal_Bool ScDocumentLoader::IsError() const { if ( pDocShell && pMedium ) return pMedium->GetError() != ERRCODE_NONE; else return sal_True; } String ScDocumentLoader::GetTitle() const { if ( pDocShell ) return pDocShell->GetTitle(); else return EMPTY_STRING; }