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_fpicker.hxx"
30 
31 //------------------------------------------------------------------------
32 // includes
33 //------------------------------------------------------------------------
34 
35 #include "VistaFilePickerEventHandler.hxx"
36 #include "asyncrequests.hxx"
37 
38 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
39 #include <com/sun/star/embed/XStorage.hpp>
40 #include <com/sun/star/document/XDocumentRevisionListPersistence.hpp>
41 #include <com/sun/star/util/RevisionTag.hpp>
42 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
43 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
44 
45 #include <comphelper/processfactory.hxx>
46 #include <comphelper/storagehelper.hxx>
47 //#include <tools/urlobj.hxx>
48 //#include <unotools/ucbhelper.hxx>
49 
50 #include <osl/file.hxx>
51 
52 //------------------------------------------------------------------------
53 // namespace directives
54 //------------------------------------------------------------------------
55 
56 namespace css = ::com::sun::star;
57 
58 namespace fpicker{
59 namespace win32{
60 namespace vista{
61 
62 //------------------------------------------------------------------------
63 // defines
64 //------------------------------------------------------------------------
65 
66 //-----------------------------------------------------------------------------------------
67 VistaFilePickerEventHandler::VistaFilePickerEventHandler(IVistaFilePickerInternalNotify* pInternalNotify)
68     : m_nRefCount           (0       )
69     , m_nListenerHandle     (0       )
70     , m_pDialog             (        )
71     , m_lListener           (m_aMutex)
72 	, m_pInternalNotify     (pInternalNotify)
73 {
74 }
75 
76 //-----------------------------------------------------------------------------------------
77 VistaFilePickerEventHandler::~VistaFilePickerEventHandler()
78 {
79 }
80 
81 //-----------------------------------------------------------------------------------------
82 HRESULT STDMETHODCALLTYPE VistaFilePickerEventHandler::QueryInterface(REFIID rIID    ,
83                                                                       void** ppObject)
84 {
85 	*ppObject=NULL;
86 
87 	if ( rIID == IID_IUnknown )
88 		*ppObject = (IUnknown*)(IFileDialogEvents*)this;
89 
90 	if ( rIID == IID_IFileDialogEvents )
91 		*ppObject = (IFileDialogEvents*)this;
92 
93 	if ( rIID == IID_IFileDialogControlEvents )
94 		*ppObject = (IFileDialogControlEvents*)this;
95 
96 	if ( *ppObject != NULL )
97 	{
98 		((IUnknown*)*ppObject)->AddRef();
99 		return S_OK;
100 	}
101 
102 	return E_NOINTERFACE;
103 }
104 
105 //-----------------------------------------------------------------------------------------
106 ULONG STDMETHODCALLTYPE VistaFilePickerEventHandler::AddRef()
107 {
108 	return osl_incrementInterlockedCount(&m_nRefCount);
109 }
110 
111 //-----------------------------------------------------------------------------------------
112 ULONG STDMETHODCALLTYPE VistaFilePickerEventHandler::Release()
113 {
114 	ULONG nReturn = --m_nRefCount;
115 	if ( m_nRefCount == 0 )
116 		delete this;
117 
118     return nReturn;
119 }
120 
121 //-----------------------------------------------------------------------------------------
122 STDMETHODIMP VistaFilePickerEventHandler::OnFileOk(IFileDialog* /*pDialog*/)
123 {
124     return E_NOTIMPL;
125 }
126 
127 //-----------------------------------------------------------------------------------------
128 STDMETHODIMP VistaFilePickerEventHandler::OnFolderChanging(IFileDialog* /*pDialog*/,
129                                                            IShellItem*  /*pFolder*/)
130 {
131     return E_NOTIMPL;
132 }
133 
134 //-----------------------------------------------------------------------------------------
135 STDMETHODIMP VistaFilePickerEventHandler::OnFolderChange(IFileDialog* /*pDialog*/)
136 {
137     impl_sendEvent(E_DIRECTORY_CHANGED, 0);
138     return S_OK;
139 }
140 
141 //-----------------------------------------------------------------------------
142 ::rtl::OUString lcl_getURLFromShellItem2 (IShellItem* pItem)
143 {
144     LPOLESTR pStr = NULL;
145 	::rtl::OUString sURL;
146 
147 	SIGDN   eConversion = SIGDN_FILESYSPATH;
148     HRESULT hr          = pItem->GetDisplayName ( eConversion, &pStr );
149 
150     if ( FAILED(hr) )
151 	{
152 		eConversion = SIGDN_URL;
153 		hr          = pItem->GetDisplayName ( eConversion, &pStr );
154 
155 		if ( FAILED(hr) )
156 			return ::rtl::OUString();
157 
158 		sURL = ::rtl::OUString(reinterpret_cast<sal_Unicode*>(pStr));
159 	}
160 	else
161 	{
162 		::osl::FileBase::getFileURLFromSystemPath( reinterpret_cast<sal_Unicode*>(pStr), sURL );
163 	}
164 
165     CoTaskMemFree (pStr);
166     return sURL;
167 }
168 
169 //-----------------------------------------------------------------------------------------
170 void lcl_updateVersionListDirectly(IFileDialog* pDialog)
171 {
172     static const ::rtl::OUString SERVICENAME_REVISIONPERSISTENCE = ::rtl::OUString::createFromAscii("com.sun.star.document.DocumentRevisionListPersistence");
173     static const ::sal_Int16     CONTROL_VERSIONLIST             = css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION;
174 
175     TFileDialog          iDialog   (pDialog);
176     TFileOpenDialog      iOpen     ;
177     TFileDialogCustomize iCustomize;
178 
179 #ifdef __MINGW32__
180     iDialog->QueryInterface(IID_IFileOpenDialog, (void**)(&iOpen));
181     iDialog->QueryInterface(IID_IFileDialogCustomize, (void**)(&iCustomize));
182 #else
183     iDialog.query(&iOpen     );
184     iDialog.query(&iCustomize);
185 #endif
186 
187     // make sure version list match to the current selection always ...
188     // at least an empty version list will be better then the wrong one .-)
189     iCustomize->RemoveAllControlItems(CONTROL_VERSIONLIST);
190 
191     HRESULT                   hResult = E_FAIL;
192     ComPtr< IShellItemArray > iItems;
193     ComPtr< IShellItem >      iItem;
194 
195     if (iOpen.is())
196     {
197         hResult = iOpen->GetSelectedItems(&iItems);
198         if (FAILED(hResult))
199             return;
200 
201         DWORD nCount;
202         hResult = iItems->GetCount(&nCount);
203         if ( FAILED(hResult) )
204             return;
205 
206         // we can show one version list only within control
207         if (nCount != 1)
208             return;
209 
210         hResult = iItems->GetItemAt(0, &iItem);
211     }
212     else
213     if (iDialog.is())
214         hResult = iDialog->GetCurrentSelection(&iItem);
215 
216     if ( FAILED(hResult) )
217         return;
218 
219     const ::rtl::OUString sURL = lcl_getURLFromShellItem2(iItem);
220     if (sURL.getLength() < 1)
221         return;
222 /*
223     INetURLObject aURL(sURL);
224     if (aURL.GetProtocol() != INET_PROT_FILE)
225         return;
226 
227     ::rtl::OUString sMain = aURL.GetMainURL(INetURLObject::NO_DECODE);
228     if ( ! ::utl::UCBContentHelper::IsDocument(sURL))
229         return;
230 */
231     try
232     {
233         css::uno::Reference< css::embed::XStorage > xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(sURL, css::embed::ElementModes::READ);
234         if ( ! xStorage.is() )
235             return;
236 
237         css::uno::Reference< css::lang::XMultiServiceFactory >                 xSMGR     = ::comphelper::getProcessServiceFactory();
238         css::uno::Reference< css::document::XDocumentRevisionListPersistence > xReader   (xSMGR->createInstance(SERVICENAME_REVISIONPERSISTENCE), css::uno::UNO_QUERY_THROW);
239         css::uno::Sequence< css::util::RevisionTag >                           lVersions = xReader->load(xStorage);
240 
241         for (::sal_Int32 i=0; i<lVersions.getLength(); ++i)
242         {
243             const css::util::RevisionTag& aTag = lVersions[i];
244             iCustomize->AddControlItem(CONTROL_VERSIONLIST, i, reinterpret_cast<LPCTSTR>(aTag.Identifier.getStr()));
245         }
246         iCustomize->SetSelectedControlItem(CONTROL_VERSIONLIST, 0);
247     }
248     catch(const css::uno::Exception&)
249     {}
250 }
251 
252 //-----------------------------------------------------------------------------------------
253 STDMETHODIMP VistaFilePickerEventHandler::OnSelectionChange(IFileDialog* /*pDialog*/)
254 {
255     impl_sendEvent(E_FILE_SELECTION_CHANGED, 0);
256     //lcl_updateVersionListDirectly(pDialog);
257     return S_OK;
258 }
259 
260 //-----------------------------------------------------------------------------------------
261 STDMETHODIMP VistaFilePickerEventHandler::OnShareViolation(IFileDialog*                 /*pDialog*/  ,
262 
263                                                            IShellItem*                  /*pItem*/    ,
264 
265                                                            FDE_SHAREVIOLATION_RESPONSE* /*pResponse*/)
266 {
267     impl_sendEvent(E_CONTROL_STATE_CHANGED, css::ui::dialogs::CommonFilePickerElementIds::LISTBOX_FILTER);
268     return S_OK;
269 }
270 
271 //-----------------------------------------------------------------------------------------
272 STDMETHODIMP VistaFilePickerEventHandler::OnTypeChange(IFileDialog* pDialog)
273 {
274     UINT nFileTypeIndex;
275 	HRESULT hResult = pDialog->GetFileTypeIndex( &nFileTypeIndex );
276 
277 	if ( hResult == S_OK )
278 	{
279 		if ( m_pInternalNotify->onFileTypeChanged( nFileTypeIndex ))
280 			impl_sendEvent(E_CONTROL_STATE_CHANGED, css::ui::dialogs::CommonFilePickerElementIds::LISTBOX_FILTER);
281 	}
282 
283     return S_OK;
284 }
285 
286 //-----------------------------------------------------------------------------------------
287 STDMETHODIMP VistaFilePickerEventHandler::OnOverwrite(IFileDialog*            /*pDialog*/  ,
288                                                       IShellItem*             /*pItem*/    ,
289                                                       FDE_OVERWRITE_RESPONSE* /*pResponse*/)
290 {
291     return E_NOTIMPL;
292 }
293 
294 //-----------------------------------------------------------------------------------------
295 STDMETHODIMP VistaFilePickerEventHandler::OnItemSelected(IFileDialogCustomize* /*pCustomize*/,
296 
297                                                          DWORD                   nIDCtl      ,
298 
299                                                          DWORD                 /*nIDItem*/   )
300 {
301 
302     impl_sendEvent(E_CONTROL_STATE_CHANGED, static_cast<sal_Int16>( nIDCtl ));
303     return S_OK;
304 }
305 
306 //-----------------------------------------------------------------------------------------
307 STDMETHODIMP VistaFilePickerEventHandler::OnButtonClicked(IFileDialogCustomize* /*pCustomize*/,
308                                                           DWORD                 nIDCtl    )
309 {
310 
311     impl_sendEvent(E_CONTROL_STATE_CHANGED, static_cast<sal_Int16>( nIDCtl));
312     return S_OK;
313 }
314 
315 //-----------------------------------------------------------------------------------------
316 STDMETHODIMP VistaFilePickerEventHandler::OnCheckButtonToggled(IFileDialogCustomize* /*pCustomize*/,
317                                                                DWORD                 nIDCtl    ,
318                                                                BOOL                  bChecked  )
319 {
320 	if (nIDCtl == css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION)
321 		m_pInternalNotify->onAutoExtensionChanged(bChecked);
322 
323     impl_sendEvent(E_CONTROL_STATE_CHANGED, static_cast<sal_Int16>( nIDCtl));
324 
325     return S_OK;
326 }
327 
328 //-----------------------------------------------------------------------------------------
329 STDMETHODIMP VistaFilePickerEventHandler::OnControlActivating(IFileDialogCustomize* /*pCustomize*/,
330                                                               DWORD                 nIDCtl    )
331 {
332     impl_sendEvent(E_CONTROL_STATE_CHANGED, static_cast<sal_Int16>( nIDCtl));
333     return S_OK;
334 }
335 
336 //-----------------------------------------------------------------------------------------
337 void SAL_CALL VistaFilePickerEventHandler::addFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener )
338     throw( css::uno::RuntimeException )
339 {
340     m_lListener.addInterface(::getCppuType( (const css::uno::Reference< css::ui::dialogs::XFilePickerListener >*)NULL ), xListener);
341 }
342 
343 //-----------------------------------------------------------------------------------------
344 void SAL_CALL VistaFilePickerEventHandler::removeFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener )
345     throw( css::uno::RuntimeException )
346 {
347     m_lListener.removeInterface(::getCppuType( (const css::uno::Reference< css::ui::dialogs::XFilePickerListener >*)NULL ), xListener);
348 }
349 
350 //-----------------------------------------------------------------------------------------
351 void VistaFilePickerEventHandler::startListening( const TFileDialog& pBroadcaster )
352 {
353     static const sal_Bool STARTUP_SUSPENDED = sal_True;
354     static const sal_Bool STARTUP_WORKING   = sal_False;
355 
356     if (m_pDialog.is())
357         return;
358 
359     m_pDialog = pBroadcaster;
360     m_pDialog->Advise(this, &m_nListenerHandle);
361 }
362 
363 //-----------------------------------------------------------------------------------------
364 void VistaFilePickerEventHandler::stopListening()
365 {
366     if (m_pDialog.is())
367     {
368         m_pDialog->Unadvise(m_nListenerHandle);
369         m_pDialog.release();
370     }
371 }
372 
373 static const ::rtl::OUString PROP_CONTROL_ID      = ::rtl::OUString::createFromAscii("control_id");
374 static const ::rtl::OUString PROP_PICKER_LISTENER = ::rtl::OUString::createFromAscii("picker_listener");
375 
376 //-----------------------------------------------------------------------------------------
377 class AsyncPickerEvents : public RequestHandler
378 {
379 public:
380 
381     AsyncPickerEvents()
382     {}
383 
384     virtual ~AsyncPickerEvents()
385     {}
386 
387     virtual void before()
388     {}
389 
390     virtual void doRequest(const RequestRef& rRequest)
391     {
392         const ::sal_Int32 nEventID   = rRequest->getRequest();
393         const ::sal_Int16 nControlID = rRequest->getArgumentOrDefault(PROP_CONTROL_ID, (::sal_Int16)0);
394         const css::uno::Reference< css::ui::dialogs::XFilePickerListener > xListener = rRequest->getArgumentOrDefault(PROP_PICKER_LISTENER, css::uno::Reference< css::ui::dialogs::XFilePickerListener >());
395 
396         if ( ! xListener.is())
397             return;
398 
399         css::ui::dialogs::FilePickerEvent aEvent;
400         aEvent.ElementId = nControlID;
401 
402         switch (nEventID)
403         {
404             case VistaFilePickerEventHandler::E_FILE_SELECTION_CHANGED :
405                     xListener->fileSelectionChanged(aEvent);
406                     break;
407 
408             case VistaFilePickerEventHandler::E_DIRECTORY_CHANGED :
409                     xListener->directoryChanged(aEvent);
410                     break;
411 
412             case VistaFilePickerEventHandler::E_HELP_REQUESTED :
413                     xListener->helpRequested(aEvent);
414                     break;
415 
416             case VistaFilePickerEventHandler::E_CONTROL_STATE_CHANGED :
417                     xListener->controlStateChanged(aEvent);
418                     break;
419 
420             case VistaFilePickerEventHandler::E_DIALOG_SIZE_CHANGED :
421                     xListener->dialogSizeChanged();
422                     break;
423 
424             // no default here. Let compiler detect changes on enum set !
425         }
426     }
427 
428     virtual void after()
429     {}
430 };
431 
432 //-----------------------------------------------------------------------------------------
433 void VistaFilePickerEventHandler::impl_sendEvent(  EEventType eEventType,
434                                                  ::sal_Int16  nControlID)
435 {
436     static AsyncRequests aNotify(RequestHandlerRef(new AsyncPickerEvents()));
437 
438     ::cppu::OInterfaceContainerHelper* pContainer = m_lListener.getContainer( ::getCppuType( ( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >*) NULL ) );
439     if ( ! pContainer)
440         return;
441 
442     ::cppu::OInterfaceIteratorHelper pIterator(*pContainer);
443     while (pIterator.hasMoreElements())
444     {
445         try
446         {
447             css::uno::Reference< css::ui::dialogs::XFilePickerListener > xListener (pIterator.next(), css::uno::UNO_QUERY);
448 
449             RequestRef rRequest(new Request());
450             rRequest->setRequest (eEventType);
451             rRequest->setArgument(PROP_PICKER_LISTENER, xListener);
452 			if ( nControlID )
453 				rRequest->setArgument(PROP_CONTROL_ID, nControlID);
454 
455             aNotify.triggerRequestDirectly(rRequest);
456             //aNotify.triggerRequestNonBlocked(rRequest);
457         }
458         catch(const css::uno::RuntimeException&)
459         {
460             pIterator.remove();
461         }
462     }
463 }
464 
465 } // namespace vista
466 } // namespace win32
467 } // namespace fpicker
468