1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
20 #include <sal/config.h>
24 #include "VistaFilePickerImpl.hxx"
26 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
27 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
28 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
29 #include <com/sun/star/beans/StringPair.hpp>
30 #include <com/sun/star/awt/XWindow.hpp>
31 #include <com/sun/star/awt/XSystemDependentWindowPeer.hpp>
32 #include <com/sun/star/lang/SystemDependent.hpp>
33 #include <comphelper/sequence.hxx>
34 #include <fpicker/strings.hrc>
35 #include <fpicker/fpsofficeResMgr.hxx>
36 #include <osl/file.hxx>
37 #include <rtl/process.h>
38 #include <o3tl/char16_t2wchar_t.hxx>
39 #include <o3tl/string_view.hxx>
40 #include <vcl/svapp.hxx>
41 #include "WinImplHelper.hxx"
46 static bool is_current_process_window(HWND hwnd
)
49 GetWindowThreadProcessId(hwnd
, &pid
);
50 return (pid
== GetCurrentProcessId());
53 static HWND
choose_parent_window()
55 HWND hwnd_parent
= GetForegroundWindow();
56 if (!is_current_process_window(hwnd_parent
))
57 hwnd_parent
= GetDesktopWindow();
63 bool createFolderItem(OUString
const& url
, sal::systools::COMReference
<IShellItem
>& folder
)
66 if (osl::FileBase::getSystemPathFromFileURL(url
, path
)
67 != osl::FileBase::E_None
)
71 HRESULT res
= SHCreateItemFromParsingName(
72 o3tl::toW(path
.getStr()), nullptr,
73 IID_PPV_ARGS(&folder
));
74 return SUCCEEDED(res
);
84 // types, const etcpp.
87 const ::sal_Int16 INVALID_CONTROL_ID
= -1;
88 const ::sal_Int16 INVALID_CONTROL_ACTION
= -1;
90 // Guids used for IFileDialog::SetClientGuid
91 const GUID CLIENTID_FILEDIALOG_SIMPLE
= {0xB8628FD3, 0xA3F5, 0x4845, 0x9B, 0x62, 0xD5, 0x1E, 0xDF, 0x97, 0xC4, 0x83};
92 const GUID CLIENTID_FILEDIALOG_OPTIONS
= {0x93ED486F, 0x0D04, 0x4807, 0x8C, 0x44, 0xAC, 0x26, 0xCB, 0x6C, 0x5D, 0x36};
93 const GUID CLIENTID_FILESAVE_PASSWORD
= {0xC12D4F4C, 0x4D41, 0x4D4F, 0x97, 0xEF, 0x87, 0xF9, 0x8D, 0xB6, 0x1E, 0xA6};
94 const GUID CLIENTID_FILESAVE_SELECTION
= {0x5B2482B3, 0x0358, 0x4E09, 0xAA, 0x64, 0x2B, 0x76, 0xB2, 0xA0, 0xDD, 0xFE};
95 const GUID CLIENTID_FILESAVE_TEMPLATE
= {0x9996D877, 0x20D5, 0x424B, 0x9C, 0x2E, 0xD3, 0xB6, 0x31, 0xEC, 0xF7, 0xCE};
96 const GUID CLIENTID_FILEOPEN_LINK_TEMPLATE
= {0x32237796, 0x1509, 0x49D1, 0xBB, 0x7E, 0x63, 0xAD, 0x36, 0xAE, 0x86, 0x8C};
97 const GUID CLIENTID_FILEOPEN_LINK_ANCHOR
= {0xBE3188CB, 0x399A, 0x45AE, 0x8F, 0x78, 0x75, 0x17, 0xAF, 0x26, 0x81, 0xEA};
98 const GUID CLIENTID_FILEOPEN_PLAY
= {0x32CFB147, 0xF5AE, 0x4F90, 0xA1, 0xF1, 0x81, 0x20, 0x72, 0xBB, 0x2F, 0xC5};
99 const GUID CLIENTID_FILEOPEN_LINK
= {0x39AC4BAE, 0x7D2D, 0x46BC, 0xBE, 0x2E, 0xF8, 0x8C, 0xB5, 0x65, 0x5E, 0x6A};
102 class TDialogImplBase
105 TDialogImplBase(IFileDialog
* iDialog
)
110 virtual ~TDialogImplBase() = default;
112 TFileDialog
getComPtr() { return m_iDialog
; }
113 virtual sal::systools::COMReference
<IShellItemArray
> getResult(bool bInExecute
)
115 sal::systools::COMReference
<IShellItem
> iItem
;
119 m_iDialog
->GetCurrentSelection(&iItem
);
121 m_iDialog
->GetResult(&iItem
);
123 void* iItems
= nullptr;
125 SHCreateShellItemArrayFromShellItem(iItem
.get(), IID_IShellItemArray
, &iItems
);
126 return static_cast<IShellItemArray
*>(iItems
);
130 TFileDialog m_iDialog
;
135 template <class ComPtrDialog
, REFCLSID CLSID
> class TDialogImpl
: public TDialogImplBase
139 : TDialogImplBase(ComPtrDialog(CLSID
).get())
144 class TOpenDialogImpl
: public TDialogImpl
<TFileOpenDialog
, CLSID_FileOpenDialog
>
147 sal::systools::COMReference
<IShellItemArray
> getResult(bool bInExecute
) override
149 sal::systools::COMReference
<IShellItemArray
> iItems
;
150 TFileOpenDialog
iDialog(getComPtr(), sal::systools::COM_QUERY_THROW
);
151 bool bGetResult
= false;
154 else if (FAILED(bInExecute
? iDialog
->GetSelectedItems(&iItems
) : iDialog
->GetResults(&iItems
)))
158 iItems
= TDialogImplBase::getResult(bInExecute
);
166 using TSaveDialogImpl
= TDialogImpl
<TFileSaveDialog
, CLSID_FileSaveDialog
>;
167 using TFolderPickerDialogImpl
= TDialogImpl
<TFileOpenDialog
, CLSID_FileOpenDialog
>;
170 static OUString
lcl_getURLFromShellItem (IShellItem
* pItem
)
172 sal::systools::CoTaskMemAllocated
<wchar_t> pStr
;
173 HRESULT hr
= pItem
->GetDisplayName(SIGDN_FILESYSPATH
, &pStr
);
176 // tdf#155176: One could think that querying SIGDN_URL would go first. But Windows uses
177 // current 8-bit codepage for the filenames, and URL-encodes those octets. So check it
178 // only after SIGDN_FILESYSPATH query failed (can it ever happen?)
179 if (SUCCEEDED(pItem
->GetDisplayName(SIGDN_URL
, &pStr
)))
180 return OUString(o3tl::toU(pStr
));
182 hr
= pItem
->GetDisplayName(SIGDN_PARENTRELATIVEPARSING
, &pStr
);
185 GUID known_folder_id
;
186 wchar_t* pStr2
= pStr
;
187 if (pStr2
[0] == ':' && pStr2
[1] == ':' && pStr2
[2] == '{')
189 hr
= IIDFromString(pStr2
, &known_folder_id
);
191 hr
= SHGetKnownFolderPath(known_folder_id
, 0, nullptr, &pStr
);
197 hr
= SHGetKnownFolderPath(FOLDERID_Documents
, 0, nullptr, &pStr
);
201 ::osl::FileBase::getFileURLFromSystemPath(OUString(o3tl::toU(pStr
)), sURL
);
205 // Vista file picker shows the filter mask next to filter name in the list; so we need to remove the
206 // mask from the filter name to avoid duplicating masks
207 static OUString
lcl_AdjustFilterName(const OUString
& sName
)
209 const sal_Int32 idx
= sName
.indexOf("(.");
210 return (idx
> 0) ? OUString(o3tl::trim(sName
.subView(0, idx
))) : sName
;
213 // rvStrings holds the OUStrings, pointers to which data are stored in returned COMDLG_FILTERSPEC
214 static ::std::vector
<COMDLG_FILTERSPEC
> lcl_buildFilterList(CFilterContainer
& rContainer
,
215 std::vector
<OUString
>& rvStrings
)
217 ::std::vector
< COMDLG_FILTERSPEC
> lList
;
218 CFilterContainer::FILTER_ENTRY_T aFilter
;
220 rContainer
.beginEnumFilter( );
221 while( rContainer
.getNextFilter(aFilter
) )
223 COMDLG_FILTERSPEC aSpec
;
225 rvStrings
.push_back(lcl_AdjustFilterName(aFilter
.first
)); // to avoid dangling pointer
226 aSpec
.pszName
= o3tl::toW(rvStrings
.back().getStr());
227 aSpec
.pszSpec
= o3tl::toW(aFilter
.second
.getStr());
229 lList
.push_back(aSpec
);
236 VistaFilePickerImpl::VistaFilePickerImpl()
238 , m_iEventHandler(new VistaFilePickerEventHandler(this))
239 , m_bInExecute (false)
240 , m_bWasExecuted (false)
241 , m_hParentWindow(nullptr)
248 VistaFilePickerImpl::~VistaFilePickerImpl()
253 void VistaFilePickerImpl::doRequest(Request
& rRequest
)
257 switch(rRequest
.getRequest())
259 case E_ADD_PICKER_LISTENER
:
260 impl_sta_addFilePickerListener(rRequest
);
263 case E_REMOVE_PICKER_LISTENER
:
264 impl_sta_removeFilePickerListener(rRequest
);
267 case E_APPEND_FILTER
:
268 impl_sta_appendFilter(rRequest
);
271 case E_APPEND_FILTERGROUP
:
272 impl_sta_appendFilterGroup(rRequest
);
275 case E_SET_CURRENT_FILTER
:
276 impl_sta_setCurrentFilter(rRequest
);
279 case E_GET_CURRENT_FILTER
:
280 impl_sta_getCurrentFilter(rRequest
);
283 case E_CREATE_OPEN_DIALOG
:
284 impl_sta_CreateOpenDialog(rRequest
);
287 case E_CREATE_SAVE_DIALOG
:
288 impl_sta_CreateSaveDialog(rRequest
);
291 case E_CREATE_FOLDER_PICKER
:
292 impl_sta_CreateFolderPicker(rRequest
);
295 case E_SET_MULTISELECTION_MODE
:
296 impl_sta_SetMultiSelectionMode(rRequest
);
300 impl_sta_SetTitle(rRequest
);
304 impl_sta_SetFileName(rRequest
);
307 case E_SET_DIRECTORY
:
308 impl_sta_SetDirectory(rRequest
);
311 case E_GET_DIRECTORY
:
312 impl_sta_GetDirectory(rRequest
);
315 case E_SET_DEFAULT_NAME
:
316 impl_sta_SetDefaultName(rRequest
);
319 case E_GET_SELECTED_FILES
:
320 impl_sta_getSelectedFiles(rRequest
);
323 case E_SHOW_DIALOG_MODAL
:
324 impl_sta_ShowDialogModal(rRequest
);
327 case E_SET_CONTROL_VALUE
:
328 impl_sta_SetControlValue(rRequest
);
331 case E_GET_CONTROL_VALUE
:
332 impl_sta_GetControlValue(rRequest
);
335 case E_SET_CONTROL_LABEL
:
336 impl_sta_SetControlLabel(rRequest
);
339 case E_GET_CONTROL_LABEL
:
340 impl_sta_GetControlLabel(rRequest
);
343 case E_ENABLE_CONTROL
:
344 impl_sta_EnableControl(rRequest
);
347 // no default: let the compiler detect changes on enum ERequest !
355 void VistaFilePickerImpl::impl_sta_addFilePickerListener(Request
& rRequest
)
357 const css::uno::Reference
< css::ui::dialogs::XFilePickerListener
> xListener
= rRequest
.getArgumentOrDefault(PROP_PICKER_LISTENER
, css::uno::Reference
< css::ui::dialogs::XFilePickerListener
>());
358 if ( ! xListener
.is())
361 if (m_iEventHandler
.is())
363 auto* pHandlerImpl
= static_cast<VistaFilePickerEventHandler
*>(m_iEventHandler
.get());
364 pHandlerImpl
->addFilePickerListener(xListener
);
369 void VistaFilePickerImpl::impl_sta_removeFilePickerListener(Request
& rRequest
)
371 const css::uno::Reference
< css::ui::dialogs::XFilePickerListener
> xListener
= rRequest
.getArgumentOrDefault(PROP_PICKER_LISTENER
, css::uno::Reference
< css::ui::dialogs::XFilePickerListener
>());
372 if ( ! xListener
.is())
375 if (m_iEventHandler
.is())
377 auto* pHandlerImpl
= static_cast<VistaFilePickerEventHandler
*>(m_iEventHandler
.get());
378 pHandlerImpl
->removeFilePickerListener(xListener
);
383 void VistaFilePickerImpl::impl_sta_appendFilter(Request
& rRequest
)
385 const OUString sTitle
= rRequest
.getArgumentOrDefault(PROP_FILTER_TITLE
, OUString());
386 const OUString sFilter
= rRequest
.getArgumentOrDefault(PROP_FILTER_VALUE
, OUString());
388 m_lFilters
.addFilter(sTitle
, sFilter
);
392 void VistaFilePickerImpl::impl_sta_appendFilterGroup(Request
& rRequest
)
394 const css::uno::Sequence
< css::beans::StringPair
> aFilterGroup
=
395 rRequest
.getArgumentOrDefault(PROP_FILTER_GROUP
, css::uno::Sequence
< css::beans::StringPair
>());
397 if ( m_lFilters
.numFilter() > 0 && aFilterGroup
.getLength() > 0 )
398 m_lFilters
.addFilter( STRING_SEPARATOR
, "", true );
400 ::sal_Int32 c
= aFilterGroup
.getLength();
404 const css::beans::StringPair
& rFilter
= aFilterGroup
[i
];
405 m_lFilters
.addFilter(rFilter
.First
, rFilter
.Second
);
410 void VistaFilePickerImpl::impl_sta_setCurrentFilter(Request
& rRequest
)
412 const OUString sTitle
= rRequest
.getArgumentOrDefault(PROP_FILTER_TITLE
, OUString());
414 m_lFilters
.setCurrentFilter(sTitle
);
418 void VistaFilePickerImpl::impl_sta_getCurrentFilter(Request
& rRequest
)
420 TFileDialog iDialog
= impl_getBaseDialogInterface();
424 UINT nIndex
= UINT_MAX
;
425 HRESULT hResult
= iDialog
->GetFileTypeIndex(&nIndex
);
427 ( FAILED(hResult
) ) ||
428 ( nIndex
== UINT_MAX
) // COM dialog sometimes return S_OK for empty filter lists .-(
433 ::sal_Int32 nRealIndex
= nIndex
-1; // COM dialog base on 1 ... filter container on 0 .-)
435 (nRealIndex
>= 0 ) &&
436 (m_lFilters
.getFilterNameByIndex(nRealIndex
, sTitle
))
438 rRequest
.setArgument(PROP_FILTER_TITLE
, sTitle
);
439 else if ( nRealIndex
== -1 ) // Dialog not visible yet
441 sTitle
= m_lFilters
.getCurrentFilter();
442 rRequest
.setArgument(PROP_FILTER_TITLE
, sTitle
);
447 template <class TDialogImplClass
> void VistaFilePickerImpl::impl_sta_CreateDialog()
449 m_pDialog
= std::make_shared
<TDialogImplClass
>();
453 void VistaFilePickerImpl::impl_sta_InitDialog(Request
& rRequest
, DWORD nOrFlags
)
455 TFileDialog iDialog
= impl_getBaseDialogInterface();
460 iDialog
->GetOptions ( &nFlags
);
462 nFlags
&= ~FOS_FORCESHOWHIDDEN
;
463 nFlags
|= FOS_PATHMUSTEXIST
;
464 nFlags
|= FOS_DONTADDTORECENT
;
467 iDialog
->SetOptions ( nFlags
);
469 css::uno::Reference
<css::awt::XWindow
> xWindow
= rRequest
.getArgumentOrDefault(PROP_PARENT_WINDOW
, css::uno::Reference
<css::awt::XWindow
>());
472 css::uno::Reference
<css::awt::XSystemDependentWindowPeer
> xSysDepWin(xWindow
,css::uno::UNO_QUERY
);
473 if(xSysDepWin
.is()) {
474 css::uno::Sequence
<sal_Int8
> aProcessIdent(16);
475 rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8
*>(aProcessIdent
.getArray()));
476 css::uno::Any aAny
= xSysDepWin
->getWindowHandle(aProcessIdent
,css::lang::SystemDependent::SYSTEM_WIN32
);
481 m_hParentWindow
= reinterpret_cast<HWND
>(tmp
);
486 ::sal_Int32 nFeatures
= rRequest
.getArgumentOrDefault(PROP_FEATURES
, ::sal_Int32(0));
487 ::sal_Int32 nTemplate
= rRequest
.getArgumentOrDefault(PROP_TEMPLATE_DESCR
, ::sal_Int32(0));
488 impl_sta_enableFeatures(nFeatures
, nTemplate
);
490 if (m_iEventHandler
.is())
492 auto* pHandlerImpl
= static_cast<VistaFilePickerEventHandler
*>(m_iEventHandler
.get());
493 pHandlerImpl
->startListening(iDialog
);
498 void VistaFilePickerImpl::impl_sta_CreateOpenDialog(Request
& rRequest
)
500 impl_sta_CreateDialog
<TOpenDialogImpl
>();
501 impl_sta_InitDialog(rRequest
, FOS_FILEMUSTEXIST
| FOS_OVERWRITEPROMPT
);
505 void VistaFilePickerImpl::impl_sta_CreateSaveDialog(Request
& rRequest
)
507 impl_sta_CreateDialog
<TSaveDialogImpl
>();
508 impl_sta_InitDialog(rRequest
, FOS_FILEMUSTEXIST
| FOS_OVERWRITEPROMPT
);
512 void VistaFilePickerImpl::impl_sta_CreateFolderPicker(Request
& rRequest
)
514 impl_sta_CreateDialog
<TFolderPickerDialogImpl
>();
515 impl_sta_InitDialog(rRequest
, FOS_PICKFOLDERS
);
519 const ::sal_Int32 GROUP_VERSION
= 1;
520 const ::sal_Int32 GROUP_TEMPLATE
= 2;
521 const ::sal_Int32 GROUP_IMAGETEMPLATE
= 3;
522 const ::sal_Int32 GROUP_CHECKBOXES
= 4;
523 const ::sal_Int32 GROUP_IMAGEANCHOR
= 5;
526 static void setLabelToControl(TFileDialogCustomize iCustom
, sal_uInt16 nControlId
)
528 OUString aLabel
= CResourceProvider::getResString(nControlId
);
529 aLabel
= SOfficeToWindowsLabel(aLabel
);
530 iCustom
->SetControlLabel(nControlId
, o3tl::toW(aLabel
.getStr()) );
534 void VistaFilePickerImpl::impl_sta_enableFeatures(::sal_Int32 nFeatures
, ::sal_Int32 nTemplate
)
539 case css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
:
540 case css::ui::dialogs::TemplateDescription::FILEOPEN_PREVIEW
:
541 case css::ui::dialogs::TemplateDescription::FILESAVE_SIMPLE
:
542 aGUID
= CLIENTID_FILEDIALOG_SIMPLE
;
545 case css::ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION
:
546 case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS
:
547 aGUID
= CLIENTID_FILEDIALOG_OPTIONS
;
550 case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD
:
551 aGUID
= CLIENTID_FILESAVE_PASSWORD
;
554 case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION
:
555 case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_SELECTION
:
556 aGUID
= CLIENTID_FILESAVE_SELECTION
;
559 case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE
:
560 aGUID
= CLIENTID_FILESAVE_TEMPLATE
;
563 case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE
:
564 aGUID
= CLIENTID_FILEOPEN_LINK_TEMPLATE
;
567 case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR
:
568 aGUID
= CLIENTID_FILEOPEN_LINK_ANCHOR
;
571 case css::ui::dialogs::TemplateDescription::FILEOPEN_PLAY
:
572 case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PLAY
:
573 aGUID
= CLIENTID_FILEOPEN_PLAY
;
576 case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW
:
577 aGUID
= CLIENTID_FILEOPEN_LINK
;
580 TFileDialog iDialog
= impl_getBaseDialogInterface();
582 iDialog
->SetClientGuid ( aGUID
);
584 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
588 if ((nFeatures
& FEATURE_VERSION
) == FEATURE_VERSION
)
590 iCustom
->StartVisualGroup (GROUP_VERSION
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_VERSION
).replaceFirst("~","").getStr()));
591 iCustom
->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION
);
592 iCustom
->EndVisualGroup ();
593 iCustom
->MakeProminent (GROUP_VERSION
);
596 if ((nFeatures
& FEATURE_TEMPLATE
) == FEATURE_TEMPLATE
)
598 iCustom
->StartVisualGroup (GROUP_TEMPLATE
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_TEMPLATES
).replaceFirst("~","").getStr()));
599 iCustom
->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE
);
600 iCustom
->EndVisualGroup ();
601 iCustom
->MakeProminent (GROUP_TEMPLATE
);
604 if ((nFeatures
& FEATURE_IMAGETEMPLATE
) == FEATURE_IMAGETEMPLATE
)
606 iCustom
->StartVisualGroup (GROUP_IMAGETEMPLATE
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_IMAGE_TEMPLATE
).replaceFirst("~","").getStr()));
607 iCustom
->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE
);
608 iCustom
->EndVisualGroup ();
609 iCustom
->MakeProminent (GROUP_IMAGETEMPLATE
);
612 if ((nFeatures
& FEATURE_IMAGEANCHOR
) == FEATURE_IMAGEANCHOR
)
614 iCustom
->StartVisualGroup (GROUP_IMAGEANCHOR
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_IMAGE_ANCHOR
).replaceFirst("~","").getStr()));
615 iCustom
->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR
);
616 iCustom
->EndVisualGroup ();
617 iCustom
->MakeProminent (GROUP_IMAGEANCHOR
);
620 iCustom
->StartVisualGroup (GROUP_CHECKBOXES
, L
"");
622 sal_uInt16
nControlId(0);
623 if ((nFeatures
& FEATURE_AUTOEXTENSION
) == FEATURE_AUTOEXTENSION
)
625 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION
;
626 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_AUTO_EXTENSION
).replaceFirst("~","").getStr()), true);
627 setLabelToControl(iCustom
, nControlId
);
630 if ((nFeatures
& FEATURE_PASSWORD
) == FEATURE_PASSWORD
)
632 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD
;
633 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_PASSWORD
).replaceFirst("~","").getStr()), false);
634 setLabelToControl(iCustom
, nControlId
);
637 if ((nFeatures
& FEATURE_GPGPASSWORD
) == FEATURE_GPGPASSWORD
)
639 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION
;
640 iCustom
->AddCheckButton (nControlId
, L
"GpgPassword", false);
641 setLabelToControl(iCustom
, nControlId
);
644 if ((nFeatures
& FEATURE_READONLY
) == FEATURE_READONLY
)
646 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY
;
647 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_READONLY
).replaceFirst("~","").getStr()), false);
648 setLabelToControl(iCustom
, nControlId
);
651 if ((nFeatures
& FEATURE_FILTEROPTIONS
) == FEATURE_FILTEROPTIONS
)
653 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS
;
654 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_FILTER_OPTIONS
).replaceFirst("~","").getStr()), false);
655 setLabelToControl(iCustom
, nControlId
);
658 if ((nFeatures
& FEATURE_LINK
) == FEATURE_LINK
)
660 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK
;
661 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_INSERT_AS_LINK
).replaceFirst("~","").getStr()), false);
662 setLabelToControl(iCustom
, nControlId
);
665 if ((nFeatures
& FEATURE_SELECTION
) == FEATURE_SELECTION
)
667 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION
;
668 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_SELECTION
).replaceFirst("~","").getStr()), false);
669 setLabelToControl(iCustom
, nControlId
);
672 /* can be ignored ... new COM dialog supports preview native now !
673 if ((nFeatures & FEATURE_PREVIEW) == FEATURE_PREVIEW)
674 iCustom->AddCheckButton (css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, L"Preview", false);
677 iCustom
->EndVisualGroup();
679 if ((nFeatures
& FEATURE_PLAY
) == FEATURE_PLAY
)
680 iCustom
->AddPushButton (css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_PLAY
).replaceFirst("~","").getStr()));
685 void VistaFilePickerImpl::impl_sta_SetMultiSelectionMode(Request
& rRequest
)
687 const bool bMultiSelection
= rRequest
.getArgumentOrDefault(PROP_MULTISELECTION_MODE
, true);
689 TFileDialog iDialog
= impl_getBaseDialogInterface();
694 iDialog
->GetOptions(&nFlags
);
697 nFlags
|= FOS_ALLOWMULTISELECT
;
699 nFlags
&= ~FOS_ALLOWMULTISELECT
;
701 iDialog
->SetOptions ( nFlags
);
705 void VistaFilePickerImpl::impl_sta_SetTitle(Request
& rRequest
)
707 OUString sTitle
= rRequest
.getArgumentOrDefault(PROP_TITLE
, OUString());
709 TFileDialog iDialog
= impl_getBaseDialogInterface();
713 iDialog
->SetTitle(o3tl::toW(sTitle
.getStr()));
717 void VistaFilePickerImpl::impl_sta_SetFileName(Request
& rRequest
)
719 OUString sFileName
= rRequest
.getArgumentOrDefault(PROP_FILENAME
, OUString());
721 TFileDialog iDialog
= impl_getBaseDialogInterface();
725 iDialog
->SetFileName(o3tl::toW(sFileName
.getStr()));
729 void VistaFilePickerImpl::impl_sta_SetDirectory(Request
& rRequest
)
731 OUString sDirectory
= rRequest
.getArgumentOrDefault(PROP_DIRECTORY
, OUString());
735 // Vista stores last used folders for file dialogs
736 // so we don't want the application to change the folder
738 // Store the requested folder in the meantime and decide later
740 m_sDirectory
= sDirectory
;
743 TFileDialog iDialog
= impl_getBaseDialogInterface();
747 sal::systools::COMReference
<IShellItem
> pFolder
;
748 if ( !createFolderItem(sDirectory
, pFolder
) )
751 iDialog
->SetFolder(pFolder
.get());
754 OUString
VistaFilePickerImpl::GetDirectory()
756 TFileDialog iDialog
= impl_getBaseDialogInterface();
759 sal::systools::COMReference
<IShellItem
> pFolder
;
760 HRESULT hResult
= iDialog
->GetFolder( &pFolder
);
761 if ( FAILED(hResult
) )
763 return lcl_getURLFromShellItem(pFolder
.get());
766 void VistaFilePickerImpl::impl_sta_GetDirectory(Request
& rRequest
)
768 const OUString sFolder
= m_sDirectory
.isEmpty() ? GetDirectory() : m_sDirectory
;
769 if (!sFolder
.isEmpty())
770 rRequest
.setArgument(PROP_DIRECTORY
, sFolder
);
773 void VistaFilePickerImpl::impl_sta_SetDefaultName(Request
& rRequest
)
775 OUString sFilename
= rRequest
.getArgumentOrDefault(PROP_FILENAME
, OUString());
776 TFileDialog iDialog
= impl_getBaseDialogInterface();
780 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
784 // if we have the autoextension check box set, remove (or change ???) the extension of the filename
785 // so that the autoextension mechanism can do its job
787 HRESULT hResult
= iCustom
->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION
, &bValue
);
788 if ( FAILED(hResult
) )
792 sal_Int32 nSepPos
= sFilename
.lastIndexOf( '.' );
794 sFilename
= sFilename
.copy(0, nSepPos
);
797 iDialog
->SetFileName (o3tl::toW(sFilename
.getStr()));
798 m_sFilename
= sFilename
;
802 void VistaFilePickerImpl::impl_sta_setFiltersOnDialog()
804 std::vector
<OUString
> vStrings
; // to hold the adjusted filter names, pointers to which will be
805 // stored in lFilters
806 ::std::vector
< COMDLG_FILTERSPEC
> lFilters
= lcl_buildFilterList(m_lFilters
, vStrings
);
807 OUString sCurrentFilter
= m_lFilters
.getCurrentFilter();
808 sal_Int32 nCurrentFilter
= m_lFilters
.getFilterPos(sCurrentFilter
);
809 TFileDialog iDialog
= impl_getBaseDialogInterface();
812 TFileDialogCustomize iCustomize
= impl_getCustomizeInterface();
813 if (!iCustomize
.is())
816 if (lFilters
.empty())
819 COMDLG_FILTERSPEC
*pFilt
= lFilters
.data();
820 iDialog
->SetFileTypes(lFilters
.size(), pFilt
/*&lFilters[0]*/);
821 iDialog
->SetFileTypeIndex(nCurrentFilter
+ 1);
824 HRESULT hResult
= iCustomize
->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION
, &bValue
);
825 if ( FAILED(hResult
) )
830 PCWSTR lpFilterExt
= lFilters
[0].pszSpec
;
832 lpFilterExt
= wcsrchr( lpFilterExt
, '.' );
835 iDialog
->SetDefaultExtension( lpFilterExt
);
841 void VistaFilePickerImpl::impl_sta_getSelectedFiles(Request
& rRequest
)
843 if (m_pDialog
== nullptr)
846 // ask dialog for results
847 // we must react different if dialog is in execute or not .-(
848 sal::systools::COMReference
<IShellItemArray
> iItems
= m_pDialog
->getResult(m_bInExecute
);
852 // convert and pack results
853 std::vector
< OUString
> lFiles
;
854 if (DWORD nCount
; SUCCEEDED(iItems
->GetCount(&nCount
)))
856 for (DWORD i
= 0; i
< nCount
; ++i
)
858 if (sal::systools::COMReference
<IShellItem
> iItem
;
859 SUCCEEDED(iItems
->GetItemAt(i
, &iItem
)))
861 if (const OUString sURL
= lcl_getURLFromShellItem(iItem
.get()); !sURL
.isEmpty())
862 lFiles
.push_back(sURL
);
867 rRequest
.setArgument(PROP_SELECTED_FILES
, comphelper::containerToSequence(lFiles
));
871 void VistaFilePickerImpl::impl_sta_ShowDialogModal(Request
& rRequest
)
873 impl_sta_setFiltersOnDialog();
875 TFileDialog iDialog
= impl_getBaseDialogInterface();
879 // it's important to know if we are showing the dialog.
880 // Some dialog interface methods can't be called then or some
881 // tasks must be done differently .-) (e.g. see impl_sta_getSelectedFiles())
884 m_bWasExecuted
= true;
886 // we set the directory only if we have a save dialog and a filename
887 // for the other cases, the file dialog remembers its last location
888 // according to its client guid.
889 if( m_sDirectory
.getLength())
891 sal::systools::COMReference
<IShellItem
> pFolder
;
892 if ( createFolderItem(m_sDirectory
, pFolder
) )
894 if (m_sFilename
.getLength())
896 OUString
aFileURL(m_sDirectory
);
897 sal_Int32 nIndex
= aFileURL
.lastIndexOf('/');
898 if (nIndex
!= aFileURL
.getLength()-1)
900 aFileURL
+= m_sFilename
;
902 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
907 HRESULT hResult
= iCustom
->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION
, &bValue
);
911 hResult
= iDialog
->GetFileTypeIndex(&nFileType
);
912 if ( SUCCEEDED(hResult
) && nFileType
> 0 )
914 // COM dialog base on 1 ... filter container on 0 .-)
915 ::size_t nRealIndex
= nFileType
-1;
917 if (m_lFilters
.getFilterByIndex(nRealIndex
, sFilter
))
919 const sal_Int32 idx
= sFilter
.indexOf('.');
921 aFileURL
+= sFilter
.subView(idx
);
926 // Check existence of file. Set folder only for this special case
927 OUString aSystemPath
;
928 osl_getSystemPathFromFileURL( aFileURL
.pData
, &aSystemPath
.pData
);
930 WIN32_FIND_DATAW aFindFileData
;
931 HANDLE hFind
= FindFirstFileW( o3tl::toW(aSystemPath
.getStr()), &aFindFileData
);
932 if (hFind
!= INVALID_HANDLE_VALUE
)
933 iDialog
->SetFolder(pFolder
.get());
935 hResult
= iDialog
->AddPlace(pFolder
.get(), FDAP_TOP
);
940 iDialog
->AddPlace(pFolder
.get(), FDAP_TOP
);
944 HRESULT hResult
= E_FAIL
;
947 // tdf#146007: Make sure we don't hold solar mutex: COM may need to forward
948 // the execution to the main thread, and holding solar mutex could deadlock
949 SolarMutexGuard g
; // First acquire, to avoid releaser failure
950 SolarMutexReleaser r
;
951 // show dialog and wait for user decision
952 hResult
= iDialog
->Show(m_hParentWindow
? m_hParentWindow
953 : choose_parent_window()); // parent window needed
958 m_bInExecute
= false;
960 if (m_iEventHandler
.is())
962 auto* pHandlerImpl
= static_cast<VistaFilePickerEventHandler
*>(m_iEventHandler
.get());
963 pHandlerImpl
->stopListening();
966 if ( FAILED(hResult
) )
969 impl_sta_getSelectedFiles(rRequest
);
970 rRequest
.setArgument(PROP_DIALOG_SHOW_RESULT
, true);
974 TFileDialog
VistaFilePickerImpl::impl_getBaseDialogInterface()
978 if (m_pDialog
!= nullptr)
979 iDialog
= m_pDialog
->getComPtr();
985 TFileDialogCustomize
VistaFilePickerImpl::impl_getCustomizeInterface()
987 if (m_pDialog
!= nullptr)
988 return { m_pDialog
->getComPtr(), sal::systools::COM_QUERY_THROW
};
994 static void lcl_removeControlItemsWorkaround(const TFileDialogCustomize
& iCustom
,
995 ::sal_Int16 nControlId
)
997 (void)iCustom
->SetSelectedControlItem(nControlId
, 1000); // Don't care if this fails (useless?)
999 HRESULT hResult
= S_OK
;
1000 while ( SUCCEEDED(hResult
) )
1001 hResult
= iCustom
->RemoveControlItem(nControlId
, i
++);
1005 void VistaFilePickerImpl::impl_sta_SetControlValue(Request
& rRequest
)
1007 ::sal_Int16 nId
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ID
, INVALID_CONTROL_ID
);
1008 ::sal_Int16 nAction
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ACTION
, INVALID_CONTROL_ACTION
);
1009 css::uno::Any aValue
= rRequest
.getValue(PROP_CONTROL_VALUE
);
1011 // don't check for right values here ...
1012 // most parameters are optional !
1014 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
1015 if ( ! iCustom
.is())
1020 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION
:
1021 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD
:
1022 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY
:
1023 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS
:
1024 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK
:
1025 //case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW : // can be ignored ... preview is supported native now !
1026 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION
:
1028 bool bValue
= false;
1030 iCustom
->SetCheckButtonState(nId
, bValue
);
1034 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION
:
1035 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE
:
1036 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE
:
1037 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR
:
1042 case css::ui::dialogs::ControlActions::DELETE_ITEMS
:
1044 hResult
= iCustom
->RemoveAllControlItems(nId
);
1045 if ( FAILED(hResult
) )
1046 lcl_removeControlItemsWorkaround(iCustom
, nId
);
1050 case css::ui::dialogs::ControlActions::ADD_ITEMS
:
1052 aValue
>>= m_lItems
;
1053 for (::sal_Int32 i
=0; i
<m_lItems
.getLength(); ++i
)
1055 const OUString
& sItem
= m_lItems
[i
];
1056 hResult
= iCustom
->AddControlItem(nId
, i
, o3tl::toW(sItem
.getStr()));
1061 case css::ui::dialogs::ControlActions::SET_SELECT_ITEM
:
1063 ::sal_Int32 nItem
= 0;
1065 hResult
= iCustom
->SetSelectedControlItem(nId
, nItem
);
1072 case css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY
:
1080 void VistaFilePickerImpl::impl_sta_GetControlValue(Request
& rRequest
)
1082 ::sal_Int16 nId
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ID
, INVALID_CONTROL_ID
);
1084 // don't check for right values here ...
1085 // most parameters are optional !
1087 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
1088 if ( ! iCustom
.is())
1091 css::uno::Any aValue
;
1092 if( m_bWasExecuted
)
1095 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD
:
1096 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION
:
1097 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY
:
1098 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS
:
1099 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK
:
1100 //case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW : // can be ignored ... preview is supported native now !
1101 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION
:
1103 BOOL bValue
= FALSE
;
1104 HRESULT hResult
= iCustom
->GetCheckButtonState(nId
, &bValue
);
1105 if ( SUCCEEDED(hResult
) )
1106 aValue
<<= bool(bValue
);
1109 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION
:
1110 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE
:
1111 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE
:
1112 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR
:
1115 HRESULT hResult
= iCustom
->GetSelectedControlItem(nId
, &bValue
);
1116 if ( SUCCEEDED(hResult
) )
1118 const OUString
& sItem
= m_lItems
[bValue
];
1119 aValue
<<= OUString(sItem
.getStr());
1125 if (aValue
.hasValue())
1126 rRequest
.setArgument(PROP_CONTROL_VALUE
, aValue
);
1130 void VistaFilePickerImpl::impl_sta_SetControlLabel(Request
& rRequest
)
1132 ::sal_Int16 nId
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ID
, INVALID_CONTROL_ID
);
1133 OUString sLabel
= rRequest
.getArgumentOrDefault(PROP_CONTROL_LABEL
, OUString() );
1135 // don't check for right values here ...
1136 // most parameters are optional !
1138 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
1139 if ( ! iCustom
.is())
1141 iCustom
->SetControlLabel (nId
, o3tl::toW(sLabel
.getStr()));
1145 void VistaFilePickerImpl::impl_sta_GetControlLabel(Request
& /*rRequest*/)
1150 void VistaFilePickerImpl::impl_sta_EnableControl(Request
& rRequest
)
1152 ::sal_Int16 nId
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ID
, INVALID_CONTROL_ID
);
1153 bool bEnabled
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ENABLE
, true);
1155 // don't check for right values here ...
1156 // most parameters are optional !
1158 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
1159 if ( ! iCustom
.is())
1162 CDCONTROLSTATEF eState
= CDCS_VISIBLE
;
1164 eState
|= CDCS_ENABLED
;
1166 eState
|= CDCS_INACTIVE
;
1168 iCustom
->SetControlState(nId
, eState
);
1171 void VistaFilePickerImpl::impl_SetDefaultExtension( const OUString
& currentFilter
)
1173 TFileDialog iDialog
= impl_getBaseDialogInterface();
1177 if (currentFilter
.getLength())
1180 m_lFilters
.getFilterByName(currentFilter
, FilterExt
);
1182 sal_Int32 posOfPoint
= FilterExt
.indexOf(L
'.');
1183 const sal_Unicode
* pFirstExtStart
= FilterExt
.getStr() + posOfPoint
+ 1;
1185 sal_Int32 posOfSemiColon
= FilterExt
.indexOf(L
';') - 1;
1186 if (posOfSemiColon
< 0)
1187 posOfSemiColon
= FilterExt
.getLength() - 1;
1189 FilterExt
= OUString(pFirstExtStart
, posOfSemiColon
- posOfPoint
);
1190 iDialog
->SetDefaultExtension ( o3tl::toW(FilterExt
.getStr()) );
1194 void VistaFilePickerImpl::onAutoExtensionChanged (bool bChecked
)
1196 const OUString sFilter
= m_lFilters
.getCurrentFilter ();
1198 if (!m_lFilters
.getFilterByName(sFilter
, sExt
))
1201 TFileDialog iDialog
= impl_getBaseDialogInterface();
1205 PCWSTR pExt
= nullptr;
1208 pExt
= o3tl::toW(sExt
.getStr());
1209 pExt
= wcsrchr( pExt
, '.' );
1213 iDialog
->SetDefaultExtension( pExt
);
1216 bool VistaFilePickerImpl::onFileTypeChanged( UINT
/*nTypeIndex*/ )
1221 void VistaFilePickerImpl::onDirectoryChanged()
1223 m_sDirectory
= GetDirectory();
1226 } // namespace vista
1227 } // namespace win32
1228 } // namespace fpicker
1230 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */