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