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"
41 #include "attrib.hxx" // raus, wenn ResetAttrib am Dokument
42 #include "patattr.hxx" // raus, wenn ResetAttrib am Dokument
43 #include "docpool.hxx" // raus, wenn ResetAttrib am Dokument
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 //------------------------------------------------------------------------
61 ScAreaLink::ScAreaLink( SfxObjectShell
* pShell
, const String
& rFile
,
62 const String
& rFilter
, const String
& rOpt
,
63 const String
& rArea
, const ScRange
& rDest
,
64 sal_uLong nRefresh
) :
65 ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL
,FORMAT_FILE
),
66 ScRefreshTimer ( nRefresh
),
67 pImpl ( new AreaLink_Impl() ),
69 aFilterName (rFilter
),
77 OSL_ENSURE(pShell
->ISA(ScDocShell
), "ScAreaLink mit falscher ObjectShell");
78 pImpl
->m_pDocSh
= static_cast< ScDocShell
* >( pShell
);
79 SetRefreshHandler( LINK( this, ScAreaLink
, RefreshHdl
) );
80 SetRefreshControl( pImpl
->m_pDocSh
->GetDocument()->GetRefreshTimerControlAddress() );
83 ScAreaLink::~ScAreaLink()
89 void ScAreaLink::Edit(Window
* pParent
, const Link
& /* rEndEditHdl */ )
91 // use own dialog instead of SvBaseLink::Edit...
92 // DefModalDialogParent setzen, weil evtl. aus der DocShell beim ConvertFrom
93 // ein Optionen-Dialog kommt...
95 ScAbstractDialogFactory
* pFact
= ScAbstractDialogFactory::Create();
96 OSL_ENSURE(pFact
, "ScAbstractFactory create fail!");
98 AbstractScLinkedAreaDlg
* pDlg
= pFact
->CreateScLinkedAreaDlg(pParent
);
99 OSL_ENSURE(pDlg
, "Dialog create fail!");
100 pDlg
->InitFromOldLink( aFileName
, aFilterName
, aOptions
, aSourceArea
, GetRefreshDelay() );
101 pImpl
->m_pDialog
= pDlg
;
102 pDlg
->StartExecuteModal( LINK( this, ScAreaLink
, AreaEndEditHdl
) );
105 ::sfx2::SvBaseLink::UpdateResult
ScAreaLink::DataChanged(
106 const String
&, const ::com::sun::star::uno::Any
& )
108 // bei bInCreate nichts tun, damit Update gerufen werden kann, um den Status im
109 // LinkManager zu setzen, ohne die Daten im Dokument zu aendern
114 sfx2::LinkManager
* pLinkManager
=pImpl
->m_pDocSh
->GetDocument()->GetLinkManager();
115 if (pLinkManager
!=NULL
)
117 OUString aFile
, aArea
, aFilter
;
118 pLinkManager
->GetDisplayNames(this, NULL
, &aFile
, &aArea
, &aFilter
);
120 // the file dialog returns the filter name with the application prefix
122 ScDocumentLoader::RemoveAppPrefix( aFilter
);
124 // dialog doesn't set area, so keep old one
131 String aTmp
= aFilter
;
132 sfx2::MakeLnkName(aNewLinkName
, NULL
, aFile
, aArea
, &aTmp
);
134 SetName( aNewLinkName
);
137 sfx2::SvBaseLinkRef
const xThis(this); // keep yourself alive
138 Refresh( aFile
, aFilter
, aArea
, GetRefreshDelay() );
144 void ScAreaLink::Closed()
146 // Verknuepfung loeschen: Undo
148 ScDocument
* pDoc
= pImpl
->m_pDocSh
->GetDocument();
149 sal_Bool
bUndo (pDoc
->IsUndoEnabled());
150 if (bAddUndo
&& bUndo
)
152 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction( new ScUndoRemoveAreaLink( pImpl
->m_pDocSh
,
153 aFileName
, aFilterName
, aOptions
,
154 aSourceArea
, aDestArea
, GetRefreshDelay() ) );
156 bAddUndo
= false; // nur einmal
159 SCTAB nDestTab
= aDestArea
.aStart
.Tab();
160 if (pDoc
->IsStreamValid(nDestTab
))
161 pDoc
->SetStreamValid(nDestTab
, false);
163 SvBaseLink::Closed();
166 void ScAreaLink::SetDestArea(const ScRange
& rNew
)
168 aDestArea
= rNew
; // fuer Undo
171 void ScAreaLink::SetSource(const String
& rDoc
, const String
& rFlt
, const String
& rOpt
,
179 // also update link name for dialog
181 sfx2::MakeLnkName( aNewLinkName
, NULL
, aFileName
, aSourceArea
, &aFilterName
);
182 SetName( aNewLinkName
);
185 bool ScAreaLink::IsEqual( const String
& rFile
, const String
& rFilter
, const String
& rOpt
,
186 const String
& rSource
, const ScRange
& rDest
) const
188 return aFileName
== rFile
&& aFilterName
== rFilter
&& aOptions
== rOpt
&&
189 aSourceArea
== rSource
&& aDestArea
.aStart
== rDest
.aStart
;
192 // find a range with name >rAreaName< in >pSrcDoc<, return it in >rRange<
193 bool ScAreaLink::FindExtRange( ScRange
& rRange
, ScDocument
* pSrcDoc
, const OUString
& rAreaName
)
196 OUString aUpperName
= ScGlobal::pCharClass
->uppercase(rAreaName
);
197 ScRangeName
* pNames
= pSrcDoc
->GetRangeName();
198 if (pNames
) // benannte Bereiche
200 const ScRangeData
* p
= pNames
->findByUpperName(aUpperName
);
201 if (p
&& p
->IsValidReference(rRange
))
204 if (!bFound
) // Datenbankbereiche
206 ScDBCollection
* pDBColl
= pSrcDoc
->GetDBCollection();
209 const ScDBData
* pDB
= pDBColl
->getNamedDBs().findByUpperName(aUpperName
);
215 pDB
->GetArea(nTab
,nCol1
,nRow1
,nCol2
,nRow2
);
216 rRange
= ScRange( nCol1
,nRow1
,nTab
, nCol2
,nRow2
,nTab
);
221 if (!bFound
) // direct reference (range or cell)
223 ScAddress::Details
aDetails(pSrcDoc
->GetAddressConvention(), 0, 0);
224 if ( rRange
.ParseAny( rAreaName
, pSrcDoc
, aDetails
) & SCA_VALID
)
232 sal_Bool
ScAreaLink::Refresh( const String
& rNewFile
, const String
& rNewFilter
,
233 const String
& rNewArea
, sal_uLong nNewRefresh
)
235 // Dokument laden - wie TabLink
237 if (!rNewFile
.Len() || !rNewFilter
.Len())
240 String
aNewUrl( ScGlobal::GetAbsDocName( rNewFile
, pImpl
->m_pDocSh
) );
241 sal_Bool bNewUrlName
= (aNewUrl
!= aFileName
);
243 const SfxFilter
* pFilter
= pImpl
->m_pDocSh
->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter
);
247 ScDocument
* pDoc
= pImpl
->m_pDocSh
->GetDocument();
249 sal_Bool
bUndo (pDoc
->IsUndoEnabled());
250 pDoc
->SetInLinkUpdate( sal_True
);
252 // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen
253 if ( rNewFilter
!= aFilterName
)
256 SfxMedium
* pMed
= ScDocumentLoader::CreateMedium( aNewUrl
, pFilter
, aOptions
);
258 // aRef->DoClose() will be closed explicitly, but it is still more safe to use SfxObjectShellLock here
259 ScDocShell
* pSrcShell
= new ScDocShell(SFX_CREATE_MODE_INTERNAL
);
260 SfxObjectShellLock aRef
= pSrcShell
;
261 pSrcShell
->DoLoad(pMed
);
263 ScDocument
* pSrcDoc
= pSrcShell
->GetDocument();
265 // Optionen koennten gesetzt worden sein
266 String aNewOpt
= ScDocumentLoader::GetOptions(*pMed
);
270 // correct source range name list for web query import
273 if( rNewFilter
== ScDocShell::GetWebQueryFilterName() )
274 aTempArea
= ScFormatFilter::Get().GetHTMLRangeNameList( pSrcDoc
, rNewArea
);
276 aTempArea
= rNewArea
;
278 // find total size of source area
281 xub_StrLen nTokenCnt
= comphelper::string::getTokenCount(aTempArea
, ';');
282 sal_Int32 nStringIx
= 0;
285 for( nToken
= 0; nToken
< nTokenCnt
; nToken
++ )
287 String
aToken( aTempArea
.GetToken( 0, ';', nStringIx
) );
289 if( FindExtRange( aTokenRange
, pSrcDoc
, aToken
) )
291 // columns: find maximum
292 nWidth
= std::max( nWidth
, (SCCOL
)(aTokenRange
.aEnd
.Col() - aTokenRange
.aStart
.Col() + 1) );
293 // rows: add row range + 1 empty row
294 nHeight
+= aTokenRange
.aEnd
.Row() - aTokenRange
.aStart
.Row() + 2;
297 // remove the last empty row
301 // alte Daten loeschen / neue kopieren
303 ScAddress aDestPos
= aDestArea
.aStart
;
304 SCTAB nDestTab
= aDestPos
.Tab();
305 ScRange aOldRange
= aDestArea
;
306 ScRange aNewRange
= aDestArea
; // alter Bereich, wenn Datei nicht gefunden o.ae.
307 if (nWidth
> 0 && nHeight
> 0)
309 aNewRange
.aEnd
.SetCol( aNewRange
.aStart
.Col() + nWidth
- 1 );
310 aNewRange
.aEnd
.SetRow( aNewRange
.aStart
.Row() + nHeight
- 1 );
313 //! check CanFitBlock only if bDoInsert is set?
314 sal_Bool bCanDo
= ValidColRow( aNewRange
.aEnd
.Col(), aNewRange
.aEnd
.Row() ) &&
315 pDoc
->CanFitBlock( aOldRange
, aNewRange
);
318 ScDocShellModificator
aModificator( *pImpl
->m_pDocSh
);
320 SCCOL nOldEndX
= aOldRange
.aEnd
.Col();
321 SCROW nOldEndY
= aOldRange
.aEnd
.Row();
322 SCCOL nNewEndX
= aNewRange
.aEnd
.Col();
323 SCROW nNewEndY
= aNewRange
.aEnd
.Row();
324 ScRange
aMaxRange( aDestPos
,
325 ScAddress(std::max(nOldEndX
,nNewEndX
), std::max(nOldEndY
,nNewEndY
), nDestTab
) );
327 // Undo initialisieren
329 ScDocument
* pUndoDoc
= NULL
;
330 if ( bAddUndo
&& bUndo
)
332 pUndoDoc
= new ScDocument( SCDOCMODE_UNDO
);
335 if ( nNewEndX
!= nOldEndX
|| nNewEndY
!= nOldEndY
) // Bereich veraendert?
337 pUndoDoc
->InitUndo( pDoc
, 0, pDoc
->GetTableCount()-1 );
338 pDoc
->CopyToDocument( 0,0,0,MAXCOL
,MAXROW
,MAXTAB
,
339 IDF_FORMULA
, false, pUndoDoc
); // alle Formeln
342 pUndoDoc
->InitUndo( pDoc
, nDestTab
, nDestTab
); // nur Zieltabelle
343 pDoc
->CopyToDocument( aOldRange
, IDF_ALL
& ~IDF_NOTE
, false, pUndoDoc
);
345 else // ohne Einfuegen
347 pUndoDoc
->InitUndo( pDoc
, nDestTab
, nDestTab
); // nur Zieltabelle
348 pDoc
->CopyToDocument( aMaxRange
, IDF_ALL
& ~IDF_NOTE
, false, pUndoDoc
);
352 // Zellen einfuegen / loeschen
353 // DeleteAreaTab loescht auch MERGE_FLAG Attribute
356 pDoc
->FitBlock( aOldRange
, aNewRange
); // incl. loeschen
358 pDoc
->DeleteAreaTab( aMaxRange
, IDF_ALL
& ~IDF_NOTE
);
362 if (nWidth
> 0 && nHeight
> 0)
364 ScDocument
aClipDoc( SCDOCMODE_CLIP
);
365 ScRange
aNewTokenRange( aNewRange
.aStart
);
367 for( nToken
= 0; nToken
< nTokenCnt
; nToken
++ )
369 String
aToken( aTempArea
.GetToken( 0, ';', nStringIx
) );
371 if( FindExtRange( aTokenRange
, pSrcDoc
, aToken
) )
373 SCTAB nSrcTab
= aTokenRange
.aStart
.Tab();
374 ScMarkData aSourceMark
;
375 aSourceMark
.SelectOneTable( nSrcTab
); // selektieren fuer CopyToClip
376 aSourceMark
.SetMarkArea( aTokenRange
);
378 ScClipParam
aClipParam(aTokenRange
, false);
379 pSrcDoc
->CopyToClip(aClipParam
, &aClipDoc
, &aSourceMark
);
381 if ( aClipDoc
.HasAttrib( 0,0,nSrcTab
, MAXCOL
,MAXROW
,nSrcTab
,
382 HASATTR_MERGED
| HASATTR_OVERLAPPED
) )
384 //! ResetAttrib am Dokument !!!
386 ScPatternAttr
aPattern( pSrcDoc
->GetPool() );
387 aPattern
.GetItemSet().Put( ScMergeAttr() ); // Defaults
388 aPattern
.GetItemSet().Put( ScMergeFlagAttr() );
389 aClipDoc
.ApplyPatternAreaTab( 0,0, MAXCOL
,MAXROW
, nSrcTab
, aPattern
);
392 aNewTokenRange
.aEnd
.SetCol( aNewTokenRange
.aStart
.Col() + (aTokenRange
.aEnd
.Col() - aTokenRange
.aStart
.Col()) );
393 aNewTokenRange
.aEnd
.SetRow( aNewTokenRange
.aStart
.Row() + (aTokenRange
.aEnd
.Row() - aTokenRange
.aStart
.Row()) );
394 ScMarkData aDestMark
;
395 aDestMark
.SelectOneTable( nDestTab
);
396 aDestMark
.SetMarkArea( aNewTokenRange
);
397 pDoc
->CopyFromClip( aNewTokenRange
, aDestMark
, IDF_ALL
, NULL
, &aClipDoc
, false );
398 aNewTokenRange
.aStart
.SetRow( aNewTokenRange
.aEnd
.Row() + 2 );
404 String aErr
= ScGlobal::GetRscString(STR_LINKERROR
);
405 pDoc
->SetString( aDestPos
.Col(), aDestPos
.Row(), aDestPos
.Tab(), aErr
);
410 if ( bAddUndo
&& bUndo
)
412 ScDocument
* pRedoDoc
= new ScDocument( SCDOCMODE_UNDO
);
413 pRedoDoc
->InitUndo( pDoc
, nDestTab
, nDestTab
);
414 pDoc
->CopyToDocument( aNewRange
, IDF_ALL
& ~IDF_NOTE
, false, pRedoDoc
);
416 pImpl
->m_pDocSh
->GetUndoManager()->AddUndoAction(
417 new ScUndoUpdateAreaLink( pImpl
->m_pDocSh
,
418 aFileName
, aFilterName
, aOptions
,
419 aSourceArea
, aOldRange
, GetRefreshDelay(),
420 aNewUrl
, rNewFilter
, aNewOpt
,
421 rNewArea
, aNewRange
, nNewRefresh
,
422 pUndoDoc
, pRedoDoc
, bDoInsert
) );
425 // neue Einstellungen merken
429 if ( rNewFilter
!= aFilterName
)
430 aFilterName
= rNewFilter
;
431 if ( rNewArea
!= aSourceArea
)
432 aSourceArea
= rNewArea
;
433 if ( aNewOpt
!= aOptions
)
436 if ( aNewRange
!= aDestArea
)
437 aDestArea
= aNewRange
;
439 if ( nNewRefresh
!= GetRefreshDelay() )
440 SetRefreshDelay( nNewRefresh
);
442 SCCOL nPaintEndX
= std::max( aOldRange
.aEnd
.Col(), aNewRange
.aEnd
.Col() );
443 SCROW nPaintEndY
= std::max( aOldRange
.aEnd
.Row(), aNewRange
.aEnd
.Row() );
445 if ( aOldRange
.aEnd
.Col() != aNewRange
.aEnd
.Col() )
447 if ( aOldRange
.aEnd
.Row() != aNewRange
.aEnd
.Row() )
450 if ( !pImpl
->m_pDocSh
->AdjustRowHeight( aDestPos
.Row(), nPaintEndY
, nDestTab
) )
451 pImpl
->m_pDocSh
->PostPaint(
452 ScRange(aDestPos
.Col(), aDestPos
.Row(), nDestTab
, nPaintEndX
, nPaintEndY
, nDestTab
),
454 aModificator
.SetDocumentModified();
458 // CanFitBlock sal_False -> Probleme mit zusammengefassten Zellen
459 // oder Tabellengrenze erreicht!
462 //! Link-Dialog muss Default-Parent setzen
463 // "kann keine Zeilen einfuegen"
464 InfoBox
aBox( Application::GetDefDialogParent(),
465 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_2
) );
473 pDoc
->SetInLinkUpdate( false );
477 // notify Uno objects (for XRefreshListener)
478 //! also notify Uno objects if file name was changed!
479 ScLinkRefreshedHint aHint
;
480 aHint
.SetAreaLink( aDestPos
);
481 pDoc
->BroadcastUno( aHint
);
488 IMPL_LINK_NOARG(ScAreaLink
, RefreshHdl
)
490 long nRes
= Refresh( aFileName
, aFilterName
, aSourceArea
,
491 GetRefreshDelay() ) != 0;
495 IMPL_LINK_NOARG(ScAreaLink
, AreaEndEditHdl
)
497 // #i76514# can't use link argument to access the dialog,
498 // because it's the ScLinkedAreaDlg, not AbstractScLinkedAreaDlg
500 if ( pImpl
->m_pDialog
&& pImpl
->m_pDialog
->GetResult() == RET_OK
)
502 aOptions
= pImpl
->m_pDialog
->GetOptions();
503 Refresh( pImpl
->m_pDialog
->GetURL(), pImpl
->m_pDialog
->GetFilter(),
504 pImpl
->m_pDialog
->GetSource(), pImpl
->m_pDialog
->GetRefresh() );
506 // copy source data from members (set in Refresh) into link name for dialog
508 sfx2::MakeLnkName( aNewLinkName
, NULL
, aFileName
, aSourceArea
, &aFilterName
);
509 SetName( aNewLinkName
);
511 pImpl
->m_pDialog
= NULL
; // dialog is deleted with parent
516 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */