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 if ( pLinkMgr->urlIsVendor( rNewFile ) ) 251 return sal_False; 252 SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pImpl->m_pDocSh ); 253 if ( pFrame ) { 254 Window* pWindow = &pFrame->GetWindow(); 255 if ( pWindow ) { 256 if ( !pLinkMgr->GetUserAllowsLinkUpdate( pWindow ) ) { 257 return sal_False; 258 } 259 } 260 } 261 } 262 263 String aNewUrl( ScGlobal::GetAbsDocName( rNewFile, pImpl->m_pDocSh ) ); 264 sal_Bool bNewUrlName = (aNewUrl != aFileName); 265 266 const SfxFilter* pFilter = pImpl->m_pDocSh->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter); 267 if (!pFilter) 268 return sal_False; 269 270 ScDocument* pDoc = pImpl->m_pDocSh->GetDocument(); 271 272 sal_Bool bUndo (pDoc->IsUndoEnabled()); 273 pDoc->SetInLinkUpdate( sal_True ); 274 275 // wenn neuer Filter ausgewaehlt wurde, Optionen vergessen 276 if ( rNewFilter != aFilterName ) 277 aOptions.Erase(); 278 279 // ItemSet immer anlegen, damit die DocShell die Optionen setzen kann 280 SfxItemSet* pSet = new SfxAllItemSet( SFX_APP()->GetPool() ); 281 if ( aOptions.Len() ) 282 pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, aOptions ) ); 283 284 SfxMedium* pMed = new SfxMedium(aNewUrl, STREAM_STD_READ, sal_False, pFilter); 285 286 // aRef->DoClose() will be closed explicitly, but it is still more safe to use SfxObjectShellLock here 287 ScDocShell* pSrcShell = new ScDocShell(SFX_CREATE_MODE_INTERNAL); 288 SfxObjectShellLock aRef = pSrcShell; 289 pSrcShell->DoLoad(pMed); 290 291 ScDocument* pSrcDoc = pSrcShell->GetDocument(); 292 293 // Optionen koennten gesetzt worden sein 294 String aNewOpt = ScDocumentLoader::GetOptions(*pMed); 295 if (!aNewOpt.Len()) 296 aNewOpt = aOptions; 297 298 // correct source range name list for web query import 299 String aTempArea; 300 301 if( rNewFilter == ScDocShell::GetWebQueryFilterName() ) 302 aTempArea = ScFormatFilter::Get().GetHTMLRangeNameList( pSrcDoc, rNewArea ); 303 else 304 aTempArea = rNewArea; 305 306 // find total size of source area 307 SCCOL nWidth = 0; 308 SCROW nHeight = 0; 309 xub_StrLen nTokenCnt = aTempArea.GetTokenCount( ';' ); 310 xub_StrLen nStringIx = 0; 311 xub_StrLen nToken; 312 313 for( nToken = 0; nToken < nTokenCnt; nToken++ ) 314 { 315 String aToken( aTempArea.GetToken( 0, ';', nStringIx ) ); 316 ScRange aTokenRange; 317 if( FindExtRange( aTokenRange, pSrcDoc, aToken ) ) 318 { 319 // columns: find maximum 320 nWidth = Max( nWidth, (SCCOL)(aTokenRange.aEnd.Col() - aTokenRange.aStart.Col() + 1) ); 321 // rows: add row range + 1 empty row 322 nHeight += aTokenRange.aEnd.Row() - aTokenRange.aStart.Row() + 2; 323 } 324 } 325 // remove the last empty row 326 if( nHeight > 0 ) 327 nHeight--; 328 329 // alte Daten loeschen / neue kopieren 330 331 ScAddress aDestPos = aDestArea.aStart; 332 SCTAB nDestTab = aDestPos.Tab(); 333 ScRange aOldRange = aDestArea; 334 ScRange aNewRange = aDestArea; // alter Bereich, wenn Datei nicht gefunden o.ae. 335 if (nWidth > 0 && nHeight > 0) 336 { 337 aNewRange.aEnd.SetCol( aNewRange.aStart.Col() + nWidth - 1 ); 338 aNewRange.aEnd.SetRow( aNewRange.aStart.Row() + nHeight - 1 ); 339 } 340 341 //! check CanFitBlock only if bDoInsert is set? 342 sal_Bool bCanDo = ValidColRow( aNewRange.aEnd.Col(), aNewRange.aEnd.Row() ) && 343 pDoc->CanFitBlock( aOldRange, aNewRange ); 344 if (bCanDo) 345 { 346 ScDocShellModificator aModificator( *pImpl->m_pDocSh ); 347 348 SCCOL nOldEndX = aOldRange.aEnd.Col(); 349 SCROW nOldEndY = aOldRange.aEnd.Row(); 350 SCCOL nNewEndX = aNewRange.aEnd.Col(); 351 SCROW nNewEndY = aNewRange.aEnd.Row(); 352 ScRange aMaxRange( aDestPos, 353 ScAddress(Max(nOldEndX,nNewEndX), Max(nOldEndY,nNewEndY), nDestTab) ); 354 355 // Undo initialisieren 356 357 ScDocument* pUndoDoc = NULL; 358 ScDocument* pRedoDoc = NULL; 359 if ( bAddUndo && bUndo ) 360 { 361 pUndoDoc = new ScDocument( SCDOCMODE_UNDO ); 362 if ( bDoInsert ) 363 { 364 if ( nNewEndX != nOldEndX || nNewEndY != nOldEndY ) // Bereich veraendert? 365 { 366 pUndoDoc->InitUndo( pDoc, 0, pDoc->GetTableCount()-1 ); 367 pDoc->CopyToDocument( 0,0,0,MAXCOL,MAXROW,MAXTAB, 368 IDF_FORMULA, sal_False, pUndoDoc ); // alle Formeln 369 } 370 else 371 pUndoDoc->InitUndo( pDoc, nDestTab, nDestTab ); // nur Zieltabelle 372 pDoc->CopyToDocument( aOldRange, IDF_ALL & ~IDF_NOTE, sal_False, pUndoDoc ); 373 } 374 else // ohne Einfuegen 375 { 376 pUndoDoc->InitUndo( pDoc, nDestTab, nDestTab ); // nur Zieltabelle 377 pDoc->CopyToDocument( aMaxRange, IDF_ALL & ~IDF_NOTE, sal_False, pUndoDoc ); 378 } 379 } 380 381 // Zellen einfuegen / loeschen 382 // DeleteAreaTab loescht auch MERGE_FLAG Attribute 383 384 if (bDoInsert) 385 pDoc->FitBlock( aOldRange, aNewRange ); // incl. loeschen 386 else 387 pDoc->DeleteAreaTab( aMaxRange, IDF_ALL & ~IDF_NOTE ); 388 389 // Daten kopieren 390 391 if (nWidth > 0 && nHeight > 0) 392 { 393 ScDocument aClipDoc( SCDOCMODE_CLIP ); 394 ScRange aNewTokenRange( aNewRange.aStart ); 395 nStringIx = 0; 396 for( nToken = 0; nToken < nTokenCnt; nToken++ ) 397 { 398 String aToken( aTempArea.GetToken( 0, ';', nStringIx ) ); 399 ScRange aTokenRange; 400 if( FindExtRange( aTokenRange, pSrcDoc, aToken ) ) 401 { 402 SCTAB nSrcTab = aTokenRange.aStart.Tab(); 403 ScMarkData aSourceMark; 404 aSourceMark.SelectOneTable( nSrcTab ); // selektieren fuer CopyToClip 405 aSourceMark.SetMarkArea( aTokenRange ); 406 407 ScClipParam aClipParam(aTokenRange, false); 408 pSrcDoc->CopyToClip(aClipParam, &aClipDoc, &aSourceMark); 409 410 if ( aClipDoc.HasAttrib( 0,0,nSrcTab, MAXCOL,MAXROW,nSrcTab, 411 HASATTR_MERGED | HASATTR_OVERLAPPED ) ) 412 { 413 //! ResetAttrib am Dokument !!! 414 415 ScPatternAttr aPattern( pSrcDoc->GetPool() ); 416 aPattern.GetItemSet().Put( ScMergeAttr() ); // Defaults 417 aPattern.GetItemSet().Put( ScMergeFlagAttr() ); 418 aClipDoc.ApplyPatternAreaTab( 0,0, MAXCOL,MAXROW, nSrcTab, aPattern ); 419 } 420 421 aNewTokenRange.aEnd.SetCol( aNewTokenRange.aStart.Col() + (aTokenRange.aEnd.Col() - aTokenRange.aStart.Col()) ); 422 aNewTokenRange.aEnd.SetRow( aNewTokenRange.aStart.Row() + (aTokenRange.aEnd.Row() - aTokenRange.aStart.Row()) ); 423 ScMarkData aDestMark; 424 aDestMark.SelectOneTable( nDestTab ); 425 aDestMark.SetMarkArea( aNewTokenRange ); 426 pDoc->CopyFromClip( aNewTokenRange, aDestMark, IDF_ALL, NULL, &aClipDoc, sal_False ); 427 aNewTokenRange.aStart.SetRow( aNewTokenRange.aEnd.Row() + 2 ); 428 } 429 } 430 } 431 else 432 { 433 String aErr = ScGlobal::GetRscString(STR_LINKERROR); 434 pDoc->SetString( aDestPos.Col(), aDestPos.Row(), aDestPos.Tab(), aErr ); 435 } 436 437 // Undo eintragen 438 439 if ( bAddUndo && bUndo) 440 { 441 pRedoDoc = new ScDocument( SCDOCMODE_UNDO ); 442 pRedoDoc->InitUndo( pDoc, nDestTab, nDestTab ); 443 pDoc->CopyToDocument( aNewRange, IDF_ALL & ~IDF_NOTE, sal_False, pRedoDoc ); 444 445 pImpl->m_pDocSh->GetUndoManager()->AddUndoAction( 446 new ScUndoUpdateAreaLink( pImpl->m_pDocSh, 447 aFileName, aFilterName, aOptions, 448 aSourceArea, aOldRange, GetRefreshDelay(), 449 aNewUrl, rNewFilter, aNewOpt, 450 rNewArea, aNewRange, nNewRefresh, 451 pUndoDoc, pRedoDoc, bDoInsert ) ); 452 } 453 454 // neue Einstellungen merken 455 456 if ( bNewUrlName ) 457 aFileName = aNewUrl; 458 if ( rNewFilter != aFilterName ) 459 aFilterName = rNewFilter; 460 if ( rNewArea != aSourceArea ) 461 aSourceArea = rNewArea; 462 if ( aNewOpt != aOptions ) 463 aOptions = aNewOpt; 464 465 if ( aNewRange != aDestArea ) 466 aDestArea = aNewRange; 467 468 if ( nNewRefresh != GetRefreshDelay() ) 469 SetRefreshDelay( nNewRefresh ); 470 471 SCCOL nPaintEndX = Max( aOldRange.aEnd.Col(), aNewRange.aEnd.Col() ); 472 SCROW nPaintEndY = Max( aOldRange.aEnd.Row(), aNewRange.aEnd.Row() ); 473 474 if ( aOldRange.aEnd.Col() != aNewRange.aEnd.Col() ) 475 nPaintEndX = MAXCOL; 476 if ( aOldRange.aEnd.Row() != aNewRange.aEnd.Row() ) 477 nPaintEndY = MAXROW; 478 479 if ( !pImpl->m_pDocSh->AdjustRowHeight( aDestPos.Row(), nPaintEndY, nDestTab ) ) 480 pImpl->m_pDocSh->PostPaint( aDestPos.Col(),aDestPos.Row(),nDestTab, 481 nPaintEndX,nPaintEndY,nDestTab, PAINT_GRID ); 482 aModificator.SetDocumentModified(); 483 } 484 else 485 { 486 // CanFitBlock sal_False -> Probleme mit zusammengefassten Zellen 487 // oder Tabellengrenze erreicht! 488 //! Zellschutz ??? 489 490 //! Link-Dialog muss Default-Parent setzen 491 // "kann keine Zeilen einfuegen" 492 InfoBox aBox( Application::GetDefDialogParent(), 493 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_2 ) ); 494 aBox.Execute(); 495 } 496 497 // aufraeumen 498 499 aRef->DoClose(); 500 501 pDoc->SetInLinkUpdate( sal_False ); 502 503 if (bCanDo) 504 { 505 // notify Uno objects (for XRefreshListener) 506 //! also notify Uno objects if file name was changed! 507 ScLinkRefreshedHint aHint; 508 aHint.SetAreaLink( aDestPos ); 509 pDoc->BroadcastUno( aHint ); 510 } 511 512 return bCanDo; 513 } 514 515 516 IMPL_LINK( ScAreaLink, RefreshHdl, ScAreaLink*, EMPTYARG ) 517 { 518 long nRes = Refresh( aFileName, aFilterName, aSourceArea, 519 GetRefreshDelay() ) != 0; 520 return nRes; 521 } 522 523 IMPL_LINK( ScAreaLink, AreaEndEditHdl, void*, EMPTYARG ) 524 { 525 // #i76514# can't use link argument to access the dialog, 526 // because it's the ScLinkedAreaDlg, not AbstractScLinkedAreaDlg 527 528 if ( pImpl->m_pDialog && pImpl->m_pDialog->GetResult() == RET_OK ) 529 { 530 aOptions = pImpl->m_pDialog->GetOptions(); 531 Refresh( pImpl->m_pDialog->GetURL(), pImpl->m_pDialog->GetFilter(), 532 pImpl->m_pDialog->GetSource(), pImpl->m_pDialog->GetRefresh() ); 533 534 // copy source data from members (set in Refresh) into link name for dialog 535 String aNewLinkName; 536 sfx2::MakeLnkName( aNewLinkName, NULL, aFileName, aSourceArea, &aFilterName ); 537 SetName( aNewLinkName ); 538 } 539 pImpl->m_pDialog = NULL; // dialog is deleted with parent 540 541 return 0; 542 } 543 544