From f9e9b19c92204988cd536586dfb29cfc2c65e5fb Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Wed, 8 Jan 2020 19:51:00 +0000 Subject: [PATCH 01/12] Add wxDD_MULTIPLE wxDirDialog style and implement it for GTK Add wxDirDialog::GetPaths() similar to the existing member of wxFileDialog with the same name and also taking, for consistency, wxArrayString as the output parameter. --- include/wx/dirdlg.h | 14 ++++++++++++++ include/wx/gtk/dirdlg.h | 3 ++- interface/wx/dirdlg.h | 17 +++++++++++++++++ src/gtk/dirdlg.cpp | 35 ++++++++++++++++++++++++++++++----- 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/include/wx/dirdlg.h b/include/wx/dirdlg.h index dfdbffc737..a53efee977 100644 --- a/include/wx/dirdlg.h +++ b/include/wx/dirdlg.h @@ -25,8 +25,21 @@ 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 // deprecated, on by default now, use wxDD_DIR_MUST_EXIST to disable it #define wxDD_NEW_DIR_BUTTON 0 @@ -75,6 +88,7 @@ public: virtual wxString GetMessage() const { return m_message; } virtual wxString GetPath() const { return m_path; } + virtual void GetPaths(wxArrayString& paths) const { paths.Empty(); paths.Add(m_path); } protected: wxString m_message; diff --git a/include/wx/gtk/dirdlg.h b/include/wx/gtk/dirdlg.h index 1e4473ee66..d4b00cf0d8 100644 --- a/include/wx/gtk/dirdlg.h +++ b/include/wx/gtk/dirdlg.h @@ -39,6 +39,7 @@ public: // overrides from wxGenericDirDialog wxString GetPath() const wxOVERRIDE; void SetPath(const wxString& path) wxOVERRIDE; + void GetPaths(wxArrayString& paths) const wxOVERRIDE; // Implementation only. @@ -55,7 +56,7 @@ protected: private: - wxString m_selectedDirectory; + wxArrayString m_paths; wxDECLARE_DYNAMIC_CLASS(wxDirDialog); }; diff --git a/interface/wx/dirdlg.h b/interface/wx/dirdlg.h index 19d9827a9b..b5b638666c 100644 --- a/interface/wx/dirdlg.h +++ b/interface/wx/dirdlg.h @@ -7,6 +7,8 @@ #define wxDD_CHANGE_DIR 0x0100 #define wxDD_DIR_MUST_EXIST 0x0200 +#define wxDD_MULTIPLE 0x0400 + #define wxDD_NEW_DIR_BUTTON 0 // deprecated, on by default now, #define wxDD_DEFAULT_STYLE (wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) @@ -43,6 +45,11 @@ 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 + @endStyleTable Notice that @c wxRESIZE_BORDER has special side effect under Windows @@ -109,6 +116,16 @@ public: */ 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. */ diff --git a/src/gtk/dirdlg.cpp b/src/gtk/dirdlg.cpp index 005771a54e..88e352df3b 100644 --- a/src/gtk/dirdlg.cpp +++ b/src/gtk/dirdlg.cpp @@ -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,15 @@ 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) ); + + // 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 +140,24 @@ 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(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()); } EndDialog(wxID_OK); @@ -168,7 +187,13 @@ void wxDirDialog::SetPath(const wxString& dir) wxString wxDirDialog::GetPath() const { - return m_selectedDirectory; + return m_paths.Last(); +} + +void wxDirDialog::GetPaths(wxArrayString& paths) const +{ + paths.Empty(); + paths = m_paths; } #endif // wxUSE_DIRDLG From b43f9b0ea421dda659c18faf1002453b7dda0148 Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Wed, 8 Jan 2020 19:51:19 +0000 Subject: [PATCH 02/12] Update sample to use multiple selection in wxDirDialog --- samples/dialogs/dialogs.cpp | 32 ++++++++++++++++++++++++++++++++ samples/dialogs/dialogs.h | 2 ++ 2 files changed, 34 insertions(+) diff --git a/samples/dialogs/dialogs.cpp b/samples/dialogs/dialogs.cpp index c5c1cc1da7..8298c7f172 100644 --- a/samples/dialogs/dialogs.cpp +++ b/samples/dialogs/dialogs.cpp @@ -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 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; + 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 diff --git a/samples/dialogs/dialogs.h b/samples/dialogs/dialogs.h index 085e9eaeba..4bde46e3c6 100644 --- a/samples/dialogs/dialogs.h +++ b/samples/dialogs/dialogs.h @@ -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, From 79d73d4eb3fc0d8e8cfcbb9781a848a98e406e09 Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Thu, 16 Jan 2020 21:00:34 +0000 Subject: [PATCH 03/12] Add show hidden folders flag to wxDirDialog Add wxDD_SHOW_HIDDEN similar to the existing wxFD_SHOW_HIDDEN. --- include/wx/dirdlg.h | 1 + interface/wx/dirdlg.h | 8 ++++++-- samples/dialogs/dialogs.cpp | 4 ++-- src/gtk/dirdlg.cpp | 3 +++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/wx/dirdlg.h b/include/wx/dirdlg.h index a53efee977..8a732eef20 100644 --- a/include/wx/dirdlg.h +++ b/include/wx/dirdlg.h @@ -40,6 +40,7 @@ extern WXDLLIMPEXP_DATA_CORE(const char) wxDirSelectorPromptStr[]; #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 diff --git a/interface/wx/dirdlg.h b/interface/wx/dirdlg.h index b5b638666c..d4f6c6eaa6 100644 --- a/interface/wx/dirdlg.h +++ b/interface/wx/dirdlg.h @@ -5,9 +5,11 @@ // 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, @@ -45,11 +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. + @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 diff --git a/samples/dialogs/dialogs.cpp b/samples/dialogs/dialogs.cpp index 8298c7f172..9a69411e62 100644 --- a/samples/dialogs/dialogs.cpp +++ b/samples/dialogs/dialogs.cpp @@ -489,7 +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 directories\tAlt-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 @@ -1809,7 +1809,7 @@ void MyFrame::DirChooseNew(wxCommandEvent& WXUNUSED(event) ) 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; + int style = wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST | wxDD_MULTIPLE | wxDD_SHOW_HIDDEN; wxString dirHome; wxGetHomeDir(&dirHome); diff --git a/src/gtk/dirdlg.cpp b/src/gtk/dirdlg.cpp index 88e352df3b..65fa6a6bf5 100644 --- a/src/gtk/dirdlg.cpp +++ b/src/gtk/dirdlg.cpp @@ -121,6 +121,9 @@ bool wxDirDialog::Create(wxWindow* parent, 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 From b98660b996bfdf39784259228b5c7d0ff3cad646 Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Fri, 17 Jan 2020 00:14:53 +0000 Subject: [PATCH 04/12] Use non-deprecated NSOpenPanel methods in wxDirDialog on OSX --- src/osx/cocoa/dirdlg.mm | 42 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/osx/cocoa/dirdlg.mm b/src/osx/cocoa/dirdlg.mm index c46c515a58..903f8e9084 100644 --- a/src/osx/cocoa/dirdlg.mm +++ b/src/osx/cocoa/dirdlg.mm @@ -73,15 +73,17 @@ WX_NSOpenPanel wxDirDialog::OSXCreatePanel() const if ( !HasFlag(wxDD_DIR_MUST_EXIST) ) [oPanel setCanCreateDirectories: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 +98,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 +113,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 +134,15 @@ void wxDirDialog::ModalFinishedCallback(void* panel, int returnCode) if (returnCode == NSOKButton ) { NSOpenPanel* oPanel = (NSOpenPanel*)panel; - SetPath( wxCFStringRef::AsStringWithNormalizationFormC([[oPanel filenames] objectAtIndex:0])); + + NSArray* selectedURL = [oPanel URLs]; + + for ( NSURL* url in selectedURL ) + { + SetPath([url fileSystemRepresentation]); + break; + } + result = wxID_OK; } SetReturnCode(result); @@ -144,6 +151,5 @@ void wxDirDialog::ModalFinishedCallback(void* panel, int returnCode) SendWindowModalDialogEvent ( wxEVT_WINDOW_MODAL_DIALOG_CLOSED ); } -wxGCC_WARNING_RESTORE(deprecated-declarations) #endif // wxUSE_DIRDLG From 5e1cf4cdf2c46062f757827338d02ef7345e33db Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Fri, 17 Jan 2020 00:14:53 +0000 Subject: [PATCH 05/12] Implement hidden directory support in wxOSX too Add support for wxDD_SHOW_HIDDEN style to wxDirDialog in wxOSX. --- src/osx/cocoa/dirdlg.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/osx/cocoa/dirdlg.mm b/src/osx/cocoa/dirdlg.mm index 903f8e9084..e3f86108e9 100644 --- a/src/osx/cocoa/dirdlg.mm +++ b/src/osx/cocoa/dirdlg.mm @@ -73,6 +73,9 @@ WX_NSOpenPanel wxDirDialog::OSXCreatePanel() const if ( !HasFlag(wxDD_DIR_MUST_EXIST) ) [oPanel setCanCreateDirectories:YES]; + if ( HasFlag(wxDD_SHOW_HIDDEN) ) + [oPanel setShowsHiddenFiles:YES]; + // Set the directory to use if ( !m_path.IsEmpty() ) { From 61afcae0bedf7680255e4a88c94353963d9261da Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Fri, 17 Jan 2020 00:14:53 +0000 Subject: [PATCH 06/12] Implement multiple selection support in wxOSX Add support for wxDD_MULTIPLE style to wxDirDialog in wxOSX too. --- include/wx/osx/dirdlg.h | 6 ++++++ src/osx/cocoa/dirdlg.mm | 20 ++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/wx/osx/dirdlg.h b/include/wx/osx/dirdlg.h index a77cd0f880..cf22b6ad4d 100644 --- a/include/wx/osx/dirdlg.h +++ b/include/wx/osx/dirdlg.h @@ -47,6 +47,10 @@ public: virtual int ShowModal() wxOVERRIDE; + virtual wxString GetPath() const wxOVERRIDE; + virtual void GetPaths(wxArrayString& paths) const 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(); + wxArrayString m_paths; + wxDECLARE_DYNAMIC_CLASS(wxDirDialog); }; diff --git a/src/osx/cocoa/dirdlg.mm b/src/osx/cocoa/dirdlg.mm index e3f86108e9..93bb4ad6bb 100644 --- a/src/osx/cocoa/dirdlg.mm +++ b/src/osx/cocoa/dirdlg.mm @@ -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); @@ -73,6 +76,9 @@ WX_NSOpenPanel wxDirDialog::OSXCreatePanel() const if ( !HasFlag(wxDD_DIR_MUST_EXIST) ) [oPanel setCanCreateDirectories:YES]; + if ( HasFlag(wxDD_MULTIPLE) ) + [oPanel setAllowsMultipleSelection:YES]; + if ( HasFlag(wxDD_SHOW_HIDDEN) ) [oPanel setShowsHiddenFiles:YES]; @@ -142,8 +148,7 @@ void wxDirDialog::ModalFinishedCallback(void* panel, int returnCode) for ( NSURL* url in selectedURL ) { - SetPath([url fileSystemRepresentation]); - break; + m_paths.Add([url fileSystemRepresentation]); } result = wxID_OK; @@ -154,5 +159,16 @@ void wxDirDialog::ModalFinishedCallback(void* panel, int returnCode) SendWindowModalDialogEvent ( wxEVT_WINDOW_MODAL_DIALOG_CLOSED ); } +wxString wxDirDialog::GetPath() const +{ + return m_paths.Last(); +} + +void wxDirDialog::GetPaths(wxArrayString& paths) const +{ + paths.Empty(); + paths = m_paths; +} + #endif // wxUSE_DIRDLG From 7230acd110a465f9718526d2b8f134d04eb6d7a3 Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Fri, 17 Jan 2020 00:59:16 +0000 Subject: [PATCH 07/12] Fix setting the title for the wxDirDialog on OSX OSX 10.11+ doesn't actually display the title, so update documentation to reference SetMessage instead. For pre-10.11 override the SetTitle method to set the title of the NSOpenPanel instead of the window. Also change so the directory is not updated unless it is provided. Closes #15143. --- include/wx/osx/dirdlg.h | 5 +++++ interface/wx/dirdlg.h | 5 +++++ src/osx/cocoa/dirdlg.mm | 12 ++++++++++++ 3 files changed, 22 insertions(+) diff --git a/include/wx/osx/dirdlg.h b/include/wx/osx/dirdlg.h index cf22b6ad4d..ea95b70c5a 100644 --- a/include/wx/osx/dirdlg.h +++ b/include/wx/osx/dirdlg.h @@ -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; + virtual wxString GetPath() const wxOVERRIDE; virtual void GetPaths(wxArrayString& paths) const wxOVERRIDE; @@ -69,6 +73,7 @@ private: void Init(); wxArrayString m_paths; + wxString m_title; wxDECLARE_DYNAMIC_CLASS(wxDirDialog); }; diff --git a/interface/wx/dirdlg.h b/interface/wx/dirdlg.h index d4f6c6eaa6..c8f5b29728 100644 --- a/interface/wx/dirdlg.h +++ b/interface/wx/dirdlg.h @@ -71,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} diff --git a/src/osx/cocoa/dirdlg.mm b/src/osx/cocoa/dirdlg.mm index 93bb4ad6bb..9c21b7ea95 100644 --- a/src/osx/cocoa/dirdlg.mm +++ b/src/osx/cocoa/dirdlg.mm @@ -73,6 +73,12 @@ 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]; @@ -159,6 +165,12 @@ void wxDirDialog::ModalFinishedCallback(void* panel, int returnCode) SendWindowModalDialogEvent ( wxEVT_WINDOW_MODAL_DIALOG_CLOSED ); } +void wxDirDialog::SetTitle(const wxString &title) +{ + m_title = title; + wxDialog::SetTitle(title); +} + wxString wxDirDialog::GetPath() const { return m_paths.Last(); From ade5030c565843de3afac5fc15eb1152e8f9c54e Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Tue, 2 Jun 2020 10:36:53 +0100 Subject: [PATCH 08/12] Warn on incompatible wxDirDialog styles --- include/wx/dirdlg.h | 7 ++++++- interface/wx/dirdlg.h | 3 +++ src/gtk/dirdlg.cpp | 1 + src/osx/cocoa/dirdlg.mm | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/wx/dirdlg.h b/include/wx/dirdlg.h index 8a732eef20..8e1a589ee9 100644 --- a/include/wx/dirdlg.h +++ b/include/wx/dirdlg.h @@ -88,7 +88,12 @@ 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.Empty(); paths.Add(m_path); } protected: diff --git a/interface/wx/dirdlg.h b/interface/wx/dirdlg.h index c8f5b29728..bd46febbec 100644 --- a/interface/wx/dirdlg.h +++ b/interface/wx/dirdlg.h @@ -122,6 +122,9 @@ 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; diff --git a/src/gtk/dirdlg.cpp b/src/gtk/dirdlg.cpp index 65fa6a6bf5..5aeb52924e 100644 --- a/src/gtk/dirdlg.cpp +++ b/src/gtk/dirdlg.cpp @@ -190,6 +190,7 @@ void wxDirDialog::SetPath(const wxString& dir) wxString wxDirDialog::GetPath() const { + wxCHECK_MSG( !HasFlag(wxDD_MULTIPLE), wxString(), "When using wxDD_MULTIPLE, must call GetPaths() instead" ); return m_paths.Last(); } diff --git a/src/osx/cocoa/dirdlg.mm b/src/osx/cocoa/dirdlg.mm index 9c21b7ea95..d526b57774 100644 --- a/src/osx/cocoa/dirdlg.mm +++ b/src/osx/cocoa/dirdlg.mm @@ -173,6 +173,7 @@ void wxDirDialog::SetTitle(const wxString &title) wxString wxDirDialog::GetPath() const { + wxCHECK_MSG( !HasFlag(wxDD_MULTIPLE), wxString(), "When using wxDD_MULTIPLE, must call GetPaths() instead" ); return m_paths.Last(); } From 83aa1a19a522c7aa01bfb788a2c6c35ff03d0fbf Mon Sep 17 00:00:00 2001 From: PB Date: Sun, 7 Jun 2020 16:24:49 +0200 Subject: [PATCH 09/12] Implement MSW support for wxDD_MULTIPLE and wxDD_SHOW_HIDDEN --- include/wx/msw/dirdlg.h | 11 +- src/msw/dirdlg.cpp | 390 +++++++++++++++++++++++----------------- 2 files changed, 231 insertions(+), 170 deletions(-) diff --git a/include/wx/msw/dirdlg.h b/include/wx/msw/dirdlg.h index b840ee7629..c06404cd0f 100644 --- a/include/wx/msw/dirdlg.h +++ b/include/wx/msw/dirdlg.h @@ -24,13 +24,22 @@ public: void SetPath(const wxString& path) wxOVERRIDE; + // can be used only when wxDD_MULTIPLE flag is not set + wxString GetPath() const wxOVERRIDE; + + // should be used only when wxDD_MULTIPLE flag is set + void GetPaths(wxArrayString& paths) const wxOVERRIDE; + virtual int ShowModal() wxOVERRIDE; private: + // Used for wxDD_MULTIPLE + wxArrayString m_paths; + // 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); }; diff --git a/src/msw/dirdlg.cpp b/src/msw/dirdlg.cpp index dbd567889b..39080de388 100644 --- a/src/msw/dirdlg.cpp +++ b/src/msw/dirdlg.cpp @@ -44,107 +44,31 @@ #include -// 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 +88,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& fileDialog); +bool GetPathsFromIFileOpenDialog(const wxCOMPtr& fileDialog, bool multipleSelection, + wxArrayString& paths); +bool ConvertIShellItemToPath(const wxCOMPtr& 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 +123,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); } @@ -212,6 +150,19 @@ void wxDirDialog::SetPath(const wxString& path) } } +wxString wxDirDialog::GetPath() const +{ + wxCHECK_MSG( !HasFlag(wxDD_MULTIPLE), wxEmptyString, + "When using wxDD_MULTIPLE, must call GetPaths() instead" ); + + return m_path; +} + +void wxDirDialog::GetPaths(wxArrayString& paths) const +{ + paths = m_paths; +} + int wxDirDialog::ShowModal() { WX_HOOK_MODAL_DIALOG(); @@ -219,9 +170,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 +182,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 +190,7 @@ int wxDirDialog::ShowModal() } if ( rc == wxID_NONE ) -#endif // wxUSE_IFILEDIALOG +#endif // wxUSE_IFILEOPENDIALOG { rc = ShowSHBrowseForFolder(hWndParent); } @@ -309,39 +262,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 fileDialog; + HRESULT hr = S_OK; + wxCOMPtr 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.front(); + } + + 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& fileDialog) +{ + HRESULT hr = S_OK; + wxCOMPtr 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 +366,14 @@ int wxDirDialog::ShowIFileDialog(WXHWND owner) if ( !pfnSHCreateItemFromParsingName ) { wxLogLastError(wxS("SHCreateItemFromParsingName() not found")); - return wxID_NONE; + return false; } wxCOMPtr 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 +383,124 @@ 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 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& fileDialog, bool multipleSelection, + wxArrayString& paths) +{ + HRESULT hr = S_OK; + wxString path; + wxArrayString tempPaths; -// ---------------------------------------------------------------------------- -// private functions -// ---------------------------------------------------------------------------- + if ( multipleSelection ) + { + wxCOMPtr 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 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 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& item, wxString& path) +{ + LPOLESTR pathOLE = NULL; + const HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &pathOLE); + + if ( FAILED(hr) ) + { + wxLogApiError(wxS("IShellItem::GetDisplayName"), hr); + return false; + } + + path = pathOLE; + CoTaskMemFree(pathOLE); + + return true; +} + +#endif // wxUSE_IFILEOPENDIALOG + +// callback used in wxDirDialog::ShowSHBrowseForFolder() static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) { From d8f460200a2986e188cced9d97519e87797cccee Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Jul 2020 03:40:53 +0200 Subject: [PATCH 10/12] Use wxCoTaskMemPtr<> instead of manual ::CoTaskMemFree() No real changes, just use a smart pointer instead of manual memory management calls. --- src/msw/dirdlg.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/msw/dirdlg.cpp b/src/msw/dirdlg.cpp index 39080de388..3e8044d1d5 100644 --- a/src/msw/dirdlg.cpp +++ b/src/msw/dirdlg.cpp @@ -40,6 +40,7 @@ #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 @@ -483,8 +484,8 @@ bool GetPathsFromIFileOpenDialog(const wxCOMPtr& fileDialog, bo // helper function for wxDirDialog::ShowIFileOpenDialog() bool ConvertIShellItemToPath(const wxCOMPtr& item, wxString& path) { - LPOLESTR pathOLE = NULL; - const HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &pathOLE); + wxCoTaskMemPtr pOLEPath; + const HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &pOLEPath); if ( FAILED(hr) ) { @@ -492,8 +493,7 @@ bool ConvertIShellItemToPath(const wxCOMPtr& item, wxString& path) return false; } - path = pathOLE; - CoTaskMemFree(pathOLE); + path = pOLEPath; return true; } From 2b0323ebc84d155ed5c7c31a22d7604db7a6fa67 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Jul 2020 03:43:23 +0200 Subject: [PATCH 11/12] Avoid using wxString::Empty() This is confusingly similar to std::string::empty() which doesn't do the same thing, so prefer using clear() instead. And simply remove Empty() calls before the assignment, as they're useless. No real changes. --- include/wx/dirdlg.h | 6 +++++- src/gtk/dirdlg.cpp | 1 - src/osx/cocoa/dirdlg.mm | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/wx/dirdlg.h b/include/wx/dirdlg.h index 8e1a589ee9..edad929cfe 100644 --- a/include/wx/dirdlg.h +++ b/include/wx/dirdlg.h @@ -94,7 +94,11 @@ public: return m_path; } - virtual void GetPaths(wxArrayString& paths) const { paths.Empty(); paths.Add(m_path); } + virtual void GetPaths(wxArrayString& paths) const + { + paths.clear(); + paths.push_back(m_path); + } protected: wxString m_message; diff --git a/src/gtk/dirdlg.cpp b/src/gtk/dirdlg.cpp index 5aeb52924e..b7246b262e 100644 --- a/src/gtk/dirdlg.cpp +++ b/src/gtk/dirdlg.cpp @@ -196,7 +196,6 @@ wxString wxDirDialog::GetPath() const void wxDirDialog::GetPaths(wxArrayString& paths) const { - paths.Empty(); paths = m_paths; } diff --git a/src/osx/cocoa/dirdlg.mm b/src/osx/cocoa/dirdlg.mm index d526b57774..8e446a8d10 100644 --- a/src/osx/cocoa/dirdlg.mm +++ b/src/osx/cocoa/dirdlg.mm @@ -179,7 +179,6 @@ wxString wxDirDialog::GetPath() const void wxDirDialog::GetPaths(wxArrayString& paths) const { - paths.Empty(); paths = m_paths; } From a47fd95e45d6fa0e39fc947bb036796e4fe81928 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Jul 2020 03:52:15 +0200 Subject: [PATCH 12/12] Avoid overriding wxDirDialog::GetPath[s]() unnecessarily Don't duplicate practically the same code in all ports, just add m_paths itself to the base class. The only drawback of doing this is that it's unused in the ports not (yet) using it, but this saves enough code in the aggregate to be worth it. --- include/wx/dirdlg.h | 7 ++++--- include/wx/gtk/dirdlg.h | 4 ---- include/wx/msw/dirdlg.h | 9 --------- include/wx/osx/dirdlg.h | 5 ----- src/gtk/dirdlg.cpp | 16 +++++----------- src/msw/dirdlg.cpp | 15 +-------------- src/osx/cocoa/dirdlg.mm | 17 +++++------------ 7 files changed, 15 insertions(+), 58 deletions(-) diff --git a/include/wx/dirdlg.h b/include/wx/dirdlg.h index edad929cfe..fba5f4d6e4 100644 --- a/include/wx/dirdlg.h +++ b/include/wx/dirdlg.h @@ -90,19 +90,20 @@ public: virtual wxString GetMessage() const { return m_message; } virtual wxString GetPath() const { - wxCHECK_MSG( !HasFlag(wxDD_MULTIPLE), wxString(), "When using wxDD_MULTIPLE, must call GetPaths() instead" ); + wxCHECK_MSG( !HasFlag(wxDD_MULTIPLE), wxString(), + "When using wxDD_MULTIPLE, must call GetPaths() instead" ); return m_path; } virtual void GetPaths(wxArrayString& paths) const { - paths.clear(); - paths.push_back(m_path); + paths = m_paths; } protected: wxString m_message; wxString m_path; + wxArrayString m_paths; }; diff --git a/include/wx/gtk/dirdlg.h b/include/wx/gtk/dirdlg.h index d4b00cf0d8..e8029a21c7 100644 --- a/include/wx/gtk/dirdlg.h +++ b/include/wx/gtk/dirdlg.h @@ -37,9 +37,7 @@ public: public: // overrides from wxGenericDirDialog - wxString GetPath() const wxOVERRIDE; void SetPath(const wxString& path) wxOVERRIDE; - void GetPaths(wxArrayString& paths) const wxOVERRIDE; // Implementation only. @@ -56,8 +54,6 @@ protected: private: - wxArrayString m_paths; - wxDECLARE_DYNAMIC_CLASS(wxDirDialog); }; diff --git a/include/wx/msw/dirdlg.h b/include/wx/msw/dirdlg.h index c06404cd0f..b99ff5b4f8 100644 --- a/include/wx/msw/dirdlg.h +++ b/include/wx/msw/dirdlg.h @@ -24,18 +24,9 @@ public: void SetPath(const wxString& path) wxOVERRIDE; - // can be used only when wxDD_MULTIPLE flag is not set - wxString GetPath() const wxOVERRIDE; - - // should be used only when wxDD_MULTIPLE flag is set - void GetPaths(wxArrayString& paths) const wxOVERRIDE; - virtual int ShowModal() wxOVERRIDE; private: - // Used for wxDD_MULTIPLE - wxArrayString m_paths; - // The real implementations of ShowModal(), used for Windows versions // before and since Vista. int ShowSHBrowseForFolder(WXHWND owner); diff --git a/include/wx/osx/dirdlg.h b/include/wx/osx/dirdlg.h index ea95b70c5a..376905b029 100644 --- a/include/wx/osx/dirdlg.h +++ b/include/wx/osx/dirdlg.h @@ -51,10 +51,6 @@ public: // only for compatibility with older versions virtual void SetTitle(const wxString& title) wxOVERRIDE; - virtual wxString GetPath() const wxOVERRIDE; - virtual void GetPaths(wxArrayString& paths) const wxOVERRIDE; - - #if wxOSX_USE_COCOA virtual void ShowWindowModal() wxOVERRIDE; virtual void ModalFinishedCallback(void* panel, int returnCode) wxOVERRIDE; @@ -72,7 +68,6 @@ private: // Common part of all ctors. void Init(); - wxArrayString m_paths; wxString m_title; wxDECLARE_DYNAMIC_CLASS(wxDirDialog); diff --git a/src/gtk/dirdlg.cpp b/src/gtk/dirdlg.cpp index b7246b262e..317acab13e 100644 --- a/src/gtk/dirdlg.cpp +++ b/src/gtk/dirdlg.cpp @@ -163,6 +163,11 @@ void wxDirDialog::GTKOnAccept() wxSetWorkingDirectory(m_paths.Last()); } + if (!HasFlag(wxDD_MULTIPLE)) + { + m_path = m_paths.Last(); + } + EndDialog(wxID_OK); } @@ -188,15 +193,4 @@ void wxDirDialog::SetPath(const wxString& dir) } } -wxString wxDirDialog::GetPath() const -{ - wxCHECK_MSG( !HasFlag(wxDD_MULTIPLE), wxString(), "When using wxDD_MULTIPLE, must call GetPaths() instead" ); - return m_paths.Last(); -} - -void wxDirDialog::GetPaths(wxArrayString& paths) const -{ - paths = m_paths; -} - #endif // wxUSE_DIRDLG diff --git a/src/msw/dirdlg.cpp b/src/msw/dirdlg.cpp index 3e8044d1d5..21e0dc8224 100644 --- a/src/msw/dirdlg.cpp +++ b/src/msw/dirdlg.cpp @@ -151,19 +151,6 @@ void wxDirDialog::SetPath(const wxString& path) } } -wxString wxDirDialog::GetPath() const -{ - wxCHECK_MSG( !HasFlag(wxDD_MULTIPLE), wxEmptyString, - "When using wxDD_MULTIPLE, must call GetPaths() instead" ); - - return m_path; -} - -void wxDirDialog::GetPaths(wxArrayString& paths) const -{ - paths = m_paths; -} - int wxDirDialog::ShowModal() { WX_HOOK_MODAL_DIALOG(); @@ -293,7 +280,7 @@ int wxDirDialog::ShowIFileOpenDialog(WXHWND owner) { if ( !HasFlag(wxDD_MULTIPLE) ) { - m_path = m_paths.front(); + m_path = m_paths.Last(); } return wxID_OK; diff --git a/src/osx/cocoa/dirdlg.mm b/src/osx/cocoa/dirdlg.mm index 8e446a8d10..373e1e8a94 100644 --- a/src/osx/cocoa/dirdlg.mm +++ b/src/osx/cocoa/dirdlg.mm @@ -157,6 +157,11 @@ void wxDirDialog::ModalFinishedCallback(void* panel, int returnCode) m_paths.Add([url fileSystemRepresentation]); } + if ( !HasFlag(wxDD_MULTIPLE) ) + { + m_path = m_paths.Last(); + } + result = wxID_OK; } SetReturnCode(result); @@ -171,16 +176,4 @@ void wxDirDialog::SetTitle(const wxString &title) wxDialog::SetTitle(title); } -wxString wxDirDialog::GetPath() const -{ - wxCHECK_MSG( !HasFlag(wxDD_MULTIPLE), wxString(), "When using wxDD_MULTIPLE, must call GetPaths() instead" ); - return m_paths.Last(); -} - -void wxDirDialog::GetPaths(wxArrayString& paths) const -{ - paths = m_paths; -} - - #endif // wxUSE_DIRDLG