/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #ifdef _MSC_VER #pragma optimize("",off) #endif //------------------------------------------------------------------ #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" using ::rtl::OUString; 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( false ), bInEdit( false ), bAddUndo( true ), bDoPaint( 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( false ), bInEdit( false ), bAddUndo( true ), bDoPaint( true ) { pImpl->m_pDocSh = static_cast< ScDocShell* >( pShell ); SetRefreshHandler( LINK( this, ScTableLink, RefreshHdl ) ); SetRefreshControl( pImpl->m_pDocSh->GetDocument()->GetRefreshTimerControlAddress() ); } ScTableLink::~ScTableLink() { // Verbindung aufheben StopRefreshTimer(); String aEmpty; ScDocument* pDoc = pImpl->m_pDocSh->GetDocument(); SCTAB nCount = pDoc->GetTableCount(); for (SCTAB nTab=0; nTabIsLinked(nTab) && aFileName.equals(pDoc->GetLinkDoc(nTab))) pDoc->SetLink( nTab, SC_LINK_NONE, aEmpty, aEmpty, aEmpty, aEmpty, 0 ); delete pImpl; } void 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 = true; SvBaseLink::Edit( pParent, LINK( this, ScTableLink, TableEndEditHdl ) ); } ::sfx2::SvBaseLink::UpdateResult ScTableLink::DataChanged( const String&, const ::com::sun::star::uno::Any& ) { sfx2::LinkManager* pLinkManager=pImpl->m_pDocSh->GetDocument()->GetLinkManager(); if (pLinkManager!=NULL) { rtl::OUString aFile, 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 } return SUCCESS; } void 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 = 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 false; rtl::OUString aNewUrl = ScGlobal::GetAbsDocName(rNewFile, pImpl->m_pDocSh); bool bNewUrlName = !aFileName.equals(aNewUrl); const SfxFilter* pFilter = pImpl->m_pDocSh->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter); if (!pFilter) return false; ScDocument* pDoc = pImpl->m_pDocSh->GetDocument(); pDoc->SetInLinkUpdate( true ); sal_Bool bUndo(pDoc->IsUndoEnabled()); // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen if (!aFilterName.equals(rNewFilter)) aOptions = rtl::OUString(); 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.isEmpty()) pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, aOptions ) ); SfxMedium* pMed = new SfxMedium(aNewUrl, STREAM_STD_READ, pFilter, pSet); if ( bInEdit ) // only if using the edit dialog, pMed->UseInteractionHandler(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 = true; if (bAddUndo && bUndo) pUndoDoc = new ScDocument( SCDOCMODE_UNDO ); // Tabellen kopieren ScDocShellModificator aModificator( *pImpl->m_pDocSh ); sal_Bool bNotFound = false; ScDocument* pSrcDoc = pSrcShell->GetDocument(); // 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 && aFileName.equals(pDoc->GetLinkDoc(nTab))) { String aTabName = pDoc->GetLinkTab(nTab); // Undo if (bAddUndo && bUndo) { if (bFirst) pUndoDoc->InitUndo( pDoc, nTab, nTab, true, true ); else pUndoDoc->AddUndoTab( nTab, nTab, true, true ); bFirst = false; ScRange aRange(0,0,nTab,MAXCOL,MAXROW,nTab); pDoc->CopyToDocument(aRange, IDF_ALL, false, pUndoDoc); pUndoDoc->TransferDrawPage( pDoc, nTab, nTab ); pUndoDoc->SetLink( nTab, nMode, aFileName, aFilterName, aOptions, aTabName, GetRefreshDelay() ); } // Tabellenname einer ExtDocRef anpassen if ( bNewUrlName && nMode == SC_LINK_VALUE ) { rtl::OUString aName; pDoc->GetName( nTab, aName ); if ( ScGlobal::GetpTransliteration()->isEqual( ScGlobal::GetDocTabName( aFileName, aTabName ), aName ) ) { pDoc->RenameTab( nTab, ScGlobal::GetDocTabName( aNewUrl, aTabName ), false, 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, 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 ) { // 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(); } size_t nRanges = aErrorCells.size(); if ( nRanges ) // found any? { ScTokenArray aTokenArr; aTokenArr.AddOpCode( ocNotAvail ); aTokenArr.AddOpCode( ocOpen ); aTokenArr.AddOpCode( ocClose ); aTokenArr.AddOpCode( ocStop ); for (size_t nPos=0; nPos < nRanges; nPos++) { const ScRange* pRange = aErrorCells[ nPos ]; SCCOL nStartCol = pRange->aStart.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 = true; } if ( bNewUrlName || !aFilterName.equals(rNewFilter) || !aOptions.equals(aNewOpt) || pNewOptions || nNewRefresh != GetRefreshDelay() ) pDoc->SetLink( nTab, nMode, aNewUrl, rNewFilter, aNewOpt, aTabName, nNewRefresh ); } } // neue Einstellungen merken if ( bNewUrlName ) aFileName = aNewUrl; if (!aFilterName.equals(rNewFilter)) aFilterName = rNewFilter; if (!aOptions.equals(aNewOpt)) aOptions = aNewOpt; // aufraeumen 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 ); aModificator.SetDocumentModified(); } if (bNotFound) { //! Fehler ausgeben ? } pDoc->SetInLinkUpdate( false ); // notify Uno objects (for XRefreshListener) //! also notify Uno objects if file name was changed! ScLinkRefreshedHint aHint; aHint.SetSheetLink( aFileName ); pDoc->BroadcastUno( aHint ); return true; } IMPL_LINK_NOARG(ScTableLink, RefreshHdl) { 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 = false; Application::SetDefDialogParent( pImpl->m_pOldParent ); return 0; } // === ScDocumentLoader ================================================== rtl::OUString ScDocumentLoader::GetOptions( SfxMedium& rMedium ) { SfxItemSet* pSet = rMedium.GetItemSet(); const SfxPoolItem* pItem; if ( pSet && SFX_ITEM_SET == pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) ) return ((const SfxStringItem*)pItem)->GetValue(); return EMPTY_STRING; } bool ScDocumentLoader::GetFilterName( const String& rFileName, String& rFilter, String& rOptions, bool bWithContent, bool bWithInteraction ) { TypeId aScType = TYPE(ScDocShell); SfxObjectShell* pDocSh = SfxObjectShell::GetFirst( &aScType ); while ( pDocSh ) { if ( pDocSh->HasName() ) { SfxMedium* pMed = pDocSh->GetMedium(); if ( pMed->GetName().equals(rFileName) ) { rFilter = pMed->GetFilter()->GetFilterName(); rOptions = GetOptions(*pMed); return true; } } pDocSh = SfxObjectShell::GetNext( *pDocSh, &aScType ); } INetURLObject aUrl( rFileName ); INetProtocol eProt = aUrl.GetProtocol(); if ( eProt == INET_PROT_NOT_VALID ) // invalid URL? return false; // abort without creating a medium // Filter-Detection const SfxFilter* pSfxFilter = NULL; SfxMedium* pMedium = new SfxMedium( rFileName, STREAM_STD_READ ); if ( pMedium->GetError() == ERRCODE_NONE ) { if ( bWithInteraction ) pMedium->UseInteractionHandler(true); // #i73992# no longer called from GuessFilter SfxFilterMatcher aMatcher( rtl::OUString("scalc") ); if( bWithContent ) aMatcher.GuessFilter( *pMedium, &pSfxFilter ); else aMatcher.GuessFilterIgnoringContent( *pMedium, &pSfxFilter ); } sal_Bool bOK = 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; } bool ScDocumentLoader::GetFilterName( const OUString& rFilterName, OUString& rFilter, OUString& rOptions, bool bWithContent, bool bWithInteraction) { String aTmp1, aTmp2; bool bRet = GetFilterName(rFilterName, aTmp1, aTmp2, bWithContent, bWithInteraction); rFilter = aTmp1; rOptions = aTmp2; return bRet; } void ScDocumentLoader::RemoveAppPrefix( rtl::OUString& rFilterName ) { rtl::OUStringBuffer aAppPrefix; aAppPrefix.appendAscii(STRING_SCAPP); aAppPrefix.appendAscii(": "); sal_Int32 nPreLen = aAppPrefix.getLength(); if (rFilterName.copy(0, nPreLen).equals(aAppPrefix.makeStringAndClear())) rFilterName = rFilterName.copy(nPreLen); } ScDocumentLoader::ScDocumentLoader( const rtl::OUString& rFileName, rtl::OUString& rFilterName, rtl::OUString& rOptions, sal_uInt32 nRekCnt, bool bWithInteraction ) : pDocShell(0), pMedium(0) { if ( rFilterName.isEmpty() ) GetFilterName( rFileName, rFilterName, rOptions, 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.isEmpty() ) pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, rOptions ) ); pMedium = new SfxMedium( rFileName, STREAM_STD_READ, pFilter, pSet ); if ( pMedium->GetError() != ERRCODE_NONE ) return ; if ( bWithInteraction ) pMedium->UseInteractionHandler( 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 ); rtl::OUString aNew = GetOptions(*pMedium); // Optionen werden beim Laden per Dialog gesetzt if (!aNew.isEmpty() && aNew != rOptions) rOptions = aNew; } ScDocumentLoader::~ScDocumentLoader() { 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; } bool ScDocumentLoader::IsError() const { if ( pDocShell && pMedium ) return pMedium->GetError() != ERRCODE_NONE; else return true; } rtl::OUString ScDocumentLoader::GetTitle() const { if ( pDocShell ) return pDocShell->GetTitle(); else return EMPTY_STRING; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */