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