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