diff --git a/docs/changes.txt b/docs/changes.txt index 63a03e12c8..32cf5b6307 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -237,6 +237,7 @@ Changes in behaviour which may result in build errors All (GUI): +- Add wxFileDialog::AddShortcut() (#22543). - Fix grid window scrollbars when freezing part of the grid (#22602). - Avoid warnings with wxStaticText flags in C++20 (David Connet, #22656). - Fix AUI floating pane position when dragging (Konstantin S. Matveyev, #22533). diff --git a/include/wx/filedlg.h b/include/wx/filedlg.h index a68889390f..418c2b7f1e 100644 --- a/include/wx/filedlg.h +++ b/include/wx/filedlg.h @@ -56,6 +56,17 @@ enum #define wxFD_DEFAULT_STYLE wxFD_OPEN +#if wxABI_VERSION >= 30201 + +// Flags for wxFileDialog::AddShortcut(). +enum +{ + wxFD_SHORTCUT_TOP = 0x0001, + wxFD_SHORTCUT_BOTTOM = 0x0002 +}; + +#endif // wxABI_VERSION >= 3.2.1 + extern WXDLLIMPEXP_DATA_CORE(const char) wxFileDialogNameStr[]; extern WXDLLIMPEXP_DATA_CORE(const char) wxFileSelectorPromptStr[]; extern WXDLLIMPEXP_DATA_CORE(const char) wxFileSelectorDefaultWildcardStr[]; @@ -130,6 +141,14 @@ public: { return m_currentlySelectedFilterIndex; } +#if defined(__WXUNIVERSAL__) || !(defined(__WXMSW__) || defined(__WXGTK20__)) +#if wxABI_VERSION >= 30201 + // Add a shortcut to the given directory in the sidebar containing such + // shortcuts if supported. + bool AddShortcut(const wxString& directory, int flags = 0); +#endif // wxABI_VERSION >= 3.2.1 +#endif // Platforms without native implementation. + // A customize hook methods will be called by wxFileDialog later if this // function returns true, see its documentation for details. // diff --git a/include/wx/gtk/filedlg.h b/include/wx/gtk/filedlg.h index 9e3644493b..809805d68f 100644 --- a/include/wx/gtk/filedlg.h +++ b/include/wx/gtk/filedlg.h @@ -55,6 +55,9 @@ public: virtual int ShowModal() wxOVERRIDE; +#if wxABI_VERSION >= 30201 + bool AddShortcut(const wxString& directory, int flags = 0); +#endif // wxABI_VERSION >= 3.2.1 virtual bool SupportsExtraControl() const wxOVERRIDE { return true; } // Implementation only. diff --git a/include/wx/msw/filedlg.h b/include/wx/msw/filedlg.h index 8eadbcebd0..57a873e3bc 100644 --- a/include/wx/msw/filedlg.h +++ b/include/wx/msw/filedlg.h @@ -33,6 +33,9 @@ public: virtual void GetPaths(wxArrayString& paths) const wxOVERRIDE; virtual void GetFilenames(wxArrayString& files) const wxOVERRIDE; +#if wxABI_VERSION >= 30201 + bool AddShortcut(const wxString& directory, int flags = 0); +#endif // wxABI_VERSION >= 3.2.1 virtual bool SupportsExtraControl() const wxOVERRIDE { return true; } virtual int ShowModal() wxOVERRIDE; diff --git a/include/wx/msw/private/filedialog.h b/include/wx/msw/private/filedialog.h index 2f92093f5c..72f48a5134 100644 --- a/include/wx/msw/private/filedialog.h +++ b/include/wx/msw/private/filedialog.h @@ -55,6 +55,9 @@ public: // Set the initial path to show in the dialog. void SetInitialPath(const wxString& path); + // Add a shortcut. + void AddPlace(const wxString& path, FDAP fdap); + // Show the file dialog with the given parent window and options. // // Returns the selected path, or paths, in the provided output parameters, @@ -73,6 +76,9 @@ private: wxCOMPtr m_fileDialog; }; +// Initialize an IShellItem object with the given path. +HRESULT InitShellItemFromPath(wxCOMPtr& item, const wxString& path); + // Extract the filesystem path corresponding to the given shell item. HRESULT GetFSPathFromShellItem(const wxCOMPtr& item, wxString& path); diff --git a/interface/wx/filedlg.h b/interface/wx/filedlg.h index 1f3c835e09..b2f3b5671e 100644 --- a/interface/wx/filedlg.h +++ b/interface/wx/filedlg.h @@ -232,6 +232,46 @@ public: */ virtual ~wxFileDialog(); + /** + Add a directory to the list of shortcuts shown in the dialog. + + File dialogs on many platforms display a fixed list of directories + which can be easily selected by the user. This function allows to add + an application-defined directory to this list, which can be convenient + for the programs that use specific directories for their files instead + of the default user document directory (see wxStandardPaths). + + Currently this function is only implemented in wxMSW and wxGTK and does + nothing under the other platforms. Moreover, in wxMSW this function is + incompatible with the use of SetExtraControlCreator(), if you need to + use this function and customize the dialog contents, please use the + newer SetCustomizeHook() instead. + + The @ref page_samples_dialogs "dialogs sample" shows the use of this + function by adding two custom shortcuts corresponding to the + subdirectories of @c WXWIN environment variable if it is defined. + + @note In wxMSW, the shortcuts appear in a separate section called + "Application Links" by default. To change the title of this + section, the application can specify a value of the @c + FileDescription field of the version information structure in its + resource file -- if present, this string will be used as the + section title. + + @param directory The full path to the directory, which should exist. + @param flags Can be set to @c wxFD_SHORTCUT_BOTTOM (which is also the + default behaviour) to add the shortcut after the existing ones, + or @c wxFD_SHORTCUT_TOP to add it before them. Support for the + latter flag is only available in wxMSW, in wxGTK the shortcuts are + always added to the bottom of the list. + @return @true on success or @false if shortcut couldn't be added, e.g. + because this functionality is not available on the current + platform. + + @since 3.2.1 + */ + bool AddShortcut(const wxString& directory, int flags = 0); + /** Returns the path of the file currently selected in dialog. diff --git a/samples/dialogs/dialogs.cpp b/samples/dialogs/dialogs.cpp index 581f9428d3..a49d9c89d1 100644 --- a/samples/dialogs/dialogs.cpp +++ b/samples/dialogs/dialogs.cpp @@ -1824,6 +1824,18 @@ void MyFrame::FileOpen(wxCommandEvent& WXUNUSED(event) ) ) ); + // For demonstration purposes, add wxWidgets directories to the sidebar. + wxString wxdir; + if ( wxGetEnv("WXWIN", &wxdir) ) + { + dialog.AddShortcut(wxdir + "/src"); + + // By default shortcuts are added at the bottom, but we can override + // this in the ports that support it (currently only wxMSW) and add a + // shortcut added later at the top instead. + dialog.AddShortcut(wxdir + "/include", wxFD_SHORTCUT_TOP); + } + // Note: this object must remain alive until ShowModal() returns. MyCustomizeHook myCustomizer(dialog); diff --git a/src/common/fldlgcmn.cpp b/src/common/fldlgcmn.cpp index fffbfdbe8b..3cfef1bd95 100644 --- a/src/common/fldlgcmn.cpp +++ b/src/common/fldlgcmn.cpp @@ -851,6 +851,17 @@ wxString wxFileDialogBase::AppendExtension(const wxString &filePath, return filePath + ext; } +#if defined(__WXUNIVERSAL__) || !(defined(__WXMSW__) || defined(__WXGTK20__)) + +bool wxFileDialogBase::AddShortcut(const wxString& WXUNUSED(directory), + int WXUNUSED(flags)) +{ + // Not implemented by default. + return false; +} + +#endif // Platforms without native implementation. + bool wxFileDialogBase::SetCustomizeHook(wxFileDialogCustomizeHook& customizeHook) { if ( !SupportsExtraControl() ) diff --git a/src/gtk/filedlg.cpp b/src/gtk/filedlg.cpp index 5d4b69f602..20dc80b26a 100644 --- a/src/gtk/filedlg.cpp +++ b/src/gtk/filedlg.cpp @@ -20,6 +20,7 @@ #endif #include "wx/gtk/private.h" +#include "wx/gtk/private/error.h" #include "wx/gtk/private/mnemonics.h" #ifdef __UNIX__ @@ -499,4 +500,21 @@ void wxFileDialog::GTKSelectionChanged(const wxString& filename) UpdateExtraControlUI(); } +bool wxFileDialog::AddShortcut(const wxString& directory, int WXUNUSED(flags)) +{ + wxGtkError error; + + if ( !gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(m_widget), + directory.utf8_str(), + error.Out()) ) + { + wxLogDebug("Failed to add shortcut \"%s\": %s", + directory, error.GetMessage()); + + return false; + } + + return true; +} + #endif // wxUSE_FILEDLG diff --git a/src/msw/dirdlg.cpp b/src/msw/dirdlg.cpp index eb6f588665..e307b9160b 100644 --- a/src/msw/dirdlg.cpp +++ b/src/msw/dirdlg.cpp @@ -352,7 +352,7 @@ void wxIFileDialog::SetTitle(const wxString& message) } } -void wxIFileDialog::SetInitialPath(const wxString& defaultPath) +HRESULT InitShellItemFromPath(wxCOMPtr& item, const wxString& path) { HRESULT hr; @@ -383,20 +383,52 @@ void wxIFileDialog::SetInitialPath(const wxString& defaultPath) if ( !s_pfnSHCreateItemFromParsingName ) { // There is nothing we can do and the error was already reported. - return; + return E_FAIL; + } + + // SHCreateItemFromParsingName() doesn't support slashes, so if the path + // uses them, replace them with the backslashes. + wxString pathBS; + const wxString* pathWithoutSlashes; + if ( path.find('/') != wxString::npos ) + { + pathBS = path; + pathBS.Replace("/", "\\", true); + + pathWithoutSlashes = &pathBS; + } + else // Just use the original path without copying. + { + pathWithoutSlashes = &path; } - wxCOMPtr folder; hr = s_pfnSHCreateItemFromParsingName ( - defaultPath.wc_str(), + pathWithoutSlashes->wc_str(), NULL, - wxIID_PPV_ARGS(IShellItem, &folder) + wxIID_PPV_ARGS(IShellItem, &item) ); + if ( FAILED(hr) ) + { + wxLogApiError + ( + wxString::Format(wxS("SHCreateItemFromParsingName(\"%s\")"), + *pathWithoutSlashes), + hr + ); + } - // Failing to parse the folder name or set it is not really an error, - // we'll just ignore the initial directory in this case, but we should - // still show the dialog. + return hr; +} + +void wxIFileDialog::SetInitialPath(const wxString& defaultPath) +{ + wxCOMPtr folder; + + HRESULT hr = InitShellItemFromPath(folder, defaultPath); + + // Failing to parse the folder name is not really an error, e.g. it might + // not exist, so we'll just ignore the initial directory in this case. if ( SUCCEEDED(hr) ) { hr = m_fileDialog->SetFolder(folder); @@ -405,6 +437,27 @@ void wxIFileDialog::SetInitialPath(const wxString& defaultPath) } } +void wxIFileDialog::AddPlace(const wxString& path, FDAP fdap) +{ + wxCOMPtr place; + + HRESULT hr = InitShellItemFromPath(place, path); + + // Don't bother with doing anything else if we couldn't parse the path + // (debug message about failing to do it was already logged). + if ( FAILED(hr) ) + return; + + hr = m_fileDialog->AddPlace(place, fdap); + if ( FAILED(hr) ) + { + wxLogApiError + ( + wxString::Format(wxS("IFileDialog::AddPlace(\"%s\")"), path), hr + ); + } +} + } // namespace wxMSWImpl // ---------------------------------------------------------------------------- diff --git a/src/msw/filedlg.cpp b/src/msw/filedlg.cpp index b0c2c900a9..446979523e 100644 --- a/src/msw/filedlg.cpp +++ b/src/msw/filedlg.cpp @@ -714,6 +714,20 @@ public: #if wxUSE_IFILEOPENDIALOG + // Store the extra shortcut directories and their flags. + struct ShortcutData + { + ShortcutData(const wxString& path_, int flags_) + : path(path_), flags(flags_) + { + } + + wxString path; + int flags; + }; + wxVector m_customShortcuts; + + // IUnknown wxSTDMETHODIMP QueryInterface(REFIID iid, void** ppv) @@ -1181,6 +1195,28 @@ void wxFileDialog::MSWOnInitDialogHook(WXHWND hwnd) CreateExtraControl(); } +bool wxFileDialog::AddShortcut(const wxString& directory, int flags) +{ +#if wxUSE_IFILEOPENDIALOG + if ( !HasExtraControlCreator() ) + { + MSWData().m_customShortcuts.push_back( + wxFileDialogMSWData::ShortcutData(directory, flags) + ); + + return true; + } + else + { + // It could be surprising if AddShortcut() silently didn't work, so + // warn the developer about this incompatibility. + wxFAIL_MSG("Can't use both AddShortcut() and SetExtraControlCreator()"); + } +#endif // wxUSE_IFILEOPENDIALOG + + return false; +} + int wxFileDialog::ShowModal() { WX_HOOK_MODAL_DIALOG(); @@ -1576,6 +1612,23 @@ int wxFileDialog::ShowIFileDialog(WXHWND hWndParent) } + for ( wxVector::const_iterator + it = data.m_customShortcuts.begin(); + it != data.m_customShortcuts.end(); + ++it ) + { + FDAP fdap = FDAP_BOTTOM; + if ( it->flags & wxFD_SHORTCUT_TOP ) + { + wxASSERT_MSG( !(it->flags & wxFD_SHORTCUT_BOTTOM), + wxS("Can't use both wxFD_SHORTCUT_TOP and BOTTOM") ); + + fdap = FDAP_TOP; + } + + fileDialog.AddPlace(it->path, fdap); + } + // We never set the following flags currently: // // - FOS_STRICTFILETYPES diff --git a/version-script.in b/version-script.in index a19de91391..4e1d9d9205 100644 --- a/version-script.in +++ b/version-script.in @@ -12,7 +12,9 @@ # # # public symbols added in release @WX_VERSION_TAG@.2 (please keep in alphabetical order): # @WX_VERSION_TAG@.2 { -# *wxChoice*GetCurrentSelection*; +# extern "C++" +# "wxChoice::GetCurrentSelection()"; +# }; # }; # # If a symbols should have been added in this way, but is forgotten then it @@ -21,6 +23,13 @@ # and once released its version cannot be changed. +# public symbols added in 3.2.1 (please keep in alphabetical order): +@WX_VERSION_TAG@.1 { + extern "C++" { + "wxFileDialog::AddShortcut(const wxString&, int)"; + }; +}; + # symbols available since the beginning of this branch are only given # generic branch tag (don't remove this!):