bump product version to 4.1.6.2
[LibreOffice.git] / sfx2 / source / appl / fileobj.cxx
blob553b2cd0546cfaca20e0d16362033323d0c17cb8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <vcl/wrkwin.hxx>
22 #include <vcl/msgbox.hxx>
23 #include <tools/urlobj.hxx>
24 #include <tools/stream.hxx>
25 #include <sot/formats.hxx>
26 #include <vcl/graphicfilter.hxx>
27 #include <sfx2/lnkbase.hxx>
28 #include <sfx2/app.hxx>
29 #include <sfx2/progress.hxx>
30 #include <sfx2/docfilt.hxx>
31 #include <sfx2/filedlghelper.hxx>
32 #include <sot/exchange.hxx>
33 #include <com/sun/star/uno/Any.hxx>
34 #include <com/sun/star/uno/Sequence.hxx>
35 #include <sfx2/docfac.hxx>
36 #include <com/sun/star/document/XTypeDetection.hpp>
37 #include <comphelper/mediadescriptor.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <sfx2/linkmgr.hxx>
40 #include <sfx2/opengrf.hxx>
41 #include "sfx2/sfxresid.hxx"
42 #include "fileobj.hxx"
43 #include "app.hrc"
45 #define FILETYPE_TEXT 1
46 #define FILETYPE_GRF 2
47 #define FILETYPE_OBJECT 3
49 struct Impl_DownLoadData
51 Graphic aGrf;
52 Timer aTimer;
54 Impl_DownLoadData( const Link& rLink )
56 aTimer.SetTimeout( 100 );
57 aTimer.SetTimeoutHdl( rLink );
58 aGrf.SetDefaultType();
60 ~Impl_DownLoadData()
62 aTimer.Stop();
66 // --------------------------------------------------------------------------
69 SvFileObject::SvFileObject() :
70 pDownLoadData( NULL ), pOldParent( NULL ), nType( FILETYPE_TEXT )
72 bLoadAgain = sal_True;
73 bSynchron = bLoadError = bWaitForData = bDataReady = bNativFormat =
74 bClearMedium = bStateChangeCalled = bInCallDownLoad = sal_False;
78 SvFileObject::~SvFileObject()
80 if ( xMed.Is() )
82 xMed->SetDoneLink( Link() );
83 xMed.Clear();
85 delete pDownLoadData;
89 sal_Bool SvFileObject::GetData( ::com::sun::star::uno::Any & rData,
90 const String & rMimeType,
91 sal_Bool bGetSynchron )
93 sal_uIntPtr nFmt = SotExchange::GetFormatStringId( rMimeType );
94 switch( nType )
96 case FILETYPE_TEXT:
97 if( FORMAT_FILE == nFmt )
99 // The media in the application must be opened to lookup the
100 // relative file links!! This is done through the link manager
101 // of the Storage.
102 rData <<= OUString( sFileNm );
104 break;
106 case FILETYPE_GRF:
107 if( !bLoadError )
109 SfxMediumRef xTmpMed;
111 if( FORMAT_GDIMETAFILE == nFmt || FORMAT_BITMAP == nFmt ||
112 SOT_FORMATSTR_ID_SVXB == nFmt )
114 Graphic aGrf;
116 // If the native format is reqested, has to be reset at the
117 // end of the flag. Is solely in the sw/ndgrf.cxx used when
118 // the link is removed form GraphicNode.
119 sal_Bool bOldNativFormat = bNativFormat;
121 // If about to print, waiting for the data to be available
122 if( bGetSynchron )
124 // call a LoadFile every second time to test the loading
125 if( !xMed.Is() )
126 LoadFile_Impl();
128 if( !bInCallDownLoad )
130 xTmpMed = xMed;
131 while( bWaitForData )
132 Application::Reschedule();
134 xMed = xTmpMed;
135 bClearMedium = sal_True;
139 if( pDownLoadData ||
140 ( !bWaitForData && ( xMed.Is() || // was loaded as URL
141 ( bSynchron && LoadFile_Impl() && xMed.Is() ) )) )
143 // If it was loaded from the Internet, do not retry
144 if( !bGetSynchron )
145 bLoadAgain = !xMed->IsRemote();
146 bLoadError = !GetGraphic_Impl( aGrf, xMed->GetInStream() );
148 else if( !LoadFile_Impl() ||
149 !GetGraphic_Impl( aGrf, xMed.Is() ? xMed->GetInStream() : 0 ))
151 if( !xMed.Is() )
152 break;
153 aGrf.SetDefaultType();
156 if( SOT_FORMATSTR_ID_SVXB != nFmt )
157 nFmt = (bLoadError || GRAPHIC_BITMAP == aGrf.GetType())
158 ? FORMAT_BITMAP
159 : FORMAT_GDIMETAFILE;
161 SvMemoryStream aMemStm( 0, 65535 );
162 switch ( nFmt )
164 case SOT_FORMATSTR_ID_SVXB:
165 if( GRAPHIC_NONE != aGrf.GetType() )
167 aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
168 aMemStm << aGrf;
170 break;
172 case FORMAT_BITMAP:
173 if( !aGrf.GetBitmap().IsEmpty())
174 aMemStm << aGrf.GetBitmap();
175 break;
177 default:
178 if( aGrf.GetGDIMetaFile().GetActionSize() )
180 GDIMetaFile aMeta( aGrf.GetGDIMetaFile() );
181 aMeta.Write( aMemStm );
184 rData <<= css::uno::Sequence< sal_Int8 >( (sal_Int8*) aMemStm.GetData(),
185 aMemStm.Seek( STREAM_SEEK_TO_END ) );
187 bNativFormat = bOldNativFormat;
189 // Everything ready?
190 if( xMed.Is() && !bSynchron && bClearMedium )
192 xMed.Clear();
193 bClearMedium = sal_False;
197 break;
198 case FILETYPE_OBJECT:
199 // TODO/LATER: possibility to insert a new object
200 rData <<= OUString( sFileNm );
201 break;
203 return sal_True/*0 != aTypeList.Count()*/;
206 sal_Bool SvFileObject::Connect( sfx2::SvBaseLink* pLink )
208 if( !pLink || !pLink->GetLinkManager() )
209 return sal_False;
211 // Test if not another link of the same connection already exists
212 pLink->GetLinkManager()->GetDisplayNames( pLink, 0, &sFileNm, 0, &sFilter );
214 if( OBJECT_CLIENT_GRF == pLink->GetObjType() )
216 SfxObjectShellRef pShell = pLink->GetLinkManager()->GetPersist();
217 if( pShell.Is() )
219 if( pShell->IsAbortingImport() )
220 return sal_False;
222 if( pShell->GetMedium() )
223 sReferer = pShell->GetMedium()->GetName();
227 switch( pLink->GetObjType() )
229 case OBJECT_CLIENT_GRF:
230 nType = FILETYPE_GRF;
231 bSynchron = pLink->IsSynchron();
232 break;
234 case OBJECT_CLIENT_FILE:
235 nType = FILETYPE_TEXT;
236 break;
238 case OBJECT_CLIENT_OLE:
239 nType = FILETYPE_OBJECT;
240 // TODO/LATER: introduce own type to be used for exchanging
241 break;
243 default:
244 return sal_False;
247 SetUpdateTimeout( 0 );
249 // and now register by this or other found Pseudo-Object
250 AddDataAdvise( pLink, SotExchange::GetFormatMimeType( pLink->GetContentType()), 0 );
251 return sal_True;
254 sal_Bool SvFileObject::LoadFile_Impl()
256 // We are still at Loading!!
257 if( bWaitForData || !bLoadAgain || xMed.Is() || pDownLoadData )
258 return sal_False;
260 // at the moment on the current DocShell
261 xMed = new SfxMedium( sFileNm, STREAM_STD_READ );
262 SvLinkSource::StreamToLoadFrom aStreamToLoadFrom =
263 getStreamToLoadFrom();
264 xMed->setStreamToLoadFrom(
265 aStreamToLoadFrom.m_xInputStreamToLoadFrom,
266 aStreamToLoadFrom.m_bIsReadOnly);
268 if( !bSynchron )
270 bLoadAgain = bDataReady = bInNewData = sal_False;
271 bWaitForData = sal_True;
273 SfxMediumRef xTmpMed = xMed;
274 bInCallDownLoad = sal_True;
275 xMed->DownLoad( STATIC_LINK( this, SvFileObject, LoadGrfReady_Impl ) );
276 bInCallDownLoad = sal_False;
278 bClearMedium = !xMed.Is();
279 if( bClearMedium )
280 xMed = xTmpMed; // If already finished in DownLoad
281 return bDataReady;
284 bWaitForData = sal_True;
285 bDataReady = bInNewData = sal_False;
286 xMed->DownLoad();
287 bLoadAgain = !xMed->IsRemote();
288 bWaitForData = sal_False;
290 // Graphic is finished, also send DataChanged of the Status change:
291 SendStateChg_Impl( xMed->GetInStream() && xMed->GetInStream()->GetError()
292 ? sfx2::LinkManager::STATE_LOAD_ERROR : sfx2::LinkManager::STATE_LOAD_OK );
293 return sal_True;
297 sal_Bool SvFileObject::GetGraphic_Impl( Graphic& rGrf, SvStream* pStream )
299 GraphicFilter& rGF = GraphicFilter::GetGraphicFilter();
301 const sal_uInt16 nFilter = sFilter.Len() && rGF.GetImportFormatCount()
302 ? rGF.GetImportFormatNumber( sFilter )
303 : GRFILTER_FORMAT_DONTKNOW;
305 String aEmptyStr;
306 int nRes;
308 // To avoid that a native link is created
309 if( ( !pStream || !pDownLoadData ) && !rGrf.IsLink() &&
310 !rGrf.GetContext() && !bNativFormat )
311 rGrf.SetLink( GfxLink() );
313 if( !pStream )
314 nRes = xMed.Is() ? GRFILTER_OPENERROR
315 : rGF.ImportGraphic( rGrf, INetURLObject(sFileNm),
316 nFilter );
317 else if( !pDownLoadData )
319 pStream->Seek( STREAM_SEEK_TO_BEGIN );
320 nRes = rGF.ImportGraphic( rGrf, aEmptyStr, *pStream, nFilter );
322 else
324 nRes = rGF.ImportGraphic( pDownLoadData->aGrf, aEmptyStr,
325 *pStream, nFilter );
327 if( pDownLoadData )
329 rGrf = pDownLoadData->aGrf;
330 if( GRAPHIC_NONE == rGrf.GetType() )
331 rGrf.SetDefaultType();
334 if( !pDownLoadData->aGrf.GetContext() )
336 delete pDownLoadData, pDownLoadData = 0;
337 bDataReady = sal_True;
338 bWaitForData = sal_False;
343 if( pStream && ERRCODE_IO_PENDING == pStream->GetError() )
344 pStream->ResetError();
346 #ifdef DBG_UTIL
347 if( nRes )
349 if( xMed.Is() && !pStream )
351 DBG_WARNING3( "Graphic error [%d] - [%s] URL[%s]",
352 nRes,
353 xMed->GetPhysicalName().getStr(),
354 sFileNm.GetBuffer() );
356 else
358 DBG_WARNING2( "Graphic error [%d] - [%s]",
359 nRes, sFileNm.GetBuffer() );
362 #endif
364 return GRFILTER_OK == nRes;
367 /** detect the filter of the given file
369 @param _rURL
370 specifies the URL of the file which filter is to detected.<br/>
371 If the URL doesn't denote a valid (existent and accessible) file, the
372 request is silently dropped.
374 String impl_getFilter( const String& _rURL )
376 String sFilter;
377 if ( _rURL.Len() == 0 )
378 return sFilter;
382 css::uno::Reference< ::com::sun::star::document::XTypeDetection > xTypeDetection(
383 ::comphelper::getProcessServiceFactory()->createInstance(
384 OUString("com.sun.star.document.TypeDetection") ),
385 css::uno::UNO_QUERY );
386 if ( xTypeDetection.is() )
388 ::comphelper::MediaDescriptor aDescr;
389 aDescr[ ::comphelper::MediaDescriptor::PROP_URL() ] <<= OUString( _rURL );
390 css::uno::Sequence< css::beans::PropertyValue > aDescrList =
391 aDescr.getAsConstPropertyValueList();
392 OUString sType = xTypeDetection->queryTypeByDescriptor( aDescrList, sal_True );
393 if ( !sType.isEmpty() )
395 // Honor a selected/detected filter.
396 for (sal_Int32 i=0; i < aDescrList.getLength(); ++i)
398 if (aDescrList[i].Name == "FilterName")
400 OUString aFilterName;
401 if (aDescrList[i].Value >>= aFilterName)
403 sFilter = aFilterName;
404 break;
408 if (!sFilter.Len())
410 css::uno::Reference< css::container::XNameAccess > xTypeCont( xTypeDetection,
411 css::uno::UNO_QUERY );
412 if ( xTypeCont.is() )
414 /* XXX: for fdo#69948 scenario the sequence returned by
415 * getByName() contains an empty PreferredFilter
416 * property value (since? expected?) */
417 ::comphelper::SequenceAsHashMap lTypeProps( xTypeCont->getByName( sType ) );
418 sFilter = lTypeProps.getUnpackedValueOrDefault(
419 OUString("PreferredFilter"), OUString() );
425 catch( const css::uno::Exception& )
429 return sFilter;
432 void SvFileObject::Edit( Window* pParent, sfx2::SvBaseLink* pLink, const Link& rEndEditHdl )
434 aEndEditLink = rEndEditHdl;
435 String sFile, sRange, sTmpFilter;
436 if( pLink && pLink->GetLinkManager() )
438 pLink->GetLinkManager()->GetDisplayNames( pLink, 0, &sFile, &sRange, &sTmpFilter );
440 switch( pLink->GetObjType() )
442 case OBJECT_CLIENT_GRF:
444 nType = FILETYPE_GRF; // If not set already
446 SvxOpenGraphicDialog aDlg(SfxResId(RID_SVXSTR_EDITGRFLINK).toString());
447 aDlg.EnableLink(sal_False);
448 aDlg.SetPath( sFile, sal_True );
449 aDlg.SetCurrentFilter( sTmpFilter );
451 if( !aDlg.Execute() )
453 sFile = aDlg.GetPath();
454 sFile += ::sfx2::cTokenSeparator;
455 sFile += ::sfx2::cTokenSeparator;
456 sFile += aDlg.GetCurrentFilter();
458 if ( aEndEditLink.IsSet() )
459 aEndEditLink.Call( &sFile );
461 else
462 sFile.Erase();
464 break;
466 case OBJECT_CLIENT_OLE:
468 nType = FILETYPE_OBJECT; // if not set already
469 pOldParent = Application::GetDefDialogParent();
470 Application::SetDefDialogParent( pParent );
472 ::sfx2::FileDialogHelper & rFileDlg =
473 pLink->GetInsertFileDialog( String() );
474 rFileDlg.StartExecuteModal(
475 LINK( this, SvFileObject, DialogClosedHdl ) );
477 break;
479 case OBJECT_CLIENT_FILE:
481 nType = FILETYPE_TEXT; // if not set already
482 pOldParent = Application::GetDefDialogParent();
483 Application::SetDefDialogParent( pParent );
485 String sFactory;
486 SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
487 if ( pShell )
488 sFactory = pShell->GetFactory().GetFactoryName();
490 ::sfx2::FileDialogHelper & rFileDlg =
491 pLink->GetInsertFileDialog(sFactory);
492 rFileDlg.StartExecuteModal(
493 LINK( this, SvFileObject, DialogClosedHdl ) );
495 break;
497 default:
498 sFile.Erase();
503 IMPL_STATIC_LINK( SvFileObject, LoadGrfReady_Impl, void*, EMPTYARG )
505 // When we come form here there it can not be an error no more.
506 pThis->bLoadError = sal_False;
507 pThis->bWaitForData = sal_False;
508 pThis->bInCallDownLoad = sal_False;
510 if( !pThis->bInNewData && !pThis->bDataReady )
512 // Graphic is finished, also send DataChanged from Status change
513 pThis->bDataReady = sal_True;
514 pThis->SendStateChg_Impl( sfx2::LinkManager::STATE_LOAD_OK );
516 // and then send the data again
517 pThis->NotifyDataChanged();
520 if( pThis->bDataReady )
522 pThis->bLoadAgain = sal_True;
523 if( pThis->xMed.Is() )
525 pThis->xMed->SetDoneLink( Link() );
527 Application::PostUserEvent(
528 STATIC_LINK( pThis, SvFileObject, DelMedium_Impl ),
529 new SfxMediumRef( pThis->xMed ));
530 pThis->xMed.Clear();
532 if( pThis->pDownLoadData )
533 delete pThis->pDownLoadData, pThis->pDownLoadData = 0;
536 return 0;
539 IMPL_STATIC_LINK( SvFileObject, DelMedium_Impl, SfxMediumRef*, pDelMed )
541 (void)pThis;
542 delete pDelMed;
543 return 0;
546 IMPL_LINK( SvFileObject, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg )
548 String sFile;
549 Application::SetDefDialogParent( pOldParent );
551 if ( FILETYPE_TEXT == nType || FILETYPE_OBJECT == nType )
553 if ( _pFileDlg && _pFileDlg->GetError() == ERRCODE_NONE )
555 String sURL( _pFileDlg->GetPath() );
556 sFile = sURL;
557 sFile += ::sfx2::cTokenSeparator;
558 sFile += ::sfx2::cTokenSeparator;
559 sFile += impl_getFilter( sURL );
562 else
564 SAL_WARN( "sfx2.appl", "SvFileObject::DialogClosedHdl(): wrong file type" );
567 if ( aEndEditLink.IsSet() )
568 aEndEditLink.Call( &sFile );
569 return 0;
572 /* [Description]
574 The method determines whether the data-object can be read from a DDE.
576 The following can be returned:
577 ERRCODE_NONE if it has been completely read
578 ERRCODE_SO_PENDING if it has not been completely read
579 ERRCODE_SO_FALSE otherwise
581 sal_Bool SvFileObject::IsPending() const
583 return FILETYPE_GRF == nType && !bLoadError &&
584 ( pDownLoadData || bWaitForData );
586 sal_Bool SvFileObject::IsDataComplete() const
588 sal_Bool bRet = sal_False;
589 if( FILETYPE_GRF != nType )
590 bRet = sal_True;
591 else if( !bLoadError && ( !bWaitForData && !pDownLoadData ))
593 SvFileObject* pThis = (SvFileObject*)this;
594 if( bDataReady ||
595 ( bSynchron && pThis->LoadFile_Impl() && xMed.Is() ) )
596 bRet = sal_True;
597 else
599 INetURLObject aUrl( sFileNm );
600 if( aUrl.HasError() ||
601 INET_PROT_NOT_VALID == aUrl.GetProtocol() )
602 bRet = sal_True;
605 return bRet;
610 void SvFileObject::CancelTransfers()
612 // unsubscribe from the cache if in the middle of loading
613 if( !bDataReady )
615 // Do not set-up again
616 bLoadAgain = sal_False;
617 bDataReady = bLoadError = bWaitForData = sal_True;
618 SendStateChg_Impl( sfx2::LinkManager::STATE_LOAD_ABORT );
623 void SvFileObject::SendStateChg_Impl( sfx2::LinkManager::LinkState nState )
625 if( !bStateChangeCalled && HasDataLinks() )
627 css::uno::Any aAny;
628 aAny <<= OUString::valueOf( (sal_Int32)nState );
629 DataChanged( SotExchange::GetFormatName(
630 sfx2::LinkManager::RegisterStatusInfoId()), aAny );
631 bStateChangeCalled = sal_True;
636 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */