| 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 |