From 58772e4992e1a0601bddbf76fddbf4bb916cf85e Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Mon, 26 Jun 2006 01:04:44 +0000
Subject: [PATCH] picker controls improvements: fixes to valid paths
 recognition and event generation under GTK (patch 1510064)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@39838 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/latex/wx/clrpicker.tex  |  6 +---
 docs/latex/wx/dirpicker.tex  |  8 +++--
 docs/latex/wx/filepicker.tex |  4 ++-
 docs/latex/wx/fontpicker.tex |  3 +-
 include/wx/filepicker.h      | 68 +++++++++++++++++++++---------------
 include/wx/gtk/filepicker.h  | 32 +++++++++++------
 src/common/filepickercmn.cpp | 50 ++++++++++++++++++++++----
 src/gtk/filepicker.cpp       | 32 +++++++++++++++--
 8 files changed, 146 insertions(+), 57 deletions(-)

diff --git a/docs/latex/wx/clrpicker.tex b/docs/latex/wx/clrpicker.tex
index cc4d6be23c..a6d349e46d 100644
--- a/docs/latex/wx/clrpicker.tex
+++ b/docs/latex/wx/clrpicker.tex
@@ -32,11 +32,7 @@ It is only available if \texttt{wxUSE\_COLOURPICKERCTRL} is set to $1$ (the defa
 
 \twocolwidtha{5cm}%
 \begin{twocollist}\itemsep=0pt
-\twocolitem{\windowstyle{wxCLRP\_DEFAULT}}{Default style.}
-\twocolitem{\windowstyle{wxCLRP\_USE\_TEXTCTRL}}{Creates a text control to the left of the
-picker button which is completely managed by the \helpref{wxColourPickerCtrl}{wxcolourpickerctrl}
-and which can be used by the user to specify a colour (see \helpref{SetColour}{wxcolourpickerctrlsetcolour}).
-The text control is automatically synchronized with button's value. Use functions defined in \helpref{wxPickerBase}{wxpickerbase} to modify the text control.}
+\twocolitem{\windowstyle{wxCLRP\_DEFAULT}}{The default style: 0.}
 \twocolitem{\windowstyle{wxCLRP\_USE\_TEXTCTRL}}{Creates a text control to the left of the
 picker button which is completely managed by the \helpref{wxColourPickerCtrl}{wxcolourpickerctrl}
 and which can be used by the user to specify a colour (see \helpref{SetColour}{wxcolourpickerctrlsetcolour}).
diff --git a/docs/latex/wx/dirpicker.tex b/docs/latex/wx/dirpicker.tex
index f9bd32ffea..89364c49f2 100644
--- a/docs/latex/wx/dirpicker.tex
+++ b/docs/latex/wx/dirpicker.tex
@@ -32,7 +32,8 @@ It is only available if \texttt{wxUSE\_DIRPICKERCTRL} is set to $1$ (the default
 
 \twocolwidtha{5cm}%
 \begin{twocollist}\itemsep=0pt
-\twocolitem{\windowstyle{wxDIRP\_DEFAULT\_STYLE}}{Default style.}
+\twocolitem{\windowstyle{wxDIRP\_DEFAULT\_STYLE}}{The default style: includes
+wxDIRP\_DIR\_MUST\_EXIST and, under wxMSW only, wxDIRP\_USE\_TEXTCTRL.}
 \twocolitem{\windowstyle{wxDIRP\_USE\_TEXTCTRL}}{Creates a text control to the left of the
 picker button which is completely managed by the \helpref{wxDirPickerCtrl}{wxdirpickerctrl}
 and which can be used by the user to specify a path (see \helpref{SetPath}{wxdirpickerctrlsetpath}).
@@ -47,7 +48,10 @@ The text control is automatically synchronized with button's value. Use function
 
 \twocolwidtha{7cm}%
 \begin{twocollist}\itemsep=0pt
-\twocolitem{{\bf EVT\_DIRPICKER\_CHANGED(id, func)}}{The user changed the directory selected in the control either using the button or using text control (see wxDIRP_USE_TEXTCTRL; note that in this case the event is fired only if the user's input is valid, e.g. an existing directory path).}
+\twocolitem{{\bf EVT\_DIRPICKER\_CHANGED(id, func)}}{The user changed the
+directory selected in the control either using the button or using text control
+(see wxDIRP\_USE\_TEXTCTRL; note that in this case the event is fired only if
+the user's input is valid, e.g. an existing directory path).}
 \end{twocollist}
 
 \wxheading{See also}
diff --git a/docs/latex/wx/filepicker.tex b/docs/latex/wx/filepicker.tex
index f21c9e5687..46b2a7db49 100644
--- a/docs/latex/wx/filepicker.tex
+++ b/docs/latex/wx/filepicker.tex
@@ -32,7 +32,9 @@ It is only available if \texttt{wxUSE\_FILEPICKERCTRL} is set to $1$ (the defaul
 
 \twocolwidtha{5cm}%
 \begin{twocollist}\itemsep=0pt
-\twocolitem{\windowstyle{wxFLP\_DEFAULT\_STYLE}}{Default style.}
+\twocolitem{\windowstyle{wxFLP\_DEFAULT\_STYLE}}{The default style: includes
+wxFLP\_OPEN | wxFLP\_FILE\_MUST\_EXIST and, under wxMSW only,
+wxFLP\_USE\_TEXTCTRL.}
 \twocolitem{\windowstyle{wxFLP\_USE\_TEXTCTRL}}{Creates a text control to the left of the
 picker button which is completely managed by the \helpref{wxFilePickerCtrl}{wxfilepickerctrl}
 and which can be used by the user to specify a path (see \helpref{SetPath}{wxfilepickerctrlsetpath}).
diff --git a/docs/latex/wx/fontpicker.tex b/docs/latex/wx/fontpicker.tex
index 10f7f31c93..e3a46bbc0c 100644
--- a/docs/latex/wx/fontpicker.tex
+++ b/docs/latex/wx/fontpicker.tex
@@ -32,7 +32,8 @@ It is only available if \texttt{wxUSE\_FONTPICKERCTRL} is set to $1$ (the defaul
 
 \twocolwidtha{5cm}%
 \begin{twocollist}\itemsep=0pt
-\twocolitem{\windowstyle{wxFNTP\_DEFAULT}}{Default style.}
+\twocolitem{\windowstyle{wxFNTP\_DEFAULT}}{The default style:
+wxFNTP\_FONTDESC\_AS\_LABEL | wxFNTP\_USEFONT\_FOR\_LABEL.}
 \twocolitem{\windowstyle{wxFNTP\_USE\_TEXTCTRL}}{Creates a text control to the left of the
 picker button which is completely managed by the \helpref{wxFontPickerCtrl}{wxfontpickerctrl}
 and which can be used by the user to specify a font (see \helpref{SetSelectedFont}{wxfontpickerctrlsetselectedfont}).
diff --git a/include/wx/filepicker.h b/include/wx/filepicker.h
index e94746736b..fb0f10f6d8 100644
--- a/include/wx/filepicker.h
+++ b/include/wx/filepicker.h
@@ -17,7 +17,6 @@
 #if wxUSE_FILEPICKERCTRL || wxUSE_DIRPICKERCTRL
 
 #include "wx/pickerbase.h"
-#include "wx/filename.h"
 
 class WXDLLIMPEXP_CORE wxDialog;
 class WXDLLIMPEXP_CORE wxFileDirPickerEvent;
@@ -96,7 +95,6 @@ class WXDLLIMPEXP_CORE wxFileDirPickerCtrlBase : public wxPickerBase
 {
 public:
     wxFileDirPickerCtrlBase() : m_bIgnoreNextTextCtrlUpdate(false) {}
-    virtual ~wxFileDirPickerCtrlBase() {}
 
     // NB: no default values since this function will never be used
     //     directly by the user and derived classes wouldn't use them
@@ -139,6 +137,9 @@ public:        // internal functions
     // Returns the event type sent by this picker
     virtual wxEventType GetEventType() const = 0;
 
+    // Returns the filtered value currently placed in the text control (if present).
+    virtual wxString GetTextCtrlValue() const = 0;
+
 protected:
 
     // true if the next UpdateTextCtrl() call is to ignore
@@ -160,16 +161,15 @@ protected:
 
 #ifdef __WXGTK__
     // GTK apps usually don't have a textctrl next to the picker
-    #define wxFLP_DEFAULT_STYLE       (wxFLP_OPEN)
+    #define wxFLP_DEFAULT_STYLE       (wxFLP_OPEN|wxFLP_FILE_MUST_EXIST)
 #else
-    #define wxFLP_DEFAULT_STYLE       (wxFLP_USE_TEXTCTRL|wxFLP_OPEN)
+    #define wxFLP_DEFAULT_STYLE       (wxFLP_USE_TEXTCTRL|wxFLP_OPEN|wxFLP_FILE_MUST_EXIST)
 #endif
 
 class WXDLLIMPEXP_CORE wxFilePickerCtrl : public wxFileDirPickerCtrlBase
 {
 public:
     wxFilePickerCtrl() {}
-    virtual ~wxFilePickerCtrl() {}
 
     wxFilePickerCtrl(wxWindow *parent,
                      wxWindowID id,
@@ -217,10 +217,11 @@ public:     // overrides
         return true;
     }
 
-    bool CheckPath(const wxString &path) const
-    {
-        return HasFlag(wxFLP_SAVE) || wxFileName::FileExists(path);
-    }
+    // return true if the given path is valid for this control
+    bool CheckPath(const wxString& path) const;
+
+    // return the text control value in canonical form
+    wxString GetTextCtrlValue() const;
 
     bool IsCwdToUpdate() const
         { return HasFlag(wxFLP_CHANGE_DIR); }
@@ -255,35 +256,43 @@ private:
 
 #ifdef __WXGTK__
     // GTK apps usually don't have a textctrl next to the picker
-    #define wxDIRP_DEFAULT_STYLE       0
+    #define wxDIRP_DEFAULT_STYLE       (wxDIRP_DIR_MUST_EXIST)
 #else
-    #define wxDIRP_DEFAULT_STYLE       (wxDIRP_USE_TEXTCTRL)
+    #define wxDIRP_DEFAULT_STYLE       (wxDIRP_USE_TEXTCTRL|wxDIRP_DIR_MUST_EXIST)
 #endif
 
 class WXDLLIMPEXP_CORE wxDirPickerCtrl : public wxFileDirPickerCtrlBase
 {
 public:
     wxDirPickerCtrl() {}
-    virtual ~wxDirPickerCtrl() {}
 
     wxDirPickerCtrl(wxWindow *parent, wxWindowID id,
-        const wxString& path = wxEmptyString,
-        const wxString& message = wxDirSelectorPromptStr,
-        const wxPoint& pos = wxDefaultPosition,
-        const wxSize& size = wxDefaultSize, long style = wxDIRP_DEFAULT_STYLE,
-        const wxValidator& validator = wxDefaultValidator,
-        const wxString& name = wxDirPickerCtrlNameStr)
-    { Create(parent, id, path, message, pos, size, style, validator, name); }
+                    const wxString& path = wxEmptyString,
+                    const wxString& message = wxDirSelectorPromptStr,
+                    const wxPoint& pos = wxDefaultPosition,
+                    const wxSize& size = wxDefaultSize,
+                    long style = wxDIRP_DEFAULT_STYLE,
+                    const wxValidator& validator = wxDefaultValidator,
+                    const wxString& name = wxDirPickerCtrlNameStr)
+    {
+        Create(parent, id, path, message, pos, size, style, validator, name);
+    }
 
     bool Create(wxWindow *parent, wxWindowID id,
-        const wxString& path = wxEmptyString,
-        const wxString& message = wxDirSelectorPromptStr,
-        const wxPoint& pos = wxDefaultPosition,
-        const wxSize& size = wxDefaultSize, long style = wxDIRP_DEFAULT_STYLE,
-        const wxValidator& validator = wxDefaultValidator,
-        const wxString& name = wxDirPickerCtrlNameStr)
-    { return wxFileDirPickerCtrlBase::CreateBase(parent, id, path, message, wxEmptyString,
-                                                 pos, size, style, validator, name); }
+                const wxString& path = wxEmptyString,
+                const wxString& message = wxDirSelectorPromptStr,
+                const wxPoint& pos = wxDefaultPosition,
+                const wxSize& size = wxDefaultSize,
+                long style = wxDIRP_DEFAULT_STYLE,
+                const wxValidator& validator = wxDefaultValidator,
+                const wxString& name = wxDirPickerCtrlNameStr)
+    {
+        return wxFileDirPickerCtrlBase::CreateBase
+               (
+                    parent, id, path, message, wxEmptyString,
+                    pos, size, style, validator, name
+               );
+    }
 
 
 public:     // overrides
@@ -297,8 +306,9 @@ public:     // overrides
         return true;
     }
 
-    bool CheckPath(const wxString &path) const
-        { if (HasFlag(wxDIRP_DIR_MUST_EXIST)) return wxFileName::DirExists(path); else return true; }
+    bool CheckPath(const wxString &path) const;
+
+    wxString GetTextCtrlValue() const;
 
     bool IsCwdToUpdate() const
         { return HasFlag(wxDIRP_CHANGE_DIR); }
diff --git a/include/wx/gtk/filepicker.h b/include/wx/gtk/filepicker.h
index 5e04b4f88a..7a9efccc53 100644
--- a/include/wx/gtk/filepicker.h
+++ b/include/wx/gtk/filepicker.h
@@ -39,12 +39,6 @@
     {                                                                               \
         m_dialog->Destroy();                                                        \
         return wxButton::Destroy();                                                 \
-    }                                                                               \
-                                                                                    \
-    virtual void SetPath(const wxString &str)                                       \
-    {                                                                               \
-        m_path=str;                                                                 \
-        UpdateDialogPath(m_dialog);                                                 \
     }
 
 
@@ -105,6 +99,8 @@ public:     // some overrides
                      ~(wxFD_SAVE | wxFD_OVERWRITE_PROMPT)) | wxFD_OPEN;
     }
 
+    virtual void SetPath(const wxString &str);
+
     // see macro defined above
     FILEDIRBTN_OVERRIDES
 
@@ -123,7 +119,7 @@ private:
 class WXDLLIMPEXP_CORE wxDirButton : public wxGenericDirButton
 {
 public:
-    wxDirButton() { m_dialog = NULL;}
+    wxDirButton() { Init(); }
     wxDirButton(wxWindow *parent,
                 wxWindowID id,
                 const wxString& label = wxFilePickerWidgetLabel,
@@ -135,7 +131,8 @@ public:
                 const wxValidator& validator = wxDefaultValidator,
                 const wxString& name = wxFilePickerWidgetNameStr)
     {
-        m_dialog = NULL;
+        Init();
+
         Create(parent, id, label, path, message, wxEmptyString,
                 pos, size, style, validator, name);
     }
@@ -157,9 +154,6 @@ public:     // overrides
                 const wxValidator& validator = wxDefaultValidator,
                 const wxString& name = wxFilePickerWidgetNameStr);
 
-    // used by the GTK callback only
-    void UpdatePath(char *gtkpath)
-        { m_path = wxString::FromAscii(gtkpath); }
 
     // GtkFileChooserButton does not support GTK_FILE_CHOOSER_CREATE_FOLDER
     // thus we must ensure that the wxDD_DIR_MUST_EXIST style was given
@@ -168,12 +162,28 @@ public:     // overrides
         return (wxGenericDirButton::GetDialogStyle() | wxDD_DIR_MUST_EXIST);
     }
 
+    virtual void SetPath(const wxString &str);
+
     // see macro defined above
     FILEDIRBTN_OVERRIDES
 
 protected:
+    // common part of all ctors
+    void Init()
+    {
+        m_dialog = NULL;
+        m_bIgnoreNextChange = false;
+    }
+
     wxDialog *m_dialog;
 
+public:    // used by the GTK callback only
+
+    bool m_bIgnoreNextChange;
+
+    void UpdatePath(const char *gtkpath)
+        { m_path = wxString::FromAscii(gtkpath); }
+
 private:
     DECLARE_DYNAMIC_CLASS(wxDirButton)
 };
diff --git a/src/common/filepickercmn.cpp b/src/common/filepickercmn.cpp
index 3591e7d654..27fb86a331 100644
--- a/src/common/filepickercmn.cpp
+++ b/src/common/filepickercmn.cpp
@@ -27,6 +27,7 @@
 #if wxUSE_FILEPICKERCTRL || wxUSE_DIRPICKERCTRL
 
 #include "wx/filepicker.h"
+#include "wx/filename.h"
 
 #ifndef WX_PRECOMP
     #include "wx/textctrl.h"
@@ -106,9 +107,7 @@ void wxFileDirPickerCtrlBase::UpdatePickerFromTextCtrl()
     // remove the eventually present path-separator from the end of the textctrl
     // string otherwise we would generate a wxFileDirPickerEvent when changing
     // from e.g. /home/user to /home/user/ and we want to avoid it !
-    wxString newpath(m_text->GetValue());
-    if (!newpath.empty() && wxFileName::IsPathSeparator(newpath.Last()))
-        newpath.RemoveLast();
+    wxString newpath(GetTextCtrlValue());
     if (!CheckPath(newpath))
         return;       // invalid user input
 
@@ -120,7 +119,7 @@ void wxFileDirPickerCtrlBase::UpdatePickerFromTextCtrl()
         // NOTE: the path separator is required because if newpath is "C:"
         //       then no change would happen
         if (IsCwdToUpdate())
-            wxSetWorkingDirectory(newpath + wxFileName::GetPathSeparator());
+            wxSetWorkingDirectory(newpath);
 
         // fire an event
         wxFileDirPickerEvent event(GetEventType(), this, GetId(), newpath);
@@ -158,9 +157,48 @@ void wxFileDirPickerCtrlBase::OnFileDirChange(wxFileDirPickerEvent &ev)
 
 #endif  // wxUSE_FILEPICKERCTRL || wxUSE_DIRPICKERCTRL
 
+// ----------------------------------------------------------------------------
+// wxFileDirPickerCtrl
+// ----------------------------------------------------------------------------
+
 #if wxUSE_FILEPICKERCTRL
+
 IMPLEMENT_DYNAMIC_CLASS(wxFilePickerCtrl, wxPickerBase)
-#endif
+
+bool wxFilePickerCtrl::CheckPath(const wxString& path) const
+{
+    // if wxFLP_SAVE was given or wxFLP_FILE_MUST_EXIST has NOT been given we
+    // must accept any path
+    return HasFlag(wxFLP_SAVE) ||
+            !HasFlag(wxFLP_FILE_MUST_EXIST) ||
+                wxFileName::FileExists(path);
+}
+
+wxString wxFilePickerCtrl::GetTextCtrlValue() const
+{
+    // filter it through wxFileName to remove any spurious path separator
+    return wxFileName(m_text->GetValue()).GetFullPath();
+}
+
+#endif // wxUSE_FILEPICKERCTRL
+
+// ----------------------------------------------------------------------------
+// wxDirPickerCtrl
+// ----------------------------------------------------------------------------
+
 #if wxUSE_DIRPICKERCTRL
 IMPLEMENT_DYNAMIC_CLASS(wxDirPickerCtrl, wxPickerBase)
-#endif
+
+bool wxDirPickerCtrl::CheckPath(const wxString& path) const
+{
+    // if wxDIRP_DIR_MUST_EXIST has NOT been given we must accept any path
+    return !HasFlag(wxDIRP_DIR_MUST_EXIST) || wxFileName::DirExists(path);
+}
+
+wxString wxDirPickerCtrl::GetTextCtrlValue() const
+{
+    // filter it through wxFileName to remove any spurious path separator
+    return wxFileName::DirName(m_text->GetValue()).GetPath();
+}
+
+#endif // wxUSE_DIRPICKERCTRL
diff --git a/src/gtk/filepicker.cpp b/src/gtk/filepicker.cpp
index 6784f90430..f5f6a0a97b 100644
--- a/src/gtk/filepicker.cpp
+++ b/src/gtk/filepicker.cpp
@@ -20,12 +20,10 @@
 #if wxUSE_FILEPICKERCTRL && defined(__WXGTK26__)
 
 #include "wx/filepicker.h"
-
 #include "wx/tooltip.h"
 
 #include <gtk/gtk.h>
 
-#include <unistd.h> // chdir
 
 
 // ============================================================================
@@ -129,6 +127,13 @@ void wxFileButton::OnDialogOK(wxCommandEvent& ev)
     }
 }
 
+
+void wxFileButton::SetPath(const wxString &str)
+{
+    m_path = str;
+    UpdateDialogPath(m_dialog);
+}
+
 #endif      // wxUSE_FILEPICKERCTRL && defined(__WXGTK26__)
 
 
@@ -136,6 +141,8 @@ void wxFileButton::OnDialogOK(wxCommandEvent& ev)
 
 #if wxUSE_DIRPICKERCTRL && defined(__WXGTK26__)
 
+#include <unistd.h> // chdir
+
 //-----------------------------------------------------------------------------
 // "current-folder-changed"
 //-----------------------------------------------------------------------------
@@ -145,6 +152,12 @@ static void gtk_dirbutton_currentfolderchanged_callback(GtkFileChooserButton *wi
                                                         wxDirButton *p)
 {
     // update the m_path member of the wxDirButtonGTK
+    // unless the path was changed by wxDirButton::SetPath()
+    if (p->m_bIgnoreNextChange)
+    {
+        p->m_bIgnoreNextChange=false;
+        return;
+    }
     wxASSERT(p);
 
     // NB: it's important to use gtk_file_chooser_get_filename instead of
@@ -202,6 +215,7 @@ bool wxDirButton::Create( wxWindow *parent, wxWindowID id,
         m_wildcard = wildcard;
         if ((m_dialog = CreateDialog()) == NULL)
             return false;
+        SetPath(path);
 
         // little trick used to avoid problems when there are other GTK windows 'grabbed':
         // GtkFileChooserDialog won't be responsive to user events if there is another
@@ -246,4 +260,18 @@ wxDirButton::~wxDirButton()
     m_dialog->m_widget = NULL;
 }
 
+void wxDirButton::SetPath(const wxString &str)
+{
+    m_path = str;
+
+    // wxDirButton uses the "current-folder-changed" signal which is triggered also
+    // when we set the path on the dialog associated with this button; thus we need
+    // to set the following flag to avoid sending a wxFileDirPickerEvent from this
+    // function (which would be inconsistent with wxFileButton's behaviour and in
+    // general with all wxWidgets control-manipulation functions which do not send events).
+    m_bIgnoreNextChange = true;
+
+    UpdateDialogPath(m_dialog);
+}
+
 #endif      // wxUSE_DIRPICKERCTRL && defined(__WXGTK26__)
-- 
2.45.2

