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 |* Description 92 *************************************************************************/ 93 94 void LinkManager::Remove( SvBaseLink *pLink ) 95 { 96 // do not insert links double 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 // if there are still some empty ones, get rid of them 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 // do not insert links double 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 // in any case: do this first 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 // ask for the strings to be used in the 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 // first create a copy of the array, so that updated links to not interfere with ... in between!! 282 SvPtrarr aTmpArr( 255, 50 ); 283 sal_uInt16 n; 284 for( n = 0; n < aLinkTbl.Count(); ++n ) 285 { 286 SvBaseLink* pLink = *aLinkTbl[ n ]; 287 if( !pLink ) 288 { 289 Remove( n-- ); 290 continue; 291 } 292 aTmpArr.Insert( pLink, aTmpArr.Count() ); 293 } 294 295 for( n = 0; n < aTmpArr.Count(); ++n ) 296 { 297 SvBaseLink* pLink = (SvBaseLink*)aTmpArr[ n ]; 298 299 // first search the entry in the array 300 sal_uInt16 nFndPos = USHRT_MAX; 301 for( sal_uInt16 i = 0; i < aLinkTbl.Count(); ++i ) 302 if( pLink == *aLinkTbl[ i ] ) 303 { 304 nFndPos = i; 305 break; 306 } 307 308 if( USHRT_MAX == nFndPos ) 309 continue; // was not already existing! 310 311 // do not update graphic links yet 312 if( !pLink->IsVisible() || 313 ( !bUpdateGrfLinks && OBJECT_CLIENT_GRF == pLink->GetObjType() )) 314 continue; 315 316 if( bAskUpdate ) 317 { 318 int nRet = QueryBox( pParentWin, WB_YES_NO | WB_DEF_YES, SfxResId( STR_QUERY_UPDATE_LINKS ) ).Execute(); 319 if( RET_YES != nRet ) 320 { 321 SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist(); 322 323 if(pShell) 324 { 325 comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = pShell->getEmbeddedObjectContainer(); 326 rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false); 327 } 328 329 return ; // nothing should be updated 330 } 331 bAskUpdate = sal_False; // one time is OK 332 } 333 334 pLink->Update(); 335 } 336 } 337 338 /************************************************************************ 339 |* SvBaseLink::CreateObject() 340 |* 341 |* Description 342 *************************************************************************/ 343 344 SvLinkSourceRef LinkManager::CreateObj( SvBaseLink * pLink ) 345 { 346 switch( pLink->GetObjType() ) 347 { 348 case OBJECT_CLIENT_FILE: 349 case OBJECT_CLIENT_GRF: 350 case OBJECT_CLIENT_OLE: 351 return new SvFileObject; 352 case OBJECT_INTERN: 353 return new SvxInternalLink; 354 case OBJECT_CLIENT_DDE: 355 return new SvDDEObject; 356 default: 357 return SvLinkSourceRef(); 358 } 359 } 360 361 sal_Bool LinkManager::InsertServer( SvLinkSource* pObj ) 362 { 363 // do not insert double 364 if( !pObj || USHRT_MAX != aServerTbl.GetPos( pObj ) ) 365 return sal_False; 366 367 aServerTbl.Insert( pObj, aServerTbl.Count() ); 368 return sal_True; 369 } 370 371 372 void LinkManager::RemoveServer( SvLinkSource* pObj ) 373 { 374 sal_uInt16 nPos = aServerTbl.GetPos( pObj ); 375 if( USHRT_MAX != nPos ) 376 aServerTbl.Remove( nPos, 1 ); 377 } 378 379 380 void MakeLnkName( String& rName, const String* pType, const String& rFile, 381 const String& rLink, const String* pFilter ) 382 { 383 if( pType ) 384 (rName = *pType).EraseLeadingChars().EraseTrailingChars() += cTokenSeperator; 385 else if( rName.Len() ) 386 rName.Erase(); 387 388 ((rName += rFile).EraseLeadingChars().EraseTrailingChars() += 389 cTokenSeperator ).EraseLeadingChars().EraseTrailingChars() += rLink; 390 if( pFilter ) 391 ((rName += cTokenSeperator ) += *pFilter).EraseLeadingChars().EraseTrailingChars(); 392 } 393 394 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink, 395 sal_uInt16 nFileType, 396 const String& rFileNm, 397 const String* pFilterNm, 398 const String* pRange ) 399 { 400 if( !( OBJECT_CLIENT_SO & rLink.GetObjType() )) 401 return sal_False; 402 403 String sCmd( rFileNm ); 404 sCmd += ::sfx2::cTokenSeperator; 405 if( pRange ) 406 sCmd += *pRange; 407 if( pFilterNm ) 408 ( sCmd += ::sfx2::cTokenSeperator ) += *pFilterNm; 409 410 return InsertLink( &rLink, nFileType, sfx2::LINKUPDATE_ONCALL, &sCmd ); 411 } 412 413 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink ) 414 { 415 if( OBJECT_CLIENT_FILE == ( OBJECT_CLIENT_FILE & rLink.GetObjType() )) 416 return InsertLink( &rLink, rLink.GetObjType(), sfx2::LINKUPDATE_ONCALL ); 417 return sal_False; 418 } 419 420 // a transfer will be discontinued, therefore cancel all DownloadMedia 421 // (at the moment only interesting for the FileLinks!) 422 void LinkManager::CancelTransfers() 423 { 424 SvFileObject* pFileObj; 425 sfx2::SvBaseLink* pLnk; 426 427 const sfx2::SvBaseLinks& rLnks = GetLinks(); 428 for( sal_uInt16 n = rLnks.Count(); n; ) 429 if( 0 != ( pLnk = &(*rLnks[ --n ])) && 430 OBJECT_CLIENT_FILE == (OBJECT_CLIENT_FILE & pLnk->GetObjType()) && 431 0 != ( pFileObj = (SvFileObject*)pLnk->GetObj() ) ) 432 // 0 != ( pFileObj = (SvFileObject*)SvFileObject::ClassFactory()-> 433 // CastAndAddRef( pLnk->GetObj() )) ) 434 pFileObj->CancelTransfers(); 435 } 436 437 // to send status information from the FileObject to the BaseLink, there is an own ClipboardId. 438 // The SvData object has then the respective information as string. 439 // Currently this will be used for FileObject in connection with JavaScript 440 // - that needs information about Load/Abort/Error 441 sal_uIntPtr LinkManager::RegisterStatusInfoId() 442 { 443 static sal_uIntPtr nFormat = 0; 444 445 if( !nFormat ) 446 { 447 // how does the new interface look like? 448 // nFormat = Exchange::RegisterFormatName( "StatusInfo vom SvxInternalLink" ); 449 nFormat = SotExchange::RegisterFormatName( 450 String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( 451 "StatusInfo vom SvxInternalLink" ))); 452 } 453 return nFormat; 454 } 455 456 // ---------------------------------------------------------------------- 457 458 sal_Bool LinkManager::GetGraphicFromAny( const String& rMimeType, 459 const ::com::sun::star::uno::Any & rValue, 460 Graphic& rGrf ) 461 { 462 sal_Bool bRet = sal_False; 463 ::com::sun::star::uno::Sequence< sal_Int8 > aSeq; 464 if( rValue.hasValue() && ( rValue >>= aSeq ) ) 465 { 466 SvMemoryStream aMemStm( (void*)aSeq.getConstArray(), aSeq.getLength(), 467 STREAM_READ ); 468 aMemStm.Seek( 0 ); 469 470 switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) ) 471 { 472 case SOT_FORMATSTR_ID_SVXB: 473 { 474 aMemStm >> rGrf; 475 bRet = sal_True; 476 } 477 break; 478 case FORMAT_GDIMETAFILE: 479 { 480 GDIMetaFile aMtf; 481 aMtf.Read( aMemStm ); 482 rGrf = aMtf; 483 bRet = sal_True; 484 } 485 break; 486 case FORMAT_BITMAP: 487 { 488 Bitmap aBmp; 489 ReadDIB(aBmp, aMemStm, true); 490 rGrf = aBmp; 491 bRet = sal_True; 492 } 493 break; 494 } 495 } 496 return bRet; 497 } 498 499 500 // ---------------------------------------------------------------------- 501 String lcl_DDE_RelToAbs( const String& rTopic, const String& rBaseURL ) 502 { 503 String sRet; 504 INetURLObject aURL( rTopic ); 505 if( INET_PROT_NOT_VALID == aURL.GetProtocol() ) 506 utl::LocalFileHelper::ConvertSystemPathToURL( rTopic, rBaseURL, sRet ); 507 if( !sRet.Len() ) 508 sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl(), true ); 509 return sRet; 510 } 511 512 sal_Bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink ) 513 { 514 SfxObjectShell* pFndShell = 0; 515 sal_uInt16 nUpdateMode = com::sun::star::document::UpdateDocMode::NO_UPDATE; 516 String sTopic, sItem, sReferer; 517 if( pLink->GetLinkManager() && 518 pLink->GetLinkManager()->GetDisplayNames( pLink, 0, &sTopic, &sItem ) 519 && sTopic.Len() ) 520 { 521 // for the moment run through the DocumentShells and search for the ones with names: 522 523 com::sun::star::lang::Locale aLocale; 524 MsLangId::convertLanguageToLocale( LANGUAGE_SYSTEM, aLocale ); 525 CharClass aCC( aLocale ); 526 527 String sNm( sTopic ), sTmp; 528 aCC.toLower( sNm ); 529 530 TypeId aType( TYPE(SfxObjectShell) ); 531 532 sal_Bool bFirst = sal_True; 533 SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist(); 534 if( pShell && pShell->GetMedium() ) 535 { 536 sReferer = pShell->GetMedium()->GetBaseURL(); 537 SFX_ITEMSET_ARG( pShell->GetMedium()->GetItemSet(), pItem, SfxUInt16Item, SID_UPDATEDOCMODE, sal_False ); 538 if ( pItem ) 539 nUpdateMode = pItem->GetValue(); 540 } 541 542 String sNmURL( lcl_DDE_RelToAbs( sTopic, sReferer ) ); 543 aCC.toLower( sNmURL ); 544 545 if ( !pShell ) 546 { 547 bFirst = sal_False; 548 pShell = SfxObjectShell::GetFirst( &aType, sal_False ); 549 } 550 551 while( pShell ) 552 { 553 if( !sTmp.Len() ) 554 { 555 sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME ); 556 sTmp = lcl_DDE_RelToAbs(sTmp, sReferer ); 557 } 558 559 560 aCC.toLower( sTmp ); 561 if( sTmp == sNmURL ) // these we want to have 562 { 563 pFndShell = pShell; 564 break; 565 } 566 567 if( bFirst ) 568 { 569 bFirst = sal_False; 570 pShell = SfxObjectShell::GetFirst( &aType, sal_False ); 571 } 572 else 573 pShell = SfxObjectShell::GetNext( *pShell, &aType, sal_False ); 574 575 sTmp.Erase(); 576 } 577 } 578 579 // empty topics are not allowed - which document is it 580 if( !sTopic.Len() ) 581 return sal_False; 582 583 if( !pFndShell ) 584 { 585 // try to load the file: 586 INetURLObject aURL( sTopic ); 587 INetProtocol eOld = aURL.GetProtocol(); 588 aURL.SetURL( sTopic = lcl_DDE_RelToAbs( sTopic, sReferer ) ); 589 if( INET_PROT_NOT_VALID != eOld || 590 INET_PROT_HTTP != aURL.GetProtocol() ) 591 { 592 SfxStringItem aName( SID_FILE_NAME, sTopic ); 593 SfxBoolItem aMinimized(SID_MINIMIZED, sal_True); 594 SfxBoolItem aHidden(SID_HIDDEN, sal_True); 595 SfxStringItem aTarget( SID_TARGETNAME, String::CreateFromAscii("_blank") ); 596 SfxStringItem aReferer( SID_REFERER, sReferer ); 597 SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode ); 598 SfxBoolItem aReadOnly(SID_DOC_READONLY, sal_True); 599 600 // #i14200# (DDE-link crashes wordprocessor) 601 SfxAllItemSet aArgs( SFX_APP()->GetPool() ); 602 aArgs.Put(aReferer); 603 aArgs.Put(aTarget); 604 aArgs.Put(aHidden); 605 aArgs.Put(aMinimized); 606 aArgs.Put(aName); 607 aArgs.Put(aUpdate); 608 aArgs.Put(aReadOnly); 609 pFndShell = SfxObjectShell::CreateAndLoadObject( aArgs ); 610 } 611 } 612 613 sal_Bool bRet = sal_False; 614 if( pFndShell ) 615 { 616 sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem ); 617 if( pNewSrc ) 618 { 619 bRet = sal_True; 620 621 ::com::sun::star::datatransfer::DataFlavor aFl; 622 SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl ); 623 624 pLink->SetObj( pNewSrc ); 625 pNewSrc->AddDataAdvise( pLink, aFl.MimeType, 626 sfx2::LINKUPDATE_ONCALL == pLink->GetUpdateMode() 627 ? ADVISEMODE_ONLYONCE 628 : 0 ); 629 } 630 } 631 return bRet; 632 } 633 634 635 } 636 637