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 <comphelper/string.hxx>
21 #include <sfx2/app.hxx>
22 #include <sfx2/docfile.hxx>
23 #include <sfx2/fcontnr.hxx>
24 #include <sfx2/linkmgr.hxx>
25 #include <svl/stritem.hxx>
26 #include <vcl/msgbox.hxx>
28 #include "arealink.hxx"
30 #include "tablink.hxx"
31 #include "document.hxx"
33 #include "rangenam.hxx"
35 #include "undoblk.hxx"
36 #include "globstr.hrc"
37 #include "markdata.hxx"
42 #include "patattr.hxx"
43 #include "docpool.hxx"
46 #include "scabstdlg.hxx"
47 #include "clipparam.hxx"
52 AbstractScLinkedAreaDlg
* m_pDialog
;
54 AreaLink_Impl() : m_pDocSh( NULL
), m_pDialog( NULL
) {}
57 TYPEINIT1(ScAreaLink
,::sfx2::SvBaseLink
);
59 ScAreaLink::ScAreaLink( SfxObjectShell
* pShell
, const OUString
& rFile
,
60 const OUString
& rFilter
, const OUString
& rOpt
,
61 const OUString
& rArea
, const ScRange
& rDest
,
62 sal_uLong nRefresh
) :
63 ::sfx2::SvBaseLink(SfxLinkUpdateMode::ONCALL
,SotClipboardFormatId::SIMPLE_FILE
),
64 ScRefreshTimer ( nRefresh
),
65 pImpl ( new AreaLink_Impl() ),
67 aFilterName (rFilter
),
75 OSL_ENSURE(pShell
->ISA(ScDocShell
), "ScAreaLink mit falscher ObjectShell");
76 pImpl
->m_pDocSh
= static_cast< ScDocShell
* >( pShell
);
77 SetRefreshHandler( LINK( this, ScAreaLink
, RefreshHdl
) );
78 SetRefreshControl( &pImpl
->m_pDocSh
->GetDocument().GetRefreshTimerControlAddress() );
81 ScAreaLink::~ScAreaLink()
87 void ScAreaLink::Edit(vcl::Window
* pParent
, const Link
<>& /* rEndEditHdl */ )
89 // use own dialog instead of SvBaseLink::Edit...
90 // DefModalDialogParent setzen, weil evtl. aus der DocShell beim ConvertFrom
91 // ein Optionen-Dialog kommt...
93 ScAbstractDialogFactory
* pFact
= ScAbstractDialogFactory::Create();
94 OSL_ENSURE(pFact
, "ScAbstractFactory create fail!");
96 AbstractScLinkedAreaDlg
* pDlg
= pFact
->CreateScLinkedAreaDlg(pParent
);
97 OSL_ENSURE(pDlg
, "Dialog create fail!");
98 pDlg
->InitFromOldLink( aFileName
, aFilterName
, aOptions
, aSourceArea
, GetRefreshDelay() );
99 pImpl
->m_pDialog
= pDlg
;
100 pDlg
->StartExecuteModal( LINK( this, ScAreaLink
, AreaEndEditHdl
) );
103 ::sfx2::SvBaseLink::UpdateResult
ScAreaLink::DataChanged(
104 const OUString
&, const ::com::sun::star::uno::Any
& )
106 // bei bInCreate nichts tun, damit Update gerufen werden kann, um den Status im
107 // LinkManager zu setzen, ohne die Daten im Dokument zu aendern
112 sfx2::LinkManager
* pLinkManager
=pImpl
->m_pDocSh
->GetDocument().GetLinkManager();
113 if (pLinkManager
!=NULL
)
115 OUString aFile
, aArea
, aFilter
;
116 sfx2::LinkManager::GetDisplayNames(this, NULL
, &aFile
, &aArea
, &aFilter
);
118 // the file dialog returns the filter name with the application prefix
120 ScDocumentLoader::RemoveAppPrefix( aFilter
);
122 // dialog doesn't set area, so keep old one
128 OUString aNewLinkName
;
129 OUString aTmp
= aFilter
;
130 sfx2::MakeLnkName(aNewLinkName
, NULL
, aFile
, aArea
, &aTmp
);
132 SetName( aNewLinkName
);
135 sfx2::SvBaseLinkRef
const xThis(this); // keep yourself alive
136 Refresh( aFile
, aFilter
, aArea
, GetRefreshDelay() );
142 void ScAreaLink::Closed()
144 // Verknuepfung loeschen: Undo
146 ScDocument
& rDoc
= pImpl
->m_pDocSh
->GetDocument();
147 bool bUndo (rDoc
.IsUndoEnabled());
148 if (bAddUndo
&& bUndo
)
150 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction( new ScUndoRemoveAreaLink( pImpl
->m_pDocSh
,
151 aFileName
, aFilterName
, aOptions
,
152 aSourceArea
, aDestArea
, GetRefreshDelay() ) );
154 bAddUndo
= false; // nur einmal
157 SCTAB nDestTab
= aDestArea
.aStart
.Tab();
158 if (rDoc
.IsStreamValid(nDestTab
))
159 rDoc
.SetStreamValid(nDestTab
, false);
161 SvBaseLink::Closed();
164 void ScAreaLink::SetDestArea(const ScRange
& rNew
)
166 aDestArea
= rNew
; // fuer Undo
169 void ScAreaLink::SetSource(const OUString
& rDoc
, const OUString
& rFlt
, const OUString
& rOpt
,
170 const OUString
& rArea
)
177 // also update link name for dialog
178 OUString aNewLinkName
;
179 sfx2::MakeLnkName( aNewLinkName
, NULL
, aFileName
, aSourceArea
, &aFilterName
);
180 SetName( aNewLinkName
);
183 bool ScAreaLink::IsEqual( const OUString
& rFile
, const OUString
& rFilter
, const OUString
& rOpt
,
184 const OUString
& rSource
, const ScRange
& rDest
) const
186 return aFileName
== rFile
&& aFilterName
== rFilter
&& aOptions
== rOpt
&&
187 aSourceArea
== rSource
&& aDestArea
.aStart
== rDest
.aStart
;
190 // find a range with name >rAreaName< in >pSrcDoc<, return it in >rRange<
191 bool ScAreaLink::FindExtRange( ScRange
& rRange
, ScDocument
* pSrcDoc
, const OUString
& rAreaName
)
194 OUString aUpperName
= ScGlobal::pCharClass
->uppercase(rAreaName
);
195 ScRangeName
* pNames
= pSrcDoc
->GetRangeName();
196 if (pNames
) // benannte Bereiche
198 const ScRangeData
* p
= pNames
->findByUpperName(aUpperName
);
199 if (p
&& p
->IsValidReference(rRange
))
202 if (!bFound
) // Datenbankbereiche
204 ScDBCollection
* pDBColl
= pSrcDoc
->GetDBCollection();
207 const ScDBData
* pDB
= pDBColl
->getNamedDBs().findByUpperName(aUpperName
);
213 pDB
->GetArea(nTab
,nCol1
,nRow1
,nCol2
,nRow2
);
214 rRange
= ScRange( nCol1
,nRow1
,nTab
, nCol2
,nRow2
,nTab
);
219 if (!bFound
) // direct reference (range or cell)
221 ScAddress::Details
aDetails(pSrcDoc
->GetAddressConvention(), 0, 0);
222 if ( rRange
.ParseAny( rAreaName
, pSrcDoc
, aDetails
) & SCA_VALID
)
230 bool ScAreaLink::Refresh( const OUString
& rNewFile
, const OUString
& rNewFilter
,
231 const OUString
& rNewArea
, sal_uLong nNewRefresh
)
233 // Dokument laden - wie TabLink
235 if (rNewFile
.isEmpty() || rNewFilter
.isEmpty())
238 OUString
aNewUrl( ScGlobal::GetAbsDocName( rNewFile
, pImpl
->m_pDocSh
) );
239 bool bNewUrlName
= (aNewUrl
!= aFileName
);
241 const SfxFilter
* pFilter
= pImpl
->m_pDocSh
->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter
);
245 ScDocument
& rDoc
= pImpl
->m_pDocSh
->GetDocument();
247 bool bUndo (rDoc
.IsUndoEnabled());
248 rDoc
.SetInLinkUpdate( true );
250 // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen
251 if ( rNewFilter
!= aFilterName
)
254 SfxMedium
* pMed
= ScDocumentLoader::CreateMedium( aNewUrl
, pFilter
, aOptions
);
256 // aRef->DoClose() will be closed explicitly, but it is still more safe to use SfxObjectShellLock here
257 ScDocShell
* pSrcShell
= new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT
| SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS
);
258 SfxObjectShellLock aRef
= pSrcShell
;
259 pSrcShell
->DoLoad(pMed
);
261 ScDocument
& rSrcDoc
= pSrcShell
->GetDocument();
263 // Optionen koennten gesetzt worden sein
264 OUString aNewOpt
= ScDocumentLoader::GetOptions(*pMed
);
265 if (aNewOpt
.isEmpty())
268 // correct source range name list for web query import
271 if( rNewFilter
== ScDocShell::GetWebQueryFilterName() )
272 aTempArea
= ScFormatFilter::Get().GetHTMLRangeNameList( &rSrcDoc
, rNewArea
);
274 aTempArea
= rNewArea
;
276 // find total size of source area
279 sal_Int32 nTokenCnt
= comphelper::string::getTokenCount(aTempArea
, ';');
280 sal_Int32 nStringIx
= 0;
283 for( nToken
= 0; nToken
< nTokenCnt
; nToken
++ )
285 OUString
aToken( aTempArea
.getToken( 0, ';', nStringIx
) );
287 if( FindExtRange( aTokenRange
, &rSrcDoc
, aToken
) )
289 // columns: find maximum
290 nWidth
= std::max( nWidth
, (SCCOL
)(aTokenRange
.aEnd
.Col() - aTokenRange
.aStart
.Col() + 1) );
291 // rows: add row range + 1 empty row
292 nHeight
+= aTokenRange
.aEnd
.Row() - aTokenRange
.aStart
.Row() + 2;
295 // remove the last empty row
299 // alte Daten loeschen / neue kopieren
301 ScAddress aDestPos
= aDestArea
.aStart
;
302 SCTAB nDestTab
= aDestPos
.Tab();
303 ScRange aOldRange
= aDestArea
;
304 ScRange aNewRange
= aDestArea
; // alter Bereich, wenn Datei nicht gefunden o.ae.
305 if (nWidth
> 0 && nHeight
> 0)
307 aNewRange
.aEnd
.SetCol( aNewRange
.aStart
.Col() + nWidth
- 1 );
308 aNewRange
.aEnd
.SetRow( aNewRange
.aStart
.Row() + nHeight
- 1 );
311 //! check CanFitBlock only if bDoInsert is set?
312 bool bCanDo
= ValidColRow( aNewRange
.aEnd
.Col(), aNewRange
.aEnd
.Row() ) &&
313 rDoc
.CanFitBlock( aOldRange
, aNewRange
);
316 ScDocShellModificator
aModificator( *pImpl
->m_pDocSh
);
318 SCCOL nOldEndX
= aOldRange
.aEnd
.Col();
319 SCROW nOldEndY
= aOldRange
.aEnd
.Row();
320 SCCOL nNewEndX
= aNewRange
.aEnd
.Col();
321 SCROW nNewEndY
= aNewRange
.aEnd
.Row();
322 ScRange
aMaxRange( aDestPos
,
323 ScAddress(std::max(nOldEndX
,nNewEndX
), std::max(nOldEndY
,nNewEndY
), nDestTab
) );
325 // Undo initialisieren
327 ScDocument
* pUndoDoc
= NULL
;
328 if ( bAddUndo
&& bUndo
)
330 pUndoDoc
= new ScDocument( SCDOCMODE_UNDO
);
333 if ( nNewEndX
!= nOldEndX
|| nNewEndY
!= nOldEndY
) // Bereich veraendert?
335 pUndoDoc
->InitUndo( &rDoc
, 0, rDoc
.GetTableCount()-1 );
336 rDoc
.CopyToDocument( 0,0,0,MAXCOL
,MAXROW
,MAXTAB
,
337 IDF_FORMULA
, false, pUndoDoc
); // alle Formeln
340 pUndoDoc
->InitUndo( &rDoc
, nDestTab
, nDestTab
); // nur Zieltabelle
341 rDoc
.CopyToDocument( aOldRange
, IDF_ALL
& ~IDF_NOTE
, false, pUndoDoc
);
343 else // ohne Einfuegen
345 pUndoDoc
->InitUndo( &rDoc
, nDestTab
, nDestTab
); // nur Zieltabelle
346 rDoc
.CopyToDocument( aMaxRange
, IDF_ALL
& ~IDF_NOTE
, false, pUndoDoc
);
350 // Zellen einfuegen / loeschen
351 // DeleteAreaTab loescht auch MERGE_FLAG Attribute
354 rDoc
.FitBlock( aOldRange
, aNewRange
); // incl. loeschen
356 rDoc
.DeleteAreaTab( aMaxRange
, IDF_ALL
& ~IDF_NOTE
);
360 if (nWidth
> 0 && nHeight
> 0)
362 ScDocument
aClipDoc( SCDOCMODE_CLIP
);
363 ScRange
aNewTokenRange( aNewRange
.aStart
);
365 for( nToken
= 0; nToken
< nTokenCnt
; nToken
++ )
367 OUString
aToken( aTempArea
.getToken( 0, ';', nStringIx
) );
369 if( FindExtRange( aTokenRange
, &rSrcDoc
, aToken
) )
371 SCTAB nSrcTab
= aTokenRange
.aStart
.Tab();
372 ScMarkData aSourceMark
;
373 aSourceMark
.SelectOneTable( nSrcTab
); // selektieren fuer CopyToClip
374 aSourceMark
.SetMarkArea( aTokenRange
);
376 ScClipParam
aClipParam(aTokenRange
, false);
377 rSrcDoc
.CopyToClip(aClipParam
, &aClipDoc
, &aSourceMark
);
379 if ( aClipDoc
.HasAttrib( 0,0,nSrcTab
, MAXCOL
,MAXROW
,nSrcTab
,
380 HASATTR_MERGED
| HASATTR_OVERLAPPED
) )
382 //! ResetAttrib am Dokument !!!
384 ScPatternAttr
aPattern( rSrcDoc
.GetPool() );
385 aPattern
.GetItemSet().Put( ScMergeAttr() ); // Defaults
386 aPattern
.GetItemSet().Put( ScMergeFlagAttr() );
387 aClipDoc
.ApplyPatternAreaTab( 0,0, MAXCOL
,MAXROW
, nSrcTab
, aPattern
);
390 aNewTokenRange
.aEnd
.SetCol( aNewTokenRange
.aStart
.Col() + (aTokenRange
.aEnd
.Col() - aTokenRange
.aStart
.Col()) );
391 aNewTokenRange
.aEnd
.SetRow( aNewTokenRange
.aStart
.Row() + (aTokenRange
.aEnd
.Row() - aTokenRange
.aStart
.Row()) );
392 ScMarkData aDestMark
;
393 aDestMark
.SelectOneTable( nDestTab
);
394 aDestMark
.SetMarkArea( aNewTokenRange
);
395 rDoc
.CopyFromClip( aNewTokenRange
, aDestMark
, IDF_ALL
, NULL
, &aClipDoc
, false );
396 aNewTokenRange
.aStart
.SetRow( aNewTokenRange
.aEnd
.Row() + 2 );
402 OUString aErr
= ScGlobal::GetRscString(STR_LINKERROR
);
403 rDoc
.SetString( aDestPos
.Col(), aDestPos
.Row(), aDestPos
.Tab(), aErr
);
408 if ( bAddUndo
&& bUndo
)
410 ScDocument
* pRedoDoc
= new ScDocument( SCDOCMODE_UNDO
);
411 pRedoDoc
->InitUndo( &rDoc
, nDestTab
, nDestTab
);
412 rDoc
.CopyToDocument( aNewRange
, IDF_ALL
& ~IDF_NOTE
, false, pRedoDoc
);
414 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction(
415 new ScUndoUpdateAreaLink( pImpl
->m_pDocSh
,
416 aFileName
, aFilterName
, aOptions
,
417 aSourceArea
, aOldRange
, GetRefreshDelay(),
418 aNewUrl
, rNewFilter
, aNewOpt
,
419 rNewArea
, aNewRange
, nNewRefresh
,
420 pUndoDoc
, pRedoDoc
, bDoInsert
) );
423 // neue Einstellungen merken
427 if ( rNewFilter
!= aFilterName
)
428 aFilterName
= rNewFilter
;
429 if ( rNewArea
!= aSourceArea
)
430 aSourceArea
= rNewArea
;
431 if ( aNewOpt
!= aOptions
)
434 if ( aNewRange
!= aDestArea
)
435 aDestArea
= aNewRange
;
437 if ( nNewRefresh
!= GetRefreshDelay() )
438 SetRefreshDelay( nNewRefresh
);
440 SCCOL nPaintEndX
= std::max( aOldRange
.aEnd
.Col(), aNewRange
.aEnd
.Col() );
441 SCROW nPaintEndY
= std::max( aOldRange
.aEnd
.Row(), aNewRange
.aEnd
.Row() );
443 if ( aOldRange
.aEnd
.Col() != aNewRange
.aEnd
.Col() )
445 if ( aOldRange
.aEnd
.Row() != aNewRange
.aEnd
.Row() )
448 if ( !pImpl
->m_pDocSh
->AdjustRowHeight( aDestPos
.Row(), nPaintEndY
, nDestTab
) )
449 pImpl
->m_pDocSh
->PostPaint(
450 ScRange(aDestPos
.Col(), aDestPos
.Row(), nDestTab
, nPaintEndX
, nPaintEndY
, nDestTab
),
452 aModificator
.SetDocumentModified();
456 // CanFitBlock sal_False -> Probleme mit zusammengefassten Zellen
457 // oder Tabellengrenze erreicht!
460 //! Link-Dialog muss Default-Parent setzen
461 // "kann keine Zeilen einfuegen"
462 ScopedVclPtrInstance
<InfoBox
> aBox( Application::GetDefDialogParent(),
463 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_2
) );
471 rDoc
.SetInLinkUpdate( false );
475 // notify Uno objects (for XRefreshListener)
476 //! also notify Uno objects if file name was changed!
477 ScLinkRefreshedHint aHint
;
478 aHint
.SetAreaLink( aDestPos
);
479 rDoc
.BroadcastUno( aHint
);
485 IMPL_LINK_NOARG_TYPED(ScAreaLink
, RefreshHdl
, Timer
*, void)
487 Refresh( aFileName
, aFilterName
, aSourceArea
, GetRefreshDelay() );
490 IMPL_LINK_NOARG(ScAreaLink
, AreaEndEditHdl
)
492 // #i76514# can't use link argument to access the dialog,
493 // because it's the ScLinkedAreaDlg, not AbstractScLinkedAreaDlg
495 if ( pImpl
->m_pDialog
&& pImpl
->m_pDialog
->GetResult() == RET_OK
)
497 aOptions
= pImpl
->m_pDialog
->GetOptions();
498 Refresh( pImpl
->m_pDialog
->GetURL(), pImpl
->m_pDialog
->GetFilter(),
499 pImpl
->m_pDialog
->GetSource(), pImpl
->m_pDialog
->GetRefresh() );
501 // copy source data from members (set in Refresh) into link name for dialog
502 OUString aNewLinkName
;
503 sfx2::MakeLnkName( aNewLinkName
, NULL
, aFileName
, aSourceArea
, &aFilterName
);
504 SetName( aNewLinkName
);
506 pImpl
->m_pDialog
= NULL
; // dialog is deleted with parent
511 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */