Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / sc / source / ui / docshell / arealink.cxx
blobd973e2d50cbe6b63e72ff89f3dbf99788790cf4f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
32 #include "docsh.hxx"
33 #include "rangenam.hxx"
34 #include "dbdata.hxx"
35 #include "undoblk.hxx"
36 #include "globstr.hrc"
37 #include "markdata.hxx"
38 #include "hints.hxx"
39 #include "filter.hxx"
41 #include "attrib.hxx"
42 #include "patattr.hxx"
43 #include "docpool.hxx"
45 #include "sc.hrc"
46 #include "scabstdlg.hxx"
47 #include "clipparam.hxx"
49 struct AreaLink_Impl
51 ScDocShell* m_pDocSh;
52 AbstractScLinkedAreaDlg* m_pDialog;
54 AreaLink_Impl() : m_pDocSh( nullptr ), m_pDialog( nullptr ) {}
58 ScAreaLink::ScAreaLink( SfxObjectShell* pShell, const OUString& rFile,
59 const OUString& rFilter, const OUString& rOpt,
60 const OUString& rArea, const ScRange& rDest,
61 sal_uLong nRefresh ) :
62 ::sfx2::SvBaseLink(SfxLinkUpdateMode::ONCALL,SotClipboardFormatId::SIMPLE_FILE),
63 ScRefreshTimer ( nRefresh ),
64 pImpl ( new AreaLink_Impl() ),
65 aFileName (rFile),
66 aFilterName (rFilter),
67 aOptions (rOpt),
68 aSourceArea (rArea),
69 aDestArea (rDest),
70 bAddUndo (true),
71 bInCreate (false),
72 bDoInsert (true)
74 OSL_ENSURE(dynamic_cast< const ScDocShell *>( pShell ) != nullptr, "ScAreaLink mit falscher ObjectShell");
75 pImpl->m_pDocSh = static_cast< ScDocShell* >( pShell );
76 SetRefreshHandler( LINK( this, ScAreaLink, RefreshHdl ) );
77 SetRefreshControl( &pImpl->m_pDocSh->GetDocument().GetRefreshTimerControlAddress() );
80 ScAreaLink::~ScAreaLink()
82 StopRefreshTimer();
85 void ScAreaLink::Edit(vcl::Window* pParent, const Link<SvBaseLink&,void>& /* rEndEditHdl */ )
87 // use own dialog instead of SvBaseLink::Edit...
88 ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
89 OSL_ENSURE(pFact, "ScAbstractFactory create fail!");
91 AbstractScLinkedAreaDlg* pDlg = pFact->CreateScLinkedAreaDlg(pParent);
92 OSL_ENSURE(pDlg, "Dialog create fail!");
93 pDlg->InitFromOldLink( aFileName, aFilterName, aOptions, aSourceArea, GetRefreshDelay() );
94 pImpl->m_pDialog = pDlg;
95 pDlg->StartExecuteModal( LINK( this, ScAreaLink, AreaEndEditHdl ) );
98 ::sfx2::SvBaseLink::UpdateResult ScAreaLink::DataChanged(
99 const OUString&, const css::uno::Any& )
101 // bei bInCreate nichts tun, damit Update gerufen werden kann, um den Status im
102 // LinkManager zu setzen, ohne die Daten im Dokument zu aendern
104 if (bInCreate)
105 return SUCCESS;
107 sfx2::LinkManager* pLinkManager=pImpl->m_pDocSh->GetDocument().GetLinkManager();
108 if (pLinkManager!=nullptr)
110 OUString aFile, aArea, aFilter;
111 sfx2::LinkManager::GetDisplayNames(this, nullptr, &aFile, &aArea, &aFilter);
113 // the file dialog returns the filter name with the application prefix
114 // -> remove prefix
115 ScDocumentLoader::RemoveAppPrefix( aFilter );
117 // dialog doesn't set area, so keep old one
118 if (aArea.isEmpty())
120 aArea = aSourceArea;
122 // adjust in dialog:
123 OUString aNewLinkName;
124 OUString aTmp = aFilter;
125 sfx2::MakeLnkName(aNewLinkName, nullptr, aFile, aArea, &aTmp);
126 aFilter = aTmp;
127 SetName( aNewLinkName );
130 tools::SvRef<sfx2::SvBaseLink> const xThis(this); // keep yourself alive
131 Refresh( aFile, aFilter, aArea, GetRefreshDelay() );
134 return SUCCESS;
137 void ScAreaLink::Closed()
139 // Verknuepfung loeschen: Undo
141 ScDocument& rDoc = pImpl->m_pDocSh->GetDocument();
142 bool bUndo (rDoc.IsUndoEnabled());
143 if (bAddUndo && bUndo)
145 pImpl->m_pDocSh->GetUndoManager()->AddUndoAction( new ScUndoRemoveAreaLink( pImpl->m_pDocSh,
146 aFileName, aFilterName, aOptions,
147 aSourceArea, aDestArea, GetRefreshDelay() ) );
149 bAddUndo = false; // nur einmal
152 SCTAB nDestTab = aDestArea.aStart.Tab();
153 if (rDoc.IsStreamValid(nDestTab))
154 rDoc.SetStreamValid(nDestTab, false);
156 SvBaseLink::Closed();
159 void ScAreaLink::SetDestArea(const ScRange& rNew)
161 aDestArea = rNew; // fuer Undo
164 void ScAreaLink::SetSource(const OUString& rDoc, const OUString& rFlt, const OUString& rOpt,
165 const OUString& rArea)
167 aFileName = rDoc;
168 aFilterName = rFlt;
169 aOptions = rOpt;
170 aSourceArea = rArea;
172 // also update link name for dialog
173 OUString aNewLinkName;
174 sfx2::MakeLnkName( aNewLinkName, nullptr, aFileName, aSourceArea, &aFilterName );
175 SetName( aNewLinkName );
178 bool ScAreaLink::IsEqual( const OUString& rFile, const OUString& rFilter, const OUString& rOpt,
179 const OUString& rSource, const ScRange& rDest ) const
181 return aFileName == rFile && aFilterName == rFilter && aOptions == rOpt &&
182 aSourceArea == rSource && aDestArea.aStart == rDest.aStart;
185 // find a range with name >rAreaName< in >pSrcDoc<, return it in >rRange<
186 bool ScAreaLink::FindExtRange( ScRange& rRange, ScDocument* pSrcDoc, const OUString& rAreaName )
188 bool bFound = false;
189 OUString aUpperName = ScGlobal::pCharClass->uppercase(rAreaName);
190 ScRangeName* pNames = pSrcDoc->GetRangeName();
191 if (pNames) // benannte Bereiche
193 const ScRangeData* p = pNames->findByUpperName(aUpperName);
194 if (p && p->IsValidReference(rRange))
195 bFound = true;
197 if (!bFound) // Datenbankbereiche
199 ScDBCollection* pDBColl = pSrcDoc->GetDBCollection();
200 if (pDBColl)
202 const ScDBData* pDB = pDBColl->getNamedDBs().findByUpperName(aUpperName);
203 if (pDB)
205 SCTAB nTab;
206 SCCOL nCol1, nCol2;
207 SCROW nRow1, nRow2;
208 pDB->GetArea(nTab,nCol1,nRow1,nCol2,nRow2);
209 rRange = ScRange( nCol1,nRow1,nTab, nCol2,nRow2,nTab );
210 bFound = true;
214 if (!bFound) // direct reference (range or cell)
216 ScAddress::Details aDetails(pSrcDoc->GetAddressConvention(), 0, 0);
217 if ( rRange.ParseAny( rAreaName, pSrcDoc, aDetails ) & ScRefFlags::VALID )
218 bFound = true;
220 return bFound;
223 // ausfuehren:
225 bool ScAreaLink::Refresh( const OUString& rNewFile, const OUString& rNewFilter,
226 const OUString& rNewArea, sal_uLong nNewRefresh )
228 // Dokument laden - wie TabLink
230 if (rNewFile.isEmpty() || rNewFilter.isEmpty())
231 return false;
233 OUString aNewUrl( ScGlobal::GetAbsDocName( rNewFile, pImpl->m_pDocSh ) );
234 bool bNewUrlName = (aNewUrl != aFileName);
236 std::shared_ptr<const SfxFilter> pFilter = pImpl->m_pDocSh->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter);
237 if (!pFilter)
238 return false;
240 ScDocument& rDoc = pImpl->m_pDocSh->GetDocument();
242 bool bUndo (rDoc.IsUndoEnabled());
243 rDoc.SetInLinkUpdate( true );
245 // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen
246 if ( rNewFilter != aFilterName )
247 aOptions.clear();
249 SfxMedium* pMed = ScDocumentLoader::CreateMedium( aNewUrl, pFilter, aOptions);
251 // aRef->DoClose() will be closed explicitly, but it is still more safe to use SfxObjectShellLock here
252 ScDocShell* pSrcShell = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS);
253 SfxObjectShellLock aRef = pSrcShell;
254 pSrcShell->DoLoad(pMed);
256 ScDocument& rSrcDoc = pSrcShell->GetDocument();
258 // Optionen koennten gesetzt worden sein
259 OUString aNewOpt = ScDocumentLoader::GetOptions(*pMed);
260 if (aNewOpt.isEmpty())
261 aNewOpt = aOptions;
263 // correct source range name list for web query import
264 OUString aTempArea;
266 if( rNewFilter == ScDocShell::GetWebQueryFilterName() )
267 aTempArea = ScFormatFilter::Get().GetHTMLRangeNameList( &rSrcDoc, rNewArea );
268 else
269 aTempArea = rNewArea;
271 // find total size of source area
272 SCCOL nWidth = 0;
273 SCROW nHeight = 0;
274 sal_Int32 nTokenCnt = comphelper::string::getTokenCount(aTempArea, ';');
275 sal_Int32 nStringIx = 0;
276 sal_Int32 nToken;
278 for( nToken = 0; nToken < nTokenCnt; nToken++ )
280 OUString aToken( aTempArea.getToken( 0, ';', nStringIx ) );
281 ScRange aTokenRange;
282 if( FindExtRange( aTokenRange, &rSrcDoc, aToken ) )
284 // columns: find maximum
285 nWidth = std::max( nWidth, (SCCOL)(aTokenRange.aEnd.Col() - aTokenRange.aStart.Col() + 1) );
286 // rows: add row range + 1 empty row
287 nHeight += aTokenRange.aEnd.Row() - aTokenRange.aStart.Row() + 2;
290 // remove the last empty row
291 if( nHeight > 0 )
292 nHeight--;
294 // alte Daten loeschen / neue kopieren
296 ScAddress aDestPos = aDestArea.aStart;
297 SCTAB nDestTab = aDestPos.Tab();
298 ScRange aOldRange = aDestArea;
299 ScRange aNewRange = aDestArea; // alter Bereich, wenn Datei nicht gefunden o.ae.
300 if (nWidth > 0 && nHeight > 0)
302 aNewRange.aEnd.SetCol( aNewRange.aStart.Col() + nWidth - 1 );
303 aNewRange.aEnd.SetRow( aNewRange.aStart.Row() + nHeight - 1 );
306 //! check CanFitBlock only if bDoInsert is set?
307 bool bCanDo = ValidColRow( aNewRange.aEnd.Col(), aNewRange.aEnd.Row() ) &&
308 rDoc.CanFitBlock( aOldRange, aNewRange );
309 if (bCanDo)
311 ScDocShellModificator aModificator( *pImpl->m_pDocSh );
313 SCCOL nOldEndX = aOldRange.aEnd.Col();
314 SCROW nOldEndY = aOldRange.aEnd.Row();
315 SCCOL nNewEndX = aNewRange.aEnd.Col();
316 SCROW nNewEndY = aNewRange.aEnd.Row();
317 ScRange aMaxRange( aDestPos,
318 ScAddress(std::max(nOldEndX,nNewEndX), std::max(nOldEndY,nNewEndY), nDestTab) );
320 // Undo initialisieren
322 ScDocument* pUndoDoc = nullptr;
323 if ( bAddUndo && bUndo )
325 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
326 if ( bDoInsert )
328 if ( nNewEndX != nOldEndX || nNewEndY != nOldEndY ) // Bereich veraendert?
330 pUndoDoc->InitUndo( &rDoc, 0, rDoc.GetTableCount()-1 );
331 rDoc.CopyToDocument( 0,0,0,MAXCOL,MAXROW,MAXTAB,
332 InsertDeleteFlags::FORMULA, false, pUndoDoc ); // alle Formeln
334 else
335 pUndoDoc->InitUndo( &rDoc, nDestTab, nDestTab ); // nur Zieltabelle
336 rDoc.CopyToDocument( aOldRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, pUndoDoc );
338 else // ohne Einfuegen
340 pUndoDoc->InitUndo( &rDoc, nDestTab, nDestTab ); // nur Zieltabelle
341 rDoc.CopyToDocument( aMaxRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, pUndoDoc );
345 // Zellen einfuegen / loeschen
346 // DeleteAreaTab loescht auch MERGE_FLAG Attribute
348 if (bDoInsert)
349 rDoc.FitBlock( aOldRange, aNewRange ); // incl. loeschen
350 else
351 rDoc.DeleteAreaTab( aMaxRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
353 // Daten kopieren
355 if (nWidth > 0 && nHeight > 0)
357 ScDocument aClipDoc( SCDOCMODE_CLIP );
358 ScRange aNewTokenRange( aNewRange.aStart );
359 nStringIx = 0;
360 for( nToken = 0; nToken < nTokenCnt; nToken++ )
362 OUString aToken( aTempArea.getToken( 0, ';', nStringIx ) );
363 ScRange aTokenRange;
364 if( FindExtRange( aTokenRange, &rSrcDoc, aToken ) )
366 SCTAB nSrcTab = aTokenRange.aStart.Tab();
367 ScMarkData aSourceMark;
368 aSourceMark.SelectOneTable( nSrcTab ); // selektieren fuer CopyToClip
369 aSourceMark.SetMarkArea( aTokenRange );
371 ScClipParam aClipParam(aTokenRange, false);
372 rSrcDoc.CopyToClip(aClipParam, &aClipDoc, &aSourceMark, false, false);
374 if ( aClipDoc.HasAttrib( 0,0,nSrcTab, MAXCOL,MAXROW,nSrcTab,
375 HASATTR_MERGED | HASATTR_OVERLAPPED ) )
377 //! ResetAttrib am Dokument !!!
379 ScPatternAttr aPattern( rSrcDoc.GetPool() );
380 aPattern.GetItemSet().Put( ScMergeAttr() ); // Defaults
381 aPattern.GetItemSet().Put( ScMergeFlagAttr() );
382 aClipDoc.ApplyPatternAreaTab( 0,0, MAXCOL,MAXROW, nSrcTab, aPattern );
385 aNewTokenRange.aEnd.SetCol( aNewTokenRange.aStart.Col() + (aTokenRange.aEnd.Col() - aTokenRange.aStart.Col()) );
386 aNewTokenRange.aEnd.SetRow( aNewTokenRange.aStart.Row() + (aTokenRange.aEnd.Row() - aTokenRange.aStart.Row()) );
387 ScMarkData aDestMark;
388 aDestMark.SelectOneTable( nDestTab );
389 aDestMark.SetMarkArea( aNewTokenRange );
390 rDoc.CopyFromClip( aNewTokenRange, aDestMark, InsertDeleteFlags::ALL, nullptr, &aClipDoc, false );
391 aNewTokenRange.aStart.SetRow( aNewTokenRange.aEnd.Row() + 2 );
395 else
397 OUString aErr = ScGlobal::GetRscString(STR_LINKERROR);
398 rDoc.SetString( aDestPos.Col(), aDestPos.Row(), aDestPos.Tab(), aErr );
401 // Undo eintragen
403 if ( bAddUndo && bUndo)
405 ScDocument* pRedoDoc = new ScDocument( SCDOCMODE_UNDO );
406 pRedoDoc->InitUndo( &rDoc, nDestTab, nDestTab );
407 rDoc.CopyToDocument( aNewRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, pRedoDoc );
409 pImpl->m_pDocSh->GetUndoManager()->AddUndoAction(
410 new ScUndoUpdateAreaLink( pImpl->m_pDocSh,
411 aFileName, aFilterName, aOptions,
412 aSourceArea, aOldRange, GetRefreshDelay(),
413 aNewUrl, rNewFilter, aNewOpt,
414 rNewArea, aNewRange, nNewRefresh,
415 pUndoDoc, pRedoDoc, bDoInsert ) );
418 // neue Einstellungen merken
420 if ( bNewUrlName )
421 aFileName = aNewUrl;
422 if ( rNewFilter != aFilterName )
423 aFilterName = rNewFilter;
424 if ( rNewArea != aSourceArea )
425 aSourceArea = rNewArea;
426 if ( aNewOpt != aOptions )
427 aOptions = aNewOpt;
429 if ( aNewRange != aDestArea )
430 aDestArea = aNewRange;
432 if ( nNewRefresh != GetRefreshDelay() )
433 SetRefreshDelay( nNewRefresh );
435 SCCOL nPaintEndX = std::max( aOldRange.aEnd.Col(), aNewRange.aEnd.Col() );
436 SCROW nPaintEndY = std::max( aOldRange.aEnd.Row(), aNewRange.aEnd.Row() );
438 if ( aOldRange.aEnd.Col() != aNewRange.aEnd.Col() )
439 nPaintEndX = MAXCOL;
440 if ( aOldRange.aEnd.Row() != aNewRange.aEnd.Row() )
441 nPaintEndY = MAXROW;
443 if ( !pImpl->m_pDocSh->AdjustRowHeight( aDestPos.Row(), nPaintEndY, nDestTab ) )
444 pImpl->m_pDocSh->PostPaint(
445 ScRange(aDestPos.Col(), aDestPos.Row(), nDestTab, nPaintEndX, nPaintEndY, nDestTab),
446 PAINT_GRID);
447 aModificator.SetDocumentModified();
449 else
451 // CanFitBlock sal_False -> Probleme mit zusammengefassten Zellen
452 // oder Tabellengrenze erreicht!
453 //! Zellschutz ???
455 //! Link-Dialog muss Default-Parent setzen
456 // "kann keine Zeilen einfuegen"
457 ScopedVclPtrInstance<InfoBox> aBox( Application::GetDefDialogParent(),
458 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_2 ) );
459 aBox->Execute();
462 // aufraeumen
464 aRef->DoClose();
466 rDoc.SetInLinkUpdate( false );
468 if (bCanDo)
470 // notify Uno objects (for XRefreshListener)
471 //! also notify Uno objects if file name was changed!
472 ScLinkRefreshedHint aHint;
473 aHint.SetAreaLink( aDestPos );
474 rDoc.BroadcastUno( aHint );
477 return bCanDo;
480 IMPL_LINK_NOARG_TYPED(ScAreaLink, RefreshHdl, Timer *, void)
482 Refresh( aFileName, aFilterName, aSourceArea, GetRefreshDelay() );
485 IMPL_LINK_NOARG_TYPED(ScAreaLink, AreaEndEditHdl, Dialog&, void)
487 // #i76514# can't use link argument to access the dialog,
488 // because it's the ScLinkedAreaDlg, not AbstractScLinkedAreaDlg
490 if ( pImpl->m_pDialog && pImpl->m_pDialog->GetResult() == RET_OK )
492 aOptions = pImpl->m_pDialog->GetOptions();
493 Refresh( pImpl->m_pDialog->GetURL(), pImpl->m_pDialog->GetFilter(),
494 pImpl->m_pDialog->GetSource(), pImpl->m_pDialog->GetRefresh() );
496 // copy source data from members (set in Refresh) into link name for dialog
497 OUString aNewLinkName;
498 sfx2::MakeLnkName( aNewLinkName, nullptr, aFileName, aSourceArea, &aFilterName );
499 SetName( aNewLinkName );
501 pImpl->m_pDialog = nullptr; // dialog is deleted with parent
504 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */