file_chooser_linux.cc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // Copyright 2019 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. #include "remoting/host/file_transfer/file_chooser.h"
  5. #include <gtk/gtk.h>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/memory/weak_ptr.h"
  9. #include "base/threading/sequence_bound.h"
  10. #include "base/threading/sequenced_task_runner_handle.h"
  11. #include "remoting/base/string_resources.h"
  12. #include "ui/base/glib/glib_signal.h"
  13. #include "ui/base/l10n/l10n_util.h"
  14. namespace remoting {
  15. namespace {
  16. class FileChooserLinux;
  17. class GtkFileChooserOnUiThread {
  18. public:
  19. GtkFileChooserOnUiThread(
  20. scoped_refptr<base::SequencedTaskRunner> caller_task_runner,
  21. base::WeakPtr<FileChooserLinux> file_chooser_linux);
  22. GtkFileChooserOnUiThread(const GtkFileChooserOnUiThread&) = delete;
  23. GtkFileChooserOnUiThread& operator=(const GtkFileChooserOnUiThread&) = delete;
  24. ~GtkFileChooserOnUiThread();
  25. void Show();
  26. private:
  27. // Callback for when the user responds to the Open File dialog.
  28. CHROMEG_CALLBACK_1(GtkFileChooserOnUiThread,
  29. void,
  30. OnResponse,
  31. GtkWidget*,
  32. int);
  33. void RunCallback(FileChooser::Result result);
  34. void CleanUp();
  35. GObject* file_dialog_ = nullptr;
  36. scoped_refptr<base::SequencedTaskRunner> caller_task_runner_;
  37. base::WeakPtr<FileChooserLinux> file_chooser_linux_;
  38. };
  39. class FileChooserLinux : public FileChooser {
  40. public:
  41. FileChooserLinux(scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
  42. ResultCallback callback);
  43. FileChooserLinux(const FileChooserLinux&) = delete;
  44. FileChooserLinux& operator=(const FileChooserLinux&) = delete;
  45. ~FileChooserLinux() override;
  46. // FileChooser implementation.
  47. void Show() override;
  48. void RunCallback(FileChooser::Result result);
  49. private:
  50. FileChooser::ResultCallback callback_;
  51. base::SequenceBound<GtkFileChooserOnUiThread> gtk_file_chooser_on_ui_thread_;
  52. base::WeakPtrFactory<FileChooserLinux> weak_ptr_factory_{this};
  53. };
  54. GtkFileChooserOnUiThread::GtkFileChooserOnUiThread(
  55. scoped_refptr<base::SequencedTaskRunner> caller_task_runner,
  56. base::WeakPtr<FileChooserLinux> file_chooser_linux)
  57. : caller_task_runner_(std::move(caller_task_runner)),
  58. file_chooser_linux_(std::move(file_chooser_linux)) {}
  59. GtkFileChooserOnUiThread::~GtkFileChooserOnUiThread() {
  60. // Delete the dialog if it hasn't been already.
  61. CleanUp();
  62. }
  63. void GtkFileChooserOnUiThread::Show() {
  64. #if GTK_CHECK_VERSION(3, 90, 0)
  65. // GTK+ 4.0 removes the stock items for the open and cancel buttons, with the
  66. // idea that one would instead use _("_Cancel") and _("_Open") directly (using
  67. // gettext to pull the appropriate translated strings from the translations
  68. // that ship with GTK+). To avoid needing to pull in the translated strings
  69. // from GTK+ using gettext, we can just use GtkFileChooserNative (available
  70. // since 3.20), and GTK+ will provide default, localized buttons.
  71. file_dialog_ = G_OBJECT(gtk_file_chooser_native_new(
  72. l10n_util::GetStringUTF8(IDS_DOWNLOAD_FILE_DIALOG_TITLE).c_str(), nullptr,
  73. GTK_FILE_CHOOSER_ACTION_OPEN, nullptr, nullptr));
  74. #else
  75. // For older versions of GTK+, we can use GtkFileChooserDialog with stock
  76. // items for the buttons, and GTK+ will fetch the appropriate localized
  77. // strings for us. The stock items have been deprecated since 3.10, though, so
  78. // we need to suppress the warnings.
  79. G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
  80. file_dialog_ = G_OBJECT(gtk_file_chooser_dialog_new(
  81. l10n_util::GetStringUTF8(IDS_DOWNLOAD_FILE_DIALOG_TITLE).c_str(), nullptr,
  82. GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
  83. GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, nullptr));
  84. G_GNUC_END_IGNORE_DEPRECATIONS;
  85. #endif
  86. gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(file_dialog_), false);
  87. g_signal_connect(file_dialog_, "response", G_CALLBACK(OnResponseThunk), this);
  88. #if GTK_CHECK_VERSION(3, 90, 0)
  89. gtk_native_dialog_show(GTK_NATIVE_DIALOG(file_dialog_));
  90. #else
  91. gtk_widget_show_all(GTK_WIDGET(file_dialog_));
  92. #endif
  93. }
  94. void GtkFileChooserOnUiThread::RunCallback(FileChooser::Result result) {
  95. caller_task_runner_->PostTask(
  96. FROM_HERE, base::BindOnce(&FileChooserLinux::RunCallback,
  97. file_chooser_linux_, std::move(result)));
  98. }
  99. void GtkFileChooserOnUiThread::CleanUp() {
  100. if (file_dialog_) {
  101. #if GTK_CHECK_VERSION(3, 90, 0)
  102. g_object_unref(file_dialog_);
  103. #else
  104. gtk_widget_destroy(GTK_WIDGET(file_dialog_));
  105. #endif
  106. file_dialog_ = nullptr;
  107. }
  108. }
  109. void GtkFileChooserOnUiThread::OnResponse(GtkWidget* dialog, int response_id) {
  110. gchar* filename = nullptr;
  111. if (response_id == GTK_RESPONSE_ACCEPT) {
  112. filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
  113. }
  114. if (filename) {
  115. RunCallback(base::FilePath(filename));
  116. g_free(filename);
  117. } else {
  118. RunCallback(protocol::MakeFileTransferError(
  119. FROM_HERE, protocol::FileTransfer_Error_Type_CANCELED));
  120. }
  121. CleanUp();
  122. }
  123. FileChooserLinux::FileChooserLinux(
  124. scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
  125. ResultCallback callback)
  126. : callback_(std::move(callback)) {
  127. gtk_file_chooser_on_ui_thread_ =
  128. base::SequenceBound<GtkFileChooserOnUiThread>(
  129. ui_task_runner, base::SequencedTaskRunnerHandle::Get(),
  130. weak_ptr_factory_.GetWeakPtr());
  131. }
  132. void FileChooserLinux::Show() {
  133. gtk_file_chooser_on_ui_thread_.AsyncCall(&GtkFileChooserOnUiThread::Show);
  134. }
  135. void FileChooserLinux::RunCallback(FileChooser::Result result) {
  136. std::move(callback_).Run(std::move(result));
  137. }
  138. FileChooserLinux::~FileChooserLinux() = default;
  139. } // namespace
  140. std::unique_ptr<FileChooser> FileChooser::Create(
  141. scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
  142. ResultCallback callback) {
  143. return std::make_unique<FileChooserLinux>(std::move(ui_task_runner),
  144. std::move(callback));
  145. }
  146. } // namespace remoting