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_GPGSIGN
) == FEATURE_GPGSIGN
)
646 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_GPGSIGN
;
647 iCustom
->AddCheckButton (nControlId
, L
"GpgSign", false);
648 setLabelToControl(iCustom
, nControlId
);
651 if ((nFeatures
& FEATURE_READONLY
) == FEATURE_READONLY
)
653 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY
;
654 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_READONLY
).replaceFirst("~","").getStr()), false);
655 setLabelToControl(iCustom
, nControlId
);
658 if ((nFeatures
& FEATURE_FILTEROPTIONS
) == FEATURE_FILTEROPTIONS
)
660 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS
;
661 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_FILTER_OPTIONS
).replaceFirst("~","").getStr()), false);
662 setLabelToControl(iCustom
, nControlId
);
665 if ((nFeatures
& FEATURE_LINK
) == FEATURE_LINK
)
667 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK
;
668 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_INSERT_AS_LINK
).replaceFirst("~","").getStr()), false);
669 setLabelToControl(iCustom
, nControlId
);
672 if ((nFeatures
& FEATURE_SELECTION
) == FEATURE_SELECTION
)
674 nControlId
= css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION
;
675 iCustom
->AddCheckButton (nControlId
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_SELECTION
).replaceFirst("~","").getStr()), false);
676 setLabelToControl(iCustom
, nControlId
);
679 /* can be ignored ... new COM dialog supports preview native now !
680 if ((nFeatures & FEATURE_PREVIEW) == FEATURE_PREVIEW)
681 iCustom->AddCheckButton (css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, L"Preview", false);
684 iCustom
->EndVisualGroup();
686 if ((nFeatures
& FEATURE_PLAY
) == FEATURE_PLAY
)
687 iCustom
->AddPushButton (css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY
, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_PLAY
).replaceFirst("~","").getStr()));
692 void VistaFilePickerImpl::impl_sta_SetMultiSelectionMode(Request
& rRequest
)
694 const bool bMultiSelection
= rRequest
.getArgumentOrDefault(PROP_MULTISELECTION_MODE
, true);
696 TFileDialog iDialog
= impl_getBaseDialogInterface();
701 iDialog
->GetOptions(&nFlags
);
704 nFlags
|= FOS_ALLOWMULTISELECT
;
706 nFlags
&= ~FOS_ALLOWMULTISELECT
;
708 iDialog
->SetOptions ( nFlags
);
712 void VistaFilePickerImpl::impl_sta_SetTitle(Request
& rRequest
)
714 OUString sTitle
= rRequest
.getArgumentOrDefault(PROP_TITLE
, OUString());
716 TFileDialog iDialog
= impl_getBaseDialogInterface();
720 iDialog
->SetTitle(o3tl::toW(sTitle
.getStr()));
724 void VistaFilePickerImpl::impl_sta_SetFileName(Request
& rRequest
)
726 OUString sFileName
= rRequest
.getArgumentOrDefault(PROP_FILENAME
, OUString());
728 TFileDialog iDialog
= impl_getBaseDialogInterface();
732 iDialog
->SetFileName(o3tl::toW(sFileName
.getStr()));
736 void VistaFilePickerImpl::impl_sta_SetDirectory(Request
& rRequest
)
738 OUString sDirectory
= rRequest
.getArgumentOrDefault(PROP_DIRECTORY
, OUString());
742 // Vista stores last used folders for file dialogs
743 // so we don't want the application to change the folder
745 // Store the requested folder in the meantime and decide later
747 m_sDirectory
= sDirectory
;
750 TFileDialog iDialog
= impl_getBaseDialogInterface();
754 sal::systools::COMReference
<IShellItem
> pFolder
;
755 if ( !createFolderItem(sDirectory
, pFolder
) )
758 iDialog
->SetFolder(pFolder
.get());
761 OUString
VistaFilePickerImpl::GetDirectory()
763 TFileDialog iDialog
= impl_getBaseDialogInterface();
766 sal::systools::COMReference
<IShellItem
> pFolder
;
767 HRESULT hResult
= iDialog
->GetFolder( &pFolder
);
768 if ( FAILED(hResult
) )
770 return lcl_getURLFromShellItem(pFolder
.get());
773 void VistaFilePickerImpl::impl_sta_GetDirectory(Request
& rRequest
)
775 const OUString sFolder
= m_sDirectory
.isEmpty() ? GetDirectory() : m_sDirectory
;
776 if (!sFolder
.isEmpty())
777 rRequest
.setArgument(PROP_DIRECTORY
, sFolder
);
780 void VistaFilePickerImpl::impl_sta_SetDefaultName(Request
& rRequest
)
782 OUString sFilename
= rRequest
.getArgumentOrDefault(PROP_FILENAME
, OUString());
783 TFileDialog iDialog
= impl_getBaseDialogInterface();
787 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
791 // if we have the autoextension check box set, remove (or change ???) the extension of the filename
792 // so that the autoextension mechanism can do its job
794 HRESULT hResult
= iCustom
->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION
, &bValue
);
795 if ( FAILED(hResult
) )
799 sal_Int32 nSepPos
= sFilename
.lastIndexOf( '.' );
801 sFilename
= sFilename
.copy(0, nSepPos
);
804 iDialog
->SetFileName (o3tl::toW(sFilename
.getStr()));
805 m_sFilename
= sFilename
;
809 void VistaFilePickerImpl::impl_sta_setFiltersOnDialog()
811 std::vector
<OUString
> vStrings
; // to hold the adjusted filter names, pointers to which will be
812 // stored in lFilters
813 ::std::vector
< COMDLG_FILTERSPEC
> lFilters
= lcl_buildFilterList(m_lFilters
, vStrings
);
814 OUString sCurrentFilter
= m_lFilters
.getCurrentFilter();
815 sal_Int32 nCurrentFilter
= m_lFilters
.getFilterPos(sCurrentFilter
);
816 TFileDialog iDialog
= impl_getBaseDialogInterface();
819 TFileDialogCustomize iCustomize
= impl_getCustomizeInterface();
820 if (!iCustomize
.is())
823 if (lFilters
.empty())
826 COMDLG_FILTERSPEC
*pFilt
= lFilters
.data();
827 iDialog
->SetFileTypes(lFilters
.size(), pFilt
/*&lFilters[0]*/);
828 iDialog
->SetFileTypeIndex(nCurrentFilter
+ 1);
831 HRESULT hResult
= iCustomize
->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION
, &bValue
);
832 if ( FAILED(hResult
) )
837 PCWSTR lpFilterExt
= lFilters
[0].pszSpec
;
839 lpFilterExt
= wcsrchr( lpFilterExt
, '.' );
842 iDialog
->SetDefaultExtension( lpFilterExt
);
848 void VistaFilePickerImpl::impl_sta_getSelectedFiles(Request
& rRequest
)
850 if (m_pDialog
== nullptr)
853 // ask dialog for results
854 // we must react different if dialog is in execute or not .-(
855 sal::systools::COMReference
<IShellItemArray
> iItems
= m_pDialog
->getResult(m_bInExecute
);
859 // convert and pack results
860 std::vector
< OUString
> lFiles
;
861 if (DWORD nCount
; SUCCEEDED(iItems
->GetCount(&nCount
)))
863 for (DWORD i
= 0; i
< nCount
; ++i
)
865 if (sal::systools::COMReference
<IShellItem
> iItem
;
866 SUCCEEDED(iItems
->GetItemAt(i
, &iItem
)))
868 if (const OUString sURL
= lcl_getURLFromShellItem(iItem
.get()); !sURL
.isEmpty())
869 lFiles
.push_back(sURL
);
874 rRequest
.setArgument(PROP_SELECTED_FILES
, comphelper::containerToSequence(lFiles
));
878 void VistaFilePickerImpl::impl_sta_ShowDialogModal(Request
& rRequest
)
880 impl_sta_setFiltersOnDialog();
882 TFileDialog iDialog
= impl_getBaseDialogInterface();
886 // it's important to know if we are showing the dialog.
887 // Some dialog interface methods can't be called then or some
888 // tasks must be done differently .-) (e.g. see impl_sta_getSelectedFiles())
891 m_bWasExecuted
= true;
893 // we set the directory only if we have a save dialog and a filename
894 // for the other cases, the file dialog remembers its last location
895 // according to its client guid.
896 if( m_sDirectory
.getLength())
898 sal::systools::COMReference
<IShellItem
> pFolder
;
899 if ( createFolderItem(m_sDirectory
, pFolder
) )
901 if (m_sFilename
.getLength())
903 OUString
aFileURL(m_sDirectory
);
904 sal_Int32 nIndex
= aFileURL
.lastIndexOf('/');
905 if (nIndex
!= aFileURL
.getLength()-1)
907 aFileURL
+= m_sFilename
;
909 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
914 HRESULT hResult
= iCustom
->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION
, &bValue
);
918 hResult
= iDialog
->GetFileTypeIndex(&nFileType
);
919 if ( SUCCEEDED(hResult
) && nFileType
> 0 )
921 // COM dialog base on 1 ... filter container on 0 .-)
922 ::size_t nRealIndex
= nFileType
-1;
924 if (m_lFilters
.getFilterByIndex(nRealIndex
, sFilter
))
926 const sal_Int32 idx
= sFilter
.indexOf('.');
928 aFileURL
+= sFilter
.subView(idx
);
933 // Check existence of file. Set folder only for this special case
934 OUString aSystemPath
;
935 osl_getSystemPathFromFileURL( aFileURL
.pData
, &aSystemPath
.pData
);
937 WIN32_FIND_DATAW aFindFileData
;
938 HANDLE hFind
= FindFirstFileW( o3tl::toW(aSystemPath
.getStr()), &aFindFileData
);
939 if (hFind
!= INVALID_HANDLE_VALUE
)
940 iDialog
->SetFolder(pFolder
.get());
942 hResult
= iDialog
->AddPlace(pFolder
.get(), FDAP_TOP
);
947 iDialog
->AddPlace(pFolder
.get(), FDAP_TOP
);
951 HRESULT hResult
= E_FAIL
;
954 // tdf#146007: Make sure we don't hold solar mutex: COM may need to forward
955 // the execution to the main thread, and holding solar mutex could deadlock
956 SolarMutexReleaser r
;
957 // show dialog and wait for user decision
958 hResult
= iDialog
->Show(m_hParentWindow
? m_hParentWindow
959 : choose_parent_window()); // parent window needed
964 m_bInExecute
= false;
966 if (m_iEventHandler
.is())
968 auto* pHandlerImpl
= static_cast<VistaFilePickerEventHandler
*>(m_iEventHandler
.get());
969 pHandlerImpl
->stopListening();
972 if ( FAILED(hResult
) )
975 impl_sta_getSelectedFiles(rRequest
);
976 rRequest
.setArgument(PROP_DIALOG_SHOW_RESULT
, true);
980 TFileDialog
VistaFilePickerImpl::impl_getBaseDialogInterface()
984 if (m_pDialog
!= nullptr)
985 iDialog
= m_pDialog
->getComPtr();
991 TFileDialogCustomize
VistaFilePickerImpl::impl_getCustomizeInterface()
993 if (m_pDialog
!= nullptr)
994 return { m_pDialog
->getComPtr(), sal::systools::COM_QUERY_THROW
};
1000 static void lcl_removeControlItemsWorkaround(const TFileDialogCustomize
& iCustom
,
1001 ::sal_Int16 nControlId
)
1003 (void)iCustom
->SetSelectedControlItem(nControlId
, 1000); // Don't care if this fails (useless?)
1005 HRESULT hResult
= S_OK
;
1006 while ( SUCCEEDED(hResult
) )
1007 hResult
= iCustom
->RemoveControlItem(nControlId
, i
++);
1011 void VistaFilePickerImpl::impl_sta_SetControlValue(Request
& rRequest
)
1013 ::sal_Int16 nId
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ID
, INVALID_CONTROL_ID
);
1014 ::sal_Int16 nAction
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ACTION
, INVALID_CONTROL_ACTION
);
1015 css::uno::Any aValue
= rRequest
.getValue(PROP_CONTROL_VALUE
);
1017 // don't check for right values here ...
1018 // most parameters are optional !
1020 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
1021 if ( ! iCustom
.is())
1026 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION
:
1027 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD
:
1028 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY
:
1029 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS
:
1030 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK
:
1031 //case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW : // can be ignored ... preview is supported native now !
1032 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION
:
1034 bool bValue
= false;
1036 iCustom
->SetCheckButtonState(nId
, bValue
);
1040 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION
:
1041 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE
:
1042 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE
:
1043 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR
:
1048 case css::ui::dialogs::ControlActions::DELETE_ITEMS
:
1050 hResult
= iCustom
->RemoveAllControlItems(nId
);
1051 if ( FAILED(hResult
) )
1052 lcl_removeControlItemsWorkaround(iCustom
, nId
);
1056 case css::ui::dialogs::ControlActions::ADD_ITEMS
:
1058 aValue
>>= m_lItems
;
1059 for (::sal_Int32 i
=0; i
<m_lItems
.getLength(); ++i
)
1061 const OUString
& sItem
= m_lItems
[i
];
1062 hResult
= iCustom
->AddControlItem(nId
, i
, o3tl::toW(sItem
.getStr()));
1067 case css::ui::dialogs::ControlActions::SET_SELECT_ITEM
:
1069 ::sal_Int32 nItem
= 0;
1071 hResult
= iCustom
->SetSelectedControlItem(nId
, nItem
);
1078 case css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY
:
1086 void VistaFilePickerImpl::impl_sta_GetControlValue(Request
& rRequest
)
1088 ::sal_Int16 nId
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ID
, INVALID_CONTROL_ID
);
1090 // don't check for right values here ...
1091 // most parameters are optional !
1093 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
1094 if ( ! iCustom
.is())
1097 css::uno::Any aValue
;
1098 if( m_bWasExecuted
)
1101 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD
:
1102 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION
:
1103 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_GPGSIGN
:
1104 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY
:
1105 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS
:
1106 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK
:
1107 //case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW : // can be ignored ... preview is supported native now !
1108 case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION
:
1110 BOOL bValue
= FALSE
;
1111 HRESULT hResult
= iCustom
->GetCheckButtonState(nId
, &bValue
);
1112 if ( SUCCEEDED(hResult
) )
1113 aValue
<<= bool(bValue
);
1116 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION
:
1117 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE
:
1118 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE
:
1119 case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR
:
1122 HRESULT hResult
= iCustom
->GetSelectedControlItem(nId
, &bValue
);
1123 if ( SUCCEEDED(hResult
) )
1125 const OUString
& sItem
= m_lItems
[bValue
];
1132 if (aValue
.hasValue())
1133 rRequest
.setArgument(PROP_CONTROL_VALUE
, aValue
);
1137 void VistaFilePickerImpl::impl_sta_SetControlLabel(Request
& rRequest
)
1139 ::sal_Int16 nId
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ID
, INVALID_CONTROL_ID
);
1140 OUString sLabel
= rRequest
.getArgumentOrDefault(PROP_CONTROL_LABEL
, OUString() );
1142 // don't check for right values here ...
1143 // most parameters are optional !
1145 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
1146 if ( ! iCustom
.is())
1148 iCustom
->SetControlLabel (nId
, o3tl::toW(sLabel
.getStr()));
1152 void VistaFilePickerImpl::impl_sta_GetControlLabel(Request
& /*rRequest*/)
1157 void VistaFilePickerImpl::impl_sta_EnableControl(Request
& rRequest
)
1159 ::sal_Int16 nId
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ID
, INVALID_CONTROL_ID
);
1160 bool bEnabled
= rRequest
.getArgumentOrDefault(PROP_CONTROL_ENABLE
, true);
1162 // don't check for right values here ...
1163 // most parameters are optional !
1165 TFileDialogCustomize iCustom
= impl_getCustomizeInterface();
1166 if ( ! iCustom
.is())
1169 CDCONTROLSTATEF eState
= CDCS_VISIBLE
;
1171 eState
|= CDCS_ENABLED
;
1173 eState
|= CDCS_INACTIVE
;
1175 iCustom
->SetControlState(nId
, eState
);
1178 void VistaFilePickerImpl::impl_SetDefaultExtension( const OUString
& currentFilter
)
1180 TFileDialog iDialog
= impl_getBaseDialogInterface();
1184 if (currentFilter
.getLength())
1187 m_lFilters
.getFilterByName(currentFilter
, FilterExt
);
1189 sal_Int32 posOfPoint
= FilterExt
.indexOf(L
'.');
1190 const sal_Unicode
* pFirstExtStart
= FilterExt
.getStr() + posOfPoint
+ 1;
1192 sal_Int32 posOfSemiColon
= FilterExt
.indexOf(L
';') - 1;
1193 if (posOfSemiColon
< 0)
1194 posOfSemiColon
= FilterExt
.getLength() - 1;
1196 FilterExt
= OUString(pFirstExtStart
, posOfSemiColon
- posOfPoint
);
1197 iDialog
->SetDefaultExtension ( o3tl::toW(FilterExt
.getStr()) );
1201 void VistaFilePickerImpl::onAutoExtensionChanged (bool bChecked
)
1203 const OUString sFilter
= m_lFilters
.getCurrentFilter ();
1205 if (!m_lFilters
.getFilterByName(sFilter
, sExt
))
1208 TFileDialog iDialog
= impl_getBaseDialogInterface();
1212 PCWSTR pExt
= nullptr;
1215 pExt
= o3tl::toW(sExt
.getStr());
1216 pExt
= wcsrchr( pExt
, '.' );
1220 iDialog
->SetDefaultExtension( pExt
);
1223 bool VistaFilePickerImpl::onFileTypeChanged( UINT
/*nTypeIndex*/ )
1228 void VistaFilePickerImpl::onDirectoryChanged()
1230 m_sDirectory
= GetDirectory();
1233 } // namespace vista
1234 } // namespace win32
1235 } // namespace fpicker
1237 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */