xref: /AOO41X/main/sc/source/ui/docshell/arealink.cxx (revision ff0525f24f03981d56b7579b645949f111420994)
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 
66     AreaLink_Impl() : m_pDocSh( NULL ), m_pDialog( NULL ) {}
67 };
68 
69 TYPEINIT1(ScAreaLink,::sfx2::SvBaseLink);
70 
71 //------------------------------------------------------------------------
72 
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 
95 __EXPORT ScAreaLink::~ScAreaLink()
96 {
97     StopRefreshTimer();
98     delete pImpl;
99 }
100 
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 
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 
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 
175 void ScAreaLink::SetDestArea(const ScRange& rNew)
176 {
177     aDestArea = rNew;           // fuer Undo
178 }
179 
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 
194 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<
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 
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 
499 IMPL_LINK( ScAreaLink, RefreshHdl, ScAreaLink*, EMPTYARG )
500 {
501     long nRes = Refresh( aFileName, aFilterName, aSourceArea,
502         GetRefreshDelay() ) != 0;
503     return nRes;
504 }
505 
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