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 $
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"
51 #include "rangenam.hxx"
52 #include "dbcolect.hxx"
53 #include "undoblk.hxx"
54 #include "globstr.hrc"
55 #include "markdata.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"
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
,
84 ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ONCALL
,FORMAT_FILE
),
85 ScRefreshTimer ( nRefresh
),
86 pImpl ( new AreaLink_Impl() ),
88 aFilterName (rFilter
),
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()
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
133 SvxLinkManager
* pLinkManager
=pImpl
->m_pDocSh
->GetDocument()->GetLinkManager();
134 if (pLinkManager
!=NULL
)
139 pLinkManager
->GetDisplayNames( this,0,&aFile
,&aArea
,&aFilter
);
141 // the file dialog returns the filter name with the application prefix
143 ScDocumentLoader::RemoveAppPrefix( aFilter
);
145 // #81155# dialog doesn't set area, so keep old one
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
,
191 // also update link name for dialog
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
)
208 ScRangeName
* pNames
= pSrcDoc
->GetRangeName();
210 if (pNames
) // benannte Bereiche
212 if (pNames
->SearchName( rAreaName
, nPos
))
213 if ( (*pNames
)[nPos
]->IsValidReference( rRange
) )
216 if (!bFound
) // Datenbankbereiche
218 ScDBCollection
* pDBColl
= pSrcDoc
->GetDBCollection();
220 if (pDBColl
->SearchName( rAreaName
, nPos
))
225 (*pDBColl
)[nPos
]->GetArea(nTab
,nCol1
,nRow1
,nCol2
,nRow2
);
226 rRange
= ScRange( nCol1
,nRow1
,nTab
, nCol2
,nRow2
,nTab
);
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
)
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())
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
);
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
)
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
);
284 // correct source range name list for web query import
287 if( rNewFilter
== ScDocShell::GetWebQueryFilterName() )
288 aTempArea
= ScFormatFilter::Get().GetHTMLRangeNameList( pSrcDoc
, rNewArea
);
290 aTempArea
= rNewArea
;
292 // find total size of source area
295 xub_StrLen nTokenCnt
= aTempArea
.GetTokenCount( ';' );
296 xub_StrLen nStringIx
= 0;
299 for( nToken
= 0; nToken
< nTokenCnt
; nToken
++ )
301 String
aToken( aTempArea
.GetToken( 0, ';', nStringIx
) );
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
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
);
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
);
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
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
371 pDoc
->FitBlock( aOldRange
, aNewRange
); // incl. loeschen
373 pDoc
->DeleteAreaTab( aMaxRange
, IDF_ALL
& ~IDF_NOTE
);
377 if (nWidth
> 0 && nHeight
> 0)
379 ScDocument
aClipDoc( SCDOCMODE_CLIP
);
380 ScRange
aNewTokenRange( aNewRange
.aStart
);
382 for( nToken
= 0; nToken
< nTokenCnt
; nToken
++ )
384 String
aToken( aTempArea
.GetToken( 0, ';', nStringIx
) );
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 );
419 String aErr
= ScGlobal::GetRscString(STR_LINKERROR
);
420 pDoc
->SetString( aDestPos
.Col(), aDestPos
.Row(), aDestPos
.Tab(), aErr
);
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
444 if ( rNewFilter
!= aFilterName
)
445 aFilterName
= rNewFilter
;
446 if ( rNewArea
!= aSourceArea
)
447 aSourceArea
= rNewArea
;
448 if ( aNewOpt
!= aOptions
)
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() )
462 if ( aOldRange
.aEnd
.Row() != aNewRange
.aEnd
.Row() )
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();
472 // CanFitBlock FALSE -> Probleme mit zusammengefassten Zellen
473 // oder Tabellengrenze erreicht!
476 //! Link-Dialog muss Default-Parent setzen
477 // "kann keine Zeilen einfuegen"
478 InfoBox
aBox( Application::GetDefDialogParent(),
479 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_2
) );
487 pDoc
->SetInLinkUpdate( FALSE
);
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
);
502 IMPL_LINK( ScAreaLink
, RefreshHdl
, ScAreaLink
*, EMPTYARG
)
504 long nRes
= Refresh( aFileName
, aFilterName
, aSourceArea
,
505 GetRefreshDelay() ) != 0;
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
522 sfx2::MakeLnkName( aNewLinkName
, NULL
, aFileName
, aSourceArea
, &aFilterName
);
523 SetName( aNewLinkName
);
525 pImpl
->m_pDialog
= NULL
; // dialog is deleted with parent