xref: /aoo41x/main/sc/source/ui/docshell/arealink.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 
32 
33 // INCLUDE ---------------------------------------------------------
34 
35 #include <sfx2/app.hxx>
36 #include <sfx2/docfile.hxx>
37 #include <sfx2/fcontnr.hxx>
38 #include <sfx2/sfxsids.hrc>
39 #include <sfx2/linkmgr.hxx>
40 #include <svl/stritem.hxx>
41 #include <vcl/msgbox.hxx>
42 
43 #include "arealink.hxx"
44 
45 #include "tablink.hxx"
46 #include "document.hxx"
47 #include "docsh.hxx"
48 #include "rangenam.hxx"
49 #include "dbcolect.hxx"
50 #include "undoblk.hxx"
51 #include "globstr.hrc"
52 #include "markdata.hxx"
53 #include "hints.hxx"
54 #include "filter.hxx"
55 //CHINA001 #include "linkarea.hxx"			// dialog
56 
57 #include "attrib.hxx"			// raus, wenn ResetAttrib am Dokument
58 #include "patattr.hxx"			// raus, wenn ResetAttrib am Dokument
59 #include "docpool.hxx"			// raus, wenn ResetAttrib am Dokument
60 
61 #include "sc.hrc" //CHINA001
62 #include "scabstdlg.hxx" //CHINA001
63 #include "clipparam.hxx"
64 
65 struct AreaLink_Impl
66 {
67     ScDocShell* m_pDocSh;
68     AbstractScLinkedAreaDlg* m_pDialog;
69 
70     AreaLink_Impl() : m_pDocSh( NULL ), m_pDialog( NULL ) {}
71 };
72 
73 TYPEINIT1(ScAreaLink,::sfx2::SvBaseLink);
74 
75 //------------------------------------------------------------------------
76 
77 ScAreaLink::ScAreaLink( SfxObjectShell* pShell, const String& rFile,
78 						const String& rFilter, const String& rOpt,
79 						const String& rArea, const ScRange& rDest,
80 						sal_uLong nRefresh ) :
81 	::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL,FORMAT_FILE),
82 	ScRefreshTimer	( nRefresh ),
83     pImpl           ( new AreaLink_Impl() ),
84 	aFileName		(rFile),
85 	aFilterName		(rFilter),
86 	aOptions		(rOpt),
87 	aSourceArea		(rArea),
88 	aDestArea		(rDest),
89 	bAddUndo		(sal_True),
90 	bInCreate		(sal_False),
91 	bDoInsert		(sal_True)
92 {
93 	DBG_ASSERT(pShell->ISA(ScDocShell), "ScAreaLink mit falscher ObjectShell");
94     pImpl->m_pDocSh = static_cast< ScDocShell* >( pShell );
95 	SetRefreshHandler( LINK( this, ScAreaLink, RefreshHdl ) );
96     SetRefreshControl( pImpl->m_pDocSh->GetDocument()->GetRefreshTimerControlAddress() );
97 }
98 
99 __EXPORT ScAreaLink::~ScAreaLink()
100 {
101 	StopRefreshTimer();
102     delete pImpl;
103 }
104 
105 void __EXPORT ScAreaLink::Edit(Window* pParent, const Link& /* rEndEditHdl */ )
106 {
107 	//	use own dialog instead of SvBaseLink::Edit...
108 	//	DefModalDialogParent setzen, weil evtl. aus der DocShell beim ConvertFrom
109 	//	ein Optionen-Dialog kommt...
110 
111 	ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
112 	DBG_ASSERT(pFact, "ScAbstractFactory create fail!");//CHINA001
113 
114 	AbstractScLinkedAreaDlg* pDlg = pFact->CreateScLinkedAreaDlg( pParent, RID_SCDLG_LINKAREA);
115 	DBG_ASSERT(pDlg, "Dialog create fail!");//CHINA001
116 	pDlg->InitFromOldLink( aFileName, aFilterName, aOptions, aSourceArea, GetRefreshDelay() );
117     pImpl->m_pDialog = pDlg;
118     pDlg->StartExecuteModal( LINK( this, ScAreaLink, AreaEndEditHdl ) );
119 }
120 
121 void __EXPORT ScAreaLink::DataChanged( const String&,
122 									   const ::com::sun::star::uno::Any& )
123 {
124 	//	bei bInCreate nichts tun, damit Update gerufen werden kann, um den Status im
125 	//	LinkManager zu setzen, ohne die Daten im Dokument zu aendern
126 
127 	if (bInCreate)
128 		return;
129 
130     sfx2::LinkManager* pLinkManager=pImpl->m_pDocSh->GetDocument()->GetLinkManager();
131 	if (pLinkManager!=NULL)
132 	{
133 		String aFile;
134 		String aFilter;
135 		String aArea;
136 		pLinkManager->GetDisplayNames( this,0,&aFile,&aArea,&aFilter);
137 
138 		//	the file dialog returns the filter name with the application prefix
139 		//	-> remove prefix
140 		ScDocumentLoader::RemoveAppPrefix( aFilter );
141 
142 		// #81155# dialog doesn't set area, so keep old one
143 		if ( !aArea.Len() )
144 		{
145 			aArea = aSourceArea;
146 
147 			// adjust in dialog:
148             String aNewLinkName;
149             sfx2::MakeLnkName( aNewLinkName, NULL, aFile, aArea, &aFilter );
150             SetName( aNewLinkName );
151 		}
152 
153 		Refresh( aFile, aFilter, aArea, GetRefreshDelay() );
154 	}
155 }
156 
157 void __EXPORT ScAreaLink::Closed()
158 {
159 	// Verknuepfung loeschen: Undo
160 
161     ScDocument* pDoc = pImpl->m_pDocSh->GetDocument();
162 	sal_Bool bUndo (pDoc->IsUndoEnabled());
163 	if (bAddUndo && bUndo)
164 	{
165         pImpl->m_pDocSh->GetUndoManager()->AddUndoAction( new ScUndoRemoveAreaLink( pImpl->m_pDocSh,
166 														aFileName, aFilterName, aOptions,
167 														aSourceArea, aDestArea, GetRefreshDelay() ) );
168 
169 		bAddUndo = sal_False;	// nur einmal
170 	}
171 
172     SCTAB nDestTab = aDestArea.aStart.Tab();
173     if (pDoc->IsStreamValid(nDestTab))
174         pDoc->SetStreamValid(nDestTab, sal_False);
175 
176 	SvBaseLink::Closed();
177 }
178 
179 void ScAreaLink::SetDestArea(const ScRange& rNew)
180 {
181 	aDestArea = rNew;			// fuer Undo
182 }
183 
184 void ScAreaLink::SetSource(const String& rDoc, const String& rFlt, const String& rOpt,
185 								const String& rArea)
186 {
187 	aFileName	= rDoc;
188 	aFilterName	= rFlt;
189 	aOptions	= rOpt;
190 	aSourceArea	= rArea;
191 
192 	//	also update link name for dialog
193     String aNewLinkName;
194     sfx2::MakeLnkName( aNewLinkName, NULL, aFileName, aSourceArea, &aFilterName );
195     SetName( aNewLinkName );
196 }
197 
198 sal_Bool ScAreaLink::IsEqual( const String& rFile, const String& rFilter, const String& rOpt,
199 							const String& rSource, const ScRange& rDest ) const
200 {
201 	return aFileName == rFile && aFilterName == rFilter && aOptions == rOpt &&
202 			aSourceArea == rSource && aDestArea.aStart == rDest.aStart;
203 }
204 
205 // find a range with name >rAreaName< in >pSrcDoc<, return it in >rRange<
206 sal_Bool ScAreaLink::FindExtRange( ScRange& rRange, ScDocument* pSrcDoc, const String& rAreaName )
207 {
208 	sal_Bool bFound = sal_False;
209 	ScRangeName* pNames = pSrcDoc->GetRangeName();
210 	sal_uInt16 nPos;
211 	if (pNames)			// benannte Bereiche
212 	{
213 		if (pNames->SearchName( rAreaName, nPos ))
214 			if ( (*pNames)[nPos]->IsValidReference( rRange ) )
215 				bFound = sal_True;
216 	}
217 	if (!bFound)		// Datenbankbereiche
218 	{
219 		ScDBCollection*	pDBColl = pSrcDoc->GetDBCollection();
220 		if (pDBColl)
221 			if (pDBColl->SearchName( rAreaName, nPos ))
222 			{
223                 SCTAB nTab;
224                 SCCOL nCol1, nCol2;
225                 SCROW nRow1, nRow2;
226                 (*pDBColl)[nPos]->GetArea(nTab,nCol1,nRow1,nCol2,nRow2);
227 				rRange = ScRange( nCol1,nRow1,nTab, nCol2,nRow2,nTab );
228 				bFound = sal_True;
229 			}
230 	}
231 	if (!bFound)		// direct reference (range or cell)
232 	{
233         ScAddress::Details aDetails(pSrcDoc->GetAddressConvention(), 0, 0);
234 		if ( rRange.ParseAny( rAreaName, pSrcDoc, aDetails ) & SCA_VALID )
235 			bFound = sal_True;
236 	}
237 	return bFound;
238 }
239 
240 //	ausfuehren:
241 
242 sal_Bool ScAreaLink::Refresh( const String& rNewFile, const String& rNewFilter,
243 							const String& rNewArea, sal_uLong nNewRefresh )
244 {
245 	//	Dokument laden - wie TabLink
246 
247 	if (!rNewFile.Len() || !rNewFilter.Len())
248 		return sal_False;
249 
250     String aNewUrl( ScGlobal::GetAbsDocName( rNewFile, pImpl->m_pDocSh ) );
251 	sal_Bool bNewUrlName = (aNewUrl != aFileName);
252 
253     const SfxFilter* pFilter = pImpl->m_pDocSh->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter);
254 	if (!pFilter)
255 		return sal_False;
256 
257     ScDocument* pDoc = pImpl->m_pDocSh->GetDocument();
258 
259 	sal_Bool bUndo (pDoc->IsUndoEnabled());
260 	pDoc->SetInLinkUpdate( sal_True );
261 
262 	//	wenn neuer Filter ausgewaehlt wurde, Optionen vergessen
263 	if ( rNewFilter != aFilterName )
264 		aOptions.Erase();
265 
266 	//	ItemSet immer anlegen, damit die DocShell die Optionen setzen kann
267 	SfxItemSet* pSet = new SfxAllItemSet( SFX_APP()->GetPool() );
268 	if ( aOptions.Len() )
269 		pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, aOptions ) );
270 
271 	SfxMedium* pMed = new SfxMedium(aNewUrl, STREAM_STD_READ, sal_False, pFilter);
272 
273     // aRef->DoClose() will be closed explicitly, but it is still more safe to use SfxObjectShellLock here
274 	ScDocShell* pSrcShell = new ScDocShell(SFX_CREATE_MODE_INTERNAL);
275 	SfxObjectShellLock aRef = pSrcShell;
276 	pSrcShell->DoLoad(pMed);
277 
278 	ScDocument* pSrcDoc = pSrcShell->GetDocument();
279 
280 	// Optionen koennten gesetzt worden sein
281 	String aNewOpt = ScDocumentLoader::GetOptions(*pMed);
282 	if (!aNewOpt.Len())
283 		aNewOpt = aOptions;
284 
285 	// correct source range name list for web query import
286 	String aTempArea;
287 
288 	if( rNewFilter == ScDocShell::GetWebQueryFilterName() )
289 		aTempArea = ScFormatFilter::Get().GetHTMLRangeNameList( pSrcDoc, rNewArea );
290 	else
291 		aTempArea = rNewArea;
292 
293 	// find total size of source area
294 	SCCOL nWidth = 0;
295 	SCROW nHeight = 0;
296 	xub_StrLen nTokenCnt = aTempArea.GetTokenCount( ';' );
297 	xub_StrLen nStringIx = 0;
298 	xub_StrLen nToken;
299 
300 	for( nToken = 0; nToken < nTokenCnt; nToken++ )
301 	{
302 		String aToken( aTempArea.GetToken( 0, ';', nStringIx ) );
303 		ScRange aTokenRange;
304 		if( FindExtRange( aTokenRange, pSrcDoc, aToken ) )
305 		{
306 			// columns: find maximum
307 			nWidth = Max( nWidth, (SCCOL)(aTokenRange.aEnd.Col() - aTokenRange.aStart.Col() + 1) );
308 			// rows: add row range + 1 empty row
309 			nHeight += aTokenRange.aEnd.Row() - aTokenRange.aStart.Row() + 2;
310 		}
311 	}
312 	// remove the last empty row
313 	if( nHeight > 0 )
314 		nHeight--;
315 
316 	//	alte Daten loeschen / neue kopieren
317 
318 	ScAddress aDestPos = aDestArea.aStart;
319 	SCTAB nDestTab = aDestPos.Tab();
320 	ScRange aOldRange = aDestArea;
321 	ScRange aNewRange = aDestArea;			// alter Bereich, wenn Datei nicht gefunden o.ae.
322 	if (nWidth > 0 && nHeight > 0)
323 	{
324 		aNewRange.aEnd.SetCol( aNewRange.aStart.Col() + nWidth - 1 );
325 		aNewRange.aEnd.SetRow( aNewRange.aStart.Row() + nHeight - 1 );
326 	}
327 
328     //! check CanFitBlock only if bDoInsert is set?
329     sal_Bool bCanDo = ValidColRow( aNewRange.aEnd.Col(), aNewRange.aEnd.Row() ) &&
330                   pDoc->CanFitBlock( aOldRange, aNewRange );
331 	if (bCanDo)
332 	{
333         ScDocShellModificator aModificator( *pImpl->m_pDocSh );
334 
335 		SCCOL nOldEndX = aOldRange.aEnd.Col();
336 		SCROW nOldEndY = aOldRange.aEnd.Row();
337 		SCCOL nNewEndX = aNewRange.aEnd.Col();
338 		SCROW nNewEndY = aNewRange.aEnd.Row();
339 		ScRange aMaxRange( aDestPos,
340 					ScAddress(Max(nOldEndX,nNewEndX), Max(nOldEndY,nNewEndY), nDestTab) );
341 
342 		//	Undo initialisieren
343 
344 		ScDocument* pUndoDoc = NULL;
345 		ScDocument* pRedoDoc = NULL;
346 		if ( bAddUndo && bUndo )
347 		{
348 			pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
349 			if ( bDoInsert )
350 			{
351 				if ( nNewEndX != nOldEndX || nNewEndY != nOldEndY )				// Bereich veraendert?
352 				{
353 					pUndoDoc->InitUndo( pDoc, 0, pDoc->GetTableCount()-1 );
354 					pDoc->CopyToDocument( 0,0,0,MAXCOL,MAXROW,MAXTAB,
355 											IDF_FORMULA, sal_False, pUndoDoc );		// alle Formeln
356 				}
357 				else
358 					pUndoDoc->InitUndo( pDoc, nDestTab, nDestTab );				// nur Zieltabelle
359                 pDoc->CopyToDocument( aOldRange, IDF_ALL & ~IDF_NOTE, sal_False, pUndoDoc );
360 			}
361 			else		// ohne Einfuegen
362 			{
363 				pUndoDoc->InitUndo( pDoc, nDestTab, nDestTab );				// nur Zieltabelle
364                 pDoc->CopyToDocument( aMaxRange, IDF_ALL & ~IDF_NOTE, sal_False, pUndoDoc );
365 			}
366 		}
367 
368 		//	Zellen einfuegen / loeschen
369 		//	DeleteAreaTab loescht auch MERGE_FLAG Attribute
370 
371 		if (bDoInsert)
372 			pDoc->FitBlock( aOldRange, aNewRange );			// incl. loeschen
373 		else
374             pDoc->DeleteAreaTab( aMaxRange, IDF_ALL & ~IDF_NOTE );
375 
376 		//	Daten kopieren
377 
378 		if (nWidth > 0 && nHeight > 0)
379 		{
380 			ScDocument aClipDoc( SCDOCMODE_CLIP );
381 			ScRange aNewTokenRange( aNewRange.aStart );
382 			nStringIx = 0;
383 			for( nToken = 0; nToken < nTokenCnt; nToken++ )
384 			{
385 				String aToken( aTempArea.GetToken( 0, ';', nStringIx ) );
386 				ScRange aTokenRange;
387 				if( FindExtRange( aTokenRange, pSrcDoc, aToken ) )
388 				{
389 					SCTAB nSrcTab = aTokenRange.aStart.Tab();
390 					ScMarkData aSourceMark;
391 					aSourceMark.SelectOneTable( nSrcTab );		// selektieren fuer CopyToClip
392 					aSourceMark.SetMarkArea( aTokenRange );
393 
394                     ScClipParam aClipParam(aTokenRange, false);
395                     pSrcDoc->CopyToClip(aClipParam, &aClipDoc, &aSourceMark);
396 
397 					if ( aClipDoc.HasAttrib( 0,0,nSrcTab, MAXCOL,MAXROW,nSrcTab,
398 											HASATTR_MERGED | HASATTR_OVERLAPPED ) )
399 					{
400 						//!	ResetAttrib am Dokument !!!
401 
402 						ScPatternAttr aPattern( pSrcDoc->GetPool() );
403 						aPattern.GetItemSet().Put( ScMergeAttr() );				// Defaults
404 						aPattern.GetItemSet().Put( ScMergeFlagAttr() );
405 						aClipDoc.ApplyPatternAreaTab( 0,0, MAXCOL,MAXROW, nSrcTab, aPattern );
406 					}
407 
408 					aNewTokenRange.aEnd.SetCol( aNewTokenRange.aStart.Col() + (aTokenRange.aEnd.Col() - aTokenRange.aStart.Col()) );
409 					aNewTokenRange.aEnd.SetRow( aNewTokenRange.aStart.Row() + (aTokenRange.aEnd.Row() - aTokenRange.aStart.Row()) );
410 					ScMarkData aDestMark;
411 					aDestMark.SelectOneTable( nDestTab );
412 					aDestMark.SetMarkArea( aNewTokenRange );
413 					pDoc->CopyFromClip( aNewTokenRange, aDestMark, IDF_ALL, NULL, &aClipDoc, sal_False );
414 					aNewTokenRange.aStart.SetRow( aNewTokenRange.aEnd.Row() + 2 );
415 				}
416 			}
417 		}
418 		else
419 		{
420 			String aErr = ScGlobal::GetRscString(STR_LINKERROR);
421 			pDoc->SetString( aDestPos.Col(), aDestPos.Row(), aDestPos.Tab(), aErr );
422 		}
423 
424 		//	Undo eintragen
425 
426 		if ( bAddUndo && bUndo)
427 		{
428 			pRedoDoc = new ScDocument( SCDOCMODE_UNDO );
429 			pRedoDoc->InitUndo( pDoc, nDestTab, nDestTab );
430             pDoc->CopyToDocument( aNewRange, IDF_ALL & ~IDF_NOTE, sal_False, pRedoDoc );
431 
432             pImpl->m_pDocSh->GetUndoManager()->AddUndoAction(
433                 new ScUndoUpdateAreaLink( pImpl->m_pDocSh,
434 											aFileName, aFilterName, aOptions,
435 											aSourceArea, aOldRange, GetRefreshDelay(),
436 											aNewUrl, rNewFilter, aNewOpt,
437 											rNewArea, aNewRange, nNewRefresh,
438 											pUndoDoc, pRedoDoc, bDoInsert ) );
439 		}
440 
441 		//	neue Einstellungen merken
442 
443 		if ( bNewUrlName )
444 			aFileName = aNewUrl;
445 		if ( rNewFilter != aFilterName )
446 			aFilterName = rNewFilter;
447 		if ( rNewArea != aSourceArea )
448 			aSourceArea = rNewArea;
449 		if ( aNewOpt != aOptions )
450 			aOptions = aNewOpt;
451 
452 		if ( aNewRange != aDestArea )
453 			aDestArea = aNewRange;
454 
455 		if ( nNewRefresh != GetRefreshDelay() )
456 			SetRefreshDelay( nNewRefresh );
457 
458 		SCCOL nPaintEndX = Max( aOldRange.aEnd.Col(), aNewRange.aEnd.Col() );
459 		SCROW nPaintEndY = Max( aOldRange.aEnd.Row(), aNewRange.aEnd.Row() );
460 
461 		if ( aOldRange.aEnd.Col() != aNewRange.aEnd.Col() )
462 			nPaintEndX = MAXCOL;
463 		if ( aOldRange.aEnd.Row() != aNewRange.aEnd.Row() )
464 			nPaintEndY = MAXROW;
465 
466         if ( !pImpl->m_pDocSh->AdjustRowHeight( aDestPos.Row(), nPaintEndY, nDestTab ) )
467             pImpl->m_pDocSh->PostPaint( aDestPos.Col(),aDestPos.Row(),nDestTab,
468 									nPaintEndX,nPaintEndY,nDestTab, PAINT_GRID );
469 		aModificator.SetDocumentModified();
470 	}
471 	else
472 	{
473 		//	CanFitBlock sal_False -> Probleme mit zusammengefassten Zellen
474 		//						 oder Tabellengrenze erreicht!
475 		//!	Zellschutz ???
476 
477 		//!	Link-Dialog muss Default-Parent setzen
478 		//	"kann keine Zeilen einfuegen"
479 		InfoBox aBox( Application::GetDefDialogParent(),
480 						ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_2 ) );
481 		aBox.Execute();
482 	}
483 
484 	//	aufraeumen
485 
486 	aRef->DoClose();
487 
488 	pDoc->SetInLinkUpdate( sal_False );
489 
490 	if (bCanDo)
491 	{
492 		//	notify Uno objects (for XRefreshListener)
493 		//!	also notify Uno objects if file name was changed!
494 		ScLinkRefreshedHint aHint;
495 		aHint.SetAreaLink( aDestPos );
496 		pDoc->BroadcastUno( aHint );
497 	}
498 
499 	return bCanDo;
500 }
501 
502 
503 IMPL_LINK( ScAreaLink, RefreshHdl, ScAreaLink*, EMPTYARG )
504 {
505     long nRes = Refresh( aFileName, aFilterName, aSourceArea,
506         GetRefreshDelay() ) != 0;
507     return nRes;
508 }
509 
510 IMPL_LINK( ScAreaLink, AreaEndEditHdl, void*, EMPTYARG )
511 {
512     //  #i76514# can't use link argument to access the dialog,
513     //  because it's the ScLinkedAreaDlg, not AbstractScLinkedAreaDlg
514 
515     if ( pImpl->m_pDialog && pImpl->m_pDialog->GetResult() == RET_OK )
516     {
517         aOptions = pImpl->m_pDialog->GetOptions();
518         Refresh( pImpl->m_pDialog->GetURL(), pImpl->m_pDialog->GetFilter(),
519                  pImpl->m_pDialog->GetSource(), pImpl->m_pDialog->GetRefresh() );
520 
521         //  copy source data from members (set in Refresh) into link name for dialog
522         String aNewLinkName;
523         sfx2::MakeLnkName( aNewLinkName, NULL, aFileName, aSourceArea, &aFilterName );
524         SetName( aNewLinkName );
525     }
526     pImpl->m_pDialog = NULL;    // dialog is deleted with parent
527 
528     return 0;
529 }
530 
531