message_box_gtk.cc 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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/views/widget/desktop_aura/x11_desktop_handler.h"
  17. #define ANSI_FOREGROUND_RED "\x1b[31m"
  18. #define ANSI_FOREGROUND_BLACK "\x1b[30m"
  19. #define ANSI_TEXT_BOLD "\x1b[1m"
  20. #define ANSI_BACKGROUND_GRAY "\x1b[47m"
  21. #define ANSI_RESET "\x1b[0m"
  22. namespace atom {
  23. namespace {
  24. class GtkMessageBox : public NativeWindowObserver {
  25. public:
  26. GtkMessageBox(NativeWindow* parent_window,
  27. MessageBoxType type,
  28. const std::vector<std::string>& buttons,
  29. int default_id,
  30. int cancel_id,
  31. const std::string& title,
  32. const std::string& message,
  33. const std::string& detail,
  34. const std::string& checkbox_label,
  35. bool checkbox_checked,
  36. const gfx::ImageSkia& icon)
  37. : cancel_id_(cancel_id),
  38. checkbox_checked_(false),
  39. parent_(static_cast<NativeWindow*>(parent_window)) {
  40. // Create dialog.
  41. dialog_ =
  42. gtk_message_dialog_new(nullptr, // parent
  43. static_cast<GtkDialogFlags>(0), // no flags
  44. GetMessageType(type), // type
  45. GTK_BUTTONS_NONE, // no buttons
  46. "%s", message.c_str());
  47. if (!detail.empty())
  48. gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog_),
  49. "%s", detail.c_str());
  50. if (!title.empty())
  51. gtk_window_set_title(GTK_WINDOW(dialog_), title.c_str());
  52. if (!icon.isNull()) {
  53. // No easy way to obtain this programmatically, but GTK+'s docs
  54. // define GTK_ICON_SIZE_DIALOG to be 48 pixels
  55. static constexpr int pixel_width = 48;
  56. static constexpr int pixel_height = 48;
  57. GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(*icon.bitmap());
  58. GdkPixbuf* scaled_pixbuf = gdk_pixbuf_scale_simple(
  59. pixbuf, pixel_width, pixel_height, GDK_INTERP_BILINEAR);
  60. GtkWidget* w = gtk_image_new_from_pixbuf(scaled_pixbuf);
  61. gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog_), w);
  62. gtk_widget_show(w);
  63. g_clear_pointer(&scaled_pixbuf, gdk_pixbuf_unref);
  64. g_clear_pointer(&pixbuf, gdk_pixbuf_unref);
  65. }
  66. if (!checkbox_label.empty()) {
  67. GtkWidget* message_area =
  68. gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog_));
  69. GtkWidget* check_button =
  70. gtk_check_button_new_with_label(checkbox_label.c_str());
  71. g_signal_connect(check_button, "toggled",
  72. G_CALLBACK(OnCheckboxToggledThunk), this);
  73. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button),
  74. checkbox_checked);
  75. gtk_container_add(GTK_CONTAINER(message_area), check_button);
  76. gtk_widget_show(check_button);
  77. }
  78. // Add buttons.
  79. GtkDialog* dialog = GTK_DIALOG(dialog_);
  80. for (size_t i = 0; i < buttons.size(); ++i) {
  81. gtk_dialog_add_button(dialog, TranslateToStock(i, buttons[i]), i);
  82. }
  83. gtk_dialog_set_default_response(dialog, default_id);
  84. // Parent window.
  85. if (parent_) {
  86. parent_->AddObserver(this);
  87. static_cast<NativeWindowViews*>(parent_)->SetEnabled(false);
  88. libgtkui::SetGtkTransientForAura(dialog_, parent_->GetNativeWindow());
  89. gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE);
  90. }
  91. }
  92. ~GtkMessageBox() {
  93. gtk_widget_destroy(dialog_);
  94. if (parent_) {
  95. parent_->RemoveObserver(this);
  96. static_cast<NativeWindowViews*>(parent_)->SetEnabled(true);
  97. }
  98. }
  99. GtkMessageType GetMessageType(MessageBoxType type) {
  100. switch (type) {
  101. case MESSAGE_BOX_TYPE_INFORMATION:
  102. return GTK_MESSAGE_INFO;
  103. case MESSAGE_BOX_TYPE_WARNING:
  104. return GTK_MESSAGE_WARNING;
  105. case MESSAGE_BOX_TYPE_QUESTION:
  106. return GTK_MESSAGE_QUESTION;
  107. case MESSAGE_BOX_TYPE_ERROR:
  108. return GTK_MESSAGE_ERROR;
  109. default:
  110. return GTK_MESSAGE_OTHER;
  111. }
  112. }
  113. const char* TranslateToStock(int id, const std::string& text) {
  114. const std::string lower = base::ToLowerASCII(text);
  115. if (lower == "cancel")
  116. return _("_Cancel");
  117. if (lower == "no")
  118. return _("_No");
  119. if (lower == "ok")
  120. return _("_OK");
  121. if (lower == "yes")
  122. return _("_Yes");
  123. return text.c_str();
  124. }
  125. void Show() {
  126. gtk_widget_show(dialog_);
  127. // We need to call gtk_window_present after making the widgets visible to
  128. // make sure window gets correctly raised and gets focus.
  129. int time = ui::X11EventSource::GetInstance()->GetTimestamp();
  130. gtk_window_present_with_time(GTK_WINDOW(dialog_), time);
  131. }
  132. int RunSynchronous() {
  133. Show();
  134. int response = gtk_dialog_run(GTK_DIALOG(dialog_));
  135. if (response < 0)
  136. return cancel_id_;
  137. else
  138. return response;
  139. }
  140. void RunAsynchronous(const MessageBoxCallback& callback) {
  141. callback_ = callback;
  142. g_signal_connect(dialog_, "delete-event",
  143. G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
  144. g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseDialogThunk),
  145. this);
  146. Show();
  147. }
  148. void OnWindowClosed() override {
  149. parent_->RemoveObserver(this);
  150. parent_ = nullptr;
  151. }
  152. CHROMEGTK_CALLBACK_1(GtkMessageBox, void, OnResponseDialog, int);
  153. CHROMEGTK_CALLBACK_0(GtkMessageBox, void, OnCheckboxToggled);
  154. private:
  155. atom::UnresponsiveSuppressor unresponsive_suppressor_;
  156. // The id to return when the dialog is closed without pressing buttons.
  157. int cancel_id_;
  158. bool checkbox_checked_;
  159. NativeWindow* parent_;
  160. GtkWidget* dialog_;
  161. MessageBoxCallback callback_;
  162. DISALLOW_COPY_AND_ASSIGN(GtkMessageBox);
  163. };
  164. void GtkMessageBox::OnResponseDialog(GtkWidget* widget, int response) {
  165. gtk_widget_hide(dialog_);
  166. if (response < 0)
  167. callback_.Run(cancel_id_, checkbox_checked_);
  168. else
  169. callback_.Run(response, checkbox_checked_);
  170. delete this;
  171. }
  172. void GtkMessageBox::OnCheckboxToggled(GtkWidget* widget) {
  173. checkbox_checked_ = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
  174. }
  175. } // namespace
  176. int ShowMessageBox(NativeWindow* parent,
  177. MessageBoxType type,
  178. const std::vector<std::string>& buttons,
  179. int default_id,
  180. int cancel_id,
  181. int options,
  182. const std::string& title,
  183. const std::string& message,
  184. const std::string& detail,
  185. const gfx::ImageSkia& icon) {
  186. return GtkMessageBox(parent, type, buttons, default_id, cancel_id, title,
  187. message, detail, "", false, icon)
  188. .RunSynchronous();
  189. }
  190. void ShowMessageBox(NativeWindow* parent,
  191. MessageBoxType type,
  192. const std::vector<std::string>& buttons,
  193. int default_id,
  194. int cancel_id,
  195. int options,
  196. const std::string& title,
  197. const std::string& message,
  198. const std::string& detail,
  199. const std::string& checkbox_label,
  200. bool checkbox_checked,
  201. const gfx::ImageSkia& icon,
  202. const MessageBoxCallback& callback) {
  203. (new GtkMessageBox(parent, type, buttons, default_id, cancel_id, title,
  204. message, detail, checkbox_label, checkbox_checked, icon))
  205. ->RunAsynchronous(callback);
  206. }
  207. void ShowErrorBox(const base::string16& title, const base::string16& content) {
  208. if (Browser::Get()->is_ready()) {
  209. GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, {"OK"}, -1, 0, "Error",
  210. base::UTF16ToUTF8(title).c_str(),
  211. base::UTF16ToUTF8(content).c_str(), "", false,
  212. gfx::ImageSkia())
  213. .RunSynchronous();
  214. } else {
  215. fprintf(stderr, ANSI_TEXT_BOLD ANSI_BACKGROUND_GRAY ANSI_FOREGROUND_RED
  216. "%s\n" ANSI_FOREGROUND_BLACK "%s" ANSI_RESET "\n",
  217. base::UTF16ToUTF8(title).c_str(),
  218. base::UTF16ToUTF8(content).c_str());
  219. }
  220. }
  221. } // namespace atom