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_sfx2.hxx" 30 31 #include <sfx2/linkmgr.hxx> 32 #include <com/sun/star/document/UpdateDocMode.hpp> 33 #include <sfx2/objsh.hxx> 34 #include <svl/urihelper.hxx> 35 #include <sot/formats.hxx> 36 #include <tools/urlobj.hxx> 37 #include <sot/exchange.hxx> 38 #include <tools/debug.hxx> 39 #include <vcl/msgbox.hxx> 40 #include <sfx2/lnkbase.hxx> 41 #include <sfx2/app.hxx> 42 #include <vcl/graph.hxx> 43 #include <svl/stritem.hxx> 44 #include <svl/eitem.hxx> 45 #include <svl/intitem.hxx> 46 #include <unotools/localfilehelper.hxx> 47 #include <i18npool/mslangid.hxx> 48 #include <sfx2/request.hxx> 49 50 #include "fileobj.hxx" 51 #include "impldde.hxx" 52 #include "app.hrc" 53 #include "sfx2/sfxresid.hxx" 54 55 #define _SVSTDARR_STRINGSDTOR 56 #include <svl/svstdarr.hxx> 57 58 namespace sfx2 59 { 60 61 class SvxInternalLink : public sfx2::SvLinkSource 62 { 63 public: 64 SvxInternalLink() {} 65 66 virtual sal_Bool Connect( sfx2::SvBaseLink* ); 67 }; 68 69 70 SV_IMPL_PTRARR( SvBaseLinks, SvBaseLinkRefPtr ) 71 72 LinkManager::LinkManager(SfxObjectShell* p) 73 : pPersist( p ) 74 { 75 } 76 77 78 LinkManager::~LinkManager() 79 { 80 SvBaseLinkRef** ppRef = (SvBaseLinkRef**)aLinkTbl.GetData(); 81 for( sal_uInt16 n = aLinkTbl.Count(); n; --n, ++ppRef ) 82 { 83 if( (*ppRef)->Is() ) 84 { 85 (*(*ppRef))->Disconnect(); 86 (*(*ppRef))->SetLinkManager( NULL ); 87 } 88 delete *ppRef; 89 } 90 } 91 92 93 /************************************************************************ 94 |* LinkManager::Remove() 95 |* 96 |* Beschreibung 97 *************************************************************************/ 98 99 void LinkManager::Remove( SvBaseLink *pLink ) 100 { 101 // keine Links doppelt einfuegen 102 int bFound = sal_False; 103 SvBaseLinkRef** ppRef = (SvBaseLinkRef**)aLinkTbl.GetData(); 104 for( sal_uInt16 n = aLinkTbl.Count(); n; --n, ++ppRef ) 105 { 106 if( pLink == *(*ppRef) ) 107 { 108 (*(*ppRef))->Disconnect(); 109 (*(*ppRef))->SetLinkManager( NULL ); 110 (*(*ppRef)).Clear(); 111 bFound = sal_True; 112 } 113 114 // falls noch leere rum stehen sollten, weg damit 115 if( !(*ppRef)->Is() ) 116 { 117 delete *ppRef; 118 aLinkTbl.Remove( aLinkTbl.Count() - n, 1 ); 119 if( bFound ) 120 return ; 121 --ppRef; 122 } 123 } 124 } 125 126 127 void LinkManager::Remove( sal_uInt16 nPos, sal_uInt16 nCnt ) 128 { 129 if( nCnt && nPos < aLinkTbl.Count() ) 130 { 131 if( nPos + nCnt > aLinkTbl.Count() ) 132 nCnt = aLinkTbl.Count() - nPos; 133 134 SvBaseLinkRef** ppRef = (SvBaseLinkRef**)aLinkTbl.GetData() + nPos; 135 for( sal_uInt16 n = nCnt; n; --n, ++ppRef ) 136 { 137 if( (*ppRef)->Is() ) 138 { 139 (*(*ppRef))->Disconnect(); 140 (*(*ppRef))->SetLinkManager( NULL ); 141 } 142 delete *ppRef; 143 } 144 aLinkTbl.Remove( nPos, nCnt ); 145 } 146 } 147 148 149 sal_Bool LinkManager::Insert( SvBaseLink* pLink ) 150 { 151 // keine Links doppelt einfuegen 152 for( sal_uInt16 n = 0; n < aLinkTbl.Count(); ++n ) 153 { 154 SvBaseLinkRef* pTmp = aLinkTbl[ n ]; 155 if( !pTmp->Is() ) 156 aLinkTbl.DeleteAndDestroy( n-- ); 157 158 if( pLink == *pTmp ) 159 return sal_False; 160 } 161 162 SvBaseLinkRef* pTmp = new SvBaseLinkRef( pLink ); 163 pLink->SetLinkManager( this ); 164 aLinkTbl.Insert( pTmp, aLinkTbl.Count() ); 165 return sal_True; 166 } 167 168 169 sal_Bool LinkManager::InsertLink( SvBaseLink * pLink, 170 sal_uInt16 nObjType, 171 sal_uInt16 nUpdateMode, 172 const String* pName ) 173 { 174 // unbedingt zuerst 175 pLink->SetObjType( nObjType ); 176 if( pName ) 177 pLink->SetName( *pName ); 178 pLink->SetUpdateMode( nUpdateMode ); 179 return Insert( pLink ); 180 } 181 182 183 sal_Bool LinkManager::InsertDDELink( SvBaseLink * pLink, 184 const String& rServer, 185 const String& rTopic, 186 const String& rItem ) 187 { 188 if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) ) 189 return sal_False; 190 191 String sCmd; 192 ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem ); 193 194 pLink->SetObjType( OBJECT_CLIENT_DDE ); 195 pLink->SetName( sCmd ); 196 return Insert( pLink ); 197 } 198 199 200 sal_Bool LinkManager::InsertDDELink( SvBaseLink * pLink ) 201 { 202 DBG_ASSERT( OBJECT_CLIENT_SO & pLink->GetObjType(), "no OBJECT_CLIENT_SO" ); 203 if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) ) 204 return sal_False; 205 206 if( pLink->GetObjType() == OBJECT_CLIENT_SO ) 207 pLink->SetObjType( OBJECT_CLIENT_DDE ); 208 209 return Insert( pLink ); 210 } 211 212 213 // erfrage die Strings fuer den Dialog 214 sal_Bool LinkManager::GetDisplayNames( const SvBaseLink * pLink, 215 String* pType, 216 String* pFile, 217 String* pLinkStr, 218 String* pFilter ) const 219 { 220 sal_Bool bRet = sal_False; 221 const String sLNm( pLink->GetLinkSourceName() ); 222 if( sLNm.Len() ) 223 { 224 switch( pLink->GetObjType() ) 225 { 226 case OBJECT_CLIENT_FILE: 227 case OBJECT_CLIENT_GRF: 228 case OBJECT_CLIENT_OLE: 229 { 230 sal_uInt16 nPos = 0; 231 String sFile( sLNm.GetToken( 0, ::sfx2::cTokenSeperator, nPos ) ); 232 String sRange( sLNm.GetToken( 0, ::sfx2::cTokenSeperator, nPos ) ); 233 234 if( pFile ) 235 *pFile = sFile; 236 if( pLinkStr ) 237 *pLinkStr = sRange; 238 if( pFilter ) 239 *pFilter = sLNm.Copy( nPos ); 240 241 if( pType ) 242 { 243 sal_uInt16 nObjType = pLink->GetObjType(); 244 *pType = String( SfxResId( 245 ( OBJECT_CLIENT_FILE == nObjType || OBJECT_CLIENT_OLE == nObjType ) 246 ? RID_SVXSTR_FILELINK 247 : RID_SVXSTR_GRAFIKLINK )); 248 } 249 bRet = sal_True; 250 } 251 break; 252 case OBJECT_CLIENT_DDE: 253 { 254 sal_uInt16 nTmp = 0; 255 String sCmd( sLNm ); 256 String sServer( sCmd.GetToken( 0, cTokenSeperator, nTmp ) ); 257 String sTopic( sCmd.GetToken( 0, cTokenSeperator, nTmp ) ); 258 259 if( pType ) 260 *pType = sServer; 261 if( pFile ) 262 *pFile = sTopic; 263 if( pLinkStr ) 264 *pLinkStr = sCmd.Copy( nTmp ); 265 bRet = sal_True; 266 } 267 break; 268 default: 269 break; 270 } 271 } 272 273 return bRet; 274 } 275 276 277 void LinkManager::UpdateAllLinks( 278 sal_Bool bAskUpdate, 279 sal_Bool /*bCallErrHdl*/, 280 sal_Bool bUpdateGrfLinks, 281 Window* pParentWin ) 282 { 283 SvStringsDtor aApps, aTopics, aItems; 284 String sApp, sTopic, sItem; 285 286 // erstmal eine Kopie vom Array machen, damit sich updatende Links in 287 // Links in ... nicht dazwischen funken!! 288 SvPtrarr aTmpArr( 255, 50 ); 289 sal_uInt16 n; 290 for( n = 0; n < aLinkTbl.Count(); ++n ) 291 { 292 SvBaseLink* pLink = *aLinkTbl[ n ]; 293 if( !pLink ) 294 { 295 Remove( n-- ); 296 continue; 297 } 298 aTmpArr.Insert( pLink, aTmpArr.Count() ); 299 } 300 301 for( n = 0; n < aTmpArr.Count(); ++n ) 302 { 303 SvBaseLink* pLink = (SvBaseLink*)aTmpArr[ n ]; 304 305 // suche erstmal im Array nach dem Eintrag 306 sal_uInt16 nFndPos = USHRT_MAX; 307 for( sal_uInt16 i = 0; i < aLinkTbl.Count(); ++i ) 308 if( pLink == *aLinkTbl[ i ] ) 309 { 310 nFndPos = i; 311 break; 312 } 313 314 if( USHRT_MAX == nFndPos ) 315 continue; // war noch nicht vorhanden! 316 317 // Graphic-Links noch nicht updaten 318 if( !pLink->IsVisible() || 319 ( !bUpdateGrfLinks && OBJECT_CLIENT_GRF == pLink->GetObjType() )) 320 continue; 321 322 if( bAskUpdate ) 323 { 324 int nRet = QueryBox( pParentWin, WB_YES_NO | WB_DEF_YES, SfxResId( STR_QUERY_UPDATE_LINKS ) ).Execute(); 325 if( RET_YES != nRet ) 326 return ; // es soll nichts geupdatet werden 327 bAskUpdate = sal_False; // einmal reicht 328 } 329 330 pLink->Update(); 331 } 332 } 333 334 /************************************************************************ 335 |* SvBaseLink::CreateObject() 336 |* 337 |* Beschreibung 338 *************************************************************************/ 339 340 SvLinkSourceRef LinkManager::CreateObj( SvBaseLink * pLink ) 341 { 342 switch( pLink->GetObjType() ) 343 { 344 case OBJECT_CLIENT_FILE: 345 case OBJECT_CLIENT_GRF: 346 case OBJECT_CLIENT_OLE: 347 return new SvFileObject; 348 case OBJECT_INTERN: 349 return new SvxInternalLink; 350 case OBJECT_CLIENT_DDE: 351 return new SvDDEObject; 352 default: 353 return SvLinkSourceRef(); 354 } 355 } 356 357 sal_Bool LinkManager::InsertServer( SvLinkSource* pObj ) 358 { 359 // keine doppelt einfuegen 360 if( !pObj || USHRT_MAX != aServerTbl.GetPos( pObj ) ) 361 return sal_False; 362 363 aServerTbl.Insert( pObj, aServerTbl.Count() ); 364 return sal_True; 365 } 366 367 368 void LinkManager::RemoveServer( SvLinkSource* pObj ) 369 { 370 sal_uInt16 nPos = aServerTbl.GetPos( pObj ); 371 if( USHRT_MAX != nPos ) 372 aServerTbl.Remove( nPos, 1 ); 373 } 374 375 376 void MakeLnkName( String& rName, const String* pType, const String& rFile, 377 const String& rLink, const String* pFilter ) 378 { 379 if( pType ) 380 (rName = *pType).EraseLeadingChars().EraseTrailingChars() += cTokenSeperator; 381 else if( rName.Len() ) 382 rName.Erase(); 383 384 ((rName += rFile).EraseLeadingChars().EraseTrailingChars() += 385 cTokenSeperator ).EraseLeadingChars().EraseTrailingChars() += rLink; 386 if( pFilter ) 387 ((rName += cTokenSeperator ) += *pFilter).EraseLeadingChars().EraseTrailingChars(); 388 } 389 390 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink, 391 sal_uInt16 nFileType, 392 const String& rFileNm, 393 const String* pFilterNm, 394 const String* pRange ) 395 { 396 if( !( OBJECT_CLIENT_SO & rLink.GetObjType() )) 397 return sal_False; 398 399 String sCmd( rFileNm ); 400 sCmd += ::sfx2::cTokenSeperator; 401 if( pRange ) 402 sCmd += *pRange; 403 if( pFilterNm ) 404 ( sCmd += ::sfx2::cTokenSeperator ) += *pFilterNm; 405 406 return InsertLink( &rLink, nFileType, sfx2::LINKUPDATE_ONCALL, &sCmd ); 407 } 408 409 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink ) 410 { 411 if( OBJECT_CLIENT_FILE == ( OBJECT_CLIENT_FILE & rLink.GetObjType() )) 412 return InsertLink( &rLink, rLink.GetObjType(), sfx2::LINKUPDATE_ONCALL ); 413 return sal_False; 414 } 415 416 // eine Uebertragung wird abgebrochen, also alle DownloadMedien canceln 417 // (ist zur Zeit nur fuer die FileLinks interressant!) 418 void LinkManager::CancelTransfers() 419 { 420 SvFileObject* pFileObj; 421 sfx2::SvBaseLink* pLnk; 422 423 const sfx2::SvBaseLinks& rLnks = GetLinks(); 424 for( sal_uInt16 n = rLnks.Count(); n; ) 425 if( 0 != ( pLnk = &(*rLnks[ --n ])) && 426 OBJECT_CLIENT_FILE == (OBJECT_CLIENT_FILE & pLnk->GetObjType()) && 427 0 != ( pFileObj = (SvFileObject*)pLnk->GetObj() ) ) 428 // 0 != ( pFileObj = (SvFileObject*)SvFileObject::ClassFactory()-> 429 // CastAndAddRef( pLnk->GetObj() )) ) 430 pFileObj->CancelTransfers(); 431 } 432 433 // um Status Informationen aus dem FileObject an den BaseLink zu 434 // senden, gibt es eine eigene ClipBoardId. Das SvData-Object hat 435 // dann die entsprechenden Informationen als String. 436 // Wird zur Zeit fuer FileObject in Verbindung mit JavaScript benoetigt 437 // - das braucht Informationen ueber Load/Abort/Error 438 sal_uIntPtr LinkManager::RegisterStatusInfoId() 439 { 440 static sal_uIntPtr nFormat = 0; 441 442 if( !nFormat ) 443 { 444 // wie sieht die neue Schnittstelle aus? 445 // nFormat = Exchange::RegisterFormatName( "StatusInfo vom SvxInternalLink" ); 446 nFormat = SotExchange::RegisterFormatName( 447 String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( 448 "StatusInfo vom SvxInternalLink" ))); 449 } 450 return nFormat; 451 } 452 453 // ---------------------------------------------------------------------- 454 455 sal_Bool LinkManager::GetGraphicFromAny( const String& rMimeType, 456 const ::com::sun::star::uno::Any & rValue, 457 Graphic& rGrf ) 458 { 459 sal_Bool bRet = sal_False; 460 ::com::sun::star::uno::Sequence< sal_Int8 > aSeq; 461 if( rValue.hasValue() && ( rValue >>= aSeq ) ) 462 { 463 SvMemoryStream aMemStm( (void*)aSeq.getConstArray(), aSeq.getLength(), 464 STREAM_READ ); 465 aMemStm.Seek( 0 ); 466 467 switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) ) 468 { 469 case SOT_FORMATSTR_ID_SVXB: 470 { 471 aMemStm >> rGrf; 472 bRet = sal_True; 473 } 474 break; 475 case FORMAT_GDIMETAFILE: 476 { 477 GDIMetaFile aMtf; 478 aMtf.Read( aMemStm ); 479 rGrf = aMtf; 480 bRet = sal_True; 481 } 482 break; 483 case FORMAT_BITMAP: 484 { 485 Bitmap aBmp; 486 aMemStm >> aBmp; 487 rGrf = aBmp; 488 bRet = sal_True; 489 } 490 break; 491 } 492 } 493 return bRet; 494 } 495 496 497 // ---------------------------------------------------------------------- 498 String lcl_DDE_RelToAbs( const String& rTopic, const String& rBaseURL ) 499 { 500 String sRet; 501 INetURLObject aURL( rTopic ); 502 if( INET_PROT_NOT_VALID == aURL.GetProtocol() ) 503 utl::LocalFileHelper::ConvertSystemPathToURL( rTopic, rBaseURL, sRet ); 504 if( !sRet.Len() ) 505 sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl(), true ); 506 return sRet; 507 } 508 509 sal_Bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink ) 510 { 511 SfxObjectShell* pFndShell = 0; 512 sal_uInt16 nUpdateMode = com::sun::star::document::UpdateDocMode::NO_UPDATE; 513 String sTopic, sItem, sReferer; 514 if( pLink->GetLinkManager() && 515 pLink->GetLinkManager()->GetDisplayNames( pLink, 0, &sTopic, &sItem ) 516 && sTopic.Len() ) 517 { 518 // erstmal nur ueber die DocumentShells laufen und die mit dem 519 // Namen heraussuchen: 520 521 com::sun::star::lang::Locale aLocale; 522 MsLangId::convertLanguageToLocale( LANGUAGE_SYSTEM, aLocale ); 523 CharClass aCC( aLocale ); 524 525 String sNm( sTopic ), sTmp; 526 aCC.toLower( sNm ); 527 528 TypeId aType( TYPE(SfxObjectShell) ); 529 530 sal_Bool bFirst = sal_True; 531 SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist(); 532 if( pShell && pShell->GetMedium() ) 533 { 534 sReferer = pShell->GetMedium()->GetBaseURL(); 535 SFX_ITEMSET_ARG( pShell->GetMedium()->GetItemSet(), pItem, SfxUInt16Item, SID_UPDATEDOCMODE, sal_False ); 536 if ( pItem ) 537 nUpdateMode = pItem->GetValue(); 538 } 539 540 String sNmURL( lcl_DDE_RelToAbs( sTopic, sReferer ) ); 541 aCC.toLower( sNmURL ); 542 543 if ( !pShell ) 544 { 545 bFirst = sal_False; 546 pShell = SfxObjectShell::GetFirst( &aType, sal_False ); 547 } 548 549 while( pShell ) 550 { 551 if( !sTmp.Len() ) 552 { 553 sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME ); 554 sTmp = lcl_DDE_RelToAbs(sTmp, sReferer ); 555 } 556 557 558 aCC.toLower( sTmp ); 559 if( sTmp == sNmURL ) // die wollen wir haben 560 { 561 pFndShell = pShell; 562 break; 563 } 564 565 if( bFirst ) 566 { 567 bFirst = sal_False; 568 pShell = SfxObjectShell::GetFirst( &aType, sal_False ); 569 } 570 else 571 pShell = SfxObjectShell::GetNext( *pShell, &aType, sal_False ); 572 573 sTmp.Erase(); 574 } 575 } 576 577 // empty topics are not allowed - which document is it 578 if( !sTopic.Len() ) 579 return sal_False; 580 581 if( !pFndShell ) 582 { 583 // dann versuche die Datei zu laden: 584 INetURLObject aURL( sTopic ); 585 INetProtocol eOld = aURL.GetProtocol(); 586 aURL.SetURL( sTopic = lcl_DDE_RelToAbs( sTopic, sReferer ) ); 587 if( INET_PROT_NOT_VALID != eOld || 588 INET_PROT_HTTP != aURL.GetProtocol() ) 589 { 590 SfxStringItem aName( SID_FILE_NAME, sTopic ); 591 SfxBoolItem aMinimized(SID_MINIMIZED, sal_True); 592 SfxBoolItem aHidden(SID_HIDDEN, sal_True); 593 SfxStringItem aTarget( SID_TARGETNAME, String::CreateFromAscii("_blank") ); 594 SfxStringItem aReferer( SID_REFERER, sReferer ); 595 SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode ); 596 SfxBoolItem aReadOnly(SID_DOC_READONLY, sal_True); 597 598 // #i14200# (DDE-link crashes wordprocessor) 599 SfxAllItemSet aArgs( SFX_APP()->GetPool() ); 600 aArgs.Put(aReferer); 601 aArgs.Put(aTarget); 602 aArgs.Put(aHidden); 603 aArgs.Put(aMinimized); 604 aArgs.Put(aName); 605 aArgs.Put(aUpdate); 606 aArgs.Put(aReadOnly); 607 pFndShell = SfxObjectShell::CreateAndLoadObject( aArgs ); 608 } 609 } 610 611 sal_Bool bRet = sal_False; 612 if( pFndShell ) 613 { 614 sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem ); 615 if( pNewSrc ) 616 { 617 bRet = sal_True; 618 619 ::com::sun::star::datatransfer::DataFlavor aFl; 620 SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl ); 621 622 pLink->SetObj( pNewSrc ); 623 pNewSrc->AddDataAdvise( pLink, aFl.MimeType, 624 sfx2::LINKUPDATE_ONCALL == pLink->GetUpdateMode() 625 ? ADVISEMODE_ONLYONCE 626 : 0 ); 627 } 628 } 629 return bRet; 630 } 631 632 633 } 634 635 636 637