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