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