1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/filepicker.cpp
3 // Purpose:     implementation of wxFileButton and wxDirButton
4 // Author:      Francesco Montorsi
5 // Modified By:
6 // Created:     15/04/2006
7 // Id:          $Id: filepicker.cpp 54732 2008-07-20 22:48:34Z VZ $
8 // Copyright:   (c) Francesco Montorsi
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 
13 // ----------------------------------------------------------------------------
14 // headers
15 // ----------------------------------------------------------------------------
16 
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
19 
20 #if wxUSE_FILEPICKERCTRL && defined(__WXGTK26__)
21 
22 #include "wx/filepicker.h"
23 #include "wx/tooltip.h"
24 
25 #include "wx/gtk/private.h"
26 
27 // ============================================================================
28 // implementation
29 // ============================================================================
30 
31 //-----------------------------------------------------------------------------
32 // wxFileButton
33 //-----------------------------------------------------------------------------
34 
IMPLEMENT_DYNAMIC_CLASS(wxFileButton,wxButton)35 IMPLEMENT_DYNAMIC_CLASS(wxFileButton, wxButton)
36 
37 bool wxFileButton::Create( wxWindow *parent, wxWindowID id,
38                         const wxString &label, const wxString &path,
39                         const wxString &message, const wxString &wildcard,
40                         const wxPoint &pos, const wxSize &size,
41                         long style, const wxValidator& validator,
42                         const wxString &name )
43 {
44     // we can't use the native button for wxFLP_SAVE pickers as it can only
45     // open existing files and there is no way to create a new file using it
46     if ( !(style & wxFLP_SAVE) && !gtk_check_version(2,6,0) )
47     {
48         // VERY IMPORTANT: this code is identic to relative code in wxDirButton;
49         //                 if you find a problem here, fix it also in wxDirButton !
50 
51         m_needParent = true;
52         m_acceptsFocus = true;
53 
54         if (!PreCreation( parent, pos, size ) ||
55             !wxControl::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
56                                     validator, name))
57         {
58             wxFAIL_MSG( wxT("wxFileButton creation failed") );
59             return false;
60         }
61 
62         // create the dialog associated with this button
63         // NB: unlike generic implementation, native GTK implementation needs to create
64         //     the filedialog here as it needs to use gtk_file_chooser_button_new_with_dialog()
65         SetWindowStyle(style);
66         m_path = path;
67         m_message = message;
68         m_wildcard = wildcard;
69         if ((m_dialog = CreateDialog()) == NULL)
70             return false;
71 
72         // little trick used to avoid problems when there are other GTK windows 'grabbed':
73         // GtkFileChooserDialog won't be responsive to user events if there is another
74         // window which called gtk_grab_add (and this happens if e.g. a wxDialog is running
75         // in modal mode in the application - see wxDialogGTK::ShowModal).
76         // An idea could be to put the grab on the m_dialog->m_widget when the GtkFileChooserButton
77         // is clicked and then remove it as soon as the user closes the dialog itself.
78         // Unfortunately there's no way to hook in the 'clicked' event of the GtkFileChooserButton,
79         // thus we add grab on m_dialog->m_widget when it's shown and remove it when it's
80         // hidden simply using its "show" and "hide" events - clean & simple :)
81         g_signal_connect(m_dialog->m_widget, "show", G_CALLBACK(gtk_grab_add), NULL);
82         g_signal_connect(m_dialog->m_widget, "hide", G_CALLBACK(gtk_grab_remove), NULL);
83 
84         // NOTE: we deliberately ignore the given label as GtkFileChooserButton
85         //       use as label the currently selected file
86         m_widget = gtk_file_chooser_button_new_with_dialog( m_dialog->m_widget );
87         gtk_widget_show( GTK_WIDGET(m_widget) );
88 
89         // we need to know when the dialog has been dismissed clicking OK...
90         // NOTE: the "clicked" signal is not available for a GtkFileChooserButton
91         //       thus we are forced to use wxFileDialog's event
92         m_dialog->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
93                 wxCommandEventHandler(wxFileButton::OnDialogOK),
94                 NULL, this);
95 
96         m_parent->DoAddChild( this );
97 
98         PostCreation(size);
99         SetInitialSize(size);
100     }
101     else
102         return wxGenericFileButton::Create(parent, id, label, path, message, wildcard,
103                                            pos, size, style, validator, name);
104     return true;
105 }
106 
~wxFileButton()107 wxFileButton::~wxFileButton()
108 {
109     // GtkFileChooserButton will automatically destroy the
110     // GtkFileChooserDialog associated with m_dialog.
111     // Thus we have to set its m_widget to NULL to avoid
112     // double destruction on same widget
113     if (m_dialog)
114     	m_dialog->m_widget = NULL;
115 }
116 
OnDialogOK(wxCommandEvent & ev)117 void wxFileButton::OnDialogOK(wxCommandEvent& ev)
118 {
119     // the wxFileDialog associated with the GtkFileChooserButton has been closed
120     // using the OK button, thus the selected file has changed...
121     if (ev.GetId() == wxID_OK)
122     {
123         // ...update our path
124         UpdatePathFromDialog(m_dialog);
125 
126         // ...and fire an event
127         wxFileDirPickerEvent event(wxEVT_COMMAND_FILEPICKER_CHANGED, this, GetId(), m_path);
128         GetEventHandler()->ProcessEvent(event);
129     }
130 }
131 
SetPath(const wxString & str)132 void wxFileButton::SetPath(const wxString &str)
133 {
134     m_path = str;
135     if (m_dialog)
136     	UpdateDialogPath(m_dialog);
137 }
138 
139 #endif      // wxUSE_FILEPICKERCTRL && defined(__WXGTK26__)
140 
141 
142 
143 
144 #if wxUSE_DIRPICKERCTRL && defined(__WXGTK26__)
145 
146 #include <unistd.h> // chdir
147 
148 //-----------------------------------------------------------------------------
149 // "current-folder-changed"
150 //-----------------------------------------------------------------------------
151 
152 extern "C" {
gtk_dirbutton_currentfolderchanged_callback(GtkFileChooserButton * widget,wxDirButton * p)153 static void gtk_dirbutton_currentfolderchanged_callback(GtkFileChooserButton *widget,
154                                                         wxDirButton *p)
155 {
156     // update the m_path member of the wxDirButtonGTK
157     // unless the path was changed by wxDirButton::SetPath()
158     if (p->m_bIgnoreNextChange)
159     {
160         p->m_bIgnoreNextChange=false;
161         return;
162     }
163     wxASSERT(p);
164 
165     // NB: it's important to use gtk_file_chooser_get_filename instead of
166     //     gtk_file_chooser_get_current_folder (see GTK docs) !
167     wxGtkString filename(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)));
168     p->UpdatePath(filename);
169 
170     // since GtkFileChooserButton when used to pick directories also uses a combobox,
171     // maybe that the current folder has been changed but not through the GtkFileChooserDialog
172     // and thus the 'gtk_filedialog_ok_callback' could have not been called...
173     // thus we need to make sure the current working directory is updated if wxDIRP_CHANGE_DIR
174     // style was given.
175     if (p->HasFlag(wxDIRP_CHANGE_DIR))
176         chdir(filename);
177 
178     // ...and fire an event
179     wxFileDirPickerEvent event(wxEVT_COMMAND_DIRPICKER_CHANGED, p, p->GetId(), p->GetPath());
180     p->GetEventHandler()->ProcessEvent(event);
181 }
182 }
183 
184 
185 //-----------------------------------------------------------------------------
186 // wxDirButtonGTK
187 //-----------------------------------------------------------------------------
188 
IMPLEMENT_DYNAMIC_CLASS(wxDirButton,wxButton)189 IMPLEMENT_DYNAMIC_CLASS(wxDirButton, wxButton)
190 
191 bool wxDirButton::Create( wxWindow *parent, wxWindowID id,
192                         const wxString &label, const wxString &path,
193                         const wxString &message, const wxString &wildcard,
194                         const wxPoint &pos, const wxSize &size,
195                         long style, const wxValidator& validator,
196                         const wxString &name )
197 {
198     if (!gtk_check_version(2,6,0))
199     {
200         // VERY IMPORTANT: this code is identic to relative code in wxFileButton;
201         //                 if you find a problem here, fix it also in wxFileButton !
202 
203         m_needParent = true;
204         m_acceptsFocus = true;
205 
206         if (!PreCreation( parent, pos, size ) ||
207             !wxControl::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
208                                     validator, name))
209         {
210             wxFAIL_MSG( wxT("wxDirButtonGTK creation failed") );
211             return false;
212         }
213 
214         // create the dialog associated with this button
215         SetWindowStyle(style);
216         m_message = message;
217         m_wildcard = wildcard;
218         if ((m_dialog = CreateDialog()) == NULL)
219             return false;
220         SetPath(path);
221 
222         // little trick used to avoid problems when there are other GTK windows 'grabbed':
223         // GtkFileChooserDialog won't be responsive to user events if there is another
224         // window which called gtk_grab_add (and this happens if e.g. a wxDialog is running
225         // in modal mode in the application - see wxDialogGTK::ShowModal).
226         // An idea could be to put the grab on the m_dialog->m_widget when the GtkFileChooserButton
227         // is clicked and then remove it as soon as the user closes the dialog itself.
228         // Unfortunately there's no way to hook in the 'clicked' event of the GtkFileChooserButton,
229         // thus we add grab on m_dialog->m_widget when it's shown and remove it when it's
230         // hidden simply using its "show" and "hide" events - clean & simple :)
231         g_signal_connect(m_dialog->m_widget, "show", G_CALLBACK(gtk_grab_add), NULL);
232         g_signal_connect(m_dialog->m_widget, "hide", G_CALLBACK(gtk_grab_remove), NULL);
233 
234 
235         // NOTE: we deliberately ignore the given label as GtkFileChooserButton
236         //       use as label the currently selected file
237         m_widget = gtk_file_chooser_button_new_with_dialog( m_dialog->m_widget );
238 
239         gtk_widget_show( GTK_WIDGET(m_widget) );
240 
241         // GtkFileChooserButton signals
242         g_signal_connect(m_widget, "current-folder-changed",
243                          G_CALLBACK(gtk_dirbutton_currentfolderchanged_callback), this);
244 
245         m_parent->DoAddChild( this );
246 
247         PostCreation(size);
248         SetInitialSize(size);
249     }
250     else
251         return wxGenericDirButton::Create(parent, id, label, path, message, wildcard,
252                                           pos, size, style, validator, name);
253     return true;
254 }
255 
~wxDirButton()256 wxDirButton::~wxDirButton()
257 {
258     // GtkFileChooserButton will automatically destroy the
259     // GtkFileChooserDialog associated with m_dialog.
260     // Thus we have to set its m_widget to NULL to avoid
261     // double destruction on same widget
262     if (m_dialog)
263     	m_dialog->m_widget = NULL;
264 }
265 
SetPath(const wxString & str)266 void wxDirButton::SetPath(const wxString& str)
267 {
268     if ( m_path == str )
269     {
270         // don't do anything and especially don't set m_bIgnoreNextChange
271         return;
272     }
273 
274     m_path = str;
275 
276     // wxDirButton uses the "current-folder-changed" signal which is triggered also
277     // when we set the path on the dialog associated with this button; thus we need
278     // to set the following flag to avoid sending a wxFileDirPickerEvent from this
279     // function (which would be inconsistent with wxFileButton's behaviour and in
280     // general with all wxWidgets control-manipulation functions which do not send events).
281     m_bIgnoreNextChange = true;
282 
283 	if (m_dialog)
284     	UpdateDialogPath(m_dialog);
285 }
286 
287 #endif      // wxUSE_DIRPICKERCTRL && defined(__WXGTK26__)
288