message_box_gtk.cc 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // Copyright (c) 2015 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/ui/message_box.h"
  5. #include <glib/gi18n.h>
  6. #include "atom/browser/browser.h"
  7. #include "atom/browser/native_window_observer.h"
  8. #include "atom/browser/native_window_views.h"
  9. #include "atom/browser/unresponsive_suppressor.h"
  10. #include "base/callback.h"
  11. #include "base/strings/string_util.h"
  12. #include "base/strings/utf_string_conversions.h"
  13. #include "chrome/browser/ui/libgtkui/gtk_signal.h"
  14. #include "chrome/browser/ui/libgtkui/gtk_util.h"
  15. #include "chrome/browser/ui/libgtkui/skia_utils_gtk.h"
  16. #include "ui/gfx/image/image_skia.h"
  17. #include "ui/views/widget/desktop_aura/x11_desktop_handler.h"
  18. #define ANSI_FOREGROUND_RED "\x1b[31m"
  19. #define ANSI_FOREGROUND_BLACK "\x1b[30m"
  20. #define ANSI_TEXT_BOLD "\x1b[1m"
  21. #define ANSI_BACKGROUND_GRAY "\x1b[47m"
  22. #define ANSI_RESET "\x1b[0m"
  23. namespace atom {
  24. namespace {
  25. class GtkMessageBox : public NativeWindowObserver {
  26. public:
  27. GtkMessageBox(NativeWindow* parent_window,
  28. MessageBoxType type,
  29. const std::vector<std::string>& buttons,
  30. int default_id,
  31. int cancel_id,
  32. const std::string& title,
  33. const std::string& message,
  34. const std::string& detail,
  35. const std::string& checkbox_label,
  36. bool checkbox_checked)
  37. : cancel_id_(cancel_id),
  38. parent_(static_cast<NativeWindow*>(parent_window)) {
  39. // Create dialog.
  40. dialog_ =
  41. gtk_message_dialog_new(nullptr, // parent
  42. static_cast<GtkDialogFlags>(0), // no flags
  43. GetMessageType(type), // type
  44. GTK_BUTTONS_NONE, // no buttons
  45. "%s", message.c_str());
  46. if (!detail.empty())
  47. gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog_),
  48. "%s", detail.c_str());
  49. if (!title.empty())
  50. gtk_window_set_title(GTK_WINDOW(dialog_), title.c_str());
  51. if (!checkbox_label.empty()) {
  52. GtkWidget* message_area =
  53. gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog_));
  54. GtkWidget* check_button =
  55. gtk_check_button_new_with_label(checkbox_label.c_str());
  56. g_signal_connect(check_button, "toggled",
  57. G_CALLBACK(OnCheckboxToggledThunk), this);
  58. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button),
  59. checkbox_checked);
  60. gtk_container_add(GTK_CONTAINER(message_area), check_button);
  61. }
  62. // Add buttons.
  63. for (size_t i = 0; i < buttons.size(); ++i) {
  64. GtkWidget* button = gtk_dialog_add_button(
  65. GTK_DIALOG(dialog_), TranslateToStock(i, buttons[i]), i);
  66. if (static_cast<int>(i) == default_id)
  67. gtk_widget_grab_focus(button);
  68. }
  69. // Parent window.
  70. if (parent_) {
  71. parent_->AddObserver(this);
  72. static_cast<NativeWindowViews*>(parent_)->SetEnabled(false);
  73. libgtkui::SetGtkTransientForAura(dialog_, parent_->GetNativeWindow());
  74. gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE);
  75. }
  76. }
  77. ~GtkMessageBox() override {
  78. gtk_widget_destroy(dialog_);
  79. if (parent_) {
  80. parent_->RemoveObserver(this);
  81. static_cast<NativeWindowViews*>(parent_)->SetEnabled(true);
  82. }
  83. }
  84. GtkMessageType GetMessageType(MessageBoxType type) {
  85. switch (type) {
  86. case MESSAGE_BOX_TYPE_INFORMATION:
  87. return GTK_MESSAGE_INFO;
  88. case MESSAGE_BOX_TYPE_WARNING:
  89. return GTK_MESSAGE_WARNING;
  90. case MESSAGE_BOX_TYPE_QUESTION:
  91. return GTK_MESSAGE_QUESTION;
  92. case MESSAGE_BOX_TYPE_ERROR:
  93. return GTK_MESSAGE_ERROR;
  94. default:
  95. return GTK_MESSAGE_OTHER;
  96. }
  97. }
  98. const char* TranslateToStock(int id, const std::string& text) {
  99. const std::string lower = base::ToLowerASCII(text);
  100. if (lower == "cancel")
  101. return _("_Cancel");
  102. if (lower == "no")
  103. return _("_No");
  104. if (lower == "ok")
  105. return _("_OK");
  106. if (lower == "yes")
  107. return _("_Yes");
  108. return text.c_str();
  109. }
  110. void Show() {
  111. gtk_widget_show_all(dialog_);
  112. // We need to call gtk_window_present after making the widgets visible to
  113. // make sure window gets correctly raised and gets focus.
  114. int time = ui::X11EventSource::GetInstance()->GetTimestamp();
  115. gtk_window_present_with_time(GTK_WINDOW(dialog_), time);
  116. }
  117. int RunSynchronous() {
  118. Show();
  119. int response = gtk_dialog_run(GTK_DIALOG(dialog_));
  120. if (response < 0)
  121. return cancel_id_;
  122. else
  123. return response;
  124. }
  125. void RunAsynchronous(const MessageBoxCallback& callback) {
  126. callback_ = callback;
  127. g_signal_connect(dialog_, "delete-event",
  128. G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
  129. g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseDialogThunk),
  130. this);
  131. Show();
  132. }
  133. void OnWindowClosed() override {
  134. parent_->RemoveObserver(this);
  135. parent_ = nullptr;
  136. }
  137. CHROMEGTK_CALLBACK_1(GtkMessageBox, void, OnResponseDialog, int);
  138. CHROMEGTK_CALLBACK_0(GtkMessageBox, void, OnCheckboxToggled);
  139. private:
  140. atom::UnresponsiveSuppressor unresponsive_suppressor_;
  141. // The id to return when the dialog is closed without pressing buttons.
  142. int cancel_id_ = 0;
  143. bool checkbox_checked_ = false;
  144. NativeWindow* parent_;
  145. GtkWidget* dialog_;
  146. MessageBoxCallback callback_;
  147. DISALLOW_COPY_AND_ASSIGN(GtkMessageBox);
  148. };
  149. void GtkMessageBox::OnResponseDialog(GtkWidget* widget, int response) {
  150. gtk_widget_hide(dialog_);
  151. if (response < 0)
  152. callback_.Run(cancel_id_, checkbox_checked_);
  153. else
  154. callback_.Run(response, checkbox_checked_);
  155. delete this;
  156. }
  157. void GtkMessageBox::OnCheckboxToggled(GtkWidget* widget) {
  158. checkbox_checked_ = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
  159. }
  160. } // namespace
  161. int ShowMessageBox(NativeWindow* parent,
  162. MessageBoxType type,
  163. const std::vector<std::string>& buttons,
  164. int default_id,
  165. int cancel_id,
  166. int options,
  167. const std::string& title,
  168. const std::string& message,
  169. const std::string& detail,
  170. const gfx::ImageSkia& /*icon*/) {
  171. return GtkMessageBox(parent, type, buttons, default_id, cancel_id, title,
  172. message, detail, "", false)
  173. .RunSynchronous();
  174. }
  175. void ShowMessageBox(NativeWindow* parent,
  176. MessageBoxType type,
  177. const std::vector<std::string>& buttons,
  178. int default_id,
  179. int cancel_id,
  180. int options,
  181. const std::string& title,
  182. const std::string& message,
  183. const std::string& detail,
  184. const std::string& checkbox_label,
  185. bool checkbox_checked,
  186. const gfx::ImageSkia& /*icon*/,
  187. const MessageBoxCallback& callback) {
  188. (new GtkMessageBox(parent, type, buttons, default_id, cancel_id, title,
  189. message, detail, checkbox_label, checkbox_checked))
  190. ->RunAsynchronous(callback);
  191. }
  192. void ShowErrorBox(const base::string16& title, const base::string16& content) {
  193. if (Browser::Get()->is_ready()) {
  194. GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, {"OK"}, -1, 0, "Error",
  195. base::UTF16ToUTF8(title).c_str(),
  196. base::UTF16ToUTF8(content).c_str(), "", false)
  197. .RunSynchronous();
  198. } else {
  199. fprintf(stderr,
  200. ANSI_TEXT_BOLD ANSI_BACKGROUND_GRAY ANSI_FOREGROUND_RED
  201. "%s\n" ANSI_FOREGROUND_BLACK "%s" ANSI_RESET "\n",
  202. base::UTF16ToUTF8(title).c_str(),
  203. base::UTF16ToUTF8(content).c_str());
  204. }
  205. }
  206. } // namespace atom