Merge branch 'custom-file-dialog-controls'
Implement support for custom file dialog controls in new style MSW dialogs. See #22476. Closes #14770.
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
#define wxHAS_MULTIPLE_FILEDLG_FILTERS
|
||||
#endif
|
||||
|
||||
class WXDLLIMPEXP_FWD_CORE wxFileDialogCustomizeHook;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// wxFileDialog data
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -127,6 +129,22 @@ public:
|
||||
virtual int GetCurrentlySelectedFilterIndex () const
|
||||
{ return m_currentlySelectedFilterIndex; }
|
||||
|
||||
|
||||
// A customize hook methods will be called by wxFileDialog later if this
|
||||
// function returns true, see its documentation for details.
|
||||
//
|
||||
// Note that the customizeHook object must remain alive at least until
|
||||
// ShowModal() returns.
|
||||
//
|
||||
// If this function returns false, it means that customizing the file
|
||||
// dialog is not supported on this platforms.
|
||||
virtual bool SetCustomizeHook(wxFileDialogCustomizeHook& customizeHook);
|
||||
|
||||
|
||||
// Extra controls support is deprecated now as it doesn't allow to use the
|
||||
// contemporary file dialogs under MSW, use wxFileDialogCustomize-based
|
||||
// API above instead in the new code.
|
||||
|
||||
// this function is called with wxFileDialog as parameter and should
|
||||
// create the window containing the extra controls we want to show in it
|
||||
typedef wxWindow *(*ExtraControlCreatorFunction)(wxWindow*);
|
||||
@@ -172,15 +190,17 @@ protected:
|
||||
// provide a useful implementation of GetCurrentlySelectedFilterIndex().
|
||||
int m_currentlySelectedFilterIndex;
|
||||
|
||||
wxFileDialogCustomizeHook* m_customizeHook;
|
||||
|
||||
wxWindow* m_extraControl;
|
||||
|
||||
// returns true if control is created (if it already exists returns false)
|
||||
// create and return the extra control using the given parent
|
||||
wxWindow* CreateExtraControlWithParent(wxWindow* parent) const;
|
||||
// returns true if control is created, also sets m_extraControl
|
||||
bool CreateExtraControl();
|
||||
// return true if SetExtraControlCreator() was called
|
||||
bool HasExtraControlCreator() const
|
||||
{ return m_extraControlCreator != NULL; }
|
||||
// get the size of the extra control by creating and deleting it
|
||||
wxSize GetExtraControlSize();
|
||||
// Helper function for native file dialog usage where no wx events
|
||||
// are processed.
|
||||
void UpdateExtraControlUI();
|
||||
|
||||
222
include/wx/filedlgcustomize.h
Normal file
222
include/wx/filedlgcustomize.h
Normal file
@@ -0,0 +1,222 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: wx/filedlgcustomize.h
|
||||
// Purpose: Classes for wxFileDialog customization.
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2022-05-26
|
||||
// Copyright: (c) 2022 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||
// Licence: wxWindows licence
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_FILEDLGCUSTOMIZE_H_
|
||||
#define _WX_FILEDLGCUSTOMIZE_H_
|
||||
|
||||
#include "wx/vector.h"
|
||||
|
||||
class wxFileDialogCustomControlImpl;
|
||||
class wxFileDialogButtonImpl;
|
||||
class wxFileDialogCheckBoxImpl;
|
||||
class wxFileDialogRadioButtonImpl;
|
||||
class wxFileDialogChoiceImpl;
|
||||
class wxFileDialogTextCtrlImpl;
|
||||
class wxFileDialogStaticTextImpl;
|
||||
class wxFileDialogCustomizeImpl;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxFileDialog custom controls
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// All these controls support a very limited set of functions, but use the same
|
||||
// names for the things that they do support as the corresponding "normal" wx
|
||||
// classes and also generate some (but not all) of the same events.
|
||||
|
||||
// The base class for all wxFileDialog custom controls.
|
||||
class WXDLLIMPEXP_CORE wxFileDialogCustomControl : public wxEvtHandler
|
||||
{
|
||||
public:
|
||||
void Show(bool show = true);
|
||||
void Hide() { Show(false); }
|
||||
|
||||
void Enable(bool enable = true);
|
||||
void Disable() { Enable(false); }
|
||||
|
||||
~wxFileDialogCustomControl();
|
||||
|
||||
protected:
|
||||
explicit wxFileDialogCustomControl(wxFileDialogCustomControlImpl* impl)
|
||||
: m_impl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
// By default custom controls don't generate any events, but some of them
|
||||
// override this function to allow connecting to the selected events.
|
||||
virtual bool OnDynamicBind(wxDynamicEventTableEntry& entry) wxOVERRIDE;
|
||||
|
||||
|
||||
wxFileDialogCustomControlImpl* const m_impl;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxFileDialogCustomControl);
|
||||
};
|
||||
|
||||
// A class representing a custom button.
|
||||
class WXDLLIMPEXP_CORE wxFileDialogButton : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
// Ctor is only used by wxWidgets itself.
|
||||
explicit wxFileDialogButton(wxFileDialogButtonImpl* impl);
|
||||
|
||||
protected:
|
||||
virtual bool OnDynamicBind(wxDynamicEventTableEntry& entry) wxOVERRIDE;
|
||||
|
||||
private:
|
||||
wxFileDialogButtonImpl* GetImpl() const;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxFileDialogButton);
|
||||
};
|
||||
|
||||
// A class representing a custom checkbox.
|
||||
class WXDLLIMPEXP_CORE wxFileDialogCheckBox : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
bool GetValue() const;
|
||||
void SetValue(bool value);
|
||||
|
||||
// Ctor is only used by wxWidgets itself.
|
||||
explicit wxFileDialogCheckBox(wxFileDialogCheckBoxImpl* impl);
|
||||
|
||||
protected:
|
||||
virtual bool OnDynamicBind(wxDynamicEventTableEntry& entry) wxOVERRIDE;
|
||||
|
||||
private:
|
||||
wxFileDialogCheckBoxImpl* GetImpl() const;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxFileDialogCheckBox);
|
||||
};
|
||||
|
||||
// A class representing a custom radio button.
|
||||
class WXDLLIMPEXP_CORE wxFileDialogRadioButton : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
bool GetValue() const;
|
||||
void SetValue(bool value);
|
||||
|
||||
// Ctor is only used by wxWidgets itself.
|
||||
explicit wxFileDialogRadioButton(wxFileDialogRadioButtonImpl* impl);
|
||||
|
||||
protected:
|
||||
virtual bool OnDynamicBind(wxDynamicEventTableEntry& entry) wxOVERRIDE;
|
||||
|
||||
private:
|
||||
wxFileDialogRadioButtonImpl* GetImpl() const;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxFileDialogRadioButton);
|
||||
};
|
||||
|
||||
// A class representing a custom combobox button.
|
||||
class WXDLLIMPEXP_CORE wxFileDialogChoice : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
int GetSelection() const;
|
||||
void SetSelection(int n);
|
||||
|
||||
// Ctor is only used by wxWidgets itself.
|
||||
explicit wxFileDialogChoice(wxFileDialogChoiceImpl* impl);
|
||||
|
||||
protected:
|
||||
virtual bool OnDynamicBind(wxDynamicEventTableEntry& entry) wxOVERRIDE;
|
||||
|
||||
private:
|
||||
wxFileDialogChoiceImpl* GetImpl() const;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxFileDialogChoice);
|
||||
};
|
||||
|
||||
// A class representing a custom text control.
|
||||
class WXDLLIMPEXP_CORE wxFileDialogTextCtrl : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
wxString GetValue() const;
|
||||
void SetValue(const wxString& text);
|
||||
|
||||
// Ctor is only used by wxWidgets itself.
|
||||
explicit wxFileDialogTextCtrl(wxFileDialogTextCtrlImpl* impl);
|
||||
|
||||
private:
|
||||
wxFileDialogTextCtrlImpl* GetImpl() const;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxFileDialogTextCtrl);
|
||||
};
|
||||
|
||||
// A class representing a custom static text.
|
||||
class WXDLLIMPEXP_CORE wxFileDialogStaticText : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
void SetLabelText(const wxString& text);
|
||||
|
||||
// Ctor is only used by wxWidgets itself.
|
||||
explicit wxFileDialogStaticText(wxFileDialogStaticTextImpl* impl);
|
||||
|
||||
private:
|
||||
wxFileDialogStaticTextImpl* GetImpl() const;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxFileDialogStaticText);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxFileDialogCustomizer is used by wxFileDialogCustomizeHook
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class WXDLLIMPEXP_CORE wxFileDialogCustomize
|
||||
{
|
||||
public:
|
||||
wxFileDialogButton* AddButton(const wxString& label);
|
||||
wxFileDialogCheckBox* AddCheckBox(const wxString& label);
|
||||
wxFileDialogRadioButton* AddRadioButton(const wxString& label);
|
||||
wxFileDialogChoice* AddChoice(size_t n, const wxString* strings);
|
||||
wxFileDialogTextCtrl* AddTextCtrl(const wxString& label = wxString());
|
||||
wxFileDialogStaticText* AddStaticText(const wxString& label);
|
||||
|
||||
~wxFileDialogCustomize();
|
||||
|
||||
protected:
|
||||
// Ctor is only used by wxWidgets itself.
|
||||
//
|
||||
// Note that we don't take ownership of the implementation pointer here,
|
||||
// see the comment in the dtor for more details.
|
||||
explicit wxFileDialogCustomize(wxFileDialogCustomizeImpl* impl)
|
||||
: m_impl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
wxVector<wxFileDialogCustomControl*> m_controls;
|
||||
|
||||
private:
|
||||
template <typename T> T* StoreAndReturn(T* control);
|
||||
|
||||
wxFileDialogCustomizeImpl* const m_impl;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxFileDialogCustomize);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxFileDialogCustomizeHook: used by wxFileDialog itself
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class WXDLLIMPEXP_CORE wxFileDialogCustomizeHook
|
||||
{
|
||||
public:
|
||||
// This method must be overridden to add custom controls to the dialog
|
||||
// using the provided customizer object.
|
||||
virtual void AddCustomControls(wxFileDialogCustomize& customizer) = 0;
|
||||
|
||||
// This method may be overridden to update the custom controls whenever
|
||||
// something changes in the dialog.
|
||||
virtual void UpdateCustomControls() { }
|
||||
|
||||
// This method should typically be overridden to save the values of the
|
||||
// custom controls when the dialog is accepted.
|
||||
virtual void TransferDataFromCustomControls() { }
|
||||
|
||||
virtual ~wxFileDialogCustomizeHook();
|
||||
};
|
||||
|
||||
#endif // _WX_FILEDLGCUSTOMIZE_H_
|
||||
@@ -11,6 +11,8 @@
|
||||
#ifndef _WX_FILEDLG_H_
|
||||
#define _WX_FILEDLG_H_
|
||||
|
||||
class wxFileDialogMSWData;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// wxFileDialog
|
||||
//-------------------------------------------------------------------------
|
||||
@@ -27,26 +29,14 @@ public:
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& sz = wxDefaultSize,
|
||||
const wxString& name = wxASCII_STR(wxFileDialogNameStr));
|
||||
virtual ~wxFileDialog();
|
||||
|
||||
virtual void GetPaths(wxArrayString& paths) const wxOVERRIDE;
|
||||
virtual void GetFilenames(wxArrayString& files) const wxOVERRIDE;
|
||||
virtual bool SupportsExtraControl() const wxOVERRIDE { return true; }
|
||||
void MSWOnInitDialogHook(WXHWND hwnd);
|
||||
|
||||
virtual int ShowModal() wxOVERRIDE;
|
||||
|
||||
// wxMSW-specific implementation from now on
|
||||
// -----------------------------------------
|
||||
|
||||
// called from the hook procedure on CDN_INITDONE reception
|
||||
virtual void MSWOnInitDone(WXHWND hDlg);
|
||||
|
||||
// called from the hook procedure on CDN_SELCHANGE.
|
||||
void MSWOnSelChange(WXHWND hDlg);
|
||||
|
||||
// called from the hook procedure on CDN_TYPECHANGE.
|
||||
void MSWOnTypeChange(WXHWND hDlg, int nFilterIndex);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void DoMoveWindow(int x, int y, int width, int height) wxOVERRIDE;
|
||||
@@ -55,12 +45,38 @@ protected:
|
||||
virtual void DoGetPosition( int *x, int *y ) const wxOVERRIDE;
|
||||
|
||||
private:
|
||||
// Allow it to call MSWOnXXX() functions below.
|
||||
friend class wxFileDialogMSWData;
|
||||
|
||||
// called when the dialog is created
|
||||
void MSWOnInitDialogHook(WXHWND hwnd);
|
||||
|
||||
// called when the dialog initialization is fully done
|
||||
void MSWOnInitDone(WXHWND hDlg);
|
||||
|
||||
// called when the currently selected file changes in the dialog
|
||||
void MSWOnSelChange(const wxString& selectedFilename);
|
||||
|
||||
// called when the currently selected type of files changes in the dialog
|
||||
void MSWOnTypeChange(int nFilterIndex);
|
||||
|
||||
// The real implementation of ShowModal() using traditional common dialog
|
||||
// functions.
|
||||
int ShowCommFileDialog(WXHWND owner);
|
||||
|
||||
// And another one using IFileDialog.
|
||||
int ShowIFileDialog(WXHWND owner);
|
||||
|
||||
// Get the data object, allocating it if necessary.
|
||||
wxFileDialogMSWData& MSWData();
|
||||
|
||||
|
||||
wxArrayString m_fileNames;
|
||||
|
||||
// remember if our SetPosition() or Centre() (which requires special
|
||||
// treatment) was called
|
||||
bool m_bMovedWindow;
|
||||
int m_centreDir; // nothing to do if 0
|
||||
// Extra data, possibly null if not needed, use MSWData() to access it if
|
||||
// it should be created on demand.
|
||||
wxFileDialogMSWData* m_data;
|
||||
|
||||
|
||||
wxDECLARE_DYNAMIC_CLASS(wxFileDialog);
|
||||
wxDECLARE_NO_COPY_CLASS(wxFileDialog);
|
||||
|
||||
@@ -39,6 +39,9 @@ extern WXDLLIMPEXP_CORE bool IsIidFromList(REFIID riid, const IID *aIids[], size
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
WARNING: This does NOT work with multiple inheritance, so multiple
|
||||
interfaces can only be supported when they inherit from each other.
|
||||
|
||||
The most dumb implementation of IUnknown methods. We don't support
|
||||
aggregation nor containment, but for 99% of cases this simple
|
||||
implementation is quite enough.
|
||||
|
||||
83
include/wx/msw/private/filedialog.h
Normal file
83
include/wx/msw/private/filedialog.h
Normal file
@@ -0,0 +1,83 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: wx/msw/private/filedialog.h
|
||||
// Purpose: IFileDialog-related functions
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2022-05-15
|
||||
// Copyright: (c) 2022 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||
// Licence: wxWindows licence
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_MSW_PRIVATE_FILEDIALOG_H_
|
||||
#define _WX_MSW_PRIVATE_FILEDIALOG_H_
|
||||
|
||||
#include "wx/msw/private.h"
|
||||
#include "wx/msw/wrapshl.h"
|
||||
|
||||
// We want to use IFileDialog if either wxDirDialog or wxFileDialog are used.
|
||||
//
|
||||
// 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.
|
||||
// And it needs OLE support to actually use these interfaces.
|
||||
#if (wxUSE_DIRDLG || wxUSE_FILEDLG) && wxUSE_DYNLIB_CLASS && wxUSE_OLE && \
|
||||
defined(__IFileOpenDialog_INTERFACE_DEFINED__)
|
||||
#define wxUSE_IFILEOPENDIALOG 1
|
||||
#else
|
||||
#define wxUSE_IFILEOPENDIALOG 0
|
||||
#endif
|
||||
|
||||
#if wxUSE_IFILEOPENDIALOG
|
||||
|
||||
#include "wx/msw/private/comptr.h"
|
||||
|
||||
namespace wxMSWImpl
|
||||
{
|
||||
|
||||
// For historical reasons, this class is defined in src/msw/dirdlg.cpp.
|
||||
class wxIFileDialog
|
||||
{
|
||||
public:
|
||||
// Create the dialog of the specified type.
|
||||
//
|
||||
// CLSID must be either CLSID_FileOpenDialog or CLSID_FileSaveDialog.
|
||||
//
|
||||
// Use IsOk() to check if the dialog was created successfully.
|
||||
explicit wxIFileDialog(const CLSID& clsid);
|
||||
|
||||
// If this returns false, the dialog can't be used at all.
|
||||
bool IsOk() const { return m_fileDialog.Get() != NULL; }
|
||||
|
||||
// Set the dialog title.
|
||||
void SetTitle(const wxString& title);
|
||||
|
||||
// Set the initial path to show in the dialog.
|
||||
void SetInitialPath(const wxString& path);
|
||||
|
||||
// Show the file dialog with the given parent window and options.
|
||||
//
|
||||
// Returns the selected path, or paths, in the provided output parameters,
|
||||
// depending on whether FOS_ALLOWMULTISELECT is part of the options.
|
||||
//
|
||||
// The return value is wxID_OK if any paths were returned, wxID_CANCEL if the
|
||||
// dialog was cancelled.
|
||||
int
|
||||
Show(HWND owner, int options, wxArrayString* pathsOut, wxString* pathOut);
|
||||
|
||||
// Behave as IFileDialog.
|
||||
IFileDialog* Get() const { return m_fileDialog.Get(); }
|
||||
IFileDialog* operator->() const { return m_fileDialog.Get(); }
|
||||
|
||||
private:
|
||||
wxCOMPtr<IFileDialog> m_fileDialog;
|
||||
};
|
||||
|
||||
// Extract the filesystem path corresponding to the given shell item.
|
||||
HRESULT GetFSPathFromShellItem(const wxCOMPtr<IShellItem>& item, wxString& path);
|
||||
|
||||
} // namespace wxMSWImpl
|
||||
|
||||
#endif // wxUSE_IFILEOPENDIALOG
|
||||
|
||||
#endif // _WX_MSW_PRIVATE_FILEDIALOG_H_
|
||||
85
include/wx/private/filedlgcustomize.h
Normal file
85
include/wx/private/filedlgcustomize.h
Normal file
@@ -0,0 +1,85 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: wx/private/filedlgcustomize.h
|
||||
// Purpose: Private helpers used for wxFileDialog customization
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2022-05-26
|
||||
// Copyright: (c) 2022 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||
// Licence: wxWindows licence
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_PRIVATE_FILEDLGCUSTOMIZE_H_
|
||||
#define _WX_PRIVATE_FILEDLGCUSTOMIZE_H_
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxFileDialogCustomControlImpl: interface for all custom controls
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class wxFileDialogCustomControlImpl
|
||||
{
|
||||
public:
|
||||
virtual void Show(bool show) = 0;
|
||||
virtual void Enable(bool enable) = 0;
|
||||
|
||||
virtual bool DoBind(wxEvtHandler* handler);
|
||||
|
||||
virtual ~wxFileDialogCustomControlImpl();
|
||||
};
|
||||
|
||||
// This class is defined for symmetry with the other ones, but there are no
|
||||
// button-specific methods so far.
|
||||
class wxFileDialogButtonImpl : public wxFileDialogCustomControlImpl
|
||||
{
|
||||
};
|
||||
|
||||
class wxFileDialogCheckBoxImpl : public wxFileDialogCustomControlImpl
|
||||
{
|
||||
public:
|
||||
virtual bool GetValue() = 0;
|
||||
virtual void SetValue(bool value) = 0;
|
||||
};
|
||||
|
||||
class wxFileDialogRadioButtonImpl : public wxFileDialogCustomControlImpl
|
||||
{
|
||||
public:
|
||||
virtual bool GetValue() = 0;
|
||||
virtual void SetValue(bool value) = 0;
|
||||
};
|
||||
|
||||
class wxFileDialogChoiceImpl : public wxFileDialogCustomControlImpl
|
||||
{
|
||||
public:
|
||||
virtual int GetSelection() = 0;
|
||||
virtual void SetSelection(int n) = 0;
|
||||
};
|
||||
|
||||
class wxFileDialogTextCtrlImpl : public wxFileDialogCustomControlImpl
|
||||
{
|
||||
public:
|
||||
virtual wxString GetValue() = 0;
|
||||
virtual void SetValue(const wxString& value) = 0;
|
||||
};
|
||||
|
||||
class wxFileDialogStaticTextImpl : public wxFileDialogCustomControlImpl
|
||||
{
|
||||
public:
|
||||
virtual void SetLabelText(const wxString& text) = 0;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxFileDialogCustomizeImpl: interface for actual customizers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class wxFileDialogCustomizeImpl
|
||||
{
|
||||
public:
|
||||
virtual wxFileDialogButtonImpl* AddButton(const wxString& label) = 0;
|
||||
virtual wxFileDialogCheckBoxImpl* AddCheckBox(const wxString& label) = 0;
|
||||
virtual wxFileDialogRadioButtonImpl* AddRadioButton(const wxString& label) = 0;
|
||||
virtual wxFileDialogChoiceImpl* AddChoice(size_t n, const wxString* strings) = 0;
|
||||
virtual wxFileDialogTextCtrlImpl* AddTextCtrl(const wxString& label) = 0;
|
||||
virtual wxFileDialogStaticTextImpl* AddStaticText(const wxString& label) = 0;
|
||||
|
||||
virtual ~wxFileDialogCustomizeImpl();
|
||||
};
|
||||
|
||||
#endif // _WX_PRIVATE_FILEDLGCUSTOMIZE_H_
|
||||
@@ -94,7 +94,9 @@ const char wxFileSelectorDefaultWildcardStr[];
|
||||
}
|
||||
@endcode
|
||||
|
||||
@remarks
|
||||
|
||||
@section filedialog_filters Wildcard Filters
|
||||
|
||||
All implementations of the wxFileDialog provide a wildcard filter. Typing a filename
|
||||
containing wildcards (*, ?) in the filename text item, and clicking on Ok, will
|
||||
result in only those files matching the pattern being displayed.
|
||||
@@ -103,13 +105,9 @@ const char wxFileSelectorDefaultWildcardStr[];
|
||||
@code
|
||||
"BMP and GIF files (*.bmp;*.gif)|*.bmp;*.gif|PNG files (*.png)|*.png"
|
||||
@endcode
|
||||
It must be noted that wildcard support in the native Motif file dialog is quite
|
||||
limited: only one file type is supported, and it is displayed without the
|
||||
descriptive test; "BMP files (*.bmp)|*.bmp" is displayed as "*.bmp", and both
|
||||
"BMP files (*.bmp)|*.bmp|GIF files (*.gif)|*.gif" and "Image files|*.bmp;*.gif"
|
||||
are errors.
|
||||
|
||||
On Mac macOS in the open file dialog the filter choice box is not shown by default.
|
||||
Instead all given wildcards are appplied at the same time: So in the above
|
||||
Instead all given wildcards are applied at the same time: So in the above
|
||||
example all bmp, gif and png files are displayed. To enforce the
|
||||
display of the filter choice set the corresponding wxSystemOptions before calling
|
||||
the file open dialog:
|
||||
@@ -121,6 +119,33 @@ const char wxFileSelectorDefaultWildcardStr[];
|
||||
matching all file types. The files which does not match the currently selected
|
||||
file type are greyed out and are not selectable.
|
||||
|
||||
|
||||
@section filedialog_customize Dialog Customization
|
||||
|
||||
Uniquely among the other standard dialogs, wxFileDialog can be customized
|
||||
by adding extra controls to it. Moreover, there are two ways to do it: the
|
||||
first one is to define a callback function and use SetExtraControlCreator()
|
||||
to tell the dialog to call it, while the second one requires defining a
|
||||
class inheriting from wxFileDialogCustomizeHook and implementing its
|
||||
virtual functions, notably wxFileDialogCustomizeHook::AddCustomControls()
|
||||
where the extra controls have to be created, and finally calling
|
||||
SetCustomizeHook() with this custom hook object.
|
||||
|
||||
The first approach is somewhat simpler and more flexible, as it allows to
|
||||
create any kind of custom controls, but is not supported by the "new style"
|
||||
(where "new" means used since Windows Vista, i.e. circa 2007) file dialogs
|
||||
under MSW. Because of this, calling SetExtraControlCreator() in wxMSW
|
||||
forces the use of old style (Windows XP) dialogs, that may look out of
|
||||
place. The second approach is implemented by the MSW dialogs natively and
|
||||
doesn't suffer from this limitation, so its use is recommended, especially
|
||||
if the few simple control types supported by it (see wxFileDialogCustomize
|
||||
for more information about the supported controls) are sufficient for your
|
||||
needs.
|
||||
|
||||
Both of the approaches to the dialog customization are demonstrated in the
|
||||
@ref page_samples_dialogs, please check it for more details.
|
||||
|
||||
|
||||
@beginStyleTable
|
||||
@style{wxFD_DEFAULT_STYLE}
|
||||
Equivalent to @c wxFD_OPEN.
|
||||
@@ -322,6 +347,27 @@ public:
|
||||
*/
|
||||
virtual wxString GetWildcard() const;
|
||||
|
||||
/**
|
||||
Set the hook to be used for customizing the dialog contents.
|
||||
|
||||
This function can be called before calling ShowModal() to specify that
|
||||
the dialog contents should be customized using the provided hook. See
|
||||
wxFileDialogCustomizeHook documentation and @ref page_samples_dialogs
|
||||
for the examples of using it.
|
||||
|
||||
@note In order to define a custom hook object, @c wx/filedlgcustomize.h
|
||||
must be included in addition to the usual @c wx/filedlg.h header.
|
||||
|
||||
@param customizeHook The hook object that will be used by the dialog.
|
||||
This object must remain valid at least until ShowModal() returns.
|
||||
|
||||
@return @true if the hook was successfully set or @false if customizing
|
||||
the file dialog is not supported by the current platform.
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
bool SetCustomizeHook(wxFileDialogCustomizeHook& customizeHook);
|
||||
|
||||
/**
|
||||
Sets the default directory.
|
||||
*/
|
||||
@@ -343,6 +389,11 @@ public:
|
||||
The @c creator function should take pointer to parent window (file dialog)
|
||||
and should return a window allocated with operator new.
|
||||
|
||||
@note Using SetExtraControlCreator() in wxMSW forces the use of "old
|
||||
style" (Windows XP-like) file dialogs, instead of the newer
|
||||
(Vista-like) ones and is not recommended for this reason. Prefer to
|
||||
use SetCustomizeHook() instead.
|
||||
|
||||
@since 2.9.0
|
||||
*/
|
||||
bool SetExtraControlCreator(ExtraControlCreatorFunction creator);
|
||||
|
||||
397
interface/wx/filedlgcustomize.h
Normal file
397
interface/wx/filedlgcustomize.h
Normal file
@@ -0,0 +1,397 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: interface/wx/filedlgcustomize.h
|
||||
// Purpose: Documentation of classes used for wxFileDialog customization.
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2022-06-03
|
||||
// Copyright: (c) 2022 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||
// Licence: wxWindows licence
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
The base class for all wxFileDialog custom controls.
|
||||
|
||||
Unlike normal controls, inheriting from wxWindow, custom controls in
|
||||
wxFileDialog are not actual windows, but they do provide some window-like
|
||||
operations, and can be disabled or hidden, just as the windows are.
|
||||
|
||||
Also, similarly to the normal windows, objects of this and derived classes
|
||||
belong to wxWidgets and must @e not be deleted by the application code.
|
||||
|
||||
Unlike windows, custom controls cannot be created directly, but can only be
|
||||
returned from wxFileDialogCustomize functions that are specifically
|
||||
provided for creating them.
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
class wxFileDialogCustomControl : public wxEvtHandler
|
||||
{
|
||||
public:
|
||||
/// Show or hide this control.
|
||||
void Show(bool show = true);
|
||||
|
||||
/// Hide this control.
|
||||
///
|
||||
/// This is equivalent to @c Show(false).
|
||||
void Hide();
|
||||
|
||||
/// Enable or disable this control.
|
||||
void Enable(bool enable = true);
|
||||
|
||||
/// Disable this control.
|
||||
///
|
||||
/// This is equivalent to @c Enable(false).
|
||||
void Disable();
|
||||
};
|
||||
|
||||
/**
|
||||
Represents a custom button inside wxFileDialog.
|
||||
|
||||
Objects of this class can only be created by
|
||||
wxFileDialogCustomize::AddButton().
|
||||
|
||||
It is possible to bind to wxEVT_BUTTON events on this object, which will be
|
||||
generated when the button is clicked.
|
||||
|
||||
See wxFileDialogCustomControl for more information.
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
class wxFileDialogButton : public wxFileDialogCustomControl
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
Represents a custom checkbox inside wxFileDialog.
|
||||
|
||||
Objects of this class can only be created by
|
||||
wxFileDialogCustomize::AddCheckBox().
|
||||
|
||||
It is possible to bind to wxEVT_CHECKBOX events on this object, which will
|
||||
be generated when the checkbox is clicked.
|
||||
|
||||
See wxFileDialogCustomControl for more information.
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
class wxFileDialogCheckBox : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
/// Return @true if the checkbox is checked.
|
||||
bool GetValue() const;
|
||||
|
||||
/// Check or uncheck the checkbox.
|
||||
void SetValue(bool value);
|
||||
};
|
||||
|
||||
/**
|
||||
Represents a custom radio button inside wxFileDialog.
|
||||
|
||||
Objects of this class can only be created by
|
||||
wxFileDialogCustomize::AddRadioButton().
|
||||
|
||||
It is possible to bind to wxEVT_RADIOBUTTON events on this object, which
|
||||
will be generated when the radio button is selected.
|
||||
|
||||
See wxFileDialogCustomControl for more information.
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
class wxFileDialogRadioButton : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
/// Return @true if the radio button is selected.
|
||||
bool GetValue() const;
|
||||
|
||||
/// Select the value of the radio button.
|
||||
///
|
||||
/// Using @false for @a value is not supported, this argument only exists
|
||||
/// for consistency with wxRadioButton::SetValue().
|
||||
void SetValue(bool value);
|
||||
};
|
||||
|
||||
/**
|
||||
Represents a custom read-only combobox inside wxFileDialog.
|
||||
|
||||
Objects of this class can only be created by
|
||||
wxFileDialogCustomize::AddChoice().
|
||||
|
||||
It is possible to bind to wxEVT_CHOICE events on this object, which
|
||||
will be generated when the selection in the combobox changes.
|
||||
|
||||
See wxFileDialogCustomControl for more information.
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
class wxFileDialogChoice : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
/// Return the index of the selected item, possibly wxNOT_FOUND.
|
||||
int GetSelection() const;
|
||||
|
||||
/// Set the selection to the item with the given index.
|
||||
///
|
||||
/// Using @c wxNOT_FOUND for @a n is not supported, once a selection is
|
||||
/// made it cannot be undone.
|
||||
void SetSelection(int n);
|
||||
};
|
||||
|
||||
/**
|
||||
Represents a custom text control inside wxFileDialog.
|
||||
|
||||
Objects of this class can only be created by
|
||||
wxFileDialogCustomize::AddTextCtrl().
|
||||
|
||||
Objects of this class don't generate any events currently.
|
||||
|
||||
See wxFileDialogCustomControl for more information.
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
class wxFileDialogTextCtrl : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
/// Get the current value entered into the control.
|
||||
wxString GetValue() const;
|
||||
|
||||
/// Set the current control value.
|
||||
void SetValue(const wxString& text);
|
||||
};
|
||||
|
||||
/**
|
||||
Represents a custom static text inside wxFileDialog.
|
||||
|
||||
Objects of this class can only be created by
|
||||
wxFileDialogCustomize::AddStaticText().
|
||||
|
||||
Objects of this class don't generate any events.
|
||||
|
||||
See wxFileDialogCustomControl for more information.
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
class wxFileDialogStaticText : public wxFileDialogCustomControl
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Set the text shown in the label.
|
||||
|
||||
Any ampersands in the @a text will be escaped, there is no need to do
|
||||
it manually, e.g. using wxControl::EscapeMnemonics().
|
||||
*/
|
||||
void SetLabelText(const wxString& text);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Used with wxFileDialogCustomizeHook to add custom controls to wxFileDialog.
|
||||
|
||||
An object of this class is passed to wxFileDialogCustomizeHook::AddCustomControls()
|
||||
to allow it to actually add controls to the dialog.
|
||||
|
||||
The pointers returned by the functions of this class belong to wxWidgets
|
||||
and should @e not be deleted by the application, just as wxWindow-derived
|
||||
objects (even if these controls do not inherit from wxWindow). These
|
||||
pointers become invalid when wxFileDialog::ShowModal() returns, and the
|
||||
dialog containing them is destroyed, and the latest point at which they can
|
||||
be still used is when wxFileDialogCustomizeHook::TransferDataFromCustomControls()
|
||||
is called.
|
||||
|
||||
@library{wxcore}
|
||||
@category{cmndlg}
|
||||
|
||||
@see wxFileDialog
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
class wxFileDialogCustomize
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Add a button with the specified label.
|
||||
*/
|
||||
wxFileDialogButton* AddButton(const wxString& label);
|
||||
|
||||
/**
|
||||
Add a checkbox with the specified label.
|
||||
*/
|
||||
wxFileDialogCheckBox* AddCheckBox(const wxString& label);
|
||||
|
||||
/**
|
||||
Add a radio button with the specified label.
|
||||
|
||||
The first radio button added will be initially checked. All the radio
|
||||
buttons added immediately after it will become part of the same radio
|
||||
group and will not be checked, but checking any one of them later will
|
||||
uncheck the first button and all the other ones.
|
||||
|
||||
If two consecutive but distinct radio groups are required,
|
||||
AddStaticText() with an empty label can be used to separate them.
|
||||
*/
|
||||
wxFileDialogRadioButton* AddRadioButton(const wxString& label);
|
||||
|
||||
/**
|
||||
Add a read-only combobox with the specified contents.
|
||||
|
||||
The combobox doesn't have any initial selection, i.e.
|
||||
wxFileDialogChoice::GetSelection() returns @c wxNOT_FOUND, if some item
|
||||
must be selected, use wxFileDialogChoice::SetSelection() explicitly to
|
||||
do it.
|
||||
|
||||
@param n The number of strings, must be positive, as there is no way to
|
||||
add more strings later and creating an empty combobox is not very
|
||||
useful.
|
||||
@param strings A non-@NULL pointer to an array of @a n strings.
|
||||
*/
|
||||
wxFileDialogChoice* AddChoice(size_t n, const wxString* strings);
|
||||
|
||||
/**
|
||||
Add a text control with an optional label preceding it.
|
||||
|
||||
Unlike all the other functions for adding controls, the @a label
|
||||
parameter here doesn't specify the contents of the text control itself,
|
||||
but rather the label appearing before it. Unlike static controls added
|
||||
by AddStaticText(), this label is guaranteed to be immediately adjacent
|
||||
to it.
|
||||
|
||||
If @a label is empty, no label is created.
|
||||
*/
|
||||
wxFileDialogTextCtrl* AddTextCtrl(const wxString& label = wxString());
|
||||
|
||||
/**
|
||||
Add a static text with the given contents.
|
||||
|
||||
The contents of the static text can be updated later, i.e. it doesn't
|
||||
need to be actually static.
|
||||
*/
|
||||
wxFileDialogStaticText* AddStaticText(const wxString& label);
|
||||
};
|
||||
|
||||
/**
|
||||
Base class for customization hooks used with wxFileDialog.
|
||||
|
||||
wxFileDialogCustomizeHook is an abstract base class, i.e. in order to use a
|
||||
concrete class inheriting from it and implementing its pure virtual
|
||||
AddCustomControls() function must be defined. Then an object of this class
|
||||
should be passed to wxFileDialog::SetCustomizeHook(), which will result in
|
||||
its AddCustomControls() being called before the dialog is shown,
|
||||
UpdateCustomControls() being called whenever something changes in the
|
||||
dialog while it is shown and, finally, TransferDataFromCustomControls()
|
||||
being called when the user accepts their choice in the dialog.
|
||||
|
||||
Putting all this together, here is an example of customizing the file
|
||||
dialog using this class:
|
||||
@code
|
||||
class EncryptHook : public wxFileDialogCustomizeHook
|
||||
{
|
||||
public:
|
||||
// Override to add custom controls using the provided customizer object.
|
||||
void AddCustomControls(wxFileDialogCustomize& customizer) override
|
||||
{
|
||||
// Suppose we can encrypt files when saving them.
|
||||
m_checkbox = customizer.AddCheckBox("Encrypt");
|
||||
|
||||
// While m_checkbox is not a wxCheckBox, it looks almost like one
|
||||
// and, in particular, we can bind to custom control events as usual.
|
||||
m_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) {
|
||||
// We can also call wxWindow-like functions on them.
|
||||
m_button->Enable(event.IsChecked());
|
||||
});
|
||||
|
||||
// The encryption parameters can be edited in a dedicated dialog.
|
||||
m_button = customizer.AddButton("Parameters...");
|
||||
m_button->Bind(wxEVT_BUTTON, [](wxCommandEvent&) {
|
||||
... show the encryption parameters dialog here ...
|
||||
});
|
||||
}
|
||||
|
||||
// Override to save the values of the custom controls.
|
||||
void TransferDataFromCustomControls() override
|
||||
{
|
||||
// Save the checkbox value, as we won't be able to use it any more
|
||||
// once this function returns.
|
||||
m_encrypt = m_checkbox->GetValue();
|
||||
}
|
||||
|
||||
// Just a simple accessor to get the results.
|
||||
bool Encrypt() const { return m_encrypt; }
|
||||
|
||||
private:
|
||||
wxFileDialogButton* m_button;
|
||||
wxFileDialogCheckBox* m_checkbox;
|
||||
|
||||
bool m_encrypt = false;
|
||||
};
|
||||
|
||||
void SomeFunc()
|
||||
{
|
||||
wxFileDialog dialog(NULL, "Save document", wxString(), "file.my",
|
||||
"My files (*.my)|*.my",
|
||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
|
||||
// This object may be destroyed before the dialog, but must remain
|
||||
// alive until ShowModal() returns.
|
||||
EncryptHook customizeHook;
|
||||
|
||||
if ( dialog.ShowModal() == wxID_OK )
|
||||
{
|
||||
if ( customizeHook.Encrypt() )
|
||||
... save with encryption ...
|
||||
else
|
||||
... save without encryption ...
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
|
||||
@library{wxcore}
|
||||
@category{cmndlg}
|
||||
|
||||
@see wxFileDialog
|
||||
|
||||
@since 3.1.7
|
||||
*/
|
||||
class wxFileDialogCustomizeHook
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Must be overridden to add custom controls to the dialog using the
|
||||
provided customizer object.
|
||||
|
||||
Call wxFileDialogCustomize functions to add controls and possibly bind
|
||||
to their events.
|
||||
|
||||
Note that there is no possibility to define the custom controls layout,
|
||||
they will appear more or less consecutively, but the exact layout is
|
||||
determined by the current platform.
|
||||
*/
|
||||
virtual void AddCustomControls(wxFileDialogCustomize& customizer) = 0;
|
||||
|
||||
/**
|
||||
May be overridden to update the custom controls whenever something
|
||||
changes in the dialog.
|
||||
|
||||
This function is called when the user selects a file, changes the
|
||||
directory or changes the current filter in the dialog, for example.
|
||||
It can be used to update the custom controls state depending on the
|
||||
currently selected file, for example.
|
||||
|
||||
Note that it is @e not necessarily called when the value of a custom
|
||||
control changes.
|
||||
|
||||
Base class version does nothing.
|
||||
*/
|
||||
virtual void UpdateCustomControls();
|
||||
|
||||
/**
|
||||
Should typically be overridden to save the values of the custom
|
||||
controls when the dialog is accepted.
|
||||
|
||||
Custom controls are destroyed and cannot be used any longer once
|
||||
wxFileDialog::ShowModal() returns, so their values must be retrieved in
|
||||
this function, which is called just before this happens.
|
||||
|
||||
This function is @e not called if the user cancels the dialog.
|
||||
|
||||
Base class version does nothing.
|
||||
*/
|
||||
virtual void TransferDataFromCustomControls();
|
||||
};
|
||||
@@ -87,6 +87,7 @@
|
||||
|
||||
#if wxUSE_FILEDLG
|
||||
#include "wx/filedlg.h"
|
||||
#include "wx/filedlgcustomize.h"
|
||||
#endif // wxUSE_FILEDLG
|
||||
|
||||
#if wxUSE_DIRDLG
|
||||
@@ -507,6 +508,17 @@ bool MyApp::OnInit()
|
||||
filedlg_menu->Append(DIALOGS_FILE_SAVE_GENERIC, "Sa&ve file (generic)");
|
||||
#endif // USE_FILEDLG_GENERIC
|
||||
|
||||
filedlg_menu->AppendSeparator();
|
||||
filedlg_menu->AppendRadioItem(
|
||||
DIALOGS_FILE_USE_CUSTOMIZER,
|
||||
"Use new customization API",
|
||||
"Use wxFileDialog::SetCustomizeHook() for file dialog customization"
|
||||
);
|
||||
filedlg_menu->AppendRadioItem(
|
||||
DIALOGS_FILE_USE_EXTRA_CONTROL_CREATOR,
|
||||
"Use old customization API",
|
||||
"Use wxFileDialog::SetExtraControlCreator() for file dialog customization"
|
||||
);
|
||||
#ifdef __WXOSX_COCOA__
|
||||
filedlg_menu->AppendSeparator();
|
||||
filedlg_menu->AppendCheckItem(DIALOGS_MAC_TOGGLE_ALWAYS_SHOW_TYPES,
|
||||
@@ -1562,6 +1574,42 @@ void MyFrame::AddRemove(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
#if wxUSE_FILEDLG
|
||||
|
||||
// Simple function showing the current wxFileDialog state.
|
||||
wxString GetFileDialogStateDescription(wxFileDialogBase* dialog)
|
||||
{
|
||||
const wxString fn = dialog->GetCurrentlySelectedFilename();
|
||||
|
||||
wxString msg;
|
||||
if ( fn.empty() )
|
||||
msg = "Nothing";
|
||||
else if ( wxFileName::FileExists(fn) )
|
||||
msg = "File";
|
||||
else if ( wxFileName::DirExists(fn) )
|
||||
msg = "Directory";
|
||||
else
|
||||
msg = "Something else";
|
||||
|
||||
msg += " selected";
|
||||
|
||||
const int filter = dialog->GetCurrentlySelectedFilterIndex();
|
||||
if ( filter != wxNOT_FOUND )
|
||||
msg += wxString::Format(" (filter=%d)", filter);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Another helper translating demo combobox selection.
|
||||
wxString GetFileDialogPaperSize(int selection)
|
||||
{
|
||||
switch ( selection )
|
||||
{
|
||||
case -1: return "<none>";
|
||||
case 0: return "A4";
|
||||
case 1: return "Letter";
|
||||
default: return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
// panel with custom controls for file dialog
|
||||
class MyExtraPanel : public wxPanel
|
||||
{
|
||||
@@ -1569,7 +1617,8 @@ public:
|
||||
MyExtraPanel(wxWindow *parent);
|
||||
wxString GetInfo() const
|
||||
{
|
||||
return wxString::Format("checkbox=%d, text=\"%s\"", m_checked, m_str);
|
||||
return wxString::Format("paper=%s (%s), enabled=%d, text=\"%s\"",
|
||||
m_paperSize, m_paperOrient, m_checked, m_str);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1579,6 +1628,21 @@ private:
|
||||
m_btn->Enable(m_checked);
|
||||
}
|
||||
|
||||
void OnRadioButton(wxCommandEvent& event)
|
||||
{
|
||||
if ( event.GetEventObject() == m_radioPortrait )
|
||||
m_paperOrient = "portrait";
|
||||
else if ( event.GetEventObject() == m_radioLandscape )
|
||||
m_paperOrient = "landscape";
|
||||
else
|
||||
m_paperOrient = "unknown";
|
||||
}
|
||||
|
||||
void OnChoice(wxCommandEvent& event)
|
||||
{
|
||||
m_paperSize = GetFileDialogPaperSize(event.GetSelection());
|
||||
}
|
||||
|
||||
void OnText(wxCommandEvent& event)
|
||||
{
|
||||
m_str = event.GetString();
|
||||
@@ -1590,32 +1654,19 @@ private:
|
||||
// wxGenericFileDialog, so we need to cast to the base class. In a
|
||||
// typical application, we would cast to just wxFileDialog instead.
|
||||
wxFileDialogBase* const dialog = wxStaticCast(GetParent(), wxFileDialogBase);
|
||||
const wxString fn = dialog->GetCurrentlySelectedFilename();
|
||||
|
||||
wxString msg;
|
||||
if ( fn.empty() )
|
||||
msg = "Nothing";
|
||||
else if ( wxFileName::FileExists(fn) )
|
||||
msg = "File";
|
||||
else if ( wxFileName::DirExists(fn) )
|
||||
msg = "Directory";
|
||||
else
|
||||
msg = "Something else";
|
||||
|
||||
msg += " selected";
|
||||
|
||||
const int filter = dialog->GetCurrentlySelectedFilterIndex();
|
||||
if ( filter != wxNOT_FOUND )
|
||||
msg += wxString::Format(" (filter=%d)", filter);
|
||||
|
||||
event.SetText(msg);
|
||||
event.SetText(GetFileDialogStateDescription(dialog));
|
||||
}
|
||||
|
||||
wxString m_str;
|
||||
bool m_checked;
|
||||
wxString m_paperSize;
|
||||
wxString m_paperOrient;
|
||||
|
||||
wxButton *m_btn;
|
||||
wxCheckBox *m_cb;
|
||||
wxRadioButton *m_radioPortrait;
|
||||
wxRadioButton *m_radioLandscape;
|
||||
wxStaticText *m_label;
|
||||
wxTextCtrl *m_text;
|
||||
};
|
||||
@@ -1629,6 +1680,15 @@ MyExtraPanel::MyExtraPanel(wxWindow *parent)
|
||||
m_btn->Enable(false);
|
||||
m_cb = new wxCheckBox(this, -1, "Enable Custom Button");
|
||||
m_cb->Bind(wxEVT_CHECKBOX, &MyExtraPanel::OnCheckBox, this);
|
||||
wxChoice* choiceSize = new wxChoice(this, wxID_ANY);
|
||||
choiceSize->Append("A4");
|
||||
choiceSize->Append("Letter");
|
||||
choiceSize->Bind(wxEVT_CHOICE, &MyExtraPanel::OnChoice, this);
|
||||
m_radioPortrait = new wxRadioButton(this, wxID_ANY, "&Portrait",
|
||||
wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
|
||||
m_radioPortrait->Bind(wxEVT_RADIOBUTTON, &MyExtraPanel::OnRadioButton, this);
|
||||
m_radioLandscape = new wxRadioButton(this, wxID_ANY, "&Landscape");
|
||||
m_radioLandscape->Bind(wxEVT_RADIOBUTTON, &MyExtraPanel::OnRadioButton, this);
|
||||
m_label = new wxStaticText(this, wxID_ANY, "Nothing selected");
|
||||
m_label->Bind(wxEVT_UPDATE_UI, &MyExtraPanel::OnUpdateLabelUI, this);
|
||||
|
||||
@@ -1641,6 +1701,9 @@ MyExtraPanel::MyExtraPanel(wxWindow *parent)
|
||||
wxSizerFlags().Centre().Border());
|
||||
sizerTop->Add(m_text, wxSizerFlags(1).Centre().Border());
|
||||
sizerTop->AddSpacer(10);
|
||||
sizerTop->Add(choiceSize, wxSizerFlags().Centre().Border(wxRIGHT));
|
||||
sizerTop->Add(m_radioPortrait, wxSizerFlags().Centre().Border());
|
||||
sizerTop->Add(m_radioLandscape, wxSizerFlags().Centre().Border());
|
||||
sizerTop->Add(m_cb, wxSizerFlags().Centre().Border());
|
||||
sizerTop->AddSpacer(5);
|
||||
sizerTop->Add(m_btn, wxSizerFlags().Centre().Border());
|
||||
@@ -1656,6 +1719,95 @@ static wxWindow* createMyExtraPanel(wxWindow *parent)
|
||||
return new MyExtraPanel(parent);
|
||||
}
|
||||
|
||||
// This class does the same thing as MyExtraPanel above, but uses newer API for
|
||||
// wxFileDialog customization.
|
||||
class MyCustomizeHook : public wxFileDialogCustomizeHook
|
||||
{
|
||||
public:
|
||||
// Normally we would just use wxFileDialog, but this sample allows using
|
||||
// both the real wxFileDialog and wxGenericFileDialog, so allow passing
|
||||
// either of them here.
|
||||
explicit MyCustomizeHook(wxFileDialogBase& dialog)
|
||||
: m_dialog(&dialog)
|
||||
{
|
||||
}
|
||||
|
||||
// Override pure virtual base class method to add our custom controls.
|
||||
virtual void AddCustomControls(wxFileDialogCustomize& customizer) wxOVERRIDE
|
||||
{
|
||||
// Note: all the pointers created here cease to be valid once
|
||||
// ShowModal() returns, TransferDataFromCustomControls() is the latest
|
||||
// moment when they can still be used.
|
||||
m_text = customizer.AddTextCtrl("Just some extra text:");
|
||||
const wxString sizes[] = { "A4", "Letter" };
|
||||
m_choiceSize = customizer.AddChoice(WXSIZEOF(sizes), sizes);
|
||||
m_radioPortrait = customizer.AddRadioButton("&Portrait");
|
||||
m_radioLandscape = customizer.AddRadioButton("&Landscape");
|
||||
m_cb = customizer.AddCheckBox("Enable Custom Button");
|
||||
m_cb->Bind(wxEVT_CHECKBOX, &MyCustomizeHook::OnCheckBox, this);
|
||||
m_btn = customizer.AddButton("Custom Button");
|
||||
m_btn->Bind(wxEVT_BUTTON, &MyCustomizeHook::OnButton, this);
|
||||
m_label = customizer.AddStaticText("Nothing selected");
|
||||
}
|
||||
|
||||
// Override another method called whenever something changes in the dialog.
|
||||
virtual void UpdateCustomControls() wxOVERRIDE
|
||||
{
|
||||
// Enable the button if and only if the checkbox is checked.
|
||||
m_btn->Enable(m_cb->GetValue());
|
||||
|
||||
// Enable radio buttons only if a file is selected.
|
||||
bool hasFile = wxFileName::FileExists(
|
||||
m_dialog->GetCurrentlySelectedFilename()
|
||||
);
|
||||
m_radioPortrait->Enable(hasFile);
|
||||
m_radioLandscape->Enable(hasFile);
|
||||
|
||||
// Also show the current dialog state.
|
||||
m_label->SetLabelText(GetFileDialogStateDescription(m_dialog));
|
||||
}
|
||||
|
||||
// And another one called when the dialog is accepted.
|
||||
virtual void TransferDataFromCustomControls() wxOVERRIDE
|
||||
{
|
||||
m_info.Printf("paper=%s (%s), enabled=%d, text=\"%s\"",
|
||||
GetFileDialogPaperSize(m_choiceSize->GetSelection()),
|
||||
m_radioPortrait->GetValue() ? "portrait" : "landscape",
|
||||
m_cb->GetValue(), m_text->GetValue());
|
||||
}
|
||||
|
||||
|
||||
// This is just a helper function allowing to show the values of the custom
|
||||
// controls.
|
||||
wxString GetInfo() const { return m_info; }
|
||||
|
||||
private:
|
||||
void OnCheckBox(wxCommandEvent& event)
|
||||
{
|
||||
m_btn->Enable(event.IsChecked());
|
||||
}
|
||||
|
||||
void OnButton(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
wxMessageBox("Custom button pressed", "wxWidgets dialogs sample",
|
||||
wxOK | wxICON_INFORMATION, m_dialog);
|
||||
}
|
||||
|
||||
wxFileDialogBase* const m_dialog;
|
||||
|
||||
wxFileDialogButton* m_btn;
|
||||
wxFileDialogCheckBox* m_cb;
|
||||
wxFileDialogChoice* m_choiceSize;
|
||||
wxFileDialogRadioButton* m_radioPortrait;
|
||||
wxFileDialogRadioButton* m_radioLandscape;
|
||||
wxFileDialogTextCtrl* m_text;
|
||||
wxFileDialogStaticText* m_label;
|
||||
|
||||
wxString m_info;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(MyCustomizeHook);
|
||||
};
|
||||
|
||||
void MyFrame::FileOpen(wxCommandEvent& WXUNUSED(event) )
|
||||
{
|
||||
wxFileDialog dialog
|
||||
@@ -1672,14 +1824,43 @@ void MyFrame::FileOpen(wxCommandEvent& WXUNUSED(event) )
|
||||
)
|
||||
);
|
||||
|
||||
dialog.SetExtraControlCreator(&createMyExtraPanel);
|
||||
// Note: this object must remain alive until ShowModal() returns.
|
||||
MyCustomizeHook myCustomizer(dialog);
|
||||
|
||||
// Normal programs would use either SetCustomizeHook() (preferred) or
|
||||
// SetExtraControlCreator() (if its extra flexibility is really required),
|
||||
// but, for demonstration purposes, this sample allows either one or the
|
||||
// other.
|
||||
const bool useExtra =
|
||||
GetMenuBar()->IsChecked(DIALOGS_FILE_USE_EXTRA_CONTROL_CREATOR);
|
||||
const bool hasExtra =
|
||||
useExtra ? dialog.SetExtraControlCreator(&createMyExtraPanel)
|
||||
: dialog.SetCustomizeHook(myCustomizer);
|
||||
|
||||
dialog.CentreOnParent();
|
||||
dialog.SetDirectory(wxGetHomeDir());
|
||||
|
||||
if (dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
wxString extraInfo;
|
||||
if ( hasExtra )
|
||||
{
|
||||
if ( useExtra )
|
||||
{
|
||||
wxWindow * const extra = dialog.GetExtraControl();
|
||||
extraInfo = static_cast<MyExtraPanel*>(extra)->GetInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
extraInfo = myCustomizer.GetInfo();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
extraInfo = "<not supported>";
|
||||
}
|
||||
|
||||
wxString info;
|
||||
wxWindow * const extra = dialog.GetExtraControl();
|
||||
info.Printf("Full file name: %s\n"
|
||||
"Path: %s\n"
|
||||
"Name: %s\n"
|
||||
@@ -1687,8 +1868,7 @@ void MyFrame::FileOpen(wxCommandEvent& WXUNUSED(event) )
|
||||
dialog.GetPath(),
|
||||
dialog.GetDirectory(),
|
||||
dialog.GetFilename(),
|
||||
extra ? static_cast<MyExtraPanel*>(extra)->GetInfo()
|
||||
: wxString("None"));
|
||||
extraInfo);
|
||||
wxMessageDialog dialog2(this, info, "Selected file");
|
||||
dialog2.ShowModal();
|
||||
}
|
||||
@@ -1741,6 +1921,8 @@ void MyFrame::FilesOpen(wxCommandEvent& WXUNUSED(event) )
|
||||
wxEmptyString, wxEmptyString, wildcards,
|
||||
wxFD_OPEN|wxFD_MULTIPLE);
|
||||
|
||||
dialog.Centre(wxCENTER_ON_SCREEN);
|
||||
|
||||
if (dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
wxArrayString paths, filenames;
|
||||
@@ -1886,7 +2068,8 @@ void MyFrame::FileOpenGeneric(wxCommandEvent& WXUNUSED(event) )
|
||||
"C++ files (*.cpp;*.h)|*.cpp;*.h"
|
||||
);
|
||||
|
||||
dialog.SetExtraControlCreator(&createMyExtraPanel);
|
||||
MyCustomizeHook myCustomizer(dialog);
|
||||
dialog.SetCustomizeHook(myCustomizer);
|
||||
dialog.SetDirectory(wxGetHomeDir());
|
||||
|
||||
if (dialog.ShowModal() == wxID_OK)
|
||||
@@ -1894,10 +2077,12 @@ void MyFrame::FileOpenGeneric(wxCommandEvent& WXUNUSED(event) )
|
||||
wxString info;
|
||||
info.Printf("Full file name: %s\n"
|
||||
"Path: %s\n"
|
||||
"Name: %s",
|
||||
"Name: %s\n"
|
||||
"Custom window: %s",
|
||||
dialog.GetPath(),
|
||||
dialog.GetDirectory(),
|
||||
dialog.GetFilename());
|
||||
dialog.GetFilename(),
|
||||
myCustomizer.GetInfo());
|
||||
wxMessageDialog dialog2(this, info, "Selected file");
|
||||
dialog2.ShowModal();
|
||||
}
|
||||
|
||||
@@ -620,6 +620,8 @@ enum
|
||||
DIALOGS_FILE_OPEN_GENERIC,
|
||||
DIALOGS_FILES_OPEN_GENERIC,
|
||||
DIALOGS_FILE_SAVE_GENERIC,
|
||||
DIALOGS_FILE_USE_CUSTOMIZER,
|
||||
DIALOGS_FILE_USE_EXTRA_CONTROL_CREATOR,
|
||||
DIALOGS_MAC_TOGGLE_ALWAYS_SHOW_TYPES,
|
||||
DIALOGS_DIR_CHOOSE,
|
||||
DIALOGS_DIR_CHOOSE_WINDOW_MODAL,
|
||||
|
||||
@@ -21,9 +21,21 @@
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/string.h"
|
||||
#include "wx/intl.h"
|
||||
#include "wx/panel.h"
|
||||
#include "wx/sizer.h"
|
||||
#include "wx/window.h"
|
||||
|
||||
#include "wx/button.h"
|
||||
#include "wx/checkbox.h"
|
||||
#include "wx/choice.h"
|
||||
#include "wx/radiobut.h"
|
||||
#include "wx/stattext.h"
|
||||
#include "wx/textctrl.h"
|
||||
#endif // WX_PRECOMP
|
||||
|
||||
#include "wx/filedlgcustomize.h"
|
||||
#include "wx/private/filedlgcustomize.h"
|
||||
|
||||
extern WXDLLEXPORT_DATA(const char) wxFileDialogNameStr[] = "filedlg";
|
||||
extern WXDLLEXPORT_DATA(const char) wxFileSelectorPromptStr[] = "Select a file";
|
||||
extern WXDLLEXPORT_DATA(const char) wxFileSelectorDefaultWildcardStr[] =
|
||||
@@ -34,6 +46,689 @@ extern WXDLLEXPORT_DATA(const char) wxFileSelectorDefaultWildcardStr[] =
|
||||
#endif
|
||||
;
|
||||
|
||||
// ============================================================================
|
||||
// File dialog customization support
|
||||
// ============================================================================
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxFileDialogCustomControl and derived classes
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Everything here is trivial, but has to be defined here, where the private
|
||||
// "Impl" classes are fully declared.
|
||||
|
||||
wxFileDialogCustomControlImpl::~wxFileDialogCustomControlImpl()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
wxFileDialogCustomControlImpl::DoBind(wxEvtHandler* WXUNUSED(handler))
|
||||
{
|
||||
// Do nothing here by default, some controls don't generate any events at
|
||||
// all and so this function will never be called for them.
|
||||
wxFAIL_MSG(wxS("Should be overridden if called"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wxFileDialogCustomControl::OnDynamicBind(wxDynamicEventTableEntry& entry)
|
||||
{
|
||||
wxUnusedVar(entry); // Needed when debug is disabled.
|
||||
|
||||
wxFAIL_MSG(wxString::Format
|
||||
(
|
||||
"This custom control doesn't generate the event %d.",
|
||||
entry.m_eventType
|
||||
));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void wxFileDialogCustomControl::Show(bool show)
|
||||
{
|
||||
return m_impl->Show(show);
|
||||
}
|
||||
|
||||
void wxFileDialogCustomControl::Enable(bool enable)
|
||||
{
|
||||
return m_impl->Enable(enable);
|
||||
}
|
||||
|
||||
wxFileDialogCustomControl::~wxFileDialogCustomControl()
|
||||
{
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
wxFileDialogButton::wxFileDialogButton(wxFileDialogButtonImpl* impl)
|
||||
: wxFileDialogCustomControl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
bool wxFileDialogButton::OnDynamicBind(wxDynamicEventTableEntry& entry)
|
||||
{
|
||||
if ( entry.m_eventType == wxEVT_BUTTON )
|
||||
return GetImpl()->DoBind(this);
|
||||
|
||||
return wxFileDialogCustomControl::OnDynamicBind(entry);
|
||||
}
|
||||
|
||||
wxFileDialogButtonImpl* wxFileDialogButton::GetImpl() const
|
||||
{
|
||||
return static_cast<wxFileDialogButtonImpl*>(m_impl);
|
||||
}
|
||||
|
||||
wxFileDialogCheckBox::wxFileDialogCheckBox(wxFileDialogCheckBoxImpl* impl)
|
||||
: wxFileDialogCustomControl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
bool wxFileDialogCheckBox::OnDynamicBind(wxDynamicEventTableEntry& entry)
|
||||
{
|
||||
if ( entry.m_eventType == wxEVT_CHECKBOX )
|
||||
return GetImpl()->DoBind(this);
|
||||
|
||||
return wxFileDialogCustomControl::OnDynamicBind(entry);
|
||||
}
|
||||
|
||||
wxFileDialogCheckBoxImpl* wxFileDialogCheckBox::GetImpl() const
|
||||
{
|
||||
return static_cast<wxFileDialogCheckBoxImpl*>(m_impl);
|
||||
}
|
||||
|
||||
bool wxFileDialogCheckBox::GetValue() const
|
||||
{
|
||||
return GetImpl()->GetValue();
|
||||
}
|
||||
|
||||
void wxFileDialogCheckBox::SetValue(bool value)
|
||||
{
|
||||
GetImpl()->SetValue(value);
|
||||
}
|
||||
|
||||
wxFileDialogRadioButton::wxFileDialogRadioButton(wxFileDialogRadioButtonImpl* impl)
|
||||
: wxFileDialogCustomControl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
bool wxFileDialogRadioButton::OnDynamicBind(wxDynamicEventTableEntry& entry)
|
||||
{
|
||||
if ( entry.m_eventType == wxEVT_RADIOBUTTON )
|
||||
return GetImpl()->DoBind(this);
|
||||
|
||||
return wxFileDialogCustomControl::OnDynamicBind(entry);
|
||||
}
|
||||
|
||||
wxFileDialogRadioButtonImpl* wxFileDialogRadioButton::GetImpl() const
|
||||
{
|
||||
return static_cast<wxFileDialogRadioButtonImpl*>(m_impl);
|
||||
}
|
||||
|
||||
bool wxFileDialogRadioButton::GetValue() const
|
||||
{
|
||||
return GetImpl()->GetValue();
|
||||
}
|
||||
|
||||
void wxFileDialogRadioButton::SetValue(bool value)
|
||||
{
|
||||
GetImpl()->SetValue(value);
|
||||
}
|
||||
|
||||
wxFileDialogChoice::wxFileDialogChoice(wxFileDialogChoiceImpl* impl)
|
||||
: wxFileDialogCustomControl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
bool wxFileDialogChoice::OnDynamicBind(wxDynamicEventTableEntry& entry)
|
||||
{
|
||||
if ( entry.m_eventType == wxEVT_CHOICE )
|
||||
return GetImpl()->DoBind(this);
|
||||
|
||||
return wxFileDialogCustomControl::OnDynamicBind(entry);
|
||||
}
|
||||
|
||||
wxFileDialogChoiceImpl* wxFileDialogChoice::GetImpl() const
|
||||
{
|
||||
return static_cast<wxFileDialogChoiceImpl*>(m_impl);
|
||||
}
|
||||
|
||||
int wxFileDialogChoice::GetSelection() const
|
||||
{
|
||||
return GetImpl()->GetSelection();
|
||||
}
|
||||
|
||||
void wxFileDialogChoice::SetSelection(int n)
|
||||
{
|
||||
GetImpl()->SetSelection(n);
|
||||
}
|
||||
|
||||
wxFileDialogTextCtrl::wxFileDialogTextCtrl(wxFileDialogTextCtrlImpl* impl)
|
||||
: wxFileDialogCustomControl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
wxFileDialogTextCtrlImpl* wxFileDialogTextCtrl::GetImpl() const
|
||||
{
|
||||
return static_cast<wxFileDialogTextCtrlImpl*>(m_impl);
|
||||
}
|
||||
|
||||
wxString wxFileDialogTextCtrl::GetValue() const
|
||||
{
|
||||
return GetImpl()->GetValue();
|
||||
}
|
||||
|
||||
void wxFileDialogTextCtrl::SetValue(const wxString& value)
|
||||
{
|
||||
GetImpl()->SetValue(value);
|
||||
}
|
||||
|
||||
wxFileDialogStaticText::wxFileDialogStaticText(wxFileDialogStaticTextImpl* impl)
|
||||
: wxFileDialogCustomControl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
wxFileDialogStaticTextImpl* wxFileDialogStaticText::GetImpl() const
|
||||
{
|
||||
return static_cast<wxFileDialogStaticTextImpl*>(m_impl);
|
||||
}
|
||||
|
||||
void wxFileDialogStaticText::SetLabelText(const wxString& text)
|
||||
{
|
||||
GetImpl()->SetLabelText(text);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxFileDialogCustomize
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wxFileDialogCustomizeHook::~wxFileDialogCustomizeHook()
|
||||
{
|
||||
}
|
||||
|
||||
wxFileDialogCustomizeImpl::~wxFileDialogCustomizeImpl()
|
||||
{
|
||||
}
|
||||
|
||||
wxFileDialogCustomize::~wxFileDialogCustomize()
|
||||
{
|
||||
// For consistency with the rest of wx API, we own all the custom controls
|
||||
// pointers and delete them when we're deleted.
|
||||
for ( size_t n = 0; n < m_controls.size(); ++n )
|
||||
delete m_controls[n];
|
||||
|
||||
// Do not delete m_impl, the derived classes use this object itself as
|
||||
// implementation, which allows us to avoid allocating it on the heap in
|
||||
// the first place.
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T*
|
||||
wxFileDialogCustomize::StoreAndReturn(T* control)
|
||||
{
|
||||
m_controls.push_back(control);
|
||||
return control;
|
||||
}
|
||||
|
||||
wxFileDialogButton*
|
||||
wxFileDialogCustomize::AddButton(const wxString& label)
|
||||
{
|
||||
return StoreAndReturn(new wxFileDialogButton(m_impl->AddButton(label)));
|
||||
}
|
||||
|
||||
wxFileDialogCheckBox*
|
||||
wxFileDialogCustomize::AddCheckBox(const wxString& label)
|
||||
{
|
||||
return StoreAndReturn(new wxFileDialogCheckBox(m_impl->AddCheckBox(label)));
|
||||
}
|
||||
|
||||
wxFileDialogRadioButton*
|
||||
wxFileDialogCustomize::AddRadioButton(const wxString& label)
|
||||
{
|
||||
return StoreAndReturn(new wxFileDialogRadioButton(m_impl->AddRadioButton(label)));
|
||||
}
|
||||
|
||||
wxFileDialogChoice*
|
||||
wxFileDialogCustomize::AddChoice(size_t n, const wxString* strings)
|
||||
{
|
||||
return StoreAndReturn(new wxFileDialogChoice(m_impl->AddChoice(n, strings)));
|
||||
}
|
||||
|
||||
wxFileDialogTextCtrl*
|
||||
wxFileDialogCustomize::AddTextCtrl(const wxString& label)
|
||||
{
|
||||
return StoreAndReturn(new wxFileDialogTextCtrl(m_impl->AddTextCtrl(label)));
|
||||
}
|
||||
|
||||
wxFileDialogStaticText*
|
||||
wxFileDialogCustomize::AddStaticText(const wxString& label)
|
||||
{
|
||||
return StoreAndReturn(new wxFileDialogStaticText(m_impl->AddStaticText(label)));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Generic implementation of wxFileDialogCustomize and related classes
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
namespace wxGenericCustomizer
|
||||
{
|
||||
|
||||
// Template base class for the implementation classes below inheriting from the
|
||||
// specified Impl subclass.
|
||||
template <typename T>
|
||||
class ControlImplBase : public T
|
||||
{
|
||||
public:
|
||||
explicit ControlImplBase(wxWindow* win)
|
||||
: m_win(win)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Show(bool show) wxOVERRIDE
|
||||
{
|
||||
m_win->Show(show);
|
||||
}
|
||||
|
||||
virtual void Enable(bool enable) wxOVERRIDE
|
||||
{
|
||||
m_win->Enable(enable);
|
||||
}
|
||||
|
||||
// Leave it public for Panel to access.
|
||||
wxWindow* const m_win;
|
||||
|
||||
wxDECLARE_NO_COPY_TEMPLATE_CLASS(ControlImplBase, T);
|
||||
};
|
||||
|
||||
class CustomControlImpl : public ControlImplBase<wxFileDialogCustomControlImpl>
|
||||
{
|
||||
public:
|
||||
// All custom controls are identified by their ID in this implementation.
|
||||
explicit CustomControlImpl(wxWindow* win)
|
||||
: ControlImplBase<wxFileDialogCustomControlImpl>(win)
|
||||
{
|
||||
}
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(CustomControlImpl);
|
||||
};
|
||||
|
||||
class ButtonImpl : public ControlImplBase<wxFileDialogButtonImpl>
|
||||
{
|
||||
public:
|
||||
ButtonImpl(wxWindow* parent, const wxString& label)
|
||||
: ControlImplBase<wxFileDialogButtonImpl>
|
||||
(
|
||||
new wxButton(parent, wxID_ANY, label)
|
||||
)
|
||||
{
|
||||
m_handler = NULL;
|
||||
}
|
||||
|
||||
virtual bool DoBind(wxEvtHandler* handler) wxOVERRIDE
|
||||
{
|
||||
if ( !m_handler )
|
||||
{
|
||||
m_handler = handler;
|
||||
m_win->Bind(wxEVT_BUTTON, &ButtonImpl::OnButton, this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void OnButton(wxCommandEvent& event)
|
||||
{
|
||||
// Pretend that the event is coming from the custom control and not the
|
||||
// actual wx control implementing it.
|
||||
|
||||
// Make a copy of the event to set the event object correctly.
|
||||
wxCommandEvent eventCopy(event);
|
||||
eventCopy.SetEventObject(m_handler);
|
||||
|
||||
m_handler->ProcessEvent(eventCopy);
|
||||
|
||||
// We don't need to do anything about skipping, vetoing etc as all this
|
||||
// is not used anyhow for simple command events.
|
||||
}
|
||||
|
||||
wxEvtHandler* m_handler;
|
||||
};
|
||||
|
||||
class CheckBoxImpl : public ControlImplBase<wxFileDialogCheckBoxImpl>
|
||||
{
|
||||
public:
|
||||
CheckBoxImpl(wxWindow* parent, const wxString& label)
|
||||
: ControlImplBase<wxFileDialogCheckBoxImpl>
|
||||
(
|
||||
new wxCheckBox(parent, wxID_ANY, label)
|
||||
)
|
||||
{
|
||||
m_handler = NULL;
|
||||
}
|
||||
|
||||
virtual bool GetValue() wxOVERRIDE
|
||||
{
|
||||
return GetCheckBox()->GetValue();
|
||||
}
|
||||
|
||||
virtual void SetValue(bool value) wxOVERRIDE
|
||||
{
|
||||
GetCheckBox()->SetValue(value);
|
||||
}
|
||||
|
||||
virtual bool DoBind(wxEvtHandler* handler) wxOVERRIDE
|
||||
{
|
||||
if ( !m_handler )
|
||||
{
|
||||
m_handler = handler;
|
||||
m_win->Bind(wxEVT_CHECKBOX, &CheckBoxImpl::OnCheckBox, this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
wxCheckBox* GetCheckBox() const
|
||||
{
|
||||
return static_cast<wxCheckBox*>(m_win);
|
||||
}
|
||||
|
||||
void OnCheckBox(wxCommandEvent& event)
|
||||
{
|
||||
// See comments in OnButton() above, they also apply here.
|
||||
|
||||
wxCommandEvent eventCopy(event);
|
||||
eventCopy.SetEventObject(m_handler);
|
||||
|
||||
m_handler->ProcessEvent(eventCopy);
|
||||
}
|
||||
|
||||
wxEvtHandler* m_handler;
|
||||
};
|
||||
|
||||
class RadioButtonImpl : public ControlImplBase<wxFileDialogRadioButtonImpl>
|
||||
{
|
||||
public:
|
||||
RadioButtonImpl(wxWindow* parent, const wxString& label)
|
||||
: ControlImplBase<wxFileDialogRadioButtonImpl>
|
||||
(
|
||||
new wxRadioButton(parent, wxID_ANY, label)
|
||||
)
|
||||
{
|
||||
m_handler = NULL;
|
||||
}
|
||||
|
||||
virtual bool GetValue() wxOVERRIDE
|
||||
{
|
||||
return GetRadioButton()->GetValue();
|
||||
}
|
||||
|
||||
virtual void SetValue(bool value) wxOVERRIDE
|
||||
{
|
||||
GetRadioButton()->SetValue(value);
|
||||
}
|
||||
|
||||
virtual bool DoBind(wxEvtHandler* handler) wxOVERRIDE
|
||||
{
|
||||
if ( !m_handler )
|
||||
{
|
||||
m_handler = handler;
|
||||
m_win->Bind(wxEVT_RADIOBUTTON, &RadioButtonImpl::OnRadioButton, this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
wxRadioButton* GetRadioButton() const
|
||||
{
|
||||
return static_cast<wxRadioButton*>(m_win);
|
||||
}
|
||||
|
||||
void OnRadioButton(wxCommandEvent& event)
|
||||
{
|
||||
// See comments in OnButton() above, they also apply here.
|
||||
|
||||
wxCommandEvent eventCopy(event);
|
||||
eventCopy.SetEventObject(m_handler);
|
||||
|
||||
m_handler->ProcessEvent(eventCopy);
|
||||
}
|
||||
|
||||
wxEvtHandler* m_handler;
|
||||
};
|
||||
|
||||
class ChoiceImpl : public ControlImplBase<wxFileDialogChoiceImpl>
|
||||
{
|
||||
public:
|
||||
ChoiceImpl(wxWindow* parent, size_t n, const wxString* strings)
|
||||
: ControlImplBase<wxFileDialogChoiceImpl>
|
||||
(
|
||||
new wxChoice(parent, wxID_ANY,
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
n, strings)
|
||||
)
|
||||
{
|
||||
m_handler = NULL;
|
||||
}
|
||||
|
||||
virtual int GetSelection() wxOVERRIDE
|
||||
{
|
||||
return GetChoice()->GetSelection();
|
||||
}
|
||||
|
||||
virtual void SetSelection(int selection) wxOVERRIDE
|
||||
{
|
||||
GetChoice()->SetSelection(selection);
|
||||
}
|
||||
|
||||
virtual bool DoBind(wxEvtHandler* handler) wxOVERRIDE
|
||||
{
|
||||
if ( !m_handler )
|
||||
{
|
||||
m_handler = handler;
|
||||
m_win->Bind(wxEVT_CHOICE, &ChoiceImpl::OnChoice, this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
wxChoice* GetChoice() const
|
||||
{
|
||||
return static_cast<wxChoice*>(m_win);
|
||||
}
|
||||
|
||||
void OnChoice(wxCommandEvent& event)
|
||||
{
|
||||
// See comments in OnButton() above, they also apply here.
|
||||
|
||||
wxCommandEvent eventCopy(event);
|
||||
eventCopy.SetEventObject(m_handler);
|
||||
|
||||
m_handler->ProcessEvent(eventCopy);
|
||||
}
|
||||
|
||||
wxEvtHandler* m_handler;
|
||||
};
|
||||
|
||||
class TextCtrlImpl : public ControlImplBase<wxFileDialogTextCtrlImpl>
|
||||
{
|
||||
public:
|
||||
// The dummy argument is there just for consistency with the other classes
|
||||
// and allows to keep the code simple even without vararg templates support.
|
||||
explicit TextCtrlImpl(wxWindow* parent, const wxString& WXUNUSED(dummy))
|
||||
: ControlImplBase<wxFileDialogTextCtrlImpl>
|
||||
(
|
||||
new wxTextCtrl(parent, wxID_ANY)
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
virtual wxString GetValue() wxOVERRIDE
|
||||
{
|
||||
return GetText()->GetValue();
|
||||
}
|
||||
|
||||
virtual void SetValue(const wxString& value) wxOVERRIDE
|
||||
{
|
||||
// Don't use SetValue(), we don't need any extra events here.
|
||||
return GetText()->ChangeValue(value);
|
||||
}
|
||||
|
||||
private:
|
||||
wxTextCtrl* GetText() const
|
||||
{
|
||||
return static_cast<wxTextCtrl*>(m_win);
|
||||
}
|
||||
};
|
||||
|
||||
class StaticTextImpl : public ControlImplBase<wxFileDialogStaticTextImpl>
|
||||
{
|
||||
public:
|
||||
StaticTextImpl(wxWindow* parent, const wxString& label)
|
||||
: ControlImplBase<wxFileDialogStaticTextImpl>
|
||||
(
|
||||
new wxStaticText(parent, wxID_ANY, wxControl::EscapeMnemonics(label))
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void SetLabelText(const wxString& text) wxOVERRIDE
|
||||
{
|
||||
GetStaticText()->SetLabelText(text);
|
||||
|
||||
wxWindow* const parent = m_win->GetParent();
|
||||
parent->GetSizer()->Fit(parent);
|
||||
}
|
||||
|
||||
private:
|
||||
wxStaticText* GetStaticText() const
|
||||
{
|
||||
return static_cast<wxStaticText*>(m_win);
|
||||
}
|
||||
};
|
||||
|
||||
// Generic implementation of wxFileDialogCustomize which is also a window that
|
||||
// can be used as part of the dialog.
|
||||
class Panel : public wxPanel,
|
||||
public wxFileDialogCustomize,
|
||||
private wxFileDialogCustomizeImpl
|
||||
{
|
||||
public:
|
||||
Panel(wxWindow* parent, wxFileDialogCustomizeHook& customizeHook)
|
||||
: wxPanel(parent),
|
||||
wxFileDialogCustomize(this),
|
||||
m_customizeHook(customizeHook),
|
||||
m_lastWasRadio(false)
|
||||
{
|
||||
// Use a simple horizontal sizer to layout all the controls for now.
|
||||
wxBoxSizer* const sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
SetSizer(sizer);
|
||||
|
||||
// Leave a margin before the first item.
|
||||
sizer->AddSpacer(wxSizerFlags::GetDefaultBorder());
|
||||
|
||||
// This will call our own AddXXX().
|
||||
m_customizeHook.AddCustomControls(*this);
|
||||
|
||||
// Now that everything was created, resize and layout.
|
||||
SetClientSize(sizer->ComputeFittingClientSize(this));
|
||||
sizer->Layout();
|
||||
}
|
||||
|
||||
virtual ~Panel()
|
||||
{
|
||||
m_customizeHook.TransferDataFromCustomControls();
|
||||
}
|
||||
|
||||
|
||||
// Implement wxFileDialogCustomizeImpl pure virtual methods.
|
||||
wxFileDialogButtonImpl* AddButton(const wxString& label) wxOVERRIDE
|
||||
{
|
||||
m_lastWasRadio = false;
|
||||
|
||||
return AddToLayoutAndReturn<ButtonImpl>(label);
|
||||
}
|
||||
|
||||
wxFileDialogCheckBoxImpl* AddCheckBox(const wxString& label) wxOVERRIDE
|
||||
{
|
||||
m_lastWasRadio = false;
|
||||
|
||||
return AddToLayoutAndReturn<CheckBoxImpl>(label);
|
||||
}
|
||||
|
||||
wxFileDialogRadioButtonImpl* AddRadioButton(const wxString& label) wxOVERRIDE
|
||||
{
|
||||
RadioButtonImpl* const impl = AddToLayoutAndReturn<RadioButtonImpl>(label);
|
||||
if ( !m_lastWasRadio )
|
||||
{
|
||||
// Select the first button of a new radio group.
|
||||
impl->SetValue(true);
|
||||
|
||||
m_lastWasRadio = true;
|
||||
}
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
wxFileDialogChoiceImpl* AddChoice(size_t n, const wxString* strings) wxOVERRIDE
|
||||
{
|
||||
m_lastWasRadio = false;
|
||||
|
||||
// TODO-C++11: Can't use AddToLayoutAndReturn() here easily without
|
||||
// variadic templates.
|
||||
ChoiceImpl* const impl = new ChoiceImpl(this, n, strings);
|
||||
|
||||
AddToLayout(impl->m_win);
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
|
||||
wxFileDialogTextCtrlImpl* AddTextCtrl(const wxString& label) wxOVERRIDE
|
||||
{
|
||||
m_lastWasRadio = false;
|
||||
|
||||
if ( !label.empty() )
|
||||
{
|
||||
AddToLayout(new wxStaticText(this, wxID_ANY, label));
|
||||
}
|
||||
|
||||
return AddToLayoutAndReturn<TextCtrlImpl>();
|
||||
}
|
||||
|
||||
wxFileDialogStaticTextImpl* AddStaticText(const wxString& label) wxOVERRIDE
|
||||
{
|
||||
m_lastWasRadio = false;
|
||||
|
||||
return AddToLayoutAndReturn<StaticTextImpl>(label);
|
||||
}
|
||||
|
||||
private:
|
||||
void AddToLayout(wxWindow* win)
|
||||
{
|
||||
GetSizer()->Add(win, wxSizerFlags().Center().Border(wxRIGHT));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* AddToLayoutAndReturn(const wxString& label = wxString())
|
||||
{
|
||||
T* const controlImpl = new T(this, label);
|
||||
|
||||
AddToLayout(controlImpl->m_win);
|
||||
|
||||
return controlImpl;
|
||||
}
|
||||
|
||||
|
||||
wxFileDialogCustomizeHook& m_customizeHook;
|
||||
|
||||
bool m_lastWasRadio;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(Panel);
|
||||
};
|
||||
|
||||
} // namespace wxGenericCustomizer
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// wxFileDialogBase
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -45,6 +740,7 @@ void wxFileDialogBase::Init()
|
||||
m_filterIndex = 0;
|
||||
m_currentlySelectedFilterIndex = wxNOT_FOUND;
|
||||
m_windowStyle = 0;
|
||||
m_customizeHook = NULL;
|
||||
m_extraControl = NULL;
|
||||
m_extraControlCreator = NULL;
|
||||
}
|
||||
@@ -163,6 +859,18 @@ wxString wxFileDialogBase::AppendExtension(const wxString &filePath,
|
||||
return filePath + ext;
|
||||
}
|
||||
|
||||
bool wxFileDialogBase::SetCustomizeHook(wxFileDialogCustomizeHook& customizeHook)
|
||||
{
|
||||
if ( !SupportsExtraControl() )
|
||||
return false;
|
||||
|
||||
wxASSERT_MSG( !m_extraControlCreator,
|
||||
"Call either SetExtraControlCreator() or SetCustomizeHook()" );
|
||||
|
||||
m_customizeHook = &customizeHook;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxFileDialogBase::SetExtraControlCreator(ExtraControlCreatorFunction creator)
|
||||
{
|
||||
wxCHECK_MSG( !m_extraControlCreator, false,
|
||||
@@ -172,28 +880,34 @@ bool wxFileDialogBase::SetExtraControlCreator(ExtraControlCreatorFunction creato
|
||||
return SupportsExtraControl();
|
||||
}
|
||||
|
||||
bool wxFileDialogBase::CreateExtraControl()
|
||||
wxWindow* wxFileDialogBase::CreateExtraControlWithParent(wxWindow* parent) const
|
||||
{
|
||||
if (!m_extraControlCreator || m_extraControl)
|
||||
return false;
|
||||
m_extraControl = (*m_extraControlCreator)(this);
|
||||
return true;
|
||||
if ( m_customizeHook )
|
||||
return new wxGenericCustomizer::Panel(parent, *m_customizeHook);
|
||||
|
||||
if ( m_extraControlCreator )
|
||||
return (*m_extraControlCreator)(parent);
|
||||
|
||||
// It's not an error to call this function if there are no extra controls
|
||||
// to create, just do nothing in this case.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wxSize wxFileDialogBase::GetExtraControlSize()
|
||||
bool wxFileDialogBase::CreateExtraControl()
|
||||
{
|
||||
if ( !m_extraControlCreator )
|
||||
return wxDefaultSize;
|
||||
// We're not supposed to be called more than once normally, but just do
|
||||
// nothing if we had already created the custom controls somehow.
|
||||
if ( !m_extraControl )
|
||||
m_extraControl = CreateExtraControlWithParent(this);
|
||||
|
||||
// create the extra control in an empty dialog just to find its size: this
|
||||
// is not terribly efficient but we do need to know the size before
|
||||
// creating the native dialog and this seems to be the only way
|
||||
wxDialog dlg(NULL, wxID_ANY, wxString());
|
||||
return (*m_extraControlCreator)(&dlg)->GetSize();
|
||||
return m_extraControl != NULL;
|
||||
}
|
||||
|
||||
void wxFileDialogBase::UpdateExtraControlUI()
|
||||
{
|
||||
if ( m_customizeHook )
|
||||
m_customizeHook->UpdateCustomControls();
|
||||
|
||||
if ( m_extraControl )
|
||||
m_extraControl->UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
}
|
||||
|
||||
@@ -301,7 +301,17 @@ int wxGenericFileDialog::ShowModal()
|
||||
|
||||
m_filectrl->SetDirectory(m_dir);
|
||||
|
||||
return wxDialog::ShowModal();
|
||||
const int rc = wxDialog::ShowModal();
|
||||
|
||||
// Destroy the extra controls before ShowModal() returns for consistency
|
||||
// with the native implementations.
|
||||
if (m_extraControl)
|
||||
{
|
||||
m_extraControl->Destroy();
|
||||
m_extraControl = NULL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool wxGenericFileDialog::Show( bool show )
|
||||
@@ -402,6 +412,8 @@ void wxGenericFileDialog::OnUpdateButtonsUI(wxUpdateUIEvent& event)
|
||||
// wxFileCtrl ctor itself can generate idle events, so we need this test
|
||||
if ( m_filectrl )
|
||||
event.Enable( !IsTopMostDir(m_filectrl->GetShownDirectory()) );
|
||||
|
||||
UpdateExtraControlUI();
|
||||
}
|
||||
|
||||
#ifdef wxHAS_GENERIC_FILEDIALOG
|
||||
|
||||
@@ -19,11 +19,6 @@
|
||||
// For compilers that support precompilation, includes "wx.h".
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
|
||||
#if wxUSE_DIRDLG
|
||||
|
||||
#if wxUSE_OLE
|
||||
|
||||
#include "wx/dirdlg.h"
|
||||
#include "wx/modalhook.h"
|
||||
|
||||
@@ -34,26 +29,15 @@
|
||||
#include "wx/app.h" // for GetComCtl32Version()
|
||||
#endif
|
||||
|
||||
#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 "wx/msw/private/filedialog.h"
|
||||
|
||||
#if wxUSE_IFILEOPENDIALOG
|
||||
|
||||
#include <initguid.h>
|
||||
|
||||
// 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_IFILEOPENDIALOG 0
|
||||
#endif
|
||||
#include "wx/msw/private/cotaskmemptr.h"
|
||||
#include "wx/dynlib.h"
|
||||
|
||||
#if wxUSE_IFILEOPENDIALOG
|
||||
// IFileDialog related declarations missing from some compilers headers.
|
||||
|
||||
#if defined(__VISUALC__)
|
||||
@@ -68,6 +52,31 @@ DEFINE_GUID(IID_IShellItem,
|
||||
|
||||
#endif // wxUSE_IFILEOPENDIALOG
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// private functions prototypes
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if wxUSE_IFILEOPENDIALOG
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// helper functions for wxDirDialog::ShowIFileOpenDialog()
|
||||
bool GetPathsFromIFileOpenDialog(IFileOpenDialog* fileDialog, wxArrayString& paths);
|
||||
bool GetPathFromIFileDialog(IFileDialog* fileDialog, wxString& path);
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#endif // #if wxUSE_IFILEOPENDIALOG
|
||||
|
||||
// Note that parts of this file related to IFileDialog are still compiled even
|
||||
// when wxUSE_DIRDLG == 0 because they're used by wxUSE_FILEDLG too.
|
||||
#if wxUSE_DIRDLG
|
||||
|
||||
// callback used in wxDirDialog::ShowSHBrowseForFolder()
|
||||
static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp,
|
||||
LPARAM pData);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// constants
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -82,26 +91,6 @@ DEFINE_GUID(IID_IShellItem,
|
||||
|
||||
wxIMPLEMENT_CLASS(wxDirDialog, wxDialog);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// private functions prototypes
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#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);
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// implementation
|
||||
// ============================================================================
|
||||
@@ -253,16 +242,69 @@ int wxDirDialog::ShowSHBrowseForFolder(WXHWND owner)
|
||||
|
||||
int wxDirDialog::ShowIFileOpenDialog(WXHWND owner)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
wxCOMPtr<IFileOpenDialog> fileDialog;
|
||||
wxMSWImpl::wxIFileDialog fileDialog(CLSID_FileOpenDialog);
|
||||
if ( !fileDialog.IsOk() )
|
||||
return wxID_NONE;
|
||||
|
||||
if ( !InitIFileOpenDialog(m_message, m_path, HasFlag(wxDD_MULTIPLE),
|
||||
HasFlag(wxDD_SHOW_HIDDEN), fileDialog) )
|
||||
fileDialog.SetTitle(m_message);
|
||||
if ( !m_path.empty() )
|
||||
fileDialog.SetInitialPath(m_path);
|
||||
|
||||
// We currently always use FOS_NOCHANGEDIR even if wxDD_CHANGE_DIR was
|
||||
// specified because we change the directory ourselves in this case.
|
||||
int options = FOS_PICKFOLDERS | FOS_NOCHANGEDIR;
|
||||
if ( HasFlag(wxDD_MULTIPLE) )
|
||||
options |= FOS_ALLOWMULTISELECT;
|
||||
if ( HasFlag(wxDD_SHOW_HIDDEN) )
|
||||
options |= FOS_FORCESHOWHIDDEN;
|
||||
|
||||
return fileDialog.Show(owner, options, &m_paths, &m_path);
|
||||
}
|
||||
|
||||
#endif // wxUSE_IFILEOPENDIALOG
|
||||
|
||||
#endif // wxUSE_DIRDLG
|
||||
|
||||
#if wxUSE_IFILEOPENDIALOG
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helper functions used by wxDirDialog and wxFileDialog.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
namespace wxMSWImpl
|
||||
{
|
||||
|
||||
wxIFileDialog::wxIFileDialog(const CLSID& clsid)
|
||||
{
|
||||
HRESULT hr = ::CoCreateInstance
|
||||
(
|
||||
clsid,
|
||||
NULL, // no outer IUnknown
|
||||
CLSCTX_INPROC_SERVER,
|
||||
wxIID_PPV_ARGS(IFileOpenDialog, &m_fileDialog)
|
||||
);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
return wxID_NONE; // Failed to initialize the dialog
|
||||
wxLogApiError(wxS("CoCreateInstance(CLSID_FileOpenDialog)"), hr);
|
||||
}
|
||||
}
|
||||
|
||||
int wxIFileDialog::Show(HWND owner, int options,
|
||||
wxArrayString* pathsOut, wxString* pathOut)
|
||||
{
|
||||
wxCHECK_MSG( m_fileDialog, wxID_NONE, wxS("shouldn't be called") );
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
// allow to select only a file system object
|
||||
hr = m_fileDialog->SetOptions(options | FOS_FORCEFILESYSTEM);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
wxLogApiError(wxS("IFileOpenDialog::SetOptions"), hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = fileDialog->Show(owner);
|
||||
hr = m_fileDialog->Show(owner);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
if ( hr == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
|
||||
@@ -274,15 +316,24 @@ int wxDirDialog::ShowIFileOpenDialog(WXHWND owner)
|
||||
wxLogApiError(wxS("IFileDialog::Show"), hr);
|
||||
}
|
||||
}
|
||||
else if ( GetPathsFromIFileOpenDialog(fileDialog, HasFlag(wxDD_MULTIPLE),
|
||||
m_paths) )
|
||||
else if ( options & FOS_ALLOWMULTISELECT )
|
||||
{
|
||||
if ( !HasFlag(wxDD_MULTIPLE) )
|
||||
wxCOMPtr<IFileOpenDialog> fileOpenDialog;
|
||||
hr = m_fileDialog->QueryInterface(wxIID_PPV_ARGS(IFileOpenDialog, &fileOpenDialog));
|
||||
if ( SUCCEEDED(hr) )
|
||||
{
|
||||
m_path = m_paths.Last();
|
||||
if ( GetPathsFromIFileOpenDialog(fileOpenDialog, *pathsOut) )
|
||||
return wxID_OK;
|
||||
}
|
||||
|
||||
return wxID_OK;
|
||||
else
|
||||
{
|
||||
wxLogApiError(wxS("IFileDialog::QI(IFileOpenDialog)"), hr);
|
||||
}
|
||||
}
|
||||
else // Single selection only, path output parameter must be non-null.
|
||||
{
|
||||
if ( GetPathFromIFileDialog(m_fileDialog, *pathOut) )
|
||||
return wxID_OK;
|
||||
}
|
||||
|
||||
// Failed to show the dialog or obtain the selected folders(s)
|
||||
@@ -290,160 +341,124 @@ int wxDirDialog::ShowIFileOpenDialog(WXHWND owner)
|
||||
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)
|
||||
void wxIFileDialog::SetTitle(const wxString& message)
|
||||
{
|
||||
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(IFileOpenDialog, &dlg));
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
wxLogApiError(wxS("CoCreateInstance(CLSID_FileOpenDialog)"), hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( multipleSelection )
|
||||
options |= FOS_ALLOWMULTISELECT;
|
||||
if ( showHidden )
|
||||
options |= FOS_FORCESHOWHIDDEN;
|
||||
|
||||
hr = dlg->SetOptions(options);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
wxLogApiError(wxS("IFileOpenDialog::SetOptions"), hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = dlg->SetTitle(message.wc_str());
|
||||
HRESULT hr = m_fileDialog->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("IFileOpenDialog::SetTitle"), hr);
|
||||
}
|
||||
}
|
||||
|
||||
// set the initial path
|
||||
if ( !defaultPath.empty() )
|
||||
void wxIFileDialog::SetInitialPath(const wxString& defaultPath)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
// We need to link SHCreateItemFromParsingName() dynamically as it's
|
||||
// not available on pre-Vista systems.
|
||||
typedef HRESULT
|
||||
(WINAPI *SHCreateItemFromParsingName_t)(PCWSTR,
|
||||
IBindCtx*,
|
||||
REFIID,
|
||||
void**);
|
||||
|
||||
static SHCreateItemFromParsingName_t
|
||||
s_pfnSHCreateItemFromParsingName = (SHCreateItemFromParsingName_t)-1;
|
||||
if ( s_pfnSHCreateItemFromParsingName == (SHCreateItemFromParsingName_t)-1 )
|
||||
{
|
||||
// We need to link SHCreateItemFromParsingName() dynamically as it's
|
||||
// not available on pre-Vista systems.
|
||||
typedef HRESULT
|
||||
(WINAPI *SHCreateItemFromParsingName_t)(PCWSTR,
|
||||
IBindCtx*,
|
||||
REFIID,
|
||||
void**);
|
||||
|
||||
SHCreateItemFromParsingName_t pfnSHCreateItemFromParsingName = NULL;
|
||||
wxDynamicLibrary dllShell32;
|
||||
if ( dllShell32.Load(wxS("shell32.dll"), wxDL_VERBATIM | wxDL_QUIET) )
|
||||
{
|
||||
wxDL_INIT_FUNC(pfn, SHCreateItemFromParsingName, dllShell32);
|
||||
wxDL_INIT_FUNC(s_pfn, SHCreateItemFromParsingName, dllShell32);
|
||||
}
|
||||
|
||||
if ( !pfnSHCreateItemFromParsingName )
|
||||
if ( !s_pfnSHCreateItemFromParsingName )
|
||||
{
|
||||
wxLogLastError(wxS("SHCreateItemFromParsingName() not found"));
|
||||
return false;
|
||||
}
|
||||
|
||||
wxCOMPtr<IShellItem> folder;
|
||||
hr = pfnSHCreateItemFromParsingName(defaultPath.wc_str(),
|
||||
NULL,
|
||||
wxIID_PPV_ARGS(IShellItem,
|
||||
&folder));
|
||||
|
||||
// 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.
|
||||
if ( SUCCEEDED(hr) )
|
||||
{
|
||||
hr = dlg->SetFolder(folder);
|
||||
if ( FAILED(hr) )
|
||||
wxLogApiError(wxS("IFileOpenDialog::SetFolder"), hr);
|
||||
}
|
||||
}
|
||||
|
||||
fileDialog = dlg;
|
||||
return true;
|
||||
if ( !s_pfnSHCreateItemFromParsingName )
|
||||
{
|
||||
// There is nothing we can do and the error was already reported.
|
||||
return;
|
||||
}
|
||||
|
||||
wxCOMPtr<IShellItem> folder;
|
||||
hr = s_pfnSHCreateItemFromParsingName
|
||||
(
|
||||
defaultPath.wc_str(),
|
||||
NULL,
|
||||
wxIID_PPV_ARGS(IShellItem, &folder)
|
||||
);
|
||||
|
||||
// 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.
|
||||
if ( SUCCEEDED(hr) )
|
||||
{
|
||||
hr = m_fileDialog->SetFolder(folder);
|
||||
if ( FAILED(hr) )
|
||||
wxLogApiError(wxS("IFileOpenDialog::SetFolder"), hr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wxMSWImpl
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// private functions
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// helper function for wxDirDialog::ShowIFileOpenDialog()
|
||||
bool GetPathsFromIFileOpenDialog(const wxCOMPtr<IFileOpenDialog>& fileDialog, bool multipleSelection,
|
||||
wxArrayString& paths)
|
||||
bool GetPathsFromIFileOpenDialog(IFileOpenDialog* fileDialog, wxArrayString& paths)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
wxString path;
|
||||
wxArrayString tempPaths;
|
||||
|
||||
if ( multipleSelection )
|
||||
wxCOMPtr<IShellItemArray> itemArray;
|
||||
|
||||
hr = fileDialog->GetResults(&itemArray);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
wxLogApiError(wxS("IShellItemArray::GetResults"), hr);
|
||||
return false;
|
||||
}
|
||||
else // single selection
|
||||
|
||||
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 = fileDialog->GetResult(&item);
|
||||
hr = itemArray->GetItemAt(i, &item);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
wxLogApiError(wxS("IFileOpenDialog::GetResult"), hr);
|
||||
return false;
|
||||
// do not attempt to retrieve any other items
|
||||
// and just fail
|
||||
wxLogApiError(wxS("IShellItemArray::GetItem"), hr);
|
||||
tempPaths.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !ConvertIShellItemToPath(item, path) )
|
||||
hr = wxMSWImpl::GetFSPathFromShellItem(item, path);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
return false;
|
||||
// again, just fail
|
||||
tempPaths.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
tempPaths.push_back(path);
|
||||
@@ -456,8 +471,30 @@ bool GetPathsFromIFileOpenDialog(const wxCOMPtr<IFileOpenDialog>& fileDialog, bo
|
||||
return true;
|
||||
}
|
||||
|
||||
// helper function for wxDirDialog::ShowIFileOpenDialog()
|
||||
bool ConvertIShellItemToPath(const wxCOMPtr<IShellItem>& item, wxString& path)
|
||||
bool GetPathFromIFileDialog(IFileDialog* fileDialog, wxString& path)
|
||||
{
|
||||
wxCOMPtr<IShellItem> item;
|
||||
|
||||
HRESULT hr = fileDialog->GetResult(&item);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
wxLogApiError(wxS("IFileDialog::GetResult"), hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = wxMSWImpl::GetFSPathFromShellItem(item, path);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
HRESULT
|
||||
wxMSWImpl::GetFSPathFromShellItem(const wxCOMPtr<IShellItem>& item, wxString& path)
|
||||
{
|
||||
wxCoTaskMemPtr<WCHAR> pOLEPath;
|
||||
const HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &pOLEPath);
|
||||
@@ -465,16 +502,18 @@ bool ConvertIShellItemToPath(const wxCOMPtr<IShellItem>& item, wxString& path)
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
wxLogApiError(wxS("IShellItem::GetDisplayName"), hr);
|
||||
return false;
|
||||
return hr;
|
||||
}
|
||||
|
||||
path = pOLEPath;
|
||||
|
||||
return true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#endif // wxUSE_IFILEOPENDIALOG
|
||||
|
||||
#if wxUSE_DIRDLG
|
||||
|
||||
// callback used in wxDirDialog::ShowSHBrowseForFolder()
|
||||
static int CALLBACK
|
||||
BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
|
||||
@@ -525,6 +564,4 @@ BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // compiler/platform on which the code here compiles
|
||||
|
||||
#endif // wxUSE_DIRDLG
|
||||
|
||||
1089
src/msw/filedlg.cpp
1089
src/msw/filedlg.cpp
File diff suppressed because it is too large
Load Diff
@@ -411,12 +411,11 @@ void wxFileDialog::SetupExtraControls(WXWindow nativeWindow)
|
||||
return;
|
||||
|
||||
wxNonOwnedWindow::Create( GetParent(), nativeWindow );
|
||||
wxWindow* extracontrol = NULL;
|
||||
if ( HasExtraControlCreator() )
|
||||
{
|
||||
CreateExtraControl();
|
||||
extracontrol = GetExtraControl();
|
||||
}
|
||||
|
||||
// This won't do anything if there are no extra controls to create and
|
||||
// extracontrol will be NULL in this case.
|
||||
CreateExtraControl();
|
||||
wxWindow* const extracontrol = GetExtraControl();
|
||||
|
||||
NSView* accView = nil;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user