[wxGTK] wxFileDialog: Use native overwrite confirmation if possible
[wxWidgets.git] / src / gtk / filedlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: gtk/filedlg.cpp
3 // Purpose: native implementation of wxFileDialog
4 // Author: Robert Roebling, Zbigniew Zagorski, Mart Raudsepp
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, 2004 Zbigniew Zagorski, 2005 Mart Raudsepp
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 // Include setup.h to get wxUSE flags for compilers that do not support precompilation of headers
18 #include "wx/setup.h"
19
20 #if wxUSE_FILEDLG
21
22 #include "wx/filedlg.h"
23
24 #ifdef __WXGTK24__
25
26 #include <gtk/gtk.h>
27 #include "wx/gtk/private.h"
28
29 #include <unistd.h> // chdir
30
31 #include "wx/intl.h"
32 #include "wx/filename.h" // wxFilename
33 #include "wx/tokenzr.h" // wxStringTokenizer
34 #include "wx/filefn.h" // ::wxGetCwd
35 #include "wx/msgdlg.h" // wxMessageDialog
36
37 //-----------------------------------------------------------------------------
38 // idle system
39 //-----------------------------------------------------------------------------
40
41 extern void wxapp_install_idle_handler();
42
43 //-----------------------------------------------------------------------------
44 // "clicked" for OK-button
45 //-----------------------------------------------------------------------------
46
47 extern "C" {
48 static void gtk_filedialog_ok_callback(GtkWidget *widget, wxFileDialog *dialog)
49 {
50 int style = dialog->GetStyle();
51 gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
52
53 // gtk version numbers must be identical with the one in ctor (that calls set_do_overwrite_confirmation)
54 #if GTK_CHECK_VERSION(2,7,3)
55 if(gtk_check_version(2,7,3) != NULL)
56 #endif
57 if ((style & wxSAVE) && (style & wxOVERWRITE_PROMPT))
58 {
59 if ( g_file_test(filename, G_FILE_TEST_EXISTS) )
60 {
61 wxString msg;
62
63 msg.Printf(
64 _("File '%s' already exists, do you really want to overwrite it?"),
65 wxString(wxConvFileName->cMB2WX(filename)).c_str());
66
67 wxMessageDialog dlg(dialog, msg, _("Confirm"),
68 wxYES_NO | wxICON_QUESTION);
69 if (dlg.ShowModal() != wxID_YES)
70 return;
71 }
72 }
73
74 // change to the directory where the user went if asked
75 if (style & wxCHANGE_DIR)
76 {
77 // Use chdir to not care about filename encodings
78 gchar* folder = g_path_get_dirname(filename);
79 chdir(folder);
80 g_free(folder);
81 }
82
83 g_free(filename);
84
85 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK);
86 event.SetEventObject(dialog);
87 dialog->GetEventHandler()->ProcessEvent(event);
88 }
89 }
90
91 //-----------------------------------------------------------------------------
92 // "clicked" for Cancel-button
93 //-----------------------------------------------------------------------------
94
95 extern "C" {
96 static void gtk_filedialog_cancel_callback(GtkWidget *WXUNUSED(w),
97 wxFileDialog *dialog)
98 {
99 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
100 event.SetEventObject(dialog);
101 dialog->GetEventHandler()->ProcessEvent(event);
102 }
103 }
104
105 extern "C" {
106 static void gtk_filedialog_response_callback(GtkWidget *w,
107 gint response,
108 wxFileDialog *dialog)
109 {
110 wxapp_install_idle_handler();
111
112 if (response == GTK_RESPONSE_ACCEPT)
113 gtk_filedialog_ok_callback(w, dialog);
114 else if (response == GTK_RESPONSE_CANCEL)
115 gtk_filedialog_cancel_callback(w, dialog);
116 else // "delete"
117 {
118 gtk_filedialog_cancel_callback(w, dialog);
119 dialog->m_destroyed_by_delete = true;
120 }
121 }
122 }
123
124 #endif // __WXGTK24__
125
126 //-----------------------------------------------------------------------------
127 // wxFileDialog
128 //-----------------------------------------------------------------------------
129
130 IMPLEMENT_DYNAMIC_CLASS(wxFileDialog,wxGenericFileDialog)
131
132 BEGIN_EVENT_TABLE(wxFileDialog,wxGenericFileDialog)
133 EVT_BUTTON(wxID_OK, wxFileDialog::OnFakeOk)
134 END_EVENT_TABLE()
135
136 wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message,
137 const wxString& defaultDir,
138 const wxString& defaultFileName,
139 const wxString& wildCard,
140 long style, const wxPoint& pos)
141 : wxGenericFileDialog(parent, message, defaultDir, defaultFileName,
142 wildCard, style, pos, true )
143 {
144 #ifdef __WXGTK24__
145 if (!gtk_check_version(2,4,0))
146 {
147 wxASSERT_MSG( !( (style & wxSAVE) && (style & wxMULTIPLE) ), wxT("wxFileDialog - wxMULTIPLE used on a save dialog" ) );
148 m_needParent = false;
149 m_destroyed_by_delete = false;
150
151 if (!PreCreation(parent, pos, wxDefaultSize) ||
152 !CreateBase(parent, wxID_ANY, pos, wxDefaultSize, style,
153 wxDefaultValidator, wxT("filedialog")))
154 {
155 wxFAIL_MSG( wxT("wxFileDialog creation failed") );
156 return;
157 }
158
159 GtkFileChooserAction gtk_action;
160 GtkWindow* gtk_parent = NULL;
161 if (parent)
162 gtk_parent = GTK_WINDOW( gtk_widget_get_toplevel(parent->m_widget) );
163
164 gchar* ok_btn_stock;
165 if ( style & wxSAVE )
166 {
167 gtk_action = GTK_FILE_CHOOSER_ACTION_SAVE;
168 ok_btn_stock = GTK_STOCK_SAVE;
169 }
170 else
171 {
172 gtk_action = GTK_FILE_CHOOSER_ACTION_OPEN;
173 ok_btn_stock = GTK_STOCK_OPEN;
174 }
175
176 m_widget = gtk_file_chooser_dialog_new(
177 wxGTK_CONV(m_message),
178 gtk_parent,
179 gtk_action,
180 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
181 ok_btn_stock, GTK_RESPONSE_ACCEPT,
182 NULL);
183
184 if ( style & wxMULTIPLE )
185 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(m_widget), true);
186
187 // local-only property could be set to false to allow non-local files to be loaded.
188 // In that case get/set_uri(s) should be used instead of get/set_filename(s) everywhere
189 // and the GtkFileChooserDialog should probably also be created with a backend,
190 // e.g "gnome-vfs", "default", ... (gtk_file_chooser_dialog_new_with_backend).
191 // Currently local-only is kept as the default - true:
192 // gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(m_widget), true);
193
194 g_signal_connect(G_OBJECT(m_widget), "response",
195 GTK_SIGNAL_FUNC(gtk_filedialog_response_callback), (gpointer)this);
196
197 SetWildcard(wildCard);
198
199 if ( style & wxSAVE )
200 {
201 if ( !defaultDir.empty() )
202 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(m_widget),
203 wxConvFileName->cWX2MB(defaultDir));
204
205 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(m_widget),
206 wxConvFileName->cWX2MB(defaultFileName));
207
208 #if GTK_CHECK_VERSION(2,7,3)
209 if (!gtk_check_version(2,7,3))
210 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(m_widget), TRUE);
211 #endif
212 }
213 else
214 {
215 if ( !defaultFileName.empty() )
216 {
217 wxString dir;
218 if ( defaultDir.empty() )
219 dir = ::wxGetCwd();
220 else
221 dir = defaultDir;
222
223 gtk_file_chooser_set_filename(
224 GTK_FILE_CHOOSER(m_widget),
225 wxConvFileName->cWX2MB( wxFileName(dir, defaultFileName).GetFullPath() ) );
226 }
227 else if ( !defaultDir.empty() )
228 gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(m_widget),
229 wxConvFileName->cWX2MB(defaultDir) );
230 }
231 }
232 else
233 #endif
234 wxGenericFileDialog::Create( parent, message, defaultDir, defaultFileName, wildCard, style, pos );
235 }
236
237 wxFileDialog::~wxFileDialog()
238 {
239 #ifdef __WXGTK24__
240 if (!gtk_check_version(2,4,0))
241 {
242 if (m_destroyed_by_delete)
243 m_widget = NULL;
244 }
245 #endif
246 }
247
248 void wxFileDialog::OnFakeOk( wxCommandEvent &event )
249 {
250 #ifdef __WXGTK24__
251 if (!gtk_check_version(2,4,0))
252 wxDialog::OnOK( event );
253 else
254 #endif
255 wxGenericFileDialog::OnListOk( event );
256 }
257
258 int wxFileDialog::ShowModal()
259 {
260 #ifdef __WXGTK24__
261 if (!gtk_check_version(2,4,0))
262 return wxDialog::ShowModal();
263 else
264 #endif
265 return wxGenericFileDialog::ShowModal();
266 }
267
268 bool wxFileDialog::Show( bool show )
269 {
270 #ifdef __WXGTK24__
271 if (!gtk_check_version(2,4,0))
272 return wxDialog::Show( show );
273 else
274 #endif
275 return wxGenericFileDialog::Show( show );
276 }
277
278 void wxFileDialog::DoSetSize(int x, int y, int width, int height, int sizeFlags )
279 {
280 if (!m_wxwindow)
281 return;
282 else
283 wxGenericFileDialog::DoSetSize( x, y, width, height, sizeFlags );
284 }
285
286 wxString wxFileDialog::GetPath() const
287 {
288 #ifdef __WXGTK24__
289 if (!gtk_check_version(2,4,0))
290 return wxConvFileName->cMB2WX(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(m_widget)));
291 else
292 #endif
293 return wxGenericFileDialog::GetPath();
294 }
295
296 void wxFileDialog::GetFilenames(wxArrayString& files) const
297 {
298 #ifdef __WXGTK24__
299 if (!gtk_check_version(2,4,0))
300 {
301 GetPaths(files);
302 for (size_t n = 0; n < files.GetCount(); ++n )
303 {
304 wxFileName file(files[n]);
305 files[n] = file.GetFullName();
306 }
307 }
308 else
309 #endif
310 wxGenericFileDialog::GetFilenames( files );
311 }
312
313 void wxFileDialog::GetPaths(wxArrayString& paths) const
314 {
315 #ifdef __WXGTK24__
316 if (!gtk_check_version(2,4,0))
317 {
318 paths.Empty();
319 if (gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(m_widget)))
320 {
321 GSList *gpathsi = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(m_widget));
322 GSList *gpaths = gpathsi;
323 while (gpathsi)
324 {
325 wxString file(wxConvFileName->cMB2WX((gchar*) gpathsi->data));
326 paths.Add(file);
327 g_free(gpathsi->data);
328 gpathsi = gpathsi->next;
329 }
330
331 g_slist_free(gpaths);
332 }
333 else
334 paths.Add(GetPath());
335 }
336 else
337 #endif
338 wxGenericFileDialog::GetPaths( paths );
339 }
340
341 void wxFileDialog::SetMessage(const wxString& message)
342 {
343 #ifdef __WXGTK24__
344 if (!gtk_check_version(2,4,0))
345 {
346 m_message = message;
347 SetTitle(message);
348 }
349 else
350 #endif
351 wxGenericFileDialog::SetMessage( message );
352 }
353
354 void wxFileDialog::SetPath(const wxString& path)
355 {
356 #ifdef __WXGTK24__
357 if (!gtk_check_version(2,4,0))
358 {
359 if (path.empty()) return;
360
361 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(m_widget), wxConvFileName->cWX2MB(path));
362 }
363 else
364 #endif
365 wxGenericFileDialog::SetPath( path );
366 }
367
368 void wxFileDialog::SetDirectory(const wxString& dir)
369 {
370 #ifdef __WXGTK24__
371 if (!gtk_check_version(2,4,0))
372 {
373 if (wxDirExists(dir))
374 {
375 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(m_widget), wxConvFileName->cWX2MB(dir));
376 }
377 }
378 else
379 #endif
380 wxGenericFileDialog::SetDirectory( dir );
381 }
382
383 wxString wxFileDialog::GetDirectory() const
384 {
385 #ifdef __WXGTK24__
386 if (!gtk_check_version(2,4,0))
387 return wxConvFileName->cMB2WX(
388 gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER(m_widget) ) );
389 else
390 #endif
391 return wxGenericFileDialog::GetDirectory();
392 }
393
394 void wxFileDialog::SetFilename(const wxString& name)
395 {
396 #ifdef __WXGTK24__
397 if (!gtk_check_version(2,4,0))
398 {
399 if (GetStyle() & wxSAVE)
400 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(m_widget), wxConvFileName->cWX2MB(name));
401 else
402 SetPath(wxFileName(GetDirectory(), name).GetFullPath());
403 }
404 else
405 #endif
406 wxGenericFileDialog::SetFilename( name );
407 }
408
409 wxString wxFileDialog::GetFilename() const
410 {
411 #ifdef __WXGTK24__
412 if (!gtk_check_version(2,4,0))
413 return wxFileName(
414 wxConvFileName->cMB2WX(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(m_widget))) ).GetFullName();
415 else
416 #endif
417 return wxGenericFileDialog::GetFilename();
418 }
419
420 void wxFileDialog::SetWildcard(const wxString& wildCard)
421 {
422 #ifdef __WXGTK24__
423 if (!gtk_check_version(2,4,0))
424 {
425 // parse filters
426 wxArrayString wildDescriptions, wildFilters;
427 if (!wxParseCommonDialogsFilter(wildCard, wildDescriptions, wildFilters))
428 {
429 wxFAIL_MSG( wxT("wxFileDialog::SetWildCard - bad wildcard string") );
430 }
431 else
432 {
433 // Parsing went fine. Set m_wildCard to be returned by wxFileDialogBase::GetWildcard
434 m_wildCard = wildCard;
435
436 GtkFileChooser* chooser = GTK_FILE_CHOOSER(m_widget);
437
438 // empty current filter list:
439 GSList* ifilters = gtk_file_chooser_list_filters(chooser);
440 GSList* filters = ifilters;
441
442 while (ifilters)
443 {
444 gtk_file_chooser_remove_filter(chooser,GTK_FILE_FILTER(ifilters->data));
445 ifilters = ifilters->next;
446 }
447 g_slist_free(filters);
448
449 // add parsed to GtkChooser
450 for (size_t n = 0; n < wildFilters.GetCount(); ++n)
451 {
452 GtkFileFilter* filter = gtk_file_filter_new();
453 gtk_file_filter_set_name(filter, wxGTK_CONV(wildDescriptions[n]));
454
455 wxStringTokenizer exttok(wildFilters[n], wxT(";"));
456 while (exttok.HasMoreTokens())
457 {
458 wxString token = exttok.GetNextToken();
459 gtk_file_filter_add_pattern(filter, wxGTK_CONV(token));
460 }
461
462 gtk_file_chooser_add_filter(chooser, filter);
463 }
464
465 // Reset the filter index
466 SetFilterIndex(0);
467 }
468 }
469 else
470 #endif
471 wxGenericFileDialog::SetWildcard( wildCard );
472 }
473
474 void wxFileDialog::SetFilterIndex(int filterIndex)
475 {
476 #ifdef __WXGTK24__
477 if (!gtk_check_version(2,4,0))
478 {
479 gpointer filter;
480 GtkFileChooser *chooser = GTK_FILE_CHOOSER(m_widget);
481 GSList *filters = gtk_file_chooser_list_filters(chooser);
482
483 filter = g_slist_nth_data(filters, filterIndex);
484
485 if (filter != NULL)
486 {
487 gtk_file_chooser_set_filter(chooser, GTK_FILE_FILTER(filter));
488 }
489 else
490 {
491 wxFAIL_MSG( wxT("wxFileDialog::SetFilterIndex - bad filter index") );
492 }
493
494 g_slist_free(filters);
495 }
496 else
497 #endif
498 wxGenericFileDialog::SetFilterIndex( filterIndex );
499 }
500
501 int wxFileDialog::GetFilterIndex() const
502 {
503 #ifdef __WXGTK24__
504 if (!gtk_check_version(2,4,0))
505 {
506 GtkFileChooser *chooser = GTK_FILE_CHOOSER(m_widget);
507 GtkFileFilter *filter = gtk_file_chooser_get_filter(chooser);
508 GSList *filters = gtk_file_chooser_list_filters(chooser);
509 gint index = g_slist_index(filters, filter);
510 g_slist_free(filters);
511
512 if (index == -1)
513 {
514 wxFAIL_MSG( wxT("wxFileDialog::GetFilterIndex - bad filter index returned by gtk+") );
515 return 0;
516 }
517 else
518 return index;
519 }
520 else
521 #endif
522 return wxGenericFileDialog::GetFilterIndex();
523 }
524
525 #endif // wxUSE_FILEDLG