message_box_win.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright (c) 2013 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 "shell/browser/ui/message_box.h"
  5. #include <windows.h> // windows.h must be included first
  6. #include <commctrl.h>
  7. #include <map>
  8. #include <vector>
  9. #include "base/strings/string_util.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "base/task/post_task.h"
  12. #include "base/win/scoped_gdi_object.h"
  13. #include "shell/browser/browser.h"
  14. #include "shell/browser/native_window_views.h"
  15. #include "shell/browser/ui/win/dialog_thread.h"
  16. #include "shell/browser/unresponsive_suppressor.h"
  17. #include "ui/gfx/icon_util.h"
  18. #include "ui/gfx/image/image_skia.h"
  19. namespace electron {
  20. MessageBoxSettings::MessageBoxSettings() = default;
  21. MessageBoxSettings::MessageBoxSettings(const MessageBoxSettings&) = default;
  22. MessageBoxSettings::~MessageBoxSettings() = default;
  23. namespace {
  24. // Small command ID values are already taken by Windows, we have to start from
  25. // a large number to avoid conflicts with Windows.
  26. const int kIDStart = 100;
  27. // Get the common ID from button's name.
  28. struct CommonButtonID {
  29. int button;
  30. int id;
  31. };
  32. CommonButtonID GetCommonID(const base::string16& button) {
  33. base::string16 lower = base::ToLowerASCII(button);
  34. if (lower == L"ok")
  35. return {TDCBF_OK_BUTTON, IDOK};
  36. else if (lower == L"yes")
  37. return {TDCBF_YES_BUTTON, IDYES};
  38. else if (lower == L"no")
  39. return {TDCBF_NO_BUTTON, IDNO};
  40. else if (lower == L"cancel")
  41. return {TDCBF_CANCEL_BUTTON, IDCANCEL};
  42. else if (lower == L"retry")
  43. return {TDCBF_RETRY_BUTTON, IDRETRY};
  44. else if (lower == L"close")
  45. return {TDCBF_CLOSE_BUTTON, IDCLOSE};
  46. return {-1, -1};
  47. }
  48. // Determine whether the buttons are common buttons, if so map common ID
  49. // to button ID.
  50. void MapToCommonID(const std::vector<base::string16>& buttons,
  51. std::map<int, int>* id_map,
  52. TASKDIALOG_COMMON_BUTTON_FLAGS* button_flags,
  53. std::vector<TASKDIALOG_BUTTON>* dialog_buttons) {
  54. for (size_t i = 0; i < buttons.size(); ++i) {
  55. auto common = GetCommonID(buttons[i]);
  56. if (common.button != -1) {
  57. // It is a common button.
  58. (*id_map)[common.id] = i;
  59. (*button_flags) |= common.button;
  60. } else {
  61. // It is a custom button.
  62. dialog_buttons->push_back(
  63. {static_cast<int>(i + kIDStart), buttons[i].c_str()});
  64. }
  65. }
  66. }
  67. DialogResult ShowTaskDialogUTF16(NativeWindow* parent,
  68. MessageBoxType type,
  69. const std::vector<base::string16>& buttons,
  70. int default_id,
  71. int cancel_id,
  72. bool no_link,
  73. const base::string16& title,
  74. const base::string16& message,
  75. const base::string16& detail,
  76. const base::string16& checkbox_label,
  77. bool checkbox_checked,
  78. const gfx::ImageSkia& icon) {
  79. TASKDIALOG_FLAGS flags =
  80. TDF_SIZE_TO_CONTENT | // Show all content.
  81. TDF_ALLOW_DIALOG_CANCELLATION; // Allow canceling the dialog.
  82. TASKDIALOGCONFIG config = {0};
  83. config.cbSize = sizeof(config);
  84. config.hInstance = GetModuleHandle(NULL);
  85. config.dwFlags = flags;
  86. if (parent) {
  87. config.hwndParent = static_cast<electron::NativeWindowViews*>(parent)
  88. ->GetAcceleratedWidget();
  89. }
  90. if (default_id > 0)
  91. config.nDefaultButton = kIDStart + default_id;
  92. // TaskDialogIndirect doesn't allow empty name, if we set empty title it
  93. // will show "electron.exe" in title.
  94. base::string16 app_name = base::UTF8ToUTF16(Browser::Get()->GetName());
  95. if (title.empty())
  96. config.pszWindowTitle = app_name.c_str();
  97. else
  98. config.pszWindowTitle = title.c_str();
  99. base::win::ScopedHICON hicon;
  100. if (!icon.isNull()) {
  101. hicon = IconUtil::CreateHICONFromSkBitmap(*icon.bitmap());
  102. config.dwFlags |= TDF_USE_HICON_MAIN;
  103. config.hMainIcon = hicon.get();
  104. } else {
  105. // Show icon according to dialog's type.
  106. switch (type) {
  107. case MessageBoxType::kInformation:
  108. case MessageBoxType::kQuestion:
  109. config.pszMainIcon = TD_INFORMATION_ICON;
  110. break;
  111. case MessageBoxType::kWarning:
  112. config.pszMainIcon = TD_WARNING_ICON;
  113. break;
  114. case MessageBoxType::kError:
  115. config.pszMainIcon = TD_ERROR_ICON;
  116. break;
  117. case MessageBoxType::kNone:
  118. break;
  119. }
  120. }
  121. // If "detail" is empty then don't make message highlighted.
  122. if (detail.empty()) {
  123. config.pszContent = message.c_str();
  124. } else {
  125. config.pszMainInstruction = message.c_str();
  126. config.pszContent = detail.c_str();
  127. }
  128. if (!checkbox_label.empty()) {
  129. config.pszVerificationText = checkbox_label.c_str();
  130. if (checkbox_checked)
  131. config.dwFlags |= TDF_VERIFICATION_FLAG_CHECKED;
  132. }
  133. // Iterate through the buttons, put common buttons in dwCommonButtons
  134. // and custom buttons in pButtons.
  135. std::map<int, int> id_map;
  136. std::vector<TASKDIALOG_BUTTON> dialog_buttons;
  137. if (no_link) {
  138. for (size_t i = 0; i < buttons.size(); ++i)
  139. dialog_buttons.push_back(
  140. {static_cast<int>(i + kIDStart), buttons[i].c_str()});
  141. } else {
  142. MapToCommonID(buttons, &id_map, &config.dwCommonButtons, &dialog_buttons);
  143. }
  144. if (!dialog_buttons.empty()) {
  145. config.pButtons = &dialog_buttons.front();
  146. config.cButtons = dialog_buttons.size();
  147. if (!no_link)
  148. config.dwFlags |= TDF_USE_COMMAND_LINKS; // custom buttons as links.
  149. }
  150. int button_id;
  151. int id = 0;
  152. BOOL verificationFlagChecked = FALSE;
  153. TaskDialogIndirect(&config, &id, nullptr, &verificationFlagChecked);
  154. if (id_map.find(id) != id_map.end()) // common button.
  155. button_id = id_map[id];
  156. else if (id >= kIDStart) // custom button.
  157. button_id = id - kIDStart;
  158. else
  159. button_id = cancel_id;
  160. return std::make_pair(button_id, verificationFlagChecked);
  161. }
  162. DialogResult ShowTaskDialogUTF8(const MessageBoxSettings& settings) {
  163. std::vector<base::string16> utf16_buttons;
  164. for (const auto& button : settings.buttons)
  165. utf16_buttons.push_back(base::UTF8ToUTF16(button));
  166. const base::string16 title_16 = base::UTF8ToUTF16(settings.title);
  167. const base::string16 message_16 = base::UTF8ToUTF16(settings.message);
  168. const base::string16 detail_16 = base::UTF8ToUTF16(settings.detail);
  169. const base::string16 checkbox_label_16 =
  170. base::UTF8ToUTF16(settings.checkbox_label);
  171. return ShowTaskDialogUTF16(
  172. settings.parent_window, settings.type, utf16_buttons, settings.default_id,
  173. settings.cancel_id, settings.no_link, title_16, message_16, detail_16,
  174. checkbox_label_16, settings.checkbox_checked, settings.icon);
  175. }
  176. } // namespace
  177. int ShowMessageBoxSync(const MessageBoxSettings& settings) {
  178. electron::UnresponsiveSuppressor suppressor;
  179. DialogResult result = ShowTaskDialogUTF8(settings);
  180. return result.first;
  181. }
  182. void ShowMessageBox(const MessageBoxSettings& settings,
  183. MessageBoxCallback callback) {
  184. dialog_thread::Run(base::BindOnce(&ShowTaskDialogUTF8, settings),
  185. base::BindOnce(
  186. [](MessageBoxCallback callback, DialogResult result) {
  187. std::move(callback).Run(result.first, result.second);
  188. },
  189. std::move(callback)));
  190. }
  191. void ShowErrorBox(const base::string16& title, const base::string16& content) {
  192. electron::UnresponsiveSuppressor suppressor;
  193. ShowTaskDialogUTF16(nullptr, MessageBoxType::kError, {}, -1, 0, false,
  194. L"Error", title, content, L"", false, gfx::ImageSkia());
  195. }
  196. } // namespace electron