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