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