1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sfx2/sfxsids.hrc>
21 #include <sfx2/app.hxx>
22 #include <svl/itemset.hxx>
23 #include <svl/stritem.hxx>
24 #include <sfx2/docfile.hxx>
25 #include <sfx2/docfilt.hxx>
26 #include <sfx2/fcontnr.hxx>
27 #include <sfx2/linkmgr.hxx>
28 #include <tools/urlobj.hxx>
29 #include <unotools/transliterationwrapper.hxx>
31 #include "tablink.hxx"
33 #include "scextopt.hxx"
35 #include "document.hxx"
37 #include "globstr.hrc"
38 #include "undoblk.hxx"
39 #include "undotab.hxx"
42 #include "dociter.hxx"
43 #include <formula/opcode.hxx>
44 #include "formulacell.hxx"
45 #include "formulaiter.hxx"
46 #include "tokenarray.hxx"
51 VclPtr
<vcl::Window
> m_pOldParent
;
52 Link
<> m_aEndEditLink
;
54 TableLink_Impl() : m_pDocSh( NULL
), m_pOldParent( NULL
) {}
57 TYPEINIT1(ScTableLink
, ::sfx2::SvBaseLink
);
59 ScTableLink::ScTableLink(ScDocShell
* pDocSh
, const OUString
& rFile
,
60 const OUString
& rFilter
, const OUString
& rOpt
,
62 ::sfx2::SvBaseLink(SfxLinkUpdateMode::ONCALL
,SotClipboardFormatId::SIMPLE_FILE
),
63 ScRefreshTimer( nRefresh
),
64 pImpl( new TableLink_Impl
),
73 pImpl
->m_pDocSh
= pDocSh
;
76 ScTableLink::ScTableLink(SfxObjectShell
* pShell
, const OUString
& rFile
,
77 const OUString
& rFilter
, const OUString
& rOpt
,
79 ::sfx2::SvBaseLink(SfxLinkUpdateMode::ONCALL
,SotClipboardFormatId::SIMPLE_FILE
),
80 ScRefreshTimer( nRefresh
),
81 pImpl( new TableLink_Impl
),
90 pImpl
->m_pDocSh
= static_cast< ScDocShell
* >( pShell
);
91 SetRefreshHandler( LINK( this, ScTableLink
, RefreshHdl
) );
92 SetRefreshControl( &pImpl
->m_pDocSh
->GetDocument().GetRefreshTimerControlAddress() );
95 ScTableLink::~ScTableLink()
97 // Verbindung aufheben
101 ScDocument
& rDoc
= pImpl
->m_pDocSh
->GetDocument();
102 SCTAB nCount
= rDoc
.GetTableCount();
103 for (SCTAB nTab
=0; nTab
<nCount
; nTab
++)
104 if (rDoc
.IsLinked(nTab
) && aFileName
.equals(rDoc
.GetLinkDoc(nTab
)))
105 rDoc
.SetLink( nTab
, SC_LINK_NONE
, aEmpty
, aEmpty
, aEmpty
, aEmpty
, 0 );
109 void ScTableLink::Edit( vcl::Window
* pParent
, const Link
<>& rEndEditHdl
)
111 // DefModalDialogParent setzen, weil evtl. aus der DocShell beim ConvertFrom
112 // ein Optionen-Dialog kommt...
114 pImpl
->m_aEndEditLink
= rEndEditHdl
;
115 pImpl
->m_pOldParent
= Application::GetDefDialogParent();
117 Application::SetDefDialogParent(pParent
);
120 SvBaseLink::Edit( pParent
, LINK( this, ScTableLink
, TableEndEditHdl
) );
123 ::sfx2::SvBaseLink::UpdateResult
ScTableLink::DataChanged(
124 const OUString
&, const ::com::sun::star::uno::Any
& )
126 sfx2::LinkManager
* pLinkManager
=pImpl
->m_pDocSh
->GetDocument().GetLinkManager();
127 if (pLinkManager
!=NULL
)
129 OUString aFile
, aFilter
;
130 sfx2::LinkManager::GetDisplayNames(this, 0, &aFile
, NULL
, &aFilter
);
132 // the file dialog returns the filter name with the application prefix
134 ScDocumentLoader::RemoveAppPrefix( aFilter
);
137 Refresh( aFile
, aFilter
, NULL
, GetRefreshDelay() ); // don't load twice
142 void ScTableLink::Closed()
144 // Verknuepfung loeschen: Undo
145 ScDocument
& rDoc
= pImpl
->m_pDocSh
->GetDocument();
146 bool bUndo (rDoc
.IsUndoEnabled());
148 if (bAddUndo
&& bUndo
)
150 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction(
151 new ScUndoRemoveLink( pImpl
->m_pDocSh
, aFileName
) );
153 bAddUndo
= false; // nur einmal
156 // Verbindung wird im dtor aufgehoben
158 SvBaseLink::Closed();
161 bool ScTableLink::IsUsed() const
163 return pImpl
->m_pDocSh
->GetDocument().HasLink( aFileName
, aFilterName
, aOptions
);
166 bool ScTableLink::Refresh(const OUString
& rNewFile
, const OUString
& rNewFilter
,
167 const OUString
* pNewOptions
, sal_uLong nNewRefresh
)
171 if (rNewFile
.isEmpty() || rNewFilter
.isEmpty())
174 OUString aNewUrl
= ScGlobal::GetAbsDocName(rNewFile
, pImpl
->m_pDocSh
);
175 bool bNewUrlName
= !aFileName
.equals(aNewUrl
);
177 const SfxFilter
* pFilter
= pImpl
->m_pDocSh
->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter
);
181 ScDocument
& rDoc
= pImpl
->m_pDocSh
->GetDocument();
182 rDoc
.SetInLinkUpdate( true );
184 bool bUndo(rDoc
.IsUndoEnabled());
186 // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen
187 if (!aFilterName
.equals(rNewFilter
))
189 if ( pNewOptions
) // Optionen hart angegeben?
190 aOptions
= *pNewOptions
;
192 // ItemSet immer anlegen, damit die DocShell die Optionen setzen kann
193 SfxItemSet
* pSet
= new SfxAllItemSet( SfxGetpApp()->GetPool() );
194 if (!aOptions
.isEmpty())
195 pSet
->Put( SfxStringItem( SID_FILE_FILTEROPTIONS
, aOptions
) );
197 SfxMedium
* pMed
= new SfxMedium(aNewUrl
, STREAM_STD_READ
, pFilter
, pSet
);
199 if ( bInEdit
) // only if using the edit dialog,
200 pMed
->UseInteractionHandler(true); // enable the filter options dialog
202 // aRef->DoClose() will be called explicitly, but it is still more safe to use SfxObjectShellLock here
203 ScDocShell
* pSrcShell
= new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT
| SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS
);
204 SfxObjectShellLock aRef
= pSrcShell
;
205 pSrcShell
->DoLoad(pMed
);
207 // Optionen koennten gesetzt worden sein
208 OUString aNewOpt
= ScDocumentLoader::GetOptions(*pMed
);
209 if (aNewOpt
.isEmpty())
214 ScDocument
* pUndoDoc
= NULL
;
216 if (bAddUndo
&& bUndo
)
217 pUndoDoc
= new ScDocument( SCDOCMODE_UNDO
);
221 ScDocShellModificator
aModificator( *pImpl
->m_pDocSh
);
223 bool bNotFound
= false;
224 ScDocument
& rSrcDoc
= pSrcShell
->GetDocument();
226 // from text filters that don't set the table name,
227 // use the one table regardless of link table name
228 bool bAutoTab
= (rSrcDoc
.GetTableCount() == 1) &&
229 ScDocShell::HasAutomaticTableName( rNewFilter
);
231 SCTAB nCount
= rDoc
.GetTableCount();
232 for (SCTAB nTab
=0; nTab
<nCount
; nTab
++)
234 sal_uInt8 nMode
= rDoc
.GetLinkMode(nTab
);
235 if (nMode
&& aFileName
.equals(rDoc
.GetLinkDoc(nTab
)))
237 OUString aTabName
= rDoc
.GetLinkTab(nTab
);
241 if (bAddUndo
&& bUndo
)
244 pUndoDoc
->InitUndo( &rDoc
, nTab
, nTab
, true, true );
246 pUndoDoc
->AddUndoTab( nTab
, nTab
, true, true );
248 ScRange
aRange(0,0,nTab
,MAXCOL
,MAXROW
,nTab
);
249 rDoc
.CopyToDocument(aRange
, IDF_ALL
, false, pUndoDoc
);
250 pUndoDoc
->TransferDrawPage( &rDoc
, nTab
, nTab
);
251 pUndoDoc
->SetLink( nTab
, nMode
, aFileName
, aFilterName
,
252 aOptions
, aTabName
, GetRefreshDelay() );
253 pUndoDoc
->SetTabBgColor( nTab
, rDoc
.GetTabBgColor(nTab
) );
256 // Tabellenname einer ExtDocRef anpassen
258 if ( bNewUrlName
&& nMode
== SC_LINK_VALUE
)
261 rDoc
.GetName( nTab
, aName
);
262 if ( ScGlobal::GetpTransliteration()->isEqual(
263 ScGlobal::GetDocTabName( aFileName
, aTabName
), aName
) )
265 rDoc
.RenameTab( nTab
,
266 ScGlobal::GetDocTabName( aNewUrl
, aTabName
),
267 false, true ); // kein RefUpdate, kein ValidTabName
275 /* #i71497# check if external document is loaded successfully,
276 otherwise we may find the empty default sheet "Sheet1" in
277 rSrcDoc, even if the document does not exist. */
278 if( pMed
->GetError() == 0 )
280 // no sheet name -> use first sheet
281 if ( !aTabName
.isEmpty() && !bAutoTab
)
282 bFound
= rSrcDoc
.GetTable( aTabName
, nSrcTab
);
288 rDoc
.TransferTab( &rSrcDoc
, nSrcTab
, nTab
, false, // nicht neu einfuegen
289 (nMode
== SC_LINK_VALUE
) ); // nur Werte?
292 rDoc
.DeleteAreaTab( 0,0,MAXCOL
,MAXROW
, nTab
, IDF_ALL
);
294 bool bShowError
= true;
295 if ( nMode
== SC_LINK_VALUE
)
297 // Value link (used with external references in formulas):
298 // Look for formulas that reference the sheet, and put errors in the referenced cells.
300 ScRangeList aErrorCells
; // cells on the linked sheets that need error values
302 ScCellIterator
aIter(&rDoc
, ScRange(0,0,0,MAXCOL
,MAXROW
,MAXTAB
)); // all sheets
303 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
305 if (aIter
.getType() != CELLTYPE_FORMULA
)
308 ScFormulaCell
* pCell
= aIter
.getFormulaCell();
309 ScDetectiveRefIter
aRefIter(pCell
);
311 while ( aRefIter
.GetNextRef( aRefRange
) )
313 if ( aRefRange
.aStart
.Tab() <= nTab
&& aRefRange
.aEnd
.Tab() >= nTab
)
315 // use first cell of range references (don't fill potentially large ranges)
317 aErrorCells
.Join( ScRange( aRefRange
.aStart
) );
322 size_t nRanges
= aErrorCells
.size();
323 if ( nRanges
) // found any?
325 ScTokenArray aTokenArr
;
326 aTokenArr
.AddOpCode( ocNotAvail
);
327 aTokenArr
.AddOpCode( ocOpen
);
328 aTokenArr
.AddOpCode( ocClose
);
329 aTokenArr
.AddOpCode( ocStop
);
331 for (size_t nPos
=0; nPos
< nRanges
; nPos
++)
333 const ScRange
* pRange
= aErrorCells
[ nPos
];
334 SCCOL nStartCol
= pRange
->aStart
.Col();
335 SCROW nStartRow
= pRange
->aStart
.Row();
336 SCCOL nEndCol
= pRange
->aEnd
.Col();
337 SCROW nEndRow
= pRange
->aEnd
.Row();
338 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
; nRow
++)
339 for (SCCOL nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++)
341 ScAddress
aDestPos( nCol
, nRow
, nTab
);
342 rDoc
.SetFormula(aDestPos
, aTokenArr
);
348 // if no references were found, insert error message (don't leave the sheet empty)
353 // Normal link or no references: put error message on sheet.
355 rDoc
.SetString( 0,0,nTab
, ScGlobal::GetRscString(STR_LINKERROR
) );
356 rDoc
.SetString( 0,1,nTab
, ScGlobal::GetRscString(STR_LINKERRORFILE
) );
357 rDoc
.SetString( 1,1,nTab
, aNewUrl
);
358 rDoc
.SetString( 0,2,nTab
, ScGlobal::GetRscString(STR_LINKERRORTAB
) );
359 rDoc
.SetString( 1,2,nTab
, aTabName
);
365 if ( bNewUrlName
|| !aFilterName
.equals(rNewFilter
) ||
366 !aOptions
.equals(aNewOpt
) || pNewOptions
||
367 nNewRefresh
!= GetRefreshDelay() )
368 rDoc
.SetLink( nTab
, nMode
, aNewUrl
, rNewFilter
, aNewOpt
,
369 aTabName
, nNewRefresh
);
373 // neue Einstellungen merken
377 if (!aFilterName
.equals(rNewFilter
))
378 aFilterName
= rNewFilter
;
379 if (!aOptions
.equals(aNewOpt
))
388 if (bAddUndo
&& bUndo
)
389 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction(
390 new ScUndoRefreshLink( pImpl
->m_pDocSh
, pUndoDoc
) );
392 // Paint (koennen mehrere Tabellen sein)
396 pImpl
->m_pDocSh
->PostPaint( ScRange(0,0,0,MAXCOL
,MAXROW
,MAXTAB
),
397 PAINT_GRID
| PAINT_TOP
| PAINT_LEFT
| PAINT_EXTRAS
);
398 aModificator
.SetDocumentModified();
403 //! Fehler ausgeben ?
406 rDoc
.SetInLinkUpdate( false );
408 // notify Uno objects (for XRefreshListener)
409 //! also notify Uno objects if file name was changed!
410 ScLinkRefreshedHint aHint
;
411 aHint
.SetSheetLink( aFileName
);
412 rDoc
.BroadcastUno( aHint
);
417 IMPL_LINK_NOARG_TYPED(ScTableLink
, RefreshHdl
, Timer
*, void)
419 Refresh( aFileName
, aFilterName
, NULL
, GetRefreshDelay() );
422 IMPL_LINK( ScTableLink
, TableEndEditHdl
, ::sfx2::SvBaseLink
*, pLink
)
424 if ( pImpl
->m_aEndEditLink
.IsSet() )
425 pImpl
->m_aEndEditLink
.Call( pLink
);
427 Application::SetDefDialogParent( pImpl
->m_pOldParent
);
431 // === ScDocumentLoader ==================================================
433 OUString
ScDocumentLoader::GetOptions( SfxMedium
& rMedium
)
435 SfxItemSet
* pSet
= rMedium
.GetItemSet();
436 const SfxPoolItem
* pItem
;
437 if ( pSet
&& SfxItemState::SET
== pSet
->GetItemState( SID_FILE_FILTEROPTIONS
, true, &pItem
) )
438 return static_cast<const SfxStringItem
*>(pItem
)->GetValue();
440 return EMPTY_OUSTRING
;
443 bool ScDocumentLoader::GetFilterName( const OUString
& rFileName
,
444 OUString
& rFilter
, OUString
& rOptions
,
445 bool bWithContent
, bool bWithInteraction
)
447 TypeId aScType
= TYPE(ScDocShell
);
448 SfxObjectShell
* pDocSh
= SfxObjectShell::GetFirst( &aScType
);
451 if ( pDocSh
->HasName() )
453 SfxMedium
* pMed
= pDocSh
->GetMedium();
454 if ( pMed
->GetName().equals(rFileName
) )
456 rFilter
= pMed
->GetFilter()->GetFilterName();
457 rOptions
= GetOptions(*pMed
);
461 pDocSh
= SfxObjectShell::GetNext( *pDocSh
, &aScType
);
464 INetURLObject
aUrl( rFileName
);
465 INetProtocol eProt
= aUrl
.GetProtocol();
466 if ( eProt
== INetProtocol::NotValid
) // invalid URL?
467 return false; // abort without creating a medium
471 const SfxFilter
* pSfxFilter
= NULL
;
472 SfxMedium
* pMedium
= new SfxMedium( rFileName
, STREAM_STD_READ
);
473 if ( pMedium
->GetError() == ERRCODE_NONE
)
475 if ( bWithInteraction
)
476 pMedium
->UseInteractionHandler(true); // #i73992# no longer called from GuessFilter
478 SfxFilterMatcher
aMatcher("scalc");
480 aMatcher
.GuessFilter( *pMedium
, &pSfxFilter
);
482 aMatcher
.GuessFilterIgnoringContent( *pMedium
, &pSfxFilter
);
486 if ( pMedium
->GetError() == ERRCODE_NONE
)
489 rFilter
= pSfxFilter
->GetFilterName();
491 rFilter
= ScDocShell::GetOwnFilterName(); // sonst Calc-Datei
492 bOK
= !rFilter
.isEmpty();
499 void ScDocumentLoader::RemoveAppPrefix( OUString
& rFilterName
)
501 OUString
aAppPrefix( STRING_SCAPP
": ");
502 if (rFilterName
.startsWith( aAppPrefix
))
503 rFilterName
= rFilterName
.copy( aAppPrefix
.getLength());
506 SfxMedium
* ScDocumentLoader::CreateMedium( const OUString
& rFileName
, const SfxFilter
* pFilter
,
507 const OUString
& rOptions
)
509 // Always create SfxItemSet so ScDocShell can set options.
510 SfxItemSet
* pSet
= new SfxAllItemSet( SfxGetpApp()->GetPool() );
511 if ( !rOptions
.isEmpty() )
512 pSet
->Put( SfxStringItem( SID_FILE_FILTEROPTIONS
, rOptions
) );
514 return new SfxMedium( rFileName
, STREAM_STD_READ
, pFilter
, pSet
);
517 ScDocumentLoader::ScDocumentLoader( const OUString
& rFileName
,
518 OUString
& rFilterName
, OUString
& rOptions
,
519 sal_uInt32 nRekCnt
, bool bWithInteraction
) :
523 if ( rFilterName
.isEmpty() )
524 GetFilterName( rFileName
, rFilterName
, rOptions
, true, bWithInteraction
);
526 const SfxFilter
* pFilter
= ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( rFilterName
);
528 pMedium
= CreateMedium( rFileName
, pFilter
, rOptions
);
529 if ( pMedium
->GetError() != ERRCODE_NONE
)
532 if ( bWithInteraction
)
533 pMedium
->UseInteractionHandler( true ); // to enable the filter options dialog
535 pDocShell
= new ScDocShell( SfxModelFlags::EMBEDDED_OBJECT
| SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS
);
538 ScDocument
& rDoc
= pDocShell
->GetDocument();
539 ScExtDocOptions
* pExtDocOpt
= rDoc
.GetExtDocOptions();
542 pExtDocOpt
= new ScExtDocOptions
;
543 rDoc
.SetExtDocOptions( pExtDocOpt
);
545 pExtDocOpt
->GetDocSettings().mnLinkCnt
= nRekCnt
;
547 pDocShell
->DoLoad( pMedium
);
549 OUString aNew
= GetOptions(*pMedium
); // Optionen werden beim Laden per Dialog gesetzt
550 if (!aNew
.isEmpty() && aNew
!= rOptions
)
554 ScDocumentLoader::~ScDocumentLoader()
562 void ScDocumentLoader::ReleaseDocRef()
566 // release reference without calling DoClose - caller must
567 // have another reference to the doc and call DoClose later
575 ScDocument
* ScDocumentLoader::GetDocument()
577 return pDocShell
? &pDocShell
->GetDocument() : 0;
580 bool ScDocumentLoader::IsError() const
582 if ( pDocShell
&& pMedium
)
583 return pMedium
->GetError() != ERRCODE_NONE
;
588 OUString
ScDocumentLoader::GetTitle() const
591 return pDocShell
->GetTitle();
593 return EMPTY_OUSTRING
;
596 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */