Merge branch 'dirdialog-multi-hidden'

Add wxDD_MULTIPLE and wxDD_SHOW_HIDDEN support to wxDirDialog.

See https://github.com/wxWidgets/wxWidgets/pull/1884

Closes #18736.
This commit is contained in:
Vadim Zeitlin
2020-07-11 13:52:12 +02:00
10 changed files with 383 additions and 201 deletions

View File

@@ -25,8 +25,22 @@ extern WXDLLIMPEXP_DATA_CORE(const char) wxDirDialogNameStr[];
extern WXDLLIMPEXP_DATA_CORE(const char) wxDirDialogDefaultFolderStr[];
extern WXDLLIMPEXP_DATA_CORE(const char) wxDirSelectorPromptStr[];
/*
The flags below must coexist with the following flags in m_windowStyle
#define wxCAPTION 0x20000000
#define wxMAXIMIZE 0x00002000
#define wxCLOSE_BOX 0x00001000
#define wxSYSTEM_MENU 0x00000800
wxBORDER_NONE = 0x00200000
#define wxRESIZE_BORDER 0x00000040
#define wxDIALOG_NO_PARENT 0x00000020
*/
#define wxDD_CHANGE_DIR 0x0100
#define wxDD_DIR_MUST_EXIST 0x0200
#define wxDD_MULTIPLE 0x0400
#define wxDD_SHOW_HIDDEN 0x0001
// deprecated, on by default now, use wxDD_DIR_MUST_EXIST to disable it
#define wxDD_NEW_DIR_BUTTON 0
@@ -74,11 +88,22 @@ public:
virtual void SetPath(const wxString& path) { m_path = path; }
virtual wxString GetMessage() const { return m_message; }
virtual wxString GetPath() const { return m_path; }
virtual wxString GetPath() const
{
wxCHECK_MSG( !HasFlag(wxDD_MULTIPLE), wxString(),
"When using wxDD_MULTIPLE, must call GetPaths() instead" );
return m_path;
}
virtual void GetPaths(wxArrayString& paths) const
{
paths = m_paths;
}
protected:
wxString m_message;
wxString m_path;
wxArrayString m_paths;
};

View File

@@ -37,7 +37,6 @@ public:
public: // overrides from wxGenericDirDialog
wxString GetPath() const wxOVERRIDE;
void SetPath(const wxString& path) wxOVERRIDE;
@@ -55,8 +54,6 @@ protected:
private:
wxString m_selectedDirectory;
wxDECLARE_DYNAMIC_CLASS(wxDirDialog);
};

View File

@@ -30,7 +30,7 @@ private:
// The real implementations of ShowModal(), used for Windows versions
// before and since Vista.
int ShowSHBrowseForFolder(WXHWND owner);
int ShowIFileDialog(WXHWND owner);
int ShowIFileOpenDialog(WXHWND owner);
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxDirDialog);
};

View File

@@ -47,6 +47,10 @@ public:
virtual int ShowModal() wxOVERRIDE;
// MacOS 10.11 has removed the titlebar from the dialog, so this is provided
// only for compatibility with older versions
virtual void SetTitle(const wxString& title) wxOVERRIDE;
#if wxOSX_USE_COCOA
virtual void ShowWindowModal() wxOVERRIDE;
virtual void ModalFinishedCallback(void* panel, int returnCode) wxOVERRIDE;
@@ -64,6 +68,8 @@ private:
// Common part of all ctors.
void Init();
wxString m_title;
wxDECLARE_DYNAMIC_CLASS(wxDirDialog);
};

View File

@@ -5,8 +5,12 @@
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#define wxDD_CHANGE_DIR 0x0100
#define wxDD_DIR_MUST_EXIST 0x0200
#define wxDD_MULTIPLE 0x0400
#define wxDD_SHOW_HIDDEN 0x0001
#define wxDD_NEW_DIR_BUTTON 0 // deprecated, on by default now,
#define wxDD_DEFAULT_STYLE (wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
@@ -43,6 +47,13 @@ const char wxDirDialogNameStr[] = "wxDirCtrl";
@style{wxDD_CHANGE_DIR}
Change the current working directory to the directory chosen by the
user.
@note This flag cannot be used with the @c wxDD_MULTIPLE style.
@style{wxDD_MULTIPLE}
Allow the user to select multiple directories.
This flag is only available since wxWidgets 3.1.4
@style{wxDD_SHOW_HIDDEN}
Show hidden and system folders.
This flag is only available since wxWidgets 3.1.4
@endStyleTable
Notice that @c wxRESIZE_BORDER has special side effect under Windows
@@ -60,6 +71,11 @@ const char wxDirDialogNameStr[] = "wxDirCtrl";
@endcode
instead of just using @c wxDD_DIR_MUST_EXIST style alone.
@remarks MacOS 10.11+ does not display a title bar on the dialog. Use SetMessage()
to change the string displayed to the user at the top of the dialog after creation.
The SetTitle() method is provided for compatibility with pre-10.11 MacOS versions
that do still support displaying the title bar.
@library{wxcore}
@category{cmndlg}
@@ -106,9 +122,22 @@ public:
/**
Returns the default or user-selected path.
@note This function can't be used with dialogs which have the @c wxDD_MULTIPLE style,
use GetPaths() instead.
*/
virtual wxString GetPath() const;
/**
Fills the array @a paths with the full paths of the chosen directories.
@note This function should only be used with the dialogs which have @c wxDD_MULTIPLE style,
use GetPath() for the others.
@since 3.1.4
*/
virtual void GetPaths(wxArrayString& paths) const;
/**
Sets the message that will be displayed on the dialog.
*/

View File

@@ -202,6 +202,7 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
#if wxUSE_DIRDLG
EVT_MENU(DIALOGS_DIR_CHOOSE, MyFrame::DirChoose)
EVT_MENU(DIALOGS_DIRNEW_CHOOSE, MyFrame::DirChooseNew)
EVT_MENU(DIALOGS_DIRMULTIPLE_CHOOSE, MyFrame::DirChooseMultiple)
#endif // wxUSE_DIRDLG
#if USE_MODAL_PRESENTATION
@@ -488,6 +489,7 @@ bool MyApp::OnInit()
dir_menu->Append(DIALOGS_DIR_CHOOSE, "&Choose a directory\tCtrl-D");
dir_menu->Append(DIALOGS_DIRNEW_CHOOSE, "Choose a directory (with \"Ne&w\" button)\tShift-Ctrl-D");
dir_menu->Append(DIALOGS_DIRMULTIPLE_CHOOSE, "Choose multiple and hidden directories\tAlt-Ctrl-D");
menuDlg->Append(wxID_ANY,"&Directory operations",dir_menu);
#if USE_DIRDLG_GENERIC
@@ -1803,6 +1805,36 @@ void MyFrame::DirChooseNew(wxCommandEvent& WXUNUSED(event) )
{
DoDirChoose(wxDD_DEFAULT_STYLE & ~wxDD_DIR_MUST_EXIST);
}
void MyFrame::DirChooseMultiple(wxCommandEvent& WXUNUSED(event))
{
// pass some initial dir and the style to wxDirDialog
int style = wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST | wxDD_MULTIPLE | wxDD_SHOW_HIDDEN;
wxString dirHome;
wxGetHomeDir(&dirHome);
wxDirDialog dialog(this, "Testing multiple directory picker", dirHome, style);
if ( dialog.ShowModal() == wxID_OK )
{
wxArrayString paths;
dialog.GetPaths(paths);
wxString msg, s;
size_t count = paths.GetCount();
for ( size_t n = 0; n < count; n++ )
{
s.Printf("Directory %d: %s\n",
(int)n, paths[n]);
msg += s;
}
wxMessageDialog dialog2(this, msg, "Selected directories");
dialog2.ShowModal();
}
}
#endif // wxUSE_DIRDLG
#if USE_DIRDLG_GENERIC

View File

@@ -434,6 +434,7 @@ public:
#if wxUSE_DIRDLG
void DirChoose(wxCommandEvent& event);
void DirChooseNew(wxCommandEvent& event);
void DirChooseMultiple(wxCommandEvent& event);
#endif // wxUSE_DIRDLG
#if USE_DIRDLG_GENERIC
@@ -594,6 +595,7 @@ enum
DIALOGS_FILE_SAVE_GENERIC,
DIALOGS_DIR_CHOOSE,
DIALOGS_DIRNEW_CHOOSE,
DIALOGS_DIRMULTIPLE_CHOOSE,
DIALOGS_GENERIC_DIR_CHOOSE,
DIALOGS_TIP,
DIALOGS_NUM_ENTRY,

View File

@@ -71,6 +71,9 @@ bool wxDirDialog::Create(wxWindow* parent,
{
m_message = title;
wxASSERT_MSG( !( (style & wxDD_MULTIPLE) && (style & wxDD_CHANGE_DIR) ),
"wxDD_CHANGE_DIR can't be used together with wxDD_MULTIPLE" );
parent = GetParentForModalDialog(parent, style);
if (!PreCreation(parent, pos, wxDefaultSize) ||
@@ -110,10 +113,18 @@ bool wxDirDialog::Create(wxWindow* parent,
if (wx_is_at_least_gtk2(18))
{
gtk_file_chooser_set_create_folders(
GTK_FILE_CHOOSER(m_widget), (style & wxDD_DIR_MUST_EXIST) == 0);
GTK_FILE_CHOOSER(m_widget), !HasFlag(wxDD_DIR_MUST_EXIST) );
}
#endif
// Enable multiple selection if desired
gtk_file_chooser_set_select_multiple(
GTK_FILE_CHOOSER(m_widget), HasFlag(wxDD_MULTIPLE) );
// Enable show hidden folders if desired
gtk_file_chooser_set_show_hidden(
GTK_FILE_CHOOSER(m_widget), HasFlag(wxDD_SHOW_HIDDEN) );
// local-only property could be set to false to allow non-local files to be loaded.
// In that case get/set_uri(s) should be used instead of get/set_filename(s) everywhere
// and the GtkFileChooserDialog should probably also be created with a backend,
@@ -132,13 +143,29 @@ bool wxDirDialog::Create(wxWindow* parent,
void wxDirDialog::GTKOnAccept()
{
wxGtkString str(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(m_widget)));
m_selectedDirectory = wxString::FromUTF8(str);
GSList *fnamesi = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(m_widget));
GSList *fnames = fnamesi;
while ( fnamesi )
{
wxString dir(wxString::FromUTF8(static_cast<gchar *>(fnamesi->data)));
m_paths.Add(dir);
g_free(fnamesi->data);
fnamesi = fnamesi->next;
}
g_slist_free(fnames);
// change to the directory where the user went if asked
if (HasFlag(wxDD_CHANGE_DIR))
{
wxSetWorkingDirectory(m_selectedDirectory);
wxSetWorkingDirectory(m_paths.Last());
}
if (!HasFlag(wxDD_MULTIPLE))
{
m_path = m_paths.Last();
}
EndDialog(wxID_OK);
@@ -166,9 +193,4 @@ void wxDirDialog::SetPath(const wxString& dir)
}
}
wxString wxDirDialog::GetPath() const
{
return m_selectedDirectory;
}
#endif // wxUSE_DIRDLG

View File

@@ -40,111 +40,36 @@
#include "wx/msw/private.h"
#include "wx/msw/wrapshl.h"
#include "wx/msw/private/comptr.h"
#include "wx/msw/private/cotaskmemptr.h"
#include "wx/dynlib.h"
#include <initguid.h>
// We can only use IFileDialog under desktop Windows and we need
// wxDynamicLibrary for it.
#if wxUSE_DYNLIB_CLASS
#define wxUSE_IFILEDIALOG 1
// IFileOpenDialog implementation needs wxDynamicLibrary for
// run-time linking SHCreateItemFromParsingName(), available
// only under Windows Vista and newer.
// It also needs a compiler providing declarations and definitions
// of interfaces available in Windows Vista.
#if wxUSE_DYNLIB_CLASS && defined(__IFileOpenDialog_INTERFACE_DEFINED__)
#define wxUSE_IFILEOPENDIALOG 1
#else
#define wxUSE_IFILEDIALOG 0
#define wxUSE_IFILEOPENDIALOG 0
#endif
#if wxUSE_IFILEDIALOG
#if wxUSE_IFILEOPENDIALOG
// IFileDialog related declarations missing from some compilers headers.
// IShellItem
#ifndef __IShellItem_INTERFACE_DEFINED__
#ifndef SIGDN_FILESYSPATH
#define SIGDN_FILESYSPATH 0x80058000
#endif
struct IShellItem : public IUnknown
{
virtual HRESULT wxSTDCALL BindToHandler(IBindCtx*, REFGUID, REFIID, void**) = 0;
virtual HRESULT wxSTDCALL GetParent(IShellItem**) = 0;
virtual HRESULT wxSTDCALL GetDisplayName(DWORD, LPWSTR*) = 0;
virtual HRESULT wxSTDCALL GetAttributes(ULONG, ULONG*) = 0;
virtual HRESULT wxSTDCALL Compare(IShellItem*, DWORD, int*) = 0;
};
#endif // #ifndef __IShellItem_INTERFACE_DEFINED__
#if defined(__VISUALC__) || !defined(__IShellItem_INTERFACE_DEFINED__)
// Define this GUID in any case, even when __IShellItem_INTERFACE_DEFINED__ is
// defined in the headers we might still not have it in the actual uuid.lib,
// this happens with at least VC7 used with its original (i.e. not updated) SDK.
#if defined(__VISUALC__)
// Always define this GUID, we might still not have it in the actual uuid.lib,
// even when IShellItem interface is defined in the headers.
// This happens with at least VC7 used with its original (i.e. not updated) SDK.
// clang complains about multiple definitions, so only define it unconditionally
// when using a Visual C compiler.
DEFINE_GUID(IID_IShellItem,
0x43826D1E, 0xE718, 0x42EE, 0xBC, 0x55, 0xA1, 0xE2, 0x61, 0xC3, 0x7B, 0xFE);
#endif
struct IShellItemFilter;
struct IFileDialogEvents;
// IModalWindow
#ifndef __IModalWindow_INTERFACE_DEFINED__
struct IModalWindow : public IUnknown
{
virtual HRESULT wxSTDCALL Show(HWND) = 0;
};
#endif // #ifndef __IModalWindow_INTERFACE_DEFINED__
// IFileDialog
#ifndef __IFileDialog_INTERFACE_DEFINED__
#ifndef FOS_PICKFOLDERS
#define FOS_PICKFOLDERS 0x20
#endif
#ifndef FOS_FORCEFILESYSTEM
#define FOS_FORCEFILESYSTEM 0x40
#endif
struct _COMDLG_FILTERSPEC;
struct IFileDialog : public IModalWindow
{
virtual HRESULT wxSTDCALL SetFileTypes(UINT, const _COMDLG_FILTERSPEC*) = 0;
virtual HRESULT wxSTDCALL SetFileTypeIndex(UINT) = 0;
virtual HRESULT wxSTDCALL GetFileTypeIndex(UINT*) = 0;
virtual HRESULT wxSTDCALL Advise(IFileDialogEvents*, DWORD*) = 0;
virtual HRESULT wxSTDCALL Unadvise(DWORD) = 0;
virtual HRESULT wxSTDCALL SetOptions(DWORD) = 0;
virtual HRESULT wxSTDCALL GetOptions(DWORD*) = 0;
virtual HRESULT wxSTDCALL SetDefaultFolder(IShellItem*) = 0;
virtual HRESULT wxSTDCALL SetFolder(IShellItem*) = 0;
virtual HRESULT wxSTDCALL GetFolder(IShellItem**) = 0;
virtual HRESULT wxSTDCALL GetCurrentSelection(IShellItem**) = 0;
virtual HRESULT wxSTDCALL SetFileName(LPCWSTR) = 0;
virtual HRESULT wxSTDCALL GetFileName(LPWSTR*) = 0;
virtual HRESULT wxSTDCALL SetTitle(LPCWSTR) = 0;
virtual HRESULT wxSTDCALL SetOkButtonLabel(LPCWSTR) = 0;
virtual HRESULT wxSTDCALL SetFileNameLabel(LPCWSTR) = 0;
virtual HRESULT wxSTDCALL GetResult(IShellItem**) = 0;
virtual HRESULT wxSTDCALL AddPlace(IShellItem*, DWORD) = 0;
virtual HRESULT wxSTDCALL SetDefaultExtension(LPCWSTR) = 0;
virtual HRESULT wxSTDCALL Close(HRESULT) = 0;
virtual HRESULT wxSTDCALL SetClientGuid(REFGUID) = 0;
virtual HRESULT wxSTDCALL ClearClientData() = 0;
virtual HRESULT wxSTDCALL SetFilter(IShellItemFilter*) = 0;
};
DEFINE_GUID(CLSID_FileOpenDialog,
0xDC1C5A9C, 0xE88A, 0x4dde, 0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7);
DEFINE_GUID(IID_IFileDialog,
0x42F85136, 0xDB7E, 0x439C, 0x85, 0xF1, 0xE4, 0x07, 0x5D, 0x13, 0x5F, 0xC8);
#endif // #ifndef __IFileDialog_INTERFACE_DEFINED__
#endif // wxUSE_IFILEDIALOG
#endif // wxUSE_IFILEOPENDIALOG
// ----------------------------------------------------------------------------
// constants
@@ -164,7 +89,18 @@ wxIMPLEMENT_CLASS(wxDirDialog, wxDialog);
// private functions prototypes
// ----------------------------------------------------------------------------
// the callback proc for the dir dlg
#if wxUSE_IFILEOPENDIALOG
// helper functions for wxDirDialog::ShowIFileOpenDialog()
bool InitIFileOpenDialog(const wxString& message, const wxString& defaultPath,
bool multipleSelection, bool showHidden, wxCOMPtr<IFileOpenDialog>& fileDialog);
bool GetPathsFromIFileOpenDialog(const wxCOMPtr<IFileOpenDialog>& fileDialog, bool multipleSelection,
wxArrayString& paths);
bool ConvertIShellItemToPath(const wxCOMPtr<IShellItem>& item, wxString& path);
#endif // #if wxUSE_IFILEOPENDIALOG
// callback used in wxDirDialog::ShowSHBrowseForFolder()
static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp,
LPARAM pData);
@@ -188,6 +124,9 @@ wxDirDialog::wxDirDialog(wxWindow *parent,
m_message = message;
m_parent = parent;
wxASSERT_MSG( !( (style & wxDD_MULTIPLE) && (style & wxDD_CHANGE_DIR) ),
"wxDD_CHANGE_DIR can't be used together with wxDD_MULTIPLE" );
SetWindowStyle(style);
SetPath(defaultPath);
}
@@ -219,9 +158,11 @@ int wxDirDialog::ShowModal()
wxWindow* const parent = GetParentForModalDialog();
WXHWND hWndParent = parent ? GetHwndOf(parent) : NULL;
m_paths.clear();
// Use IFileDialog under new enough Windows, it's more user-friendly.
int rc;
#if wxUSE_IFILEDIALOG
#if wxUSE_IFILEOPENDIALOG
// While the new dialog is available under Vista, it may return a wrong
// path there (see http://support.microsoft.com/kb/969885/en-us), so we
// don't use it there by default. We could improve the version test to
@@ -229,7 +170,7 @@ int wxDirDialog::ShowModal()
// as this means that the hotfix correcting this bug is installed.
if ( wxGetWinVersion() > wxWinVersion_Vista )
{
rc = ShowIFileDialog(hWndParent);
rc = ShowIFileOpenDialog(hWndParent);
}
else
{
@@ -237,7 +178,7 @@ int wxDirDialog::ShowModal()
}
if ( rc == wxID_NONE )
#endif // wxUSE_IFILEDIALOG
#endif // wxUSE_IFILEOPENDIALOG
{
rc = ShowSHBrowseForFolder(hWndParent);
}
@@ -309,39 +250,91 @@ int wxDirDialog::ShowSHBrowseForFolder(WXHWND owner)
//
// Returns wxID_OK on success, wxID_CANCEL if cancelled by user or wxID_NONE if
// an error occurred and we should fall back onto the old dialog.
#if wxUSE_IFILEDIALOG
#if wxUSE_IFILEOPENDIALOG
int wxDirDialog::ShowIFileDialog(WXHWND owner)
int wxDirDialog::ShowIFileOpenDialog(WXHWND owner)
{
HRESULT hr;
wxCOMPtr<IFileDialog> fileDialog;
HRESULT hr = S_OK;
wxCOMPtr<IFileOpenDialog> fileDialog;
if ( !InitIFileOpenDialog(m_message, m_path, HasFlag(wxDD_MULTIPLE),
HasFlag(wxDD_SHOW_HIDDEN), fileDialog) )
{
return wxID_NONE; // Failed to initialize the dialog
}
hr = fileDialog->Show(owner);
if ( FAILED(hr) )
{
if ( hr == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
{
return wxID_CANCEL; // the user cancelled the dialog
}
else
{
wxLogApiError(wxS("IFileDialog::Show"), hr);
}
}
else if ( GetPathsFromIFileOpenDialog(fileDialog, HasFlag(wxDD_MULTIPLE),
m_paths) )
{
if ( !HasFlag(wxDD_MULTIPLE) )
{
m_path = m_paths.Last();
}
return wxID_OK;
}
// Failed to show the dialog or obtain the selected folders(s)
wxLogSysError(_("Couldn't obtain folder name"), hr);
return wxID_CANCEL;
}
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
// helper function for wxDirDialog::ShowIFileOpenDialog()
bool InitIFileOpenDialog(const wxString& message, const wxString& defaultPath,
bool multipleSelection, bool showHidden,
wxCOMPtr<IFileOpenDialog>& fileDialog)
{
HRESULT hr = S_OK;
wxCOMPtr<IFileOpenDialog> dlg;
// allow to select only a file system folder, do not change the CWD
long options = FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR;
hr = ::CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
wxIID_PPV_ARGS(IFileDialog, &fileDialog));
wxIID_PPV_ARGS(IFileOpenDialog, &dlg));
if ( FAILED(hr) )
{
wxLogApiError(wxS("CoCreateInstance(CLSID_FileOpenDialog)"), hr);
return wxID_NONE;
return false;
}
// allow user to select only a file system folder
hr = fileDialog->SetOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM);
if ( multipleSelection )
options |= FOS_ALLOWMULTISELECT;
if ( showHidden )
options |= FOS_FORCESHOWHIDDEN;
hr = dlg->SetOptions(options);
if ( FAILED(hr) )
{
wxLogApiError(wxS("IFileDialog::SetOptions"), hr);
return wxID_NONE;
wxLogApiError(wxS("IFileOpenDialog::SetOptions"), hr);
return false;
}
hr = fileDialog->SetTitle(m_message.wc_str());
hr = dlg->SetTitle(message.wc_str());
if ( FAILED(hr) )
{
// This error is not serious, let's just log it and continue even
// without the title set.
wxLogApiError(wxS("IFileDialog::SetTitle"), hr);
wxLogApiError(wxS("IFileOpenDialog::SetTitle"), hr);
}
// set the initial path
if ( !m_path.empty() )
if ( !defaultPath.empty() )
{
// We need to link SHCreateItemFromParsingName() dynamically as it's
// not available on pre-Vista systems.
@@ -361,14 +354,14 @@ int wxDirDialog::ShowIFileDialog(WXHWND owner)
if ( !pfnSHCreateItemFromParsingName )
{
wxLogLastError(wxS("SHCreateItemFromParsingName() not found"));
return wxID_NONE;
return false;
}
wxCOMPtr<IShellItem> folder;
hr = pfnSHCreateItemFromParsingName(m_path.wc_str(),
NULL,
wxIID_PPV_ARGS(IShellItem,
&folder));
hr = pfnSHCreateItemFromParsingName(defaultPath.wc_str(),
NULL,
wxIID_PPV_ARGS(IShellItem,
&folder));
// Failing to parse the folder name is not really an error, we'll just
// ignore the initial directory in this case, but we should still show
@@ -378,77 +371,123 @@ int wxDirDialog::ShowIFileDialog(WXHWND owner)
if ( hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) )
{
wxLogApiError(wxS("SHCreateItemFromParsingName"), hr);
return wxID_NONE;
return false;
}
}
else // The folder was parsed correctly.
{
hr = fileDialog->SetFolder(folder);
hr = dlg->SetFolder(folder);
if ( FAILED(hr) )
{
wxLogApiError(wxS("IFileDialog::SetFolder"), hr);
return wxID_NONE;
wxLogApiError(wxS("IFileOpenDialog::SetFolder"), hr);
return false;
}
}
}
wxString path;
hr = fileDialog->Show(owner);
if ( SUCCEEDED(hr) )
{
wxCOMPtr<IShellItem> folder;
hr = fileDialog->GetResult(&folder);
if ( SUCCEEDED(hr) )
{
LPOLESTR pathOLE = NULL;
hr = folder->GetDisplayName(SIGDN_FILESYSPATH, &pathOLE);
if ( SUCCEEDED(hr) )
{
path = pathOLE;
CoTaskMemFree(pathOLE);
}
else
{
wxLogApiError(wxS("IShellItem::GetDisplayName"), hr);
}
}
else
{
wxLogApiError(wxS("IFileDialog::GetResult"), hr);
}
}
else if ( hr == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
{
return wxID_CANCEL; // the user cancelled the dialog
}
else
{
wxLogApiError(wxS("IFileDialog::Show"), hr);
}
if ( path.empty() )
{
// the user didn't cancel the dialog and yet the path is empty
// it means there was an error, already logged by wxLogApiError()
// now report the error to the user and return
wxLogSysError(_("Couldn't obtain folder name"), hr);
return wxID_CANCEL;
}
m_path = path;
return wxID_OK;
fileDialog = dlg;
return true;
}
#endif // wxUSE_IFILEDIALOG
// helper function for wxDirDialog::ShowIFileOpenDialog()
bool GetPathsFromIFileOpenDialog(const wxCOMPtr<IFileOpenDialog>& fileDialog, bool multipleSelection,
wxArrayString& paths)
{
HRESULT hr = S_OK;
wxString path;
wxArrayString tempPaths;
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
if ( multipleSelection )
{
wxCOMPtr<IShellItemArray> itemArray;
hr = fileDialog->GetResults(&itemArray);
if ( FAILED(hr) )
{
wxLogApiError(wxS("IShellItemArray::GetResults"), hr);
return false;
}
DWORD count = 0;
hr = itemArray->GetCount(&count);
if ( FAILED(hr) )
{
wxLogApiError(wxS("IShellItemArray::GetCount"), hr);
return false;
}
for ( DWORD i = 0; i < count; ++i )
{
wxCOMPtr<IShellItem> item;
hr = itemArray->GetItemAt(i, &item);
if ( FAILED(hr) )
{
// do not attempt to retrieve any other items
// and just fail
wxLogApiError(wxS("IShellItemArray::GetItem"), hr);
tempPaths.clear();
break;
}
if ( !ConvertIShellItemToPath(item, path) )
{
// again, just fail
tempPaths.clear();
break;
}
tempPaths.push_back(path);
}
}
else // single selection
{
wxCOMPtr<IShellItem> item;
hr = fileDialog->GetResult(&item);
if ( FAILED(hr) )
{
wxLogApiError(wxS("IFileOpenDialog::GetResult"), hr);
return false;
}
if ( !ConvertIShellItemToPath(item, path) )
{
return false;
}
tempPaths.push_back(path);
}
if ( tempPaths.empty() )
return false; // there was en error
paths = tempPaths;
return true;
}
// helper function for wxDirDialog::ShowIFileOpenDialog()
bool ConvertIShellItemToPath(const wxCOMPtr<IShellItem>& item, wxString& path)
{
wxCoTaskMemPtr<WCHAR> pOLEPath;
const HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &pOLEPath);
if ( FAILED(hr) )
{
wxLogApiError(wxS("IShellItem::GetDisplayName"), hr);
return false;
}
path = pOLEPath;
return true;
}
#endif // wxUSE_IFILEOPENDIALOG
// callback used in wxDirDialog::ShowSHBrowseForFolder()
static int CALLBACK
BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
{

View File

@@ -48,6 +48,9 @@ void wxDirDialog::Create(wxWindow *parent, const wxString& message,
{
m_parent = parent;
wxASSERT_MSG( !( (style & wxDD_MULTIPLE) && (style & wxDD_CHANGE_DIR) ),
"wxDD_CHANGE_DIR can't be used together with wxDD_MULTIPLE" );
SetMessage( message );
SetWindowStyle(style);
SetPath(defaultPath);
@@ -70,18 +73,32 @@ WX_NSOpenPanel wxDirDialog::OSXCreatePanel() const
wxCFStringRef cf( m_message );
[oPanel setMessage:cf.AsNSString()];
if ( !m_title.empty() )
{
wxCFStringRef cfTitle(m_title);
[oPanel setTitle:cfTitle.AsNSString()];
}
if ( !HasFlag(wxDD_DIR_MUST_EXIST) )
[oPanel setCanCreateDirectories:YES];
if ( HasFlag(wxDD_MULTIPLE) )
[oPanel setAllowsMultipleSelection:YES];
if ( HasFlag(wxDD_SHOW_HIDDEN) )
[oPanel setShowsHiddenFiles:YES];
// Set the directory to use
if ( !m_path.IsEmpty() )
{
wxCFStringRef dir(m_path);
NSURL* dirUrl = [NSURL fileURLWithPath: dir.AsNSString() isDirectory: YES];
[oPanel setDirectoryURL: dirUrl];
}
return oPanel;
}
// We use several deprecated methods of NSOpenPanel in the code below, we
// should replace them with newer equivalents now that we don't support OS X
// versions which didn't have them (pre 10.6), but until then, get rid of
// the warning.
wxGCC_WARNING_SUPPRESS(deprecated-declarations)
void wxDirDialog::ShowWindowModal()
{
wxNonOwnedWindow* parentWindow = NULL;
@@ -96,11 +113,11 @@ void wxDirDialog::ShowWindowModal()
NSOpenPanel *oPanel = OSXCreatePanel();
NSWindow* nativeParent = parentWindow->GetWXWindow();
wxCFStringRef dir( m_path );
[oPanel beginSheetForDirectory:dir.AsNSString() file:nil types: nil
modalForWindow: nativeParent modalDelegate: m_sheetDelegate
didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo: nil];
// Create the window and have it call the ModalFinishedCallback on completion
[oPanel beginSheetModalForWindow: nativeParent completionHandler: ^(NSModalResponse returnCode){
[(ModalDialogDelegate*)m_sheetDelegate sheetDidEnd: oPanel returnCode: returnCode contextInfo: nil];
}];
}
int wxDirDialog::ShowModal()
@@ -111,15 +128,12 @@ int wxDirDialog::ShowModal()
NSOpenPanel *oPanel = OSXCreatePanel();
wxCFStringRef dir( m_path );
m_path.clear();
int returnCode = -1;
OSXBeginModalDialog();
returnCode = (NSInteger)[oPanel runModalForDirectory:dir.AsNSString() file:nil types:nil];
// Display the panel and process the result on completion
returnCode = (NSInteger)[oPanel runModal];
ModalFinishedCallback(oPanel, returnCode);
OSXEndModalDialog();
@@ -135,7 +149,19 @@ void wxDirDialog::ModalFinishedCallback(void* panel, int returnCode)
if (returnCode == NSOKButton )
{
NSOpenPanel* oPanel = (NSOpenPanel*)panel;
SetPath( wxCFStringRef::AsStringWithNormalizationFormC([[oPanel filenames] objectAtIndex:0]));
NSArray<NSURL*>* selectedURL = [oPanel URLs];
for ( NSURL* url in selectedURL )
{
m_paths.Add([url fileSystemRepresentation]);
}
if ( !HasFlag(wxDD_MULTIPLE) )
{
m_path = m_paths.Last();
}
result = wxID_OK;
}
SetReturnCode(result);
@@ -144,6 +170,10 @@ void wxDirDialog::ModalFinishedCallback(void* panel, int returnCode)
SendWindowModalDialogEvent ( wxEVT_WINDOW_MODAL_DIALOG_CLOSED );
}
wxGCC_WARNING_RESTORE(deprecated-declarations)
void wxDirDialog::SetTitle(const wxString &title)
{
m_title = title;
wxDialog::SetTitle(title);
}
#endif // wxUSE_DIRDLG