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