1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: tablink.cxx,v $
10 * $Revision: 1.31.32.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 #pragma optimize("",off)
37 //------------------------------------------------------------------
41 // INCLUDE ---------------------------------------------------------
43 #include <sfx2/sfxsids.hrc>
44 #include <sfx2/app.hxx>
45 #include <svtools/itemset.hxx>
46 #include <svtools/stritem.hxx>
47 #include <sfx2/docfile.hxx>
48 #include <sfx2/docfilt.hxx>
49 #include <sfx2/fcontnr.hxx>
50 #include <svx/linkmgr.hxx>
51 #include <tools/urlobj.hxx>
52 #include <unotools/transliterationwrapper.hxx>
54 #include "tablink.hxx"
56 #include "scextopt.hxx"
58 #include "document.hxx"
60 #include "globstr.hrc"
61 #include "undoblk.hxx"
62 #include "undotab.hxx"
66 #include "dociter.hxx"
67 #include "formula/opcode.hxx"
75 TableLink_Impl() : m_pDocSh( NULL
), m_pOldParent( NULL
) {}
78 TYPEINIT1(ScTableLink
, ::sfx2::SvBaseLink
);
80 //------------------------------------------------------------------------
82 ScTableLink::ScTableLink(ScDocShell
* pDocSh
, const String
& rFile
,
83 const String
& rFilter
, const String
& rOpt
,
85 ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL
,FORMAT_FILE
),
86 ScRefreshTimer( nRefresh
),
87 pImpl( new TableLink_Impl
),
96 pImpl
->m_pDocSh
= pDocSh
;
99 ScTableLink::ScTableLink(SfxObjectShell
* pShell
, const String
& rFile
,
100 const String
& rFilter
, const String
& rOpt
,
102 ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL
,FORMAT_FILE
),
103 ScRefreshTimer( nRefresh
),
104 pImpl( new TableLink_Impl
),
106 aFilterName(rFilter
),
113 pImpl
->m_pDocSh
= static_cast< ScDocShell
* >( pShell
);
114 SetRefreshHandler( LINK( this, ScTableLink
, RefreshHdl
) );
115 SetRefreshControl( pImpl
->m_pDocSh
->GetDocument()->GetRefreshTimerControlAddress() );
118 __EXPORT
ScTableLink::~ScTableLink()
120 // Verbindung aufheben
124 ScDocument
* pDoc
= pImpl
->m_pDocSh
->GetDocument();
125 SCTAB nCount
= pDoc
->GetTableCount();
126 for (SCTAB nTab
=0; nTab
<nCount
; nTab
++)
127 if (pDoc
->IsLinked(nTab
) && pDoc
->GetLinkDoc(nTab
)==aFileName
)
128 pDoc
->SetLink( nTab
, SC_LINK_NONE
, aEmpty
, aEmpty
, aEmpty
, aEmpty
, 0 );
132 void __EXPORT
ScTableLink::Edit( Window
* pParent
, const Link
& rEndEditHdl
)
134 // DefModalDialogParent setzen, weil evtl. aus der DocShell beim ConvertFrom
135 // ein Optionen-Dialog kommt...
137 pImpl
->m_aEndEditLink
= rEndEditHdl
;
138 pImpl
->m_pOldParent
= Application::GetDefDialogParent();
140 Application::SetDefDialogParent(pParent
);
143 SvBaseLink::Edit( pParent
, LINK( this, ScTableLink
, TableEndEditHdl
) );
146 void __EXPORT
ScTableLink::DataChanged( const String
&,
147 const ::com::sun::star::uno::Any
& )
149 SvxLinkManager
* pLinkManager
=pImpl
->m_pDocSh
->GetDocument()->GetLinkManager();
150 if (pLinkManager
!=NULL
)
154 pLinkManager
->GetDisplayNames( this,0,&aFile
,NULL
,&aFilter
);
156 // the file dialog returns the filter name with the application prefix
158 ScDocumentLoader::RemoveAppPrefix( aFilter
);
161 Refresh( aFile
, aFilter
, NULL
, GetRefreshDelay() ); // don't load twice
165 void __EXPORT
ScTableLink::Closed()
167 // Verknuepfung loeschen: Undo
168 ScDocument
* pDoc
= pImpl
->m_pDocSh
->GetDocument();
169 BOOL
bUndo (pDoc
->IsUndoEnabled());
171 if (bAddUndo
&& bUndo
)
173 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction(
174 new ScUndoRemoveLink( pImpl
->m_pDocSh
, aFileName
) );
176 bAddUndo
= FALSE
; // nur einmal
179 // Verbindung wird im dtor aufgehoben
181 SvBaseLink::Closed();
184 BOOL
ScTableLink::IsUsed() const
186 return pImpl
->m_pDocSh
->GetDocument()->HasLink( aFileName
, aFilterName
, aOptions
);
189 BOOL
ScTableLink::Refresh(const String
& rNewFile
, const String
& rNewFilter
,
190 const String
* pNewOptions
, ULONG nNewRefresh
)
194 if (!rNewFile
.Len() || !rNewFilter
.Len())
197 String
aNewUrl( ScGlobal::GetAbsDocName( rNewFile
, pImpl
->m_pDocSh
) );
198 BOOL bNewUrlName
= (aNewUrl
!= aFileName
);
200 const SfxFilter
* pFilter
= pImpl
->m_pDocSh
->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter
);
204 ScDocument
* pDoc
= pImpl
->m_pDocSh
->GetDocument();
205 pDoc
->SetInLinkUpdate( TRUE
);
207 BOOL
bUndo(pDoc
->IsUndoEnabled());
209 // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen
210 if ( rNewFilter
!= aFilterName
)
212 if ( pNewOptions
) // Optionen hart angegeben?
213 aOptions
= *pNewOptions
;
215 // ItemSet immer anlegen, damit die DocShell die Optionen setzen kann
216 SfxItemSet
* pSet
= new SfxAllItemSet( SFX_APP()->GetPool() );
217 if ( aOptions
.Len() )
218 pSet
->Put( SfxStringItem( SID_FILE_FILTEROPTIONS
, aOptions
) );
220 SfxMedium
* pMed
= new SfxMedium(aNewUrl
, STREAM_STD_READ
, FALSE
, pFilter
, pSet
);
222 if ( bInEdit
) // only if using the edit dialog,
223 pMed
->UseInteractionHandler( TRUE
); // enable the filter options dialog
225 ScDocShell
* pSrcShell
= new ScDocShell(SFX_CREATE_MODE_INTERNAL
);
226 SfxObjectShellRef aRef
= pSrcShell
;
227 pSrcShell
->DoLoad(pMed
);
229 // Optionen koennten gesetzt worden sein
230 String aNewOpt
= ScDocumentLoader::GetOptions(*pMed
);
236 ScDocument
* pUndoDoc
= NULL
;
238 if (bAddUndo
&& bUndo
)
239 pUndoDoc
= new ScDocument( SCDOCMODE_UNDO
);
243 ScDocShellModificator
aModificator( *pImpl
->m_pDocSh
);
245 BOOL bNotFound
= FALSE
;
246 ScDocument
* pSrcDoc
= pSrcShell
->GetDocument();
248 // #74835# from text filters that don't set the table name,
249 // use the one table regardless of link table name
250 BOOL bAutoTab
= (pSrcDoc
->GetTableCount() == 1) &&
251 ScDocShell::HasAutomaticTableName( rNewFilter
);
253 SCTAB nCount
= pDoc
->GetTableCount();
254 for (SCTAB nTab
=0; nTab
<nCount
; nTab
++)
256 BYTE nMode
= pDoc
->GetLinkMode(nTab
);
257 if (nMode
&& pDoc
->GetLinkDoc(nTab
)==aFileName
)
259 String aTabName
= pDoc
->GetLinkTab(nTab
);
263 if (bAddUndo
&& bUndo
)
266 pUndoDoc
->InitUndo( pDoc
, nTab
, nTab
, TRUE
, TRUE
);
268 pUndoDoc
->AddUndoTab( nTab
, nTab
, TRUE
, TRUE
);
270 ScRange
aRange(0,0,nTab
,MAXCOL
,MAXROW
,nTab
);
271 pDoc
->CopyToDocument(aRange
, IDF_ALL
, FALSE
, pUndoDoc
);
272 pUndoDoc
->TransferDrawPage( pDoc
, nTab
, nTab
);
273 pUndoDoc
->SetLink( nTab
, nMode
, aFileName
, aFilterName
,
274 aOptions
, aTabName
, GetRefreshDelay() );
277 // Tabellenname einer ExtDocRef anpassen
279 if ( bNewUrlName
&& nMode
== SC_LINK_VALUE
)
282 pDoc
->GetName( nTab
, aName
);
283 if ( ScGlobal::GetpTransliteration()->isEqual(
284 ScGlobal::GetDocTabName( aFileName
, aTabName
), aName
) )
286 pDoc
->RenameTab( nTab
,
287 ScGlobal::GetDocTabName( aNewUrl
, aTabName
),
288 FALSE
, TRUE
); // kein RefUpdate, kein ValidTabName
296 /* #i71497# check if external document is loaded successfully,
297 otherwise we may find the empty default sheet "Sheet1" in
298 pSrcDoc, even if the document does not exist. */
299 if( pMed
->GetError() == 0 )
301 // no sheet name -> use first sheet
302 if ( aTabName
.Len() && !bAutoTab
)
303 bFound
= pSrcDoc
->GetTable( aTabName
, nSrcTab
);
309 pDoc
->TransferTab( pSrcDoc
, nSrcTab
, nTab
, FALSE
, // nicht neu einfuegen
310 (nMode
== SC_LINK_VALUE
) ); // nur Werte?
313 pDoc
->DeleteAreaTab( 0,0,MAXCOL
,MAXROW
, nTab
, IDF_ALL
);
315 bool bShowError
= true;
316 if ( nMode
== SC_LINK_VALUE
)
318 // #139464# Value link (used with external references in formulas):
319 // Look for formulas that reference the sheet, and put errors in the referenced cells.
321 ScRangeList aErrorCells
; // cells on the linked sheets that need error values
323 ScCellIterator
aCellIter( pDoc
, 0,0,0, MAXCOL
,MAXROW
,MAXTAB
); // all sheets
324 ScBaseCell
* pCell
= aCellIter
.GetFirst();
327 if (pCell
->GetCellType() == CELLTYPE_FORMULA
)
329 ScFormulaCell
* pFCell
= static_cast<ScFormulaCell
*>(pCell
);
331 ScDetectiveRefIter
aRefIter( pFCell
);
333 while ( aRefIter
.GetNextRef( aRefRange
) )
335 if ( aRefRange
.aStart
.Tab() <= nTab
&& aRefRange
.aEnd
.Tab() >= nTab
)
337 // use first cell of range references (don't fill potentially large ranges)
339 aErrorCells
.Join( ScRange( aRefRange
.aStart
) );
343 pCell
= aCellIter
.GetNext();
346 ULONG nRanges
= aErrorCells
.Count();
347 if ( nRanges
) // found any?
349 ScTokenArray aTokenArr
;
350 aTokenArr
.AddOpCode( ocNotAvail
);
351 aTokenArr
.AddOpCode( ocOpen
);
352 aTokenArr
.AddOpCode( ocClose
);
353 aTokenArr
.AddOpCode( ocStop
);
355 for (ULONG nPos
=0; nPos
<nRanges
; nPos
++)
357 const ScRange
* pRange
= aErrorCells
.GetObject(nPos
);
358 SCCOL nStartCol
= pRange
->aStart
.Col();
359 SCROW nStartRow
= pRange
->aStart
.Row();
360 SCCOL nEndCol
= pRange
->aEnd
.Col();
361 SCROW nEndRow
= pRange
->aEnd
.Row();
362 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
; nRow
++)
363 for (SCCOL nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++)
365 ScAddress
aDestPos( nCol
, nRow
, nTab
);
366 ScFormulaCell
* pNewCell
= new ScFormulaCell( pDoc
, aDestPos
, &aTokenArr
);
367 pDoc
->PutCell( aDestPos
, pNewCell
);
373 // if no references were found, insert error message (don't leave the sheet empty)
378 // Normal link or no references: put error message on sheet.
380 pDoc
->SetString( 0,0,nTab
, ScGlobal::GetRscString(STR_LINKERROR
) );
381 pDoc
->SetString( 0,1,nTab
, ScGlobal::GetRscString(STR_LINKERRORFILE
) );
382 pDoc
->SetString( 1,1,nTab
, aNewUrl
);
383 pDoc
->SetString( 0,2,nTab
, ScGlobal::GetRscString(STR_LINKERRORTAB
) );
384 pDoc
->SetString( 1,2,nTab
, aTabName
);
390 if ( bNewUrlName
|| rNewFilter
!= aFilterName
||
391 aNewOpt
!= aOptions
|| pNewOptions
||
392 nNewRefresh
!= GetRefreshDelay() )
393 pDoc
->SetLink( nTab
, nMode
, aNewUrl
, rNewFilter
, aNewOpt
,
394 aTabName
, nNewRefresh
);
398 // neue Einstellungen merken
402 if ( rNewFilter
!= aFilterName
)
403 aFilterName
= rNewFilter
;
404 if ( aNewOpt
!= aOptions
)
409 // pSrcShell->DoClose();
414 if (bAddUndo
&& bUndo
)
415 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction(
416 new ScUndoRefreshLink( pImpl
->m_pDocSh
, pUndoDoc
) );
418 // Paint (koennen mehrere Tabellen sein)
422 pImpl
->m_pDocSh
->PostPaint( ScRange(0,0,0,MAXCOL
,MAXROW
,MAXTAB
),
423 PAINT_GRID
| PAINT_TOP
| PAINT_LEFT
);
424 aModificator
.SetDocumentModified();
429 //! Fehler ausgeben ?
432 pDoc
->SetInLinkUpdate( FALSE
);
434 // notify Uno objects (for XRefreshListener)
435 //! also notify Uno objects if file name was changed!
436 ScLinkRefreshedHint aHint
;
437 aHint
.SetSheetLink( aFileName
);
438 pDoc
->BroadcastUno( aHint
);
443 IMPL_LINK( ScTableLink
, RefreshHdl
, ScTableLink
*, EMPTYARG
)
445 long nRes
= Refresh( aFileName
, aFilterName
, NULL
, GetRefreshDelay() ) != 0;
449 IMPL_LINK( ScTableLink
, TableEndEditHdl
, ::sfx2::SvBaseLink
*, pLink
)
451 if ( pImpl
->m_aEndEditLink
.IsSet() )
452 pImpl
->m_aEndEditLink
.Call( pLink
);
454 Application::SetDefDialogParent( pImpl
->m_pOldParent
);
458 // === ScDocumentLoader ==================================================
460 String
ScDocumentLoader::GetOptions( SfxMedium
& rMedium
) // static
462 SfxItemSet
* pSet
= rMedium
.GetItemSet();
463 const SfxPoolItem
* pItem
;
464 if ( pSet
&& SFX_ITEM_SET
== pSet
->GetItemState( SID_FILE_FILTEROPTIONS
, TRUE
, &pItem
) )
465 return ((const SfxStringItem
*)pItem
)->GetValue();
470 BOOL
ScDocumentLoader::GetFilterName( const String
& rFileName
,
471 String
& rFilter
, String
& rOptions
,
472 BOOL bWithContent
, BOOL bWithInteraction
) // static
474 TypeId aScType
= TYPE(ScDocShell
);
475 SfxObjectShell
* pDocSh
= SfxObjectShell::GetFirst( &aScType
);
478 if ( pDocSh
->HasName() )
480 SfxMedium
* pMed
= pDocSh
->GetMedium();
481 if ( rFileName
== pMed
->GetName() )
483 rFilter
= pMed
->GetFilter()->GetFilterName();
484 rOptions
= GetOptions(*pMed
);
488 pDocSh
= SfxObjectShell::GetNext( *pDocSh
, &aScType
);
493 const SfxFilter
* pSfxFilter
= NULL
;
494 SfxMedium
* pMedium
= new SfxMedium( rFileName
, STREAM_STD_READ
, FALSE
);
495 if ( pMedium
->GetError() == ERRCODE_NONE
)
497 if ( bWithInteraction
)
498 pMedium
->UseInteractionHandler(TRUE
); // #i73992# no longer called from GuessFilter
500 SfxFilterMatcher
aMatcher( String::CreateFromAscii("scalc") );
502 aMatcher
.GuessFilter( *pMedium
, &pSfxFilter
);
504 aMatcher
.GuessFilterIgnoringContent( *pMedium
, &pSfxFilter
);
508 if ( pMedium
->GetError() == ERRCODE_NONE
)
511 rFilter
= pSfxFilter
->GetFilterName();
513 rFilter
= ScDocShell::GetOwnFilterName(); // sonst Calc-Datei
514 bOK
= (rFilter
.Len()>0);
521 void ScDocumentLoader::RemoveAppPrefix( String
& rFilterName
) // static
523 String aAppPrefix
= String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM( STRING_SCAPP
));
524 aAppPrefix
.AppendAscii(RTL_CONSTASCII_STRINGPARAM( ": " ));
525 xub_StrLen nPreLen
= aAppPrefix
.Len();
526 if ( rFilterName
.Copy(0,nPreLen
) == aAppPrefix
)
527 rFilterName
.Erase(0,nPreLen
);
530 ScDocumentLoader::ScDocumentLoader( const String
& rFileName
,
531 String
& rFilterName
, String
& rOptions
,
532 UINT32 nRekCnt
, BOOL bWithInteraction
) :
536 if ( !rFilterName
.Len() )
537 GetFilterName( rFileName
, rFilterName
, rOptions
, TRUE
, bWithInteraction
);
539 const SfxFilter
* pFilter
= ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( rFilterName
);
541 // ItemSet immer anlegen, damit die DocShell die Optionen setzen kann
542 SfxItemSet
* pSet
= new SfxAllItemSet( SFX_APP()->GetPool() );
543 if ( rOptions
.Len() )
544 pSet
->Put( SfxStringItem( SID_FILE_FILTEROPTIONS
, rOptions
) );
546 pMedium
= new SfxMedium( rFileName
, STREAM_STD_READ
, FALSE
, pFilter
, pSet
);
547 if ( pMedium
->GetError() != ERRCODE_NONE
)
550 if ( bWithInteraction
)
551 pMedium
->UseInteractionHandler( TRUE
); // to enable the filter options dialog
553 pDocShell
= new ScDocShell( SFX_CREATE_MODE_INTERNAL
);
556 ScDocument
* pDoc
= pDocShell
->GetDocument();
559 ScExtDocOptions
* pExtDocOpt
= pDoc
->GetExtDocOptions();
562 pExtDocOpt
= new ScExtDocOptions
;
563 pDoc
->SetExtDocOptions( pExtDocOpt
);
565 pExtDocOpt
->GetDocSettings().mnLinkCnt
= nRekCnt
;
568 pDocShell
->DoLoad( pMedium
);
570 String aNew
= GetOptions(*pMedium
); // Optionen werden beim Laden per Dialog gesetzt
571 if (aNew
.Len() && aNew
!= rOptions
)
575 ScDocumentLoader::~ScDocumentLoader()
578 pDocShell->DoClose();
586 void ScDocumentLoader::ReleaseDocRef()
590 // release reference without calling DoClose - caller must
591 // have another reference to the doc and call DoClose later
599 ScDocument
* ScDocumentLoader::GetDocument()
601 return pDocShell
? pDocShell
->GetDocument() : 0;
604 BOOL
ScDocumentLoader::IsError() const
606 if ( pDocShell
&& pMedium
)
607 return pMedium
->GetError() != ERRCODE_NONE
;
612 String
ScDocumentLoader::GetTitle() const
615 return pDocShell
->GetTitle();