OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <gtk/gtk.h> | 5 #include <gtk/gtk.h> |
6 #include <map> | 6 #include <map> |
7 #include <set> | 7 #include <set> |
8 | 8 |
9 #include "app/gtk_signal.h" | 9 #include "app/gtk_signal.h" |
10 #include "app/l10n_util.h" | 10 #include "app/l10n_util.h" |
11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/message_loop.h" | 13 #include "base/message_loop.h" |
14 #include "base/mime_util.h" | 14 #include "base/mime_util.h" |
15 #include "base/sys_string_conversions.h" | 15 #include "base/sys_string_conversions.h" |
| 16 #include "base/thread.h" |
| 17 #include "base/thread_restrictions.h" |
16 #include "base/utf_string_conversions.h" | 18 #include "base/utf_string_conversions.h" |
17 #include "base/thread.h" | |
18 #include "chrome/browser/browser_process.h" | 19 #include "chrome/browser/browser_process.h" |
19 #include "chrome/browser/browser_thread.h" | 20 #include "chrome/browser/browser_thread.h" |
20 #include "chrome/browser/shell_dialogs.h" | 21 #include "chrome/browser/shell_dialogs.h" |
21 #include "grit/generated_resources.h" | 22 #include "grit/generated_resources.h" |
22 | 23 |
23 // The size of the preview we display for selected image files. We set height | 24 // The size of the preview we display for selected image files. We set height |
24 // larger than width because generally there is more free space vertically | 25 // larger than width because generally there is more free space vertically |
25 // than horiztonally (setting the preview image will alway expand the width of | 26 // than horiztonally (setting the preview image will alway expand the width of |
26 // the dialog, but usually not the height). The image's aspect ratio will always | 27 // the dialog, but usually not the height). The image's aspect ratio will always |
27 // be preserved. | 28 // be preserved. |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
89 // Check whether response_id corresponds to the user cancelling/closing the | 90 // Check whether response_id corresponds to the user cancelling/closing the |
90 // dialog. Used as a helper for the below callbacks. | 91 // dialog. Used as a helper for the below callbacks. |
91 bool IsCancelResponse(gint response_id); | 92 bool IsCancelResponse(gint response_id); |
92 | 93 |
93 // Common function for OnSelectSingleFileDialogResponse and | 94 // Common function for OnSelectSingleFileDialogResponse and |
94 // OnSelectSingleFolderDialogResponse. | 95 // OnSelectSingleFolderDialogResponse. |
95 void SelectSingleFileHelper(GtkWidget* dialog, | 96 void SelectSingleFileHelper(GtkWidget* dialog, |
96 gint response_id, | 97 gint response_id, |
97 bool allow_folder); | 98 bool allow_folder); |
98 | 99 |
| 100 // Common function for CreateFileOpenDialog and CreateMultiFileOpenDialog. |
| 101 GtkWidget* CreateFileOpenHelper(const std::string& title, |
| 102 const FilePath& default_path, |
| 103 gfx::NativeWindow parent); |
| 104 |
| 105 // Wrapper for file_util::DirectoryExists() that allow access on the UI |
| 106 // thread. Use this only in the file dialog functions, where it's ok |
| 107 // because the file dialog has to do many stats anyway. One more won't |
| 108 // hurt too badly and it's likely already cached. |
| 109 bool CallDirectoryExistsOnUIThread(const FilePath& path); |
| 110 |
99 // Callback for when the user responds to a Save As or Open File dialog. | 111 // Callback for when the user responds to a Save As or Open File dialog. |
100 CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, | 112 CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, |
101 OnSelectSingleFileDialogResponse, gint); | 113 OnSelectSingleFileDialogResponse, gint); |
102 | 114 |
103 // Callback for when the user responds to a Select Folder dialog. | 115 // Callback for when the user responds to a Select Folder dialog. |
104 CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, | 116 CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, |
105 OnSelectSingleFolderDialogResponse, gint); | 117 OnSelectSingleFolderDialogResponse, gint); |
106 | 118 |
107 // Callback for when the user responds to a Open Multiple Files dialog. | 119 // Callback for when the user responds to a Open Multiple Files dialog. |
108 CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, | 120 CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
322 gtk_widget_destroy(dialog); | 334 gtk_widget_destroy(dialog); |
323 } | 335 } |
324 | 336 |
325 void SelectFileDialogImpl::FileNotSelected(GtkWidget* dialog) { | 337 void SelectFileDialogImpl::FileNotSelected(GtkWidget* dialog) { |
326 void* params = PopParamsForDialog(dialog); | 338 void* params = PopParamsForDialog(dialog); |
327 if (listener_) | 339 if (listener_) |
328 listener_->FileSelectionCanceled(params); | 340 listener_->FileSelectionCanceled(params); |
329 gtk_widget_destroy(dialog); | 341 gtk_widget_destroy(dialog); |
330 } | 342 } |
331 | 343 |
| 344 bool SelectFileDialogImpl::CallDirectoryExistsOnUIThread(const FilePath& path) { |
| 345 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 346 return file_util::DirectoryExists(path); |
| 347 } |
| 348 |
| 349 GtkWidget* SelectFileDialogImpl::CreateFileOpenHelper( |
| 350 const std::string& title, |
| 351 const FilePath& default_path, |
| 352 gfx::NativeWindow parent) { |
| 353 GtkWidget* dialog = |
| 354 gtk_file_chooser_dialog_new(title.c_str(), parent, |
| 355 GTK_FILE_CHOOSER_ACTION_OPEN, |
| 356 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
| 357 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, |
| 358 NULL); |
| 359 AddFilters(GTK_FILE_CHOOSER(dialog)); |
| 360 |
| 361 if (!default_path.empty()) { |
| 362 if (CallDirectoryExistsOnUIThread(default_path)) { |
| 363 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), |
| 364 default_path.value().c_str()); |
| 365 } else { |
| 366 // If the file doesn't exist, this will just switch to the correct |
| 367 // directory. That's good enough. |
| 368 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), |
| 369 default_path.value().c_str()); |
| 370 } |
| 371 } else if (!last_opened_path_->empty()) { |
| 372 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), |
| 373 last_opened_path_->value().c_str()); |
| 374 } |
| 375 return dialog; |
| 376 } |
| 377 |
332 GtkWidget* SelectFileDialogImpl::CreateSelectFolderDialog( | 378 GtkWidget* SelectFileDialogImpl::CreateSelectFolderDialog( |
333 const std::string& title, | 379 const std::string& title, |
334 const FilePath& default_path, | 380 const FilePath& default_path, |
335 gfx::NativeWindow parent) { | 381 gfx::NativeWindow parent) { |
336 std::string title_string = !title.empty() ? title : | 382 std::string title_string = !title.empty() ? title : |
337 l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); | 383 l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); |
338 | 384 |
339 GtkWidget* dialog = | 385 GtkWidget* dialog = |
340 gtk_file_chooser_dialog_new(title_string.c_str(), parent, | 386 gtk_file_chooser_dialog_new(title_string.c_str(), parent, |
341 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, | 387 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, |
342 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | 388 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
343 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | 389 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, |
344 NULL); | 390 NULL); |
345 | 391 |
346 if (!default_path.empty()) { | 392 if (!default_path.empty()) { |
347 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | 393 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), |
348 default_path.value().c_str()); | 394 default_path.value().c_str()); |
349 } else if (!last_opened_path_->empty()) { | 395 } else if (!last_opened_path_->empty()) { |
350 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | 396 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), |
351 last_opened_path_->value().c_str()); | 397 last_opened_path_->value().c_str()); |
352 } | 398 } |
353 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | 399 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); |
354 g_signal_connect(dialog, "response", | 400 g_signal_connect(dialog, "response", |
355 G_CALLBACK(OnSelectSingleFolderDialogResponseThunk), this); | 401 G_CALLBACK(OnSelectSingleFolderDialogResponseThunk), this); |
356 return dialog; | 402 return dialog; |
357 } | 403 } |
358 | 404 |
359 GtkWidget* SelectFileDialogImpl::CreateFileOpenDialog(const std::string& title, | 405 GtkWidget* SelectFileDialogImpl::CreateFileOpenDialog( |
360 const FilePath& default_path, gfx::NativeWindow parent) { | 406 const std::string& title, |
| 407 const FilePath& default_path, |
| 408 gfx::NativeWindow parent) { |
361 std::string title_string = !title.empty() ? title : | 409 std::string title_string = !title.empty() ? title : |
362 l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE); | 410 l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE); |
363 | 411 GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); |
364 GtkWidget* dialog = | |
365 gtk_file_chooser_dialog_new(title_string.c_str(), parent, | |
366 GTK_FILE_CHOOSER_ACTION_OPEN, | |
367 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
368 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | |
369 NULL); | |
370 | |
371 AddFilters(GTK_FILE_CHOOSER(dialog)); | |
372 if (!default_path.empty()) { | |
373 if (file_util::DirectoryExists(default_path)) { | |
374 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
375 default_path.value().c_str()); | |
376 } else { | |
377 // If the file doesn't exist, this will just switch to the correct | |
378 // directory. That's good enough. | |
379 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | |
380 default_path.value().c_str()); | |
381 } | |
382 } else if (!last_opened_path_->empty()) { | |
383 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
384 last_opened_path_->value().c_str()); | |
385 } | |
386 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | 412 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); |
387 g_signal_connect(dialog, "response", | 413 g_signal_connect(dialog, "response", |
388 G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this); | 414 G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this); |
389 return dialog; | 415 return dialog; |
390 } | 416 } |
391 | 417 |
392 GtkWidget* SelectFileDialogImpl::CreateMultiFileOpenDialog( | 418 GtkWidget* SelectFileDialogImpl::CreateMultiFileOpenDialog( |
393 const std::string& title, | 419 const std::string& title, |
394 const FilePath& default_path, | 420 const FilePath& default_path, |
395 gfx::NativeWindow parent) { | 421 gfx::NativeWindow parent) { |
396 std::string title_string = !title.empty() ? title : | 422 std::string title_string = !title.empty() ? title : |
397 l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE); | 423 l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE); |
398 | 424 GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); |
399 GtkWidget* dialog = | |
400 gtk_file_chooser_dialog_new(title_string.c_str(), parent, | |
401 GTK_FILE_CHOOSER_ACTION_OPEN, | |
402 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
403 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | |
404 NULL); | |
405 | |
406 AddFilters(GTK_FILE_CHOOSER(dialog)); | |
407 if (!default_path.empty()) { | |
408 if (file_util::DirectoryExists(default_path)) { | |
409 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
410 default_path.value().c_str()); | |
411 } else { | |
412 // If the file doesn't exist, this will just switch to the correct | |
413 // directory. That's good enough. | |
414 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | |
415 default_path.value().c_str()); | |
416 } | |
417 } else if (!last_opened_path_->empty()) { | |
418 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
419 last_opened_path_->value().c_str()); | |
420 } | |
421 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); | 425 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); |
422 g_signal_connect(dialog, "response", | 426 g_signal_connect(dialog, "response", |
423 G_CALLBACK(OnSelectMultiFileDialogResponseThunk), this); | 427 G_CALLBACK(OnSelectMultiFileDialogResponseThunk), this); |
424 return dialog; | 428 return dialog; |
425 } | 429 } |
426 | 430 |
427 GtkWidget* SelectFileDialogImpl::CreateSaveAsDialog(const std::string& title, | 431 GtkWidget* SelectFileDialogImpl::CreateSaveAsDialog(const std::string& title, |
428 const FilePath& default_path, gfx::NativeWindow parent) { | 432 const FilePath& default_path, gfx::NativeWindow parent) { |
429 std::string title_string = !title.empty() ? title : | 433 std::string title_string = !title.empty() ? title : |
430 l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); | 434 l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
508 } | 512 } |
509 | 513 |
510 FilePath path(filename); | 514 FilePath path(filename); |
511 g_free(filename); | 515 g_free(filename); |
512 | 516 |
513 if (allow_folder) { | 517 if (allow_folder) { |
514 FileSelected(dialog, path); | 518 FileSelected(dialog, path); |
515 return; | 519 return; |
516 } | 520 } |
517 | 521 |
518 // We're accessing the disk from the UI thread here, but in this case it's | 522 if (CallDirectoryExistsOnUIThread(path)) |
519 // ok because we may have just done lots of stats in the file selection | |
520 // dialog. One more won't hurt too badly. | |
521 if (file_util::DirectoryExists(path)) | |
522 FileNotSelected(dialog); | 523 FileNotSelected(dialog); |
523 else | 524 else |
524 FileSelected(dialog, path); | 525 FileSelected(dialog, path); |
525 } | 526 } |
526 | 527 |
527 void SelectFileDialogImpl::OnSelectSingleFileDialogResponse( | 528 void SelectFileDialogImpl::OnSelectSingleFileDialogResponse( |
528 GtkWidget* dialog, gint response_id) { | 529 GtkWidget* dialog, gint response_id) { |
529 return SelectSingleFileHelper(dialog, response_id, false); | 530 return SelectSingleFileHelper(dialog, response_id, false); |
530 } | 531 } |
531 | 532 |
(...skipping 12 matching lines...) Expand all Loading... |
544 GSList* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); | 545 GSList* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); |
545 if (!filenames) { | 546 if (!filenames) { |
546 FileNotSelected(dialog); | 547 FileNotSelected(dialog); |
547 return; | 548 return; |
548 } | 549 } |
549 | 550 |
550 std::vector<FilePath> filenames_fp; | 551 std::vector<FilePath> filenames_fp; |
551 for (GSList* iter = filenames; iter != NULL; iter = g_slist_next(iter)) { | 552 for (GSList* iter = filenames; iter != NULL; iter = g_slist_next(iter)) { |
552 FilePath path(static_cast<char*>(iter->data)); | 553 FilePath path(static_cast<char*>(iter->data)); |
553 g_free(iter->data); | 554 g_free(iter->data); |
554 if (file_util::DirectoryExists(path)) | 555 if (CallDirectoryExistsOnUIThread(path)) |
555 continue; | 556 continue; |
556 filenames_fp.push_back(path); | 557 filenames_fp.push_back(path); |
557 } | 558 } |
558 g_slist_free(filenames); | 559 g_slist_free(filenames); |
559 | 560 |
560 if (filenames_fp.empty()) { | 561 if (filenames_fp.empty()) { |
561 FileNotSelected(dialog); | 562 FileNotSelected(dialog); |
562 return; | 563 return; |
563 } | 564 } |
564 MultiFilesSelected(dialog, filenames_fp); | 565 MultiFilesSelected(dialog, filenames_fp); |
(...skipping 12 matching lines...) Expand all Loading... |
577 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, | 578 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, |
578 kPreviewHeight, NULL); | 579 kPreviewHeight, NULL); |
579 g_free(filename); | 580 g_free(filename); |
580 if (pixbuf) { | 581 if (pixbuf) { |
581 gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf); | 582 gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf); |
582 g_object_unref(pixbuf); | 583 g_object_unref(pixbuf); |
583 } | 584 } |
584 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser), | 585 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser), |
585 pixbuf ? TRUE : FALSE); | 586 pixbuf ? TRUE : FALSE); |
586 } | 587 } |
OLD | NEW |