xref: /trunk/main/sfx2/source/appl/linkmgr2.cxx (revision cdf0e10c)
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