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 return ; // es soll nichts geupdatet werden 322 bAskUpdate = sal_False; // einmal reicht 323 } 324 325 pLink->Update(); 326 } 327 } 328 329 /************************************************************************ 330 |* SvBaseLink::CreateObject() 331 |* 332 |* Beschreibung 333 *************************************************************************/ 334 335 SvLinkSourceRef LinkManager::CreateObj( SvBaseLink * pLink ) 336 { 337 switch( pLink->GetObjType() ) 338 { 339 case OBJECT_CLIENT_FILE: 340 case OBJECT_CLIENT_GRF: 341 case OBJECT_CLIENT_OLE: 342 return new SvFileObject; 343 case OBJECT_INTERN: 344 return new SvxInternalLink; 345 case OBJECT_CLIENT_DDE: 346 return new SvDDEObject; 347 default: 348 return SvLinkSourceRef(); 349 } 350 } 351 352 sal_Bool LinkManager::InsertServer( SvLinkSource* pObj ) 353 { 354 // keine doppelt einfuegen 355 if( !pObj || USHRT_MAX != aServerTbl.GetPos( pObj ) ) 356 return sal_False; 357 358 aServerTbl.Insert( pObj, aServerTbl.Count() ); 359 return sal_True; 360 } 361 362 363 void LinkManager::RemoveServer( SvLinkSource* pObj ) 364 { 365 sal_uInt16 nPos = aServerTbl.GetPos( pObj ); 366 if( USHRT_MAX != nPos ) 367 aServerTbl.Remove( nPos, 1 ); 368 } 369 370 371 void MakeLnkName( String& rName, const String* pType, const String& rFile, 372 const String& rLink, const String* pFilter ) 373 { 374 if( pType ) 375 (rName = *pType).EraseLeadingChars().EraseTrailingChars() += cTokenSeperator; 376 else if( rName.Len() ) 377 rName.Erase(); 378 379 ((rName += rFile).EraseLeadingChars().EraseTrailingChars() += 380 cTokenSeperator ).EraseLeadingChars().EraseTrailingChars() += rLink; 381 if( pFilter ) 382 ((rName += cTokenSeperator ) += *pFilter).EraseLeadingChars().EraseTrailingChars(); 383 } 384 385 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink, 386 sal_uInt16 nFileType, 387 const String& rFileNm, 388 const String* pFilterNm, 389 const String* pRange ) 390 { 391 if( !( OBJECT_CLIENT_SO & rLink.GetObjType() )) 392 return sal_False; 393 394 String sCmd( rFileNm ); 395 sCmd += ::sfx2::cTokenSeperator; 396 if( pRange ) 397 sCmd += *pRange; 398 if( pFilterNm ) 399 ( sCmd += ::sfx2::cTokenSeperator ) += *pFilterNm; 400 401 return InsertLink( &rLink, nFileType, sfx2::LINKUPDATE_ONCALL, &sCmd ); 402 } 403 404 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink ) 405 { 406 if( OBJECT_CLIENT_FILE == ( OBJECT_CLIENT_FILE & rLink.GetObjType() )) 407 return InsertLink( &rLink, rLink.GetObjType(), sfx2::LINKUPDATE_ONCALL ); 408 return sal_False; 409 } 410 411 // eine Uebertragung wird abgebrochen, also alle DownloadMedien canceln 412 // (ist zur Zeit nur fuer die FileLinks interressant!) 413 void LinkManager::CancelTransfers() 414 { 415 SvFileObject* pFileObj; 416 sfx2::SvBaseLink* pLnk; 417 418 const sfx2::SvBaseLinks& rLnks = GetLinks(); 419 for( sal_uInt16 n = rLnks.Count(); n; ) 420 if( 0 != ( pLnk = &(*rLnks[ --n ])) && 421 OBJECT_CLIENT_FILE == (OBJECT_CLIENT_FILE & pLnk->GetObjType()) && 422 0 != ( pFileObj = (SvFileObject*)pLnk->GetObj() ) ) 423 // 0 != ( pFileObj = (SvFileObject*)SvFileObject::ClassFactory()-> 424 // CastAndAddRef( pLnk->GetObj() )) ) 425 pFileObj->CancelTransfers(); 426 } 427 428 // um Status Informationen aus dem FileObject an den BaseLink zu 429 // senden, gibt es eine eigene ClipBoardId. Das SvData-Object hat 430 // dann die entsprechenden Informationen als String. 431 // Wird zur Zeit fuer FileObject in Verbindung mit JavaScript benoetigt 432 // - das braucht Informationen ueber Load/Abort/Error 433 sal_uIntPtr LinkManager::RegisterStatusInfoId() 434 { 435 static sal_uIntPtr nFormat = 0; 436 437 if( !nFormat ) 438 { 439 // wie sieht die neue Schnittstelle aus? 440 // nFormat = Exchange::RegisterFormatName( "StatusInfo vom SvxInternalLink" ); 441 nFormat = SotExchange::RegisterFormatName( 442 String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( 443 "StatusInfo vom SvxInternalLink" ))); 444 } 445 return nFormat; 446 } 447 448 // ---------------------------------------------------------------------- 449 450 sal_Bool LinkManager::GetGraphicFromAny( const String& rMimeType, 451 const ::com::sun::star::uno::Any & rValue, 452 Graphic& rGrf ) 453 { 454 sal_Bool bRet = sal_False; 455 ::com::sun::star::uno::Sequence< sal_Int8 > aSeq; 456 if( rValue.hasValue() && ( rValue >>= aSeq ) ) 457 { 458 SvMemoryStream aMemStm( (void*)aSeq.getConstArray(), aSeq.getLength(), 459 STREAM_READ ); 460 aMemStm.Seek( 0 ); 461 462 switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) ) 463 { 464 case SOT_FORMATSTR_ID_SVXB: 465 { 466 aMemStm >> rGrf; 467 bRet = sal_True; 468 } 469 break; 470 case FORMAT_GDIMETAFILE: 471 { 472 GDIMetaFile aMtf; 473 aMtf.Read( aMemStm ); 474 rGrf = aMtf; 475 bRet = sal_True; 476 } 477 break; 478 case FORMAT_BITMAP: 479 { 480 Bitmap aBmp; 481 ReadDIB(aBmp, aMemStm, true); 482 rGrf = aBmp; 483 bRet = sal_True; 484 } 485 break; 486 } 487 } 488 return bRet; 489 } 490 491 492 // ---------------------------------------------------------------------- 493 String lcl_DDE_RelToAbs( const String& rTopic, const String& rBaseURL ) 494 { 495 String sRet; 496 INetURLObject aURL( rTopic ); 497 if( INET_PROT_NOT_VALID == aURL.GetProtocol() ) 498 utl::LocalFileHelper::ConvertSystemPathToURL( rTopic, rBaseURL, sRet ); 499 if( !sRet.Len() ) 500 sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl(), true ); 501 return sRet; 502 } 503 504 sal_Bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink ) 505 { 506 SfxObjectShell* pFndShell = 0; 507 sal_uInt16 nUpdateMode = com::sun::star::document::UpdateDocMode::NO_UPDATE; 508 String sTopic, sItem, sReferer; 509 if( pLink->GetLinkManager() && 510 pLink->GetLinkManager()->GetDisplayNames( pLink, 0, &sTopic, &sItem ) 511 && sTopic.Len() ) 512 { 513 // erstmal nur ueber die DocumentShells laufen und die mit dem 514 // Namen heraussuchen: 515 516 com::sun::star::lang::Locale aLocale; 517 MsLangId::convertLanguageToLocale( LANGUAGE_SYSTEM, aLocale ); 518 CharClass aCC( aLocale ); 519 520 String sNm( sTopic ), sTmp; 521 aCC.toLower( sNm ); 522 523 TypeId aType( TYPE(SfxObjectShell) ); 524 525 sal_Bool bFirst = sal_True; 526 SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist(); 527 if( pShell && pShell->GetMedium() ) 528 { 529 sReferer = pShell->GetMedium()->GetBaseURL(); 530 SFX_ITEMSET_ARG( pShell->GetMedium()->GetItemSet(), pItem, SfxUInt16Item, SID_UPDATEDOCMODE, sal_False ); 531 if ( pItem ) 532 nUpdateMode = pItem->GetValue(); 533 } 534 535 String sNmURL( lcl_DDE_RelToAbs( sTopic, sReferer ) ); 536 aCC.toLower( sNmURL ); 537 538 if ( !pShell ) 539 { 540 bFirst = sal_False; 541 pShell = SfxObjectShell::GetFirst( &aType, sal_False ); 542 } 543 544 while( pShell ) 545 { 546 if( !sTmp.Len() ) 547 { 548 sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME ); 549 sTmp = lcl_DDE_RelToAbs(sTmp, sReferer ); 550 } 551 552 553 aCC.toLower( sTmp ); 554 if( sTmp == sNmURL ) // die wollen wir haben 555 { 556 pFndShell = pShell; 557 break; 558 } 559 560 if( bFirst ) 561 { 562 bFirst = sal_False; 563 pShell = SfxObjectShell::GetFirst( &aType, sal_False ); 564 } 565 else 566 pShell = SfxObjectShell::GetNext( *pShell, &aType, sal_False ); 567 568 sTmp.Erase(); 569 } 570 } 571 572 // empty topics are not allowed - which document is it 573 if( !sTopic.Len() ) 574 return sal_False; 575 576 if( !pFndShell ) 577 { 578 // dann versuche die Datei zu laden: 579 INetURLObject aURL( sTopic ); 580 INetProtocol eOld = aURL.GetProtocol(); 581 aURL.SetURL( sTopic = lcl_DDE_RelToAbs( sTopic, sReferer ) ); 582 if( INET_PROT_NOT_VALID != eOld || 583 INET_PROT_HTTP != aURL.GetProtocol() ) 584 { 585 SfxStringItem aName( SID_FILE_NAME, sTopic ); 586 SfxBoolItem aMinimized(SID_MINIMIZED, sal_True); 587 SfxBoolItem aHidden(SID_HIDDEN, sal_True); 588 SfxStringItem aTarget( SID_TARGETNAME, String::CreateFromAscii("_blank") ); 589 SfxStringItem aReferer( SID_REFERER, sReferer ); 590 SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode ); 591 SfxBoolItem aReadOnly(SID_DOC_READONLY, sal_True); 592 593 // #i14200# (DDE-link crashes wordprocessor) 594 SfxAllItemSet aArgs( SFX_APP()->GetPool() ); 595 aArgs.Put(aReferer); 596 aArgs.Put(aTarget); 597 aArgs.Put(aHidden); 598 aArgs.Put(aMinimized); 599 aArgs.Put(aName); 600 aArgs.Put(aUpdate); 601 aArgs.Put(aReadOnly); 602 pFndShell = SfxObjectShell::CreateAndLoadObject( aArgs ); 603 } 604 } 605 606 sal_Bool bRet = sal_False; 607 if( pFndShell ) 608 { 609 sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem ); 610 if( pNewSrc ) 611 { 612 bRet = sal_True; 613 614 ::com::sun::star::datatransfer::DataFlavor aFl; 615 SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl ); 616 617 pLink->SetObj( pNewSrc ); 618 pNewSrc->AddDataAdvise( pLink, aFl.MimeType, 619 sfx2::LINKUPDATE_ONCALL == pLink->GetUpdateMode() 620 ? ADVISEMODE_ONLYONCE 621 : 0 ); 622 } 623 } 624 return bRet; 625 } 626 627 628 } 629 630 631 632