OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <gtk/gtk.h> | |
6 #include <map> | |
7 #include <set> | |
8 #include <vector> | |
9 | |
10 #include "base/file_util.h" | |
11 #include "base/logging.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "base/message_loop/message_loop.h" | |
14 #include "base/strings/string_util.h" | |
15 #include "base/strings/sys_string_conversions.h" | |
16 #include "base/strings/utf_string_conversions.h" | |
17 #include "base/threading/thread.h" | |
18 #include "base/threading/thread_restrictions.h" | |
19 #include "grit/ui_strings.h" | |
20 #include "ui/base/gtk/gtk_signal.h" | |
21 #include "ui/base/l10n/l10n_util.h" | |
22 #include "ui/shell_dialogs/gtk/select_file_dialog_impl.h" | |
23 #include "ui/shell_dialogs/select_file_dialog.h" | |
24 | |
25 namespace { | |
26 | |
27 // Makes sure that .jpg also shows .JPG. | |
28 gboolean FileFilterCaseInsensitive(const GtkFileFilterInfo* file_info, | |
29 std::string* file_extension) { | |
30 return EndsWith(file_info->filename, *file_extension, false); | |
31 } | |
32 | |
33 // Deletes |data| when gtk_file_filter_add_custom() is done with it. | |
34 void OnFileFilterDataDestroyed(std::string* file_extension) { | |
35 delete file_extension; | |
36 } | |
37 | |
38 // Implementation of SelectFileDialog that shows a Gtk common dialog for | |
39 // choosing a file or folder. This acts as a modal dialog. | |
40 class SelectFileDialogImplGTK : public ui::SelectFileDialogImpl { | |
41 public: | |
42 explicit SelectFileDialogImplGTK(Listener* listener, | |
43 ui::SelectFilePolicy* policy); | |
44 | |
45 protected: | |
46 virtual ~SelectFileDialogImplGTK(); | |
47 | |
48 // SelectFileDialog implementation. | |
49 // |params| is user data we pass back via the Listener interface. | |
50 virtual void SelectFileImpl( | |
51 Type type, | |
52 const base::string16& title, | |
53 const base::FilePath& default_path, | |
54 const FileTypeInfo* file_types, | |
55 int file_type_index, | |
56 const base::FilePath::StringType& default_extension, | |
57 gfx::NativeWindow owning_window, | |
58 void* params) OVERRIDE; | |
59 | |
60 private: | |
61 virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE; | |
62 | |
63 // Add the filters from |file_types_| to |chooser|. | |
64 void AddFilters(GtkFileChooser* chooser); | |
65 | |
66 // Notifies the listener that a single file was chosen. | |
67 void FileSelected(GtkWidget* dialog, const base::FilePath& path); | |
68 | |
69 // Notifies the listener that multiple files were chosen. | |
70 void MultiFilesSelected(GtkWidget* dialog, | |
71 const std::vector<base::FilePath>& files); | |
72 | |
73 // Notifies the listener that no file was chosen (the action was canceled). | |
74 // Dialog is passed so we can find that |params| pointer that was passed to | |
75 // us when we were told to show the dialog. | |
76 void FileNotSelected(GtkWidget* dialog); | |
77 | |
78 GtkWidget* CreateSelectFolderDialog( | |
79 Type type, | |
80 const std::string& title, | |
81 const base::FilePath& default_path, | |
82 gfx::NativeWindow parent); | |
83 | |
84 GtkWidget* CreateFileOpenDialog(const std::string& title, | |
85 const base::FilePath& default_path, gfx::NativeWindow parent); | |
86 | |
87 GtkWidget* CreateMultiFileOpenDialog(const std::string& title, | |
88 const base::FilePath& default_path, gfx::NativeWindow parent); | |
89 | |
90 GtkWidget* CreateSaveAsDialog(const std::string& title, | |
91 const base::FilePath& default_path, gfx::NativeWindow parent); | |
92 | |
93 // Removes and returns the |params| associated with |dialog| from | |
94 // |params_map_|. | |
95 void* PopParamsForDialog(GtkWidget* dialog); | |
96 | |
97 // Take care of internal data structures when a file dialog is destroyed. | |
98 void FileDialogDestroyed(GtkWidget* dialog); | |
99 | |
100 // Check whether response_id corresponds to the user cancelling/closing the | |
101 // dialog. Used as a helper for the below callbacks. | |
102 bool IsCancelResponse(gint response_id); | |
103 | |
104 // Common function for OnSelectSingleFileDialogResponse and | |
105 // OnSelectSingleFolderDialogResponse. | |
106 void SelectSingleFileHelper(GtkWidget* dialog, | |
107 gint response_id, | |
108 bool allow_folder); | |
109 | |
110 // Common function for CreateFileOpenDialog and CreateMultiFileOpenDialog. | |
111 GtkWidget* CreateFileOpenHelper(const std::string& title, | |
112 const base::FilePath& default_path, | |
113 gfx::NativeWindow parent); | |
114 | |
115 // Callback for when the user responds to a Save As or Open File dialog. | |
116 CHROMEGTK_CALLBACK_1(SelectFileDialogImplGTK, void, | |
117 OnSelectSingleFileDialogResponse, int); | |
118 | |
119 // Callback for when the user responds to a Select Folder dialog. | |
120 CHROMEGTK_CALLBACK_1(SelectFileDialogImplGTK, void, | |
121 OnSelectSingleFolderDialogResponse, int); | |
122 | |
123 // Callback for when the user responds to a Open Multiple Files dialog. | |
124 CHROMEGTK_CALLBACK_1(SelectFileDialogImplGTK, void, | |
125 OnSelectMultiFileDialogResponse, int); | |
126 | |
127 // Callback for when the file chooser gets destroyed. | |
128 CHROMEGTK_CALLBACK_0(SelectFileDialogImplGTK, void, OnFileChooserDestroy); | |
129 | |
130 // Callback for when we update the preview for the selection. | |
131 CHROMEGTK_CALLBACK_0(SelectFileDialogImplGTK, void, OnUpdatePreview); | |
132 | |
133 // A map from dialog windows to the |params| user data associated with them. | |
134 std::map<GtkWidget*, void*> params_map_; | |
135 | |
136 // The GtkImage widget for showing previews of selected images. | |
137 GtkWidget* preview_; | |
138 | |
139 // All our dialogs. | |
140 std::set<GtkWidget*> dialogs_; | |
141 | |
142 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImplGTK); | |
143 }; | |
144 | |
145 // The size of the preview we display for selected image files. We set height | |
146 // larger than width because generally there is more free space vertically | |
147 // than horiztonally (setting the preview image will alway expand the width of | |
148 // the dialog, but usually not the height). The image's aspect ratio will always | |
149 // be preserved. | |
150 static const int kPreviewWidth = 256; | |
151 static const int kPreviewHeight = 512; | |
152 | |
153 SelectFileDialogImplGTK::SelectFileDialogImplGTK(Listener* listener, | |
154 ui::SelectFilePolicy* policy) | |
155 : SelectFileDialogImpl(listener, policy), | |
156 preview_(NULL) { | |
157 } | |
158 | |
159 SelectFileDialogImplGTK::~SelectFileDialogImplGTK() { | |
160 while (dialogs_.begin() != dialogs_.end()) { | |
161 gtk_widget_destroy(*(dialogs_.begin())); | |
162 } | |
163 } | |
164 | |
165 bool SelectFileDialogImplGTK::HasMultipleFileTypeChoicesImpl() { | |
166 return file_types_.extensions.size() > 1; | |
167 } | |
168 | |
169 // We ignore |default_extension|. | |
170 void SelectFileDialogImplGTK::SelectFileImpl( | |
171 Type type, | |
172 const base::string16& title, | |
173 const base::FilePath& default_path, | |
174 const FileTypeInfo* file_types, | |
175 int file_type_index, | |
176 const base::FilePath::StringType& default_extension, | |
177 gfx::NativeWindow owning_window, | |
178 void* params) { | |
179 type_ = type; | |
180 // |owning_window| can be null when user right-clicks on a downloadable item | |
181 // and chooses 'Open Link in New Tab' when 'Ask where to save each file | |
182 // before downloading.' preference is turned on. (http://crbug.com/29213) | |
183 if (owning_window) | |
184 parents_.insert(owning_window); | |
185 | |
186 std::string title_string = base::UTF16ToUTF8(title); | |
187 | |
188 file_type_index_ = file_type_index; | |
189 if (file_types) | |
190 file_types_ = *file_types; | |
191 else | |
192 file_types_.include_all_files = true; | |
193 | |
194 GtkWidget* dialog = NULL; | |
195 switch (type) { | |
196 case SELECT_FOLDER: | |
197 case SELECT_UPLOAD_FOLDER: | |
198 dialog = CreateSelectFolderDialog(type, title_string, default_path, | |
199 owning_window); | |
200 break; | |
201 case SELECT_OPEN_FILE: | |
202 dialog = CreateFileOpenDialog(title_string, default_path, owning_window); | |
203 break; | |
204 case SELECT_OPEN_MULTI_FILE: | |
205 dialog = CreateMultiFileOpenDialog(title_string, default_path, | |
206 owning_window); | |
207 break; | |
208 case SELECT_SAVEAS_FILE: | |
209 dialog = CreateSaveAsDialog(title_string, default_path, owning_window); | |
210 break; | |
211 default: | |
212 NOTREACHED(); | |
213 return; | |
214 } | |
215 g_signal_connect(dialog, "delete-event", | |
216 G_CALLBACK(gtk_widget_hide_on_delete), NULL); | |
217 dialogs_.insert(dialog); | |
218 | |
219 preview_ = gtk_image_new(); | |
220 g_signal_connect(dialog, "destroy", | |
221 G_CALLBACK(OnFileChooserDestroyThunk), this); | |
222 g_signal_connect(dialog, "update-preview", | |
223 G_CALLBACK(OnUpdatePreviewThunk), this); | |
224 gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog), preview_); | |
225 | |
226 params_map_[dialog] = params; | |
227 | |
228 // Set window-to-parent modality by adding the dialog to the same window | |
229 // group as the parent. | |
230 gtk_window_group_add_window(gtk_window_get_group(owning_window), | |
231 GTK_WINDOW(dialog)); | |
232 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); | |
233 | |
234 gtk_widget_show_all(dialog); | |
235 } | |
236 | |
237 void SelectFileDialogImplGTK::AddFilters(GtkFileChooser* chooser) { | |
238 for (size_t i = 0; i < file_types_.extensions.size(); ++i) { | |
239 GtkFileFilter* filter = NULL; | |
240 std::set<std::string> fallback_labels; | |
241 | |
242 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) { | |
243 const std::string& current_extension = file_types_.extensions[i][j]; | |
244 if (!current_extension.empty()) { | |
245 if (!filter) | |
246 filter = gtk_file_filter_new(); | |
247 scoped_ptr<std::string> file_extension( | |
248 new std::string("." + current_extension)); | |
249 fallback_labels.insert(std::string("*").append(*file_extension)); | |
250 gtk_file_filter_add_custom( | |
251 filter, | |
252 GTK_FILE_FILTER_FILENAME, | |
253 reinterpret_cast<GtkFileFilterFunc>(FileFilterCaseInsensitive), | |
254 file_extension.release(), | |
255 reinterpret_cast<GDestroyNotify>(OnFileFilterDataDestroyed)); | |
256 } | |
257 } | |
258 // We didn't find any non-empty extensions to filter on. | |
259 if (!filter) | |
260 continue; | |
261 | |
262 // The description vector may be blank, in which case we are supposed to | |
263 // use some sort of default description based on the filter. | |
264 if (i < file_types_.extension_description_overrides.size()) { | |
265 gtk_file_filter_set_name(filter, base::UTF16ToUTF8( | |
266 file_types_.extension_description_overrides[i]).c_str()); | |
267 } else { | |
268 // There is no system default filter description so we use | |
269 // the extensions themselves if the description is blank. | |
270 std::vector<std::string> fallback_labels_vector(fallback_labels.begin(), | |
271 fallback_labels.end()); | |
272 std::string fallback_label = JoinString(fallback_labels_vector, ','); | |
273 gtk_file_filter_set_name(filter, fallback_label.c_str()); | |
274 } | |
275 | |
276 gtk_file_chooser_add_filter(chooser, filter); | |
277 if (i == file_type_index_ - 1) | |
278 gtk_file_chooser_set_filter(chooser, filter); | |
279 } | |
280 | |
281 // Add the *.* filter, but only if we have added other filters (otherwise it | |
282 // is implied). | |
283 if (file_types_.include_all_files && !file_types_.extensions.empty()) { | |
284 GtkFileFilter* filter = gtk_file_filter_new(); | |
285 gtk_file_filter_add_pattern(filter, "*"); | |
286 gtk_file_filter_set_name(filter, | |
287 l10n_util::GetStringUTF8(IDS_SAVEAS_ALL_FILES).c_str()); | |
288 gtk_file_chooser_add_filter(chooser, filter); | |
289 } | |
290 } | |
291 | |
292 void SelectFileDialogImplGTK::FileSelected(GtkWidget* dialog, | |
293 const base::FilePath& path) { | |
294 if (type_ == SELECT_SAVEAS_FILE) { | |
295 *last_saved_path_ = path.DirName(); | |
296 } else if (type_ == SELECT_OPEN_FILE || type_ == SELECT_FOLDER || | |
297 type_ == SELECT_UPLOAD_FOLDER) { | |
298 *last_opened_path_ = path.DirName(); | |
299 } else { | |
300 NOTREACHED(); | |
301 } | |
302 | |
303 if (listener_) { | |
304 GtkFileFilter* selected_filter = | |
305 gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog)); | |
306 GSList* filters = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(dialog)); | |
307 int idx = g_slist_index(filters, selected_filter); | |
308 g_slist_free(filters); | |
309 listener_->FileSelected(path, idx + 1, PopParamsForDialog(dialog)); | |
310 } | |
311 gtk_widget_destroy(dialog); | |
312 } | |
313 | |
314 void SelectFileDialogImplGTK::MultiFilesSelected(GtkWidget* dialog, | |
315 const std::vector<base::FilePath>& files) { | |
316 *last_opened_path_ = files[0].DirName(); | |
317 | |
318 if (listener_) | |
319 listener_->MultiFilesSelected(files, PopParamsForDialog(dialog)); | |
320 gtk_widget_destroy(dialog); | |
321 } | |
322 | |
323 void SelectFileDialogImplGTK::FileNotSelected(GtkWidget* dialog) { | |
324 void* params = PopParamsForDialog(dialog); | |
325 if (listener_) | |
326 listener_->FileSelectionCanceled(params); | |
327 gtk_widget_destroy(dialog); | |
328 } | |
329 | |
330 GtkWidget* SelectFileDialogImplGTK::CreateFileOpenHelper( | |
331 const std::string& title, | |
332 const base::FilePath& default_path, | |
333 gfx::NativeWindow parent) { | |
334 GtkWidget* dialog = | |
335 gtk_file_chooser_dialog_new(title.c_str(), parent, | |
336 GTK_FILE_CHOOSER_ACTION_OPEN, | |
337 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
338 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | |
339 NULL); | |
340 AddFilters(GTK_FILE_CHOOSER(dialog)); | |
341 | |
342 if (!default_path.empty()) { | |
343 if (CallDirectoryExistsOnUIThread(default_path)) { | |
344 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
345 default_path.value().c_str()); | |
346 } else { | |
347 // If the file doesn't exist, this will just switch to the correct | |
348 // directory. That's good enough. | |
349 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | |
350 default_path.value().c_str()); | |
351 } | |
352 } else if (!last_opened_path_->empty()) { | |
353 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
354 last_opened_path_->value().c_str()); | |
355 } | |
356 return dialog; | |
357 } | |
358 | |
359 GtkWidget* SelectFileDialogImplGTK::CreateSelectFolderDialog( | |
360 Type type, | |
361 const std::string& title, | |
362 const base::FilePath& default_path, | |
363 gfx::NativeWindow parent) { | |
364 std::string title_string = title; | |
365 if (title_string.empty()) { | |
366 title_string = (type == SELECT_UPLOAD_FOLDER) ? | |
367 l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE) : | |
368 l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); | |
369 } | |
370 std::string accept_button_label = (type == SELECT_UPLOAD_FOLDER) ? | |
371 l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON) : | |
372 GTK_STOCK_OPEN; | |
373 | |
374 GtkWidget* dialog = | |
375 gtk_file_chooser_dialog_new(title_string.c_str(), parent, | |
376 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, | |
377 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
378 accept_button_label.c_str(), | |
379 GTK_RESPONSE_ACCEPT, | |
380 NULL); | |
381 | |
382 if (!default_path.empty()) { | |
383 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | |
384 default_path.value().c_str()); | |
385 } else if (!last_opened_path_->empty()) { | |
386 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
387 last_opened_path_->value().c_str()); | |
388 } | |
389 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
390 g_signal_connect(dialog, "response", | |
391 G_CALLBACK(OnSelectSingleFolderDialogResponseThunk), this); | |
392 return dialog; | |
393 } | |
394 | |
395 GtkWidget* SelectFileDialogImplGTK::CreateFileOpenDialog( | |
396 const std::string& title, | |
397 const base::FilePath& default_path, | |
398 gfx::NativeWindow parent) { | |
399 std::string title_string = !title.empty() ? title : | |
400 l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE); | |
401 | |
402 GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); | |
403 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
404 g_signal_connect(dialog, "response", | |
405 G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this); | |
406 return dialog; | |
407 } | |
408 | |
409 GtkWidget* SelectFileDialogImplGTK::CreateMultiFileOpenDialog( | |
410 const std::string& title, | |
411 const base::FilePath& default_path, | |
412 gfx::NativeWindow parent) { | |
413 std::string title_string = !title.empty() ? title : | |
414 l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE); | |
415 | |
416 GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); | |
417 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); | |
418 g_signal_connect(dialog, "response", | |
419 G_CALLBACK(OnSelectMultiFileDialogResponseThunk), this); | |
420 return dialog; | |
421 } | |
422 | |
423 GtkWidget* SelectFileDialogImplGTK::CreateSaveAsDialog(const std::string& title, | |
424 const base::FilePath& default_path, gfx::NativeWindow parent) { | |
425 std::string title_string = !title.empty() ? title : | |
426 l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); | |
427 | |
428 GtkWidget* dialog = | |
429 gtk_file_chooser_dialog_new(title_string.c_str(), parent, | |
430 GTK_FILE_CHOOSER_ACTION_SAVE, | |
431 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
432 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, | |
433 NULL); | |
434 | |
435 AddFilters(GTK_FILE_CHOOSER(dialog)); | |
436 if (!default_path.empty()) { | |
437 // Since the file may not already exist, we use | |
438 // set_current_folder() followed by set_current_name(), as per the | |
439 // recommendation of the GTK docs. | |
440 if (CallDirectoryExistsOnUIThread(default_path)) { | |
441 gtk_file_chooser_set_current_folder( | |
442 GTK_FILE_CHOOSER(dialog), default_path.value().c_str()); | |
443 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), ""); | |
444 } else { | |
445 gtk_file_chooser_set_current_folder( | |
446 GTK_FILE_CHOOSER(dialog), default_path.DirName().value().c_str()); | |
447 gtk_file_chooser_set_current_name( | |
448 GTK_FILE_CHOOSER(dialog), default_path.BaseName().value().c_str()); | |
449 } | |
450 } else if (!last_saved_path_->empty()) { | |
451 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
452 last_saved_path_->value().c_str()); | |
453 } | |
454 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
455 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), | |
456 TRUE); | |
457 g_signal_connect(dialog, "response", | |
458 G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this); | |
459 return dialog; | |
460 } | |
461 | |
462 void* SelectFileDialogImplGTK::PopParamsForDialog(GtkWidget* dialog) { | |
463 std::map<GtkWidget*, void*>::iterator iter = params_map_.find(dialog); | |
464 DCHECK(iter != params_map_.end()); | |
465 void* params = iter->second; | |
466 params_map_.erase(iter); | |
467 return params; | |
468 } | |
469 | |
470 void SelectFileDialogImplGTK::FileDialogDestroyed(GtkWidget* dialog) { | |
471 dialogs_.erase(dialog); | |
472 | |
473 // Parent may be NULL in a few cases: 1) on shutdown when | |
474 // AllBrowsersClosed() trigger this handler after all the browser | |
475 // windows got destroyed, or 2) when the parent tab has been opened by | |
476 // 'Open Link in New Tab' context menu on a downloadable item and | |
477 // the tab has no content (see the comment in SelectFile as well). | |
478 GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(dialog)); | |
479 if (!parent) | |
480 return; | |
481 std::set<GtkWindow*>::iterator iter = parents_.find(parent); | |
482 if (iter != parents_.end()) | |
483 parents_.erase(iter); | |
484 else | |
485 NOTREACHED(); | |
486 } | |
487 | |
488 bool SelectFileDialogImplGTK::IsCancelResponse(gint response_id) { | |
489 bool is_cancel = response_id == GTK_RESPONSE_CANCEL || | |
490 response_id == GTK_RESPONSE_DELETE_EVENT; | |
491 if (is_cancel) | |
492 return true; | |
493 | |
494 DCHECK(response_id == GTK_RESPONSE_ACCEPT); | |
495 return false; | |
496 } | |
497 | |
498 void SelectFileDialogImplGTK::SelectSingleFileHelper(GtkWidget* dialog, | |
499 gint response_id, | |
500 bool allow_folder) { | |
501 if (IsCancelResponse(response_id)) { | |
502 FileNotSelected(dialog); | |
503 return; | |
504 } | |
505 | |
506 gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | |
507 if (!filename) { | |
508 FileNotSelected(dialog); | |
509 return; | |
510 } | |
511 | |
512 base::FilePath path(filename); | |
513 g_free(filename); | |
514 | |
515 if (allow_folder) { | |
516 FileSelected(dialog, path); | |
517 return; | |
518 } | |
519 | |
520 if (CallDirectoryExistsOnUIThread(path)) | |
521 FileNotSelected(dialog); | |
522 else | |
523 FileSelected(dialog, path); | |
524 } | |
525 | |
526 void SelectFileDialogImplGTK::OnSelectSingleFileDialogResponse( | |
527 GtkWidget* dialog, int response_id) { | |
528 SelectSingleFileHelper(dialog, response_id, false); | |
529 } | |
530 | |
531 void SelectFileDialogImplGTK::OnSelectSingleFolderDialogResponse( | |
532 GtkWidget* dialog, int response_id) { | |
533 SelectSingleFileHelper(dialog, response_id, true); | |
534 } | |
535 | |
536 void SelectFileDialogImplGTK::OnSelectMultiFileDialogResponse(GtkWidget* dialog, | |
537 int response_id) { | |
538 if (IsCancelResponse(response_id)) { | |
539 FileNotSelected(dialog); | |
540 return; | |
541 } | |
542 | |
543 GSList* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); | |
544 if (!filenames) { | |
545 FileNotSelected(dialog); | |
546 return; | |
547 } | |
548 | |
549 std::vector<base::FilePath> filenames_fp; | |
550 for (GSList* iter = filenames; iter != NULL; iter = g_slist_next(iter)) { | |
551 base::FilePath path(static_cast<char*>(iter->data)); | |
552 g_free(iter->data); | |
553 if (CallDirectoryExistsOnUIThread(path)) | |
554 continue; | |
555 filenames_fp.push_back(path); | |
556 } | |
557 g_slist_free(filenames); | |
558 | |
559 if (filenames_fp.empty()) { | |
560 FileNotSelected(dialog); | |
561 return; | |
562 } | |
563 MultiFilesSelected(dialog, filenames_fp); | |
564 } | |
565 | |
566 void SelectFileDialogImplGTK::OnFileChooserDestroy(GtkWidget* dialog) { | |
567 FileDialogDestroyed(dialog); | |
568 } | |
569 | |
570 void SelectFileDialogImplGTK::OnUpdatePreview(GtkWidget* chooser) { | |
571 gchar* filename = gtk_file_chooser_get_preview_filename( | |
572 GTK_FILE_CHOOSER(chooser)); | |
573 if (!filename) | |
574 return; | |
575 // This will preserve the image's aspect ratio. | |
576 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, | |
577 kPreviewHeight, NULL); | |
578 g_free(filename); | |
579 if (pixbuf) { | |
580 gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf); | |
581 g_object_unref(pixbuf); | |
582 } | |
583 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser), | |
584 pixbuf ? TRUE : FALSE); | |
585 } | |
586 | |
587 } // namespace | |
588 | |
589 namespace ui { | |
590 | |
591 SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplGTK( | |
592 Listener* listener, ui::SelectFilePolicy* policy) { | |
593 return new SelectFileDialogImplGTK(listener, policy); | |
594 } | |
595 | |
596 } // namespace ui | |
OLD | NEW |