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"
54 TableLink_Impl() : m_pDocSh( NULL
), m_pOldParent( NULL
) {}
57 TYPEINIT1(ScTableLink
, ::sfx2::SvBaseLink
);
59 //------------------------------------------------------------------------
61 ScTableLink::ScTableLink(ScDocShell
* pDocSh
, const OUString
& rFile
,
62 const OUString
& rFilter
, const OUString
& rOpt
,
64 ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL
,FORMAT_FILE
),
65 ScRefreshTimer( nRefresh
),
66 pImpl( new TableLink_Impl
),
75 pImpl
->m_pDocSh
= pDocSh
;
78 ScTableLink::ScTableLink(SfxObjectShell
* pShell
, const OUString
& rFile
,
79 const OUString
& rFilter
, const OUString
& rOpt
,
81 ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL
,FORMAT_FILE
),
82 ScRefreshTimer( nRefresh
),
83 pImpl( new TableLink_Impl
),
92 pImpl
->m_pDocSh
= static_cast< ScDocShell
* >( pShell
);
93 SetRefreshHandler( LINK( this, ScTableLink
, RefreshHdl
) );
94 SetRefreshControl( pImpl
->m_pDocSh
->GetDocument()->GetRefreshTimerControlAddress() );
97 ScTableLink::~ScTableLink()
99 // Verbindung aufheben
103 ScDocument
* pDoc
= pImpl
->m_pDocSh
->GetDocument();
104 SCTAB nCount
= pDoc
->GetTableCount();
105 for (SCTAB nTab
=0; nTab
<nCount
; nTab
++)
106 if (pDoc
->IsLinked(nTab
) && aFileName
.equals(pDoc
->GetLinkDoc(nTab
)))
107 pDoc
->SetLink( nTab
, SC_LINK_NONE
, aEmpty
, aEmpty
, aEmpty
, aEmpty
, 0 );
111 void ScTableLink::Edit( Window
* pParent
, const Link
& rEndEditHdl
)
113 // DefModalDialogParent setzen, weil evtl. aus der DocShell beim ConvertFrom
114 // ein Optionen-Dialog kommt...
116 pImpl
->m_aEndEditLink
= rEndEditHdl
;
117 pImpl
->m_pOldParent
= Application::GetDefDialogParent();
119 Application::SetDefDialogParent(pParent
);
122 SvBaseLink::Edit( pParent
, LINK( this, ScTableLink
, TableEndEditHdl
) );
125 ::sfx2::SvBaseLink::UpdateResult
ScTableLink::DataChanged(
126 const OUString
&, const ::com::sun::star::uno::Any
& )
128 sfx2::LinkManager
* pLinkManager
=pImpl
->m_pDocSh
->GetDocument()->GetLinkManager();
129 if (pLinkManager
!=NULL
)
131 OUString aFile
, aFilter
;
132 pLinkManager
->GetDisplayNames(this, 0, &aFile
, NULL
, &aFilter
);
134 // the file dialog returns the filter name with the application prefix
136 ScDocumentLoader::RemoveAppPrefix( aFilter
);
139 Refresh( aFile
, aFilter
, NULL
, GetRefreshDelay() ); // don't load twice
144 void ScTableLink::Closed()
146 // Verknuepfung loeschen: Undo
147 ScDocument
* pDoc
= pImpl
->m_pDocSh
->GetDocument();
148 sal_Bool
bUndo (pDoc
->IsUndoEnabled());
150 if (bAddUndo
&& bUndo
)
152 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction(
153 new ScUndoRemoveLink( pImpl
->m_pDocSh
, aFileName
) );
155 bAddUndo
= false; // nur einmal
158 // Verbindung wird im dtor aufgehoben
160 SvBaseLink::Closed();
163 sal_Bool
ScTableLink::IsUsed() const
165 return pImpl
->m_pDocSh
->GetDocument()->HasLink( aFileName
, aFilterName
, aOptions
);
168 sal_Bool
ScTableLink::Refresh(const OUString
& rNewFile
, const OUString
& rNewFilter
,
169 const OUString
* pNewOptions
, sal_uLong nNewRefresh
)
173 if (rNewFile
.isEmpty() || rNewFilter
.isEmpty())
176 OUString aNewUrl
= ScGlobal::GetAbsDocName(rNewFile
, pImpl
->m_pDocSh
);
177 bool bNewUrlName
= !aFileName
.equals(aNewUrl
);
179 const SfxFilter
* pFilter
= pImpl
->m_pDocSh
->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter
);
183 ScDocument
* pDoc
= pImpl
->m_pDocSh
->GetDocument();
184 pDoc
->SetInLinkUpdate( true );
186 sal_Bool
bUndo(pDoc
->IsUndoEnabled());
188 // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen
189 if (!aFilterName
.equals(rNewFilter
))
190 aOptions
= OUString();
191 if ( pNewOptions
) // Optionen hart angegeben?
192 aOptions
= *pNewOptions
;
194 // ItemSet immer anlegen, damit die DocShell die Optionen setzen kann
195 SfxItemSet
* pSet
= new SfxAllItemSet( SFX_APP()->GetPool() );
196 if (!aOptions
.isEmpty())
197 pSet
->Put( SfxStringItem( SID_FILE_FILTEROPTIONS
, aOptions
) );
199 SfxMedium
* pMed
= new SfxMedium(aNewUrl
, STREAM_STD_READ
, pFilter
, pSet
);
201 if ( bInEdit
) // only if using the edit dialog,
202 pMed
->UseInteractionHandler(true); // enable the filter options dialog
204 // aRef->DoClose() will be called explicitly, but it is still more safe to use SfxObjectShellLock here
205 ScDocShell
* pSrcShell
= new ScDocShell(SFX_CREATE_MODE_INTERNAL
);
206 SfxObjectShellLock aRef
= pSrcShell
;
207 pSrcShell
->DoLoad(pMed
);
209 // Optionen koennten gesetzt worden sein
210 OUString aNewOpt
= ScDocumentLoader::GetOptions(*pMed
);
211 if (aNewOpt
.isEmpty())
216 ScDocument
* pUndoDoc
= NULL
;
217 sal_Bool bFirst
= true;
218 if (bAddUndo
&& bUndo
)
219 pUndoDoc
= new ScDocument( SCDOCMODE_UNDO
);
223 ScDocShellModificator
aModificator( *pImpl
->m_pDocSh
);
225 sal_Bool bNotFound
= false;
226 ScDocument
* pSrcDoc
= pSrcShell
->GetDocument();
228 // from text filters that don't set the table name,
229 // use the one table regardless of link table name
230 sal_Bool bAutoTab
= (pSrcDoc
->GetTableCount() == 1) &&
231 ScDocShell::HasAutomaticTableName( rNewFilter
);
233 SCTAB nCount
= pDoc
->GetTableCount();
234 for (SCTAB nTab
=0; nTab
<nCount
; nTab
++)
236 sal_uInt8 nMode
= pDoc
->GetLinkMode(nTab
);
237 if (nMode
&& aFileName
.equals(pDoc
->GetLinkDoc(nTab
)))
239 OUString aTabName
= pDoc
->GetLinkTab(nTab
);
243 if (bAddUndo
&& bUndo
)
246 pUndoDoc
->InitUndo( pDoc
, nTab
, nTab
, true, true );
248 pUndoDoc
->AddUndoTab( nTab
, nTab
, true, true );
250 ScRange
aRange(0,0,nTab
,MAXCOL
,MAXROW
,nTab
);
251 pDoc
->CopyToDocument(aRange
, IDF_ALL
, false, pUndoDoc
);
252 pUndoDoc
->TransferDrawPage( pDoc
, nTab
, nTab
);
253 pUndoDoc
->SetLink( nTab
, nMode
, aFileName
, aFilterName
,
254 aOptions
, aTabName
, GetRefreshDelay() );
255 pUndoDoc
->SetTabBgColor( nTab
, pDoc
->GetTabBgColor(nTab
) );
258 // Tabellenname einer ExtDocRef anpassen
260 if ( bNewUrlName
&& nMode
== SC_LINK_VALUE
)
263 pDoc
->GetName( nTab
, aName
);
264 if ( ScGlobal::GetpTransliteration()->isEqual(
265 ScGlobal::GetDocTabName( aFileName
, aTabName
), aName
) )
267 pDoc
->RenameTab( nTab
,
268 ScGlobal::GetDocTabName( aNewUrl
, aTabName
),
269 false, true ); // kein RefUpdate, kein ValidTabName
277 /* #i71497# check if external document is loaded successfully,
278 otherwise we may find the empty default sheet "Sheet1" in
279 pSrcDoc, even if the document does not exist. */
280 if( pMed
->GetError() == 0 )
282 // no sheet name -> use first sheet
283 if ( !aTabName
.isEmpty() && !bAutoTab
)
284 bFound
= pSrcDoc
->GetTable( aTabName
, nSrcTab
);
290 pDoc
->TransferTab( pSrcDoc
, nSrcTab
, nTab
, false, // nicht neu einfuegen
291 (nMode
== SC_LINK_VALUE
) ); // nur Werte?
294 pDoc
->DeleteAreaTab( 0,0,MAXCOL
,MAXROW
, nTab
, IDF_ALL
);
296 bool bShowError
= true;
297 if ( nMode
== SC_LINK_VALUE
)
299 // Value link (used with external references in formulas):
300 // Look for formulas that reference the sheet, and put errors in the referenced cells.
302 ScRangeList aErrorCells
; // cells on the linked sheets that need error values
304 ScCellIterator
aIter(pDoc
, ScRange(0,0,0,MAXCOL
,MAXROW
,MAXTAB
)); // all sheets
305 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
307 if (aIter
.getType() != CELLTYPE_FORMULA
)
310 ScFormulaCell
* pCell
= aIter
.getFormulaCell();
311 ScDetectiveRefIter
aRefIter(pCell
);
313 while ( aRefIter
.GetNextRef( aRefRange
) )
315 if ( aRefRange
.aStart
.Tab() <= nTab
&& aRefRange
.aEnd
.Tab() >= nTab
)
317 // use first cell of range references (don't fill potentially large ranges)
319 aErrorCells
.Join( ScRange( aRefRange
.aStart
) );
324 size_t nRanges
= aErrorCells
.size();
325 if ( nRanges
) // found any?
327 ScTokenArray aTokenArr
;
328 aTokenArr
.AddOpCode( ocNotAvail
);
329 aTokenArr
.AddOpCode( ocOpen
);
330 aTokenArr
.AddOpCode( ocClose
);
331 aTokenArr
.AddOpCode( ocStop
);
333 for (size_t nPos
=0; nPos
< nRanges
; nPos
++)
335 const ScRange
* pRange
= aErrorCells
[ nPos
];
336 SCCOL nStartCol
= pRange
->aStart
.Col();
337 SCROW nStartRow
= pRange
->aStart
.Row();
338 SCCOL nEndCol
= pRange
->aEnd
.Col();
339 SCROW nEndRow
= pRange
->aEnd
.Row();
340 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
; nRow
++)
341 for (SCCOL nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++)
343 ScAddress
aDestPos( nCol
, nRow
, nTab
);
344 pDoc
->SetFormula(aDestPos
, aTokenArr
);
350 // if no references were found, insert error message (don't leave the sheet empty)
355 // Normal link or no references: put error message on sheet.
357 pDoc
->SetString( 0,0,nTab
, ScGlobal::GetRscString(STR_LINKERROR
) );
358 pDoc
->SetString( 0,1,nTab
, ScGlobal::GetRscString(STR_LINKERRORFILE
) );
359 pDoc
->SetString( 1,1,nTab
, aNewUrl
);
360 pDoc
->SetString( 0,2,nTab
, ScGlobal::GetRscString(STR_LINKERRORTAB
) );
361 pDoc
->SetString( 1,2,nTab
, aTabName
);
367 if ( bNewUrlName
|| !aFilterName
.equals(rNewFilter
) ||
368 !aOptions
.equals(aNewOpt
) || pNewOptions
||
369 nNewRefresh
!= GetRefreshDelay() )
370 pDoc
->SetLink( nTab
, nMode
, aNewUrl
, rNewFilter
, aNewOpt
,
371 aTabName
, nNewRefresh
);
375 // neue Einstellungen merken
379 if (!aFilterName
.equals(rNewFilter
))
380 aFilterName
= rNewFilter
;
381 if (!aOptions
.equals(aNewOpt
))
390 if (bAddUndo
&& bUndo
)
391 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction(
392 new ScUndoRefreshLink( pImpl
->m_pDocSh
, pUndoDoc
) );
394 // Paint (koennen mehrere Tabellen sein)
398 pImpl
->m_pDocSh
->PostPaint( ScRange(0,0,0,MAXCOL
,MAXROW
,MAXTAB
),
399 PAINT_GRID
| PAINT_TOP
| PAINT_LEFT
| PAINT_EXTRAS
);
400 aModificator
.SetDocumentModified();
405 //! Fehler ausgeben ?
408 pDoc
->SetInLinkUpdate( false );
410 // notify Uno objects (for XRefreshListener)
411 //! also notify Uno objects if file name was changed!
412 ScLinkRefreshedHint aHint
;
413 aHint
.SetSheetLink( aFileName
);
414 pDoc
->BroadcastUno( aHint
);
419 IMPL_LINK_NOARG(ScTableLink
, RefreshHdl
)
421 long nRes
= Refresh( aFileName
, aFilterName
, NULL
, GetRefreshDelay() ) != 0;
425 IMPL_LINK( ScTableLink
, TableEndEditHdl
, ::sfx2::SvBaseLink
*, pLink
)
427 if ( pImpl
->m_aEndEditLink
.IsSet() )
428 pImpl
->m_aEndEditLink
.Call( pLink
);
430 Application::SetDefDialogParent( pImpl
->m_pOldParent
);
434 // === ScDocumentLoader ==================================================
436 OUString
ScDocumentLoader::GetOptions( SfxMedium
& rMedium
)
438 SfxItemSet
* pSet
= rMedium
.GetItemSet();
439 const SfxPoolItem
* pItem
;
440 if ( pSet
&& SFX_ITEM_SET
== pSet
->GetItemState( SID_FILE_FILTEROPTIONS
, true, &pItem
) )
441 return ((const SfxStringItem
*)pItem
)->GetValue();
443 return EMPTY_OUSTRING
;
446 bool ScDocumentLoader::GetFilterName( const OUString
& rFileName
,
447 OUString
& rFilter
, OUString
& rOptions
,
448 bool bWithContent
, bool bWithInteraction
)
450 TypeId aScType
= TYPE(ScDocShell
);
451 SfxObjectShell
* pDocSh
= SfxObjectShell::GetFirst( &aScType
);
454 if ( pDocSh
->HasName() )
456 SfxMedium
* pMed
= pDocSh
->GetMedium();
457 if ( pMed
->GetName().equals(rFileName
) )
459 rFilter
= pMed
->GetFilter()->GetFilterName();
460 rOptions
= GetOptions(*pMed
);
464 pDocSh
= SfxObjectShell::GetNext( *pDocSh
, &aScType
);
467 INetURLObject
aUrl( rFileName
);
468 INetProtocol eProt
= aUrl
.GetProtocol();
469 if ( eProt
== INET_PROT_NOT_VALID
) // invalid URL?
470 return false; // abort without creating a medium
474 const SfxFilter
* pSfxFilter
= NULL
;
475 SfxMedium
* pMedium
= new SfxMedium( rFileName
, STREAM_STD_READ
);
476 if ( pMedium
->GetError() == ERRCODE_NONE
)
478 if ( bWithInteraction
)
479 pMedium
->UseInteractionHandler(true); // #i73992# no longer called from GuessFilter
481 SfxFilterMatcher
aMatcher("scalc");
483 aMatcher
.GuessFilter( *pMedium
, &pSfxFilter
);
485 aMatcher
.GuessFilterIgnoringContent( *pMedium
, &pSfxFilter
);
488 sal_Bool bOK
= false;
489 if ( pMedium
->GetError() == ERRCODE_NONE
)
492 rFilter
= pSfxFilter
->GetFilterName();
494 rFilter
= ScDocShell::GetOwnFilterName(); // sonst Calc-Datei
495 bOK
= !rFilter
.isEmpty();
502 void ScDocumentLoader::RemoveAppPrefix( OUString
& rFilterName
)
504 OUString
aAppPrefix( STRING_SCAPP
": ");
505 if (rFilterName
.startsWith( aAppPrefix
))
506 rFilterName
= rFilterName
.copy( aAppPrefix
.getLength());
509 SfxMedium
* ScDocumentLoader::CreateMedium( const OUString
& rFileName
, const SfxFilter
* pFilter
,
510 const OUString
& rOptions
)
512 // Always create SfxItemSet so ScDocShell can set options.
513 SfxItemSet
* pSet
= new SfxAllItemSet( SFX_APP()->GetPool() );
514 if ( !rOptions
.isEmpty() )
515 pSet
->Put( SfxStringItem( SID_FILE_FILTEROPTIONS
, rOptions
) );
517 return new SfxMedium( rFileName
, STREAM_STD_READ
, pFilter
, pSet
);
520 ScDocumentLoader::ScDocumentLoader( const OUString
& rFileName
,
521 OUString
& rFilterName
, OUString
& rOptions
,
522 sal_uInt32 nRekCnt
, bool bWithInteraction
) :
526 if ( rFilterName
.isEmpty() )
527 GetFilterName( rFileName
, rFilterName
, rOptions
, true, bWithInteraction
);
529 const SfxFilter
* pFilter
= ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( rFilterName
);
531 pMedium
= CreateMedium( rFileName
, pFilter
, rOptions
);
532 if ( pMedium
->GetError() != ERRCODE_NONE
)
535 if ( bWithInteraction
)
536 pMedium
->UseInteractionHandler( true ); // to enable the filter options dialog
538 pDocShell
= new ScDocShell( SFX_CREATE_MODE_INTERNAL
);
541 ScDocument
* pDoc
= pDocShell
->GetDocument();
544 ScExtDocOptions
* pExtDocOpt
= pDoc
->GetExtDocOptions();
547 pExtDocOpt
= new ScExtDocOptions
;
548 pDoc
->SetExtDocOptions( pExtDocOpt
);
550 pExtDocOpt
->GetDocSettings().mnLinkCnt
= nRekCnt
;
553 pDocShell
->DoLoad( pMedium
);
555 OUString aNew
= GetOptions(*pMedium
); // Optionen werden beim Laden per Dialog gesetzt
556 if (!aNew
.isEmpty() && aNew
!= rOptions
)
560 ScDocumentLoader::~ScDocumentLoader()
568 void ScDocumentLoader::ReleaseDocRef()
572 // release reference without calling DoClose - caller must
573 // have another reference to the doc and call DoClose later
581 ScDocument
* ScDocumentLoader::GetDocument()
583 return pDocShell
? pDocShell
->GetDocument() : 0;
586 bool ScDocumentLoader::IsError() const
588 if ( pDocShell
&& pMedium
)
589 return pMedium
->GetError() != ERRCODE_NONE
;
594 OUString
ScDocumentLoader::GetTitle() const
597 return pDocShell
->GetTitle();
599 return EMPTY_OUSTRING
;
602 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */