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