Fixed strange situation when we had wxPathExists and wxDirExists with the same fincti...
[wxWidgets.git] / src / gtk / filedlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: gtk/filedlg.cpp
3 // Purpose: native implementation of wxFileDialog
4 // Author: Robert Roebling, Zbigniew Zagorski
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, 2004 Zbigniew Zagorski
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation "filedlggtk.h"
12 #endif
13
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
16
17 #if wxUSE_FILEDLG
18
19 #include "wx/filedlg.h"
20 #include "wx/utils.h"
21 #include "wx/intl.h"
22 #include "wx/filename.h"
23 #include "wx/msgdlg.h"
24
25 #include <gtk/gtk.h>
26
27 #ifdef __WXGTK24__
28 #include "wx/gtk/private.h"
29
30 //-----------------------------------------------------------------------------
31 // idle system
32 //-----------------------------------------------------------------------------
33
34 extern void wxapp_install_idle_handler();
35 extern bool g_isIdle;
36
37 //-----------------------------------------------------------------------------
38 // "clicked" for OK-button
39 //-----------------------------------------------------------------------------
40
41 static void gtk_filedialog_ok_callback(GtkWidget *widget, wxFileDialog *dialog)
42 {
43 int style = dialog->GetStyle();
44 gchar* text = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
45 wxString filename(wxGTK_CONV_BACK(text));
46
47 if ((style & wxSAVE) && (style & wxOVERWRITE_PROMPT))
48 {
49 if (wxFileExists(filename))
50 {
51 wxString msg;
52 msg.Printf(
53 _("File '%s' already exists, do you really want to overwrite it?"),
54 filename.c_str());
55
56 wxMessageDialog dlg(dialog, msg, _("Confirm"),
57 wxYES_NO | wxICON_QUESTION);
58 if (dlg.ShowModal() != wxID_YES)
59 return;
60 }
61 }
62 else if ((style & wxOPEN) && ( style & wxFILE_MUST_EXIST))
63 {
64 if (!wxFileExists( filename ))
65 {
66 wxMessageDialog dlg(dialog,
67 _("Please choose an existing file."),
68 _("Error"), wxOK | wxICON_ERROR);
69 dlg.ShowModal();
70
71 return;
72 }
73 }
74
75 // change to the directory where the user went if asked
76 if (style & wxCHANGE_DIR)
77 {
78 wxString cwd;
79 wxSplitPath(filename, &cwd, NULL, NULL);
80
81 if (cwd != wxGetCwd())
82 {
83 wxSetWorkingDirectory(cwd);
84 }
85 }
86
87 dialog->SetPath(filename);
88 dialog->UpdateFromDialog();
89
90 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK);
91 event.SetEventObject(dialog);
92 dialog->GetEventHandler()->ProcessEvent(event);
93 }
94
95 //-----------------------------------------------------------------------------
96 // "clicked" for Cancel-button
97 //-----------------------------------------------------------------------------
98
99 static void gtk_filedialog_cancel_callback(GtkWidget *WXUNUSED(w),
100 wxFileDialog *dialog)
101 {
102 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
103 event.SetEventObject(dialog);
104 dialog->GetEventHandler()->ProcessEvent(event);
105 }
106
107 static void gtk_filedialog_response_callback(GtkWidget *w,
108 int response,
109 wxFileDialog *dialog)
110 {
111 wxapp_install_idle_handler();
112
113 if (response == GTK_RESPONSE_ACCEPT)
114 gtk_filedialog_ok_callback(w, dialog);
115 else if (response == GTK_RESPONSE_CANCEL)
116 gtk_filedialog_cancel_callback(w, dialog);
117 else // "delete"
118 {
119 gtk_filedialog_cancel_callback(w, dialog);
120 dialog->m_destroyed_by_delete = true;
121 }
122 }
123 #endif
124
125 //-----------------------------------------------------------------------------
126 // wxFileDialog
127 //-----------------------------------------------------------------------------
128
129 IMPLEMENT_DYNAMIC_CLASS(wxFileDialog,wxGenericFileDialog)
130
131 BEGIN_EVENT_TABLE(wxFileDialog,wxGenericFileDialog)
132 EVT_BUTTON(wxID_OK, wxFileDialog::OnFakeOk)
133 END_EVENT_TABLE()
134
135 wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message,
136 const wxString& defaultDir,
137 const wxString& defaultFileName,
138 const wxString& wildCard,
139 long style, const wxPoint& pos)
140 : wxGenericFileDialog(parent, message, defaultDir, defaultFileName,
141 wildCard, style, pos, true )
142 {
143 #ifdef __WXGTK24__
144 if (!gtk_check_version(2,4,0))
145 {
146 m_needParent = false;
147 m_destroyed_by_delete = false;
148
149 if (!PreCreation(parent, pos, wxDefaultSize) ||
150 !CreateBase(parent, wxID_ANY, pos, wxDefaultSize, style,
151 wxDefaultValidator, wxT("filedialog")))
152 {
153 wxFAIL_MSG( wxT("wxFileDialog creation failed") );
154 return;
155 }
156
157 bool multiple = (style & wxMULTIPLE) == wxMULTIPLE;
158 GtkFileChooserAction gtk_action;
159 GtkWindow* gtk_parent = NULL;
160 if (parent)
161 gtk_parent = GTK_WINDOW(parent->m_widget);
162
163 gchar* ok_btn_stock;
164 if ((style & wxSAVE) == wxSAVE)
165 {
166 gtk_action = GTK_FILE_CHOOSER_ACTION_SAVE;
167 ok_btn_stock = GTK_STOCK_SAVE;
168 }
169 else
170 {
171 gtk_action = GTK_FILE_CHOOSER_ACTION_OPEN;
172 ok_btn_stock = GTK_STOCK_OPEN;
173 }
174 m_widget = gtk_file_chooser_dialog_new(
175 wxGTK_CONV(m_message),
176 gtk_parent,
177 gtk_action,
178 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
179 ok_btn_stock, GTK_RESPONSE_ACCEPT,
180 NULL);
181
182 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(m_widget), multiple);
183
184 gtk_signal_connect(GTK_OBJECT(m_widget),
185 "response",
186 GTK_SIGNAL_FUNC(gtk_filedialog_response_callback),
187 (gpointer*)this);
188
189 m_path = m_dir;
190 if (!m_path.empty() && m_path.Last() != wxT('/'))
191 m_path += wxT('/');
192 m_path += m_fileName;
193 SetPath(m_path);
194
195 SetWildcard(wildCard);
196 SetFilterIndex(0);
197 }
198 else
199 #endif
200 wxGenericFileDialog::Create( parent, message, defaultDir, defaultFileName, wildCard, style, pos );
201 }
202
203 wxFileDialog::~wxFileDialog()
204 {
205 #ifdef __WXGTK24__
206 if (!gtk_check_version(2,4,0))
207 {
208 if (m_destroyed_by_delete)
209 m_widget = NULL;
210 }
211 #endif
212 }
213
214 void wxFileDialog::OnFakeOk( wxCommandEvent &event )
215 {
216 #ifdef __WXGTK24__
217 if (!gtk_check_version(2,4,0))
218 wxDialog::OnOK( event );
219 else
220 #endif
221 wxGenericFileDialog::OnListOk( event );
222 }
223
224 int wxFileDialog::ShowModal()
225 {
226 #ifdef __WXGTK24__
227 if (!gtk_check_version(2,4,0))
228 return wxDialog::ShowModal();
229 else
230 #endif
231 return wxGenericFileDialog::ShowModal();
232 }
233
234 bool wxFileDialog::Show( bool show )
235 {
236 #ifdef __WXGTK24__
237 if (!gtk_check_version(2,4,0))
238 return wxDialog::Show( show );
239 else
240 #endif
241 return wxGenericFileDialog::Show( show );
242 }
243
244 void wxFileDialog::GetFilenames(wxArrayString& files) const
245 {
246 #ifdef __WXGTK24__
247 if (!gtk_check_version(2,4,0))
248 {
249 GetPaths(files);
250 for (size_t n = 0; n < files.GetCount(); n++ )
251 {
252 wxString name,ext;
253 wxSplitPath(files[n], NULL, &name, &ext);
254 if (!ext.empty())
255 {
256 name += wxT(".");
257 name += ext;
258 }
259 files[n] = name;
260 }
261 }
262 else
263 #endif
264 wxGenericFileDialog::GetFilenames( files );
265 }
266
267 void wxFileDialog::GetPaths(wxArrayString& paths) const
268 {
269 #ifdef __WXGTK24__
270 if (!gtk_check_version(2,4,0))
271 {
272 paths.Empty();
273 if (GetWindowStyle() & wxMULTIPLE)
274 {
275 GSList *gpathsi =
276 gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(m_widget));
277 GSList *gpaths = gpathsi;
278 while (gpathsi)
279 {
280 wxString file = wxGTK_CONV_BACK((gchar*) gpathsi->data);
281 paths.Add(file);
282 g_free(gpathsi->data);
283 gpathsi = gpathsi->next;
284 }
285 if (gpaths)
286 g_slist_free(gpaths);
287 }
288 else
289 {
290 paths.Add(m_fileName);
291 }
292 }
293 else
294 #endif
295 wxGenericFileDialog::GetPaths( paths );
296 }
297
298 void wxFileDialog::SetMessage(const wxString& message)
299 {
300 #ifdef __WXGTK24__
301 if (!gtk_check_version(2,4,0))
302 {
303 m_message = message;
304 SetTitle(message);
305 }
306 else
307 #endif
308 wxGenericFileDialog::SetMessage( message );
309 }
310
311 void wxFileDialog::SetPath(const wxString& path)
312 {
313 #ifdef __WXGTK24__
314 if (!gtk_check_version(2,4,0))
315 {
316 if (path.empty()) return;
317
318 wxFileName fn(path);
319 m_path = fn.GetFullPath();
320 m_dir = fn.GetPath();
321 m_fileName = fn.GetFullName();
322 UpdateDialog();
323 }
324 else
325 #endif
326 wxGenericFileDialog::SetPath( path );
327 }
328
329 void wxFileDialog::SetDirectory(const wxString& dir)
330 {
331 #ifdef __WXGTK24__
332 if (!gtk_check_version(2,4,0))
333 {
334 if (wxPathExists(dir))
335 {
336 m_dir = dir;
337 m_path = wxFileName(m_dir, m_fileName).GetFullPath();
338 UpdateDialog();
339 }
340 }
341 else
342 #endif
343 wxGenericFileDialog::SetDirectory( dir );
344 }
345
346 void wxFileDialog::SetFilename(const wxString& name)
347 {
348 #ifdef __WXGTK24__
349 if (!gtk_check_version(2,4,0))
350 {
351 m_fileName = name;
352 m_path = wxFileName(m_dir, m_fileName).GetFullPath();
353 UpdateDialog();
354 }
355 else
356 #endif
357 wxGenericFileDialog::SetFilename( name );
358 }
359
360 void wxFileDialog::SetWildcard(const wxString& wildCard)
361 {
362 #ifdef __WXGTK24__
363 if (!gtk_check_version(2,4,0))
364 {
365 m_wildCard = wildCard;
366 GtkFileChooser* chooser = GTK_FILE_CHOOSER(m_widget);
367
368 // empty current filter list:
369 GSList* ifilters = gtk_file_chooser_list_filters(chooser);
370 GSList* filters = ifilters;
371 while (ifilters)
372 {
373 gtk_file_chooser_remove_filter(chooser,GTK_FILE_FILTER(ifilters->data));
374 ifilters = ifilters->next;
375 }
376 g_slist_free(filters);
377
378 // parse filters
379 wxArrayString wildDescriptions, wildFilters;
380 if (!wxParseCommonDialogsFilter(m_wildCard, wildDescriptions, wildFilters))
381 {
382 wxFAIL_MSG( wxT("Wrong file type description") );
383 }
384 else
385 {
386 // add parsed to GtkChooser
387 for (size_t n = 0; n < wildFilters.GetCount(); n++)
388 {
389 GtkFileFilter* filter = gtk_file_filter_new();
390 gtk_file_filter_set_name(filter,wxGTK_CONV(wildDescriptions[n]));
391 wxString after = wildFilters[n];
392 do
393 {
394 wxString ext = after.BeforeFirst(wxT(';'));
395 gtk_file_filter_add_pattern(filter,wxGTK_CONV(ext));
396 if (after.Find(wxT(';')) == wxNOT_FOUND)
397 break;
398 after = after.AfterLast(wxT(';'));
399 }
400 while (!after.empty());
401
402 gtk_file_chooser_add_filter(chooser, filter);
403 }
404 }
405 }
406 else
407 #endif
408 wxGenericFileDialog::SetWildcard( wildCard );
409 }
410
411 void wxFileDialog::SetFilterIndex(int filterIndex)
412 {
413 #ifdef __WXGTK24__
414 if (!gtk_check_version(2,4,0))
415 {
416 m_filterIndex = filterIndex;
417
418 GtkFileChooser *chooser = GTK_FILE_CHOOSER(m_widget);
419 GSList *fnode = gtk_file_chooser_list_filters(chooser);
420 GSList *filters = fnode;
421 int i = 0;
422 while (fnode)
423 {
424 if (i == filterIndex)
425 {
426 gtk_file_chooser_set_filter(chooser, GTK_FILE_FILTER(fnode->data));
427 m_filterIndex = i;
428 break;
429 }
430 i++;
431 fnode = fnode->next;
432 }
433 g_slist_free(filters);
434 }
435 else
436 #endif
437 wxGenericFileDialog::SetFilterIndex( filterIndex );
438 }
439
440 void wxFileDialog::UpdateDialog()
441 {
442 #ifdef __WXGTK24__
443 // set currently selected directory to match the path:
444 if (!m_dir.empty() && wxPathExists(m_dir))
445 {
446 // NB: This is important -- if we set directory only and not the path,
447 // then dialog will still remember old path set using previous
448 // call to gtk_chooser_set_filename. If the previous directory
449 // was a subdirectory of the directory we want to select now,
450 // the dialog would still contain directory selector controls
451 // for the subdirectory (with the parent directory selected),
452 // instead of showing only the parent directory as expected.
453 // This way, we force GtkFileChooser to really change the
454 // directory. Finally, it doesn't have to be done if filename
455 // is not empty because of the code that sets the filename below.
456 if (m_fileName.empty())
457 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(m_widget),
458 wxGTK_CONV(m_dir));
459
460 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(m_widget),
461 wxGTK_CONV(m_dir));
462 }
463
464 // if the user set only the directory (e.g. by calling SetDirectory)
465 // and not the default filename, then we don't want to set the filename:
466 if (!m_fileName.empty())
467 {
468 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(m_widget),
469 wxGTK_CONV(m_path));
470
471 // pre-fill the filename when saving, too (there's no text entry
472 // control when opening a file, so it doesn't make sense to
473 // do this when opening files):
474 if (GetWindowStyle() & wxSAVE)
475 {
476 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(m_widget),
477 wxGTK_CONV(m_fileName));
478 }
479 }
480 #endif
481 }
482
483 void wxFileDialog::UpdateFromDialog()
484 {
485 #ifdef __WXGTK24__
486 // update filterIndex
487 GSList *fnode = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(m_widget));
488 GSList *filters = fnode;
489 GtkFileFilter *current =
490 gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(m_widget));
491
492 int i = 0;
493 m_filterIndex = 0;
494 while (fnode)
495 {
496 if (fnode->data == (gpointer)current)
497 {
498 m_filterIndex = i;
499 break;
500 }
501 i++;
502 fnode = fnode->next;
503 }
504 g_slist_free(filters);
505 #endif
506 }
507
508 #endif // wxUSE_FILEDLG