message_box_win.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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 "atom/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 "atom/browser/browser.h"
  10. #include "atom/browser/native_window_views.h"
  11. #include "atom/browser/unresponsive_suppressor.h"
  12. #include "base/strings/string_util.h"
  13. #include "base/strings/utf_string_conversions.h"
  14. #include "base/task/post_task.h"
  15. #include "base/threading/thread.h"
  16. #include "base/win/scoped_gdi_object.h"
  17. #include "content/public/browser/browser_task_traits.h"
  18. #include "content/public/browser/browser_thread.h"
  19. #include "ui/gfx/icon_util.h"
  20. #include "ui/gfx/image/image_skia.h"
  21. namespace atom {
  22. namespace {
  23. // Small command ID values are already taken by Windows, we have to start from
  24. // a large number to avoid conflicts with Windows.
  25. const int kIDStart = 100;
  26. // Get the common ID from button's name.
  27. struct CommonButtonID {
  28. int button;
  29. int id;
  30. };
  31. CommonButtonID GetCommonID(const base::string16& button) {
  32. base::string16 lower = base::ToLowerASCII(button);
  33. if (lower == L"ok")
  34. return {TDCBF_OK_BUTTON, IDOK};
  35. else if (lower == L"yes")
  36. return {TDCBF_YES_BUTTON, IDYES};
  37. else if (lower == L"no")
  38. return {TDCBF_NO_BUTTON, IDNO};
  39. else if (lower == L"cancel")
  40. return {TDCBF_CANCEL_BUTTON, IDCANCEL};
  41. else if (lower == L"retry")
  42. return {TDCBF_RETRY_BUTTON, IDRETRY};
  43. else if (lower == L"close")
  44. return {TDCBF_CLOSE_BUTTON, IDCLOSE};
  45. return {-1, -1};
  46. }
  47. // Determine whether the buttons are common buttons, if so map common ID
  48. // to button ID.
  49. void MapToCommonID(const std::vector<base::string16>& buttons,
  50. std::map<int, int>* id_map,
  51. TASKDIALOG_COMMON_BUTTON_FLAGS* button_flags,
  52. std::vector<TASKDIALOG_BUTTON>* dialog_buttons) {
  53. for (size_t i = 0; i < buttons.size(); ++i) {
  54. auto common = GetCommonID(buttons[i]);
  55. if (common.button != -1) {
  56. // It is a common button.
  57. (*id_map)[common.id] = i;
  58. (*button_flags) |= common.button;
  59. } else {
  60. // It is a custom button.
  61. dialog_buttons->push_back(
  62. {static_cast<int>(i + kIDStart), buttons[i].c_str()});
  63. }
  64. }
  65. }
  66. int ShowTaskDialogUTF16(NativeWindow* parent,
  67. MessageBoxType type,
  68. const std::vector<base::string16>& buttons,
  69. int default_id,
  70. int cancel_id,
  71. int options,
  72. const base::string16& title,
  73. const base::string16& message,
  74. const base::string16& detail,
  75. const base::string16& checkbox_label,
  76. bool* checkbox_checked,
  77. const gfx::ImageSkia& icon) {
  78. TASKDIALOG_FLAGS flags =
  79. TDF_SIZE_TO_CONTENT | // Show all content.
  80. TDF_ALLOW_DIALOG_CANCELLATION; // Allow canceling the dialog.
  81. TASKDIALOGCONFIG config = {0};
  82. config.cbSize = sizeof(config);
  83. config.hInstance = GetModuleHandle(NULL);
  84. config.dwFlags = flags;
  85. if (parent) {
  86. config.hwndParent =
  87. static_cast<atom::NativeWindowViews*>(parent)->GetAcceleratedWidget();
  88. }
  89. if (default_id > 0)
  90. config.nDefaultButton = kIDStart + default_id;
  91. // TaskDialogIndirect doesn't allow empty name, if we set empty title it
  92. // will show "electron.exe" in title.
  93. base::string16 app_name = base::UTF8ToUTF16(Browser::Get()->GetName());
  94. if (title.empty())
  95. config.pszWindowTitle = app_name.c_str();
  96. else
  97. config.pszWindowTitle = title.c_str();
  98. base::win::ScopedHICON hicon;
  99. if (!icon.isNull()) {
  100. hicon = IconUtil::CreateHICONFromSkBitmap(*icon.bitmap());
  101. config.dwFlags |= TDF_USE_HICON_MAIN;
  102. config.hMainIcon = hicon.get();
  103. } else {
  104. // Show icon according to dialog's type.
  105. switch (type) {
  106. case MESSAGE_BOX_TYPE_INFORMATION:
  107. case MESSAGE_BOX_TYPE_QUESTION:
  108. config.pszMainIcon = TD_INFORMATION_ICON;
  109. break;
  110. case MESSAGE_BOX_TYPE_WARNING:
  111. config.pszMainIcon = TD_WARNING_ICON;
  112. break;
  113. case MESSAGE_BOX_TYPE_ERROR:
  114. config.pszMainIcon = TD_ERROR_ICON;
  115. break;
  116. case MESSAGE_BOX_TYPE_NONE:
  117. break;
  118. }
  119. }
  120. // If "detail" is empty then don't make message hilighted.
  121. if (detail.empty()) {
  122. config.pszContent = message.c_str();
  123. } else {
  124. config.pszMainInstruction = message.c_str();
  125. config.pszContent = detail.c_str();
  126. }
  127. if (!checkbox_label.empty()) {
  128. config.pszVerificationText = checkbox_label.c_str();
  129. if (checkbox_checked && *checkbox_checked) {
  130. config.dwFlags |= TDF_VERIFICATION_FLAG_CHECKED;
  131. }
  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 (options & MESSAGE_BOX_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.size() > 0) {
  145. config.pButtons = &dialog_buttons.front();
  146. config.cButtons = dialog_buttons.size();
  147. if (!(options & MESSAGE_BOX_NO_LINK))
  148. config.dwFlags |= TDF_USE_COMMAND_LINKS; // custom buttons as links.
  149. }
  150. int id = 0;
  151. BOOL verificationFlagChecked = FALSE;
  152. TaskDialogIndirect(&config, &id, nullptr, &verificationFlagChecked);
  153. if (checkbox_checked) {
  154. *checkbox_checked = verificationFlagChecked;
  155. }
  156. if (id_map.find(id) != id_map.end()) // common button.
  157. return id_map[id];
  158. else if (id >= kIDStart) // custom button.
  159. return id - kIDStart;
  160. else
  161. return cancel_id;
  162. }
  163. int ShowTaskDialogUTF8(NativeWindow* parent,
  164. MessageBoxType type,
  165. const std::vector<std::string>& buttons,
  166. int default_id,
  167. int cancel_id,
  168. int options,
  169. const std::string& title,
  170. const std::string& message,
  171. const std::string& detail,
  172. const std::string& checkbox_label,
  173. bool* checkbox_checked,
  174. const gfx::ImageSkia& icon) {
  175. std::vector<base::string16> utf16_buttons;
  176. for (const auto& button : buttons)
  177. utf16_buttons.push_back(base::UTF8ToUTF16(button));
  178. return ShowTaskDialogUTF16(
  179. parent, type, utf16_buttons, default_id, cancel_id, options,
  180. base::UTF8ToUTF16(title), base::UTF8ToUTF16(message),
  181. base::UTF8ToUTF16(detail), base::UTF8ToUTF16(checkbox_label),
  182. checkbox_checked, icon);
  183. }
  184. void RunMessageBoxInNewThread(base::Thread* thread,
  185. NativeWindow* parent,
  186. MessageBoxType type,
  187. const std::vector<std::string>& buttons,
  188. int default_id,
  189. int cancel_id,
  190. int options,
  191. const std::string& title,
  192. const std::string& message,
  193. const std::string& detail,
  194. const std::string& checkbox_label,
  195. bool checkbox_checked,
  196. const gfx::ImageSkia& icon,
  197. MessageBoxCallback callback) {
  198. int result = ShowTaskDialogUTF8(parent, type, buttons, default_id, cancel_id,
  199. options, title, message, detail,
  200. checkbox_label, &checkbox_checked, icon);
  201. base::PostTaskWithTraits(
  202. FROM_HERE, {content::BrowserThread::UI},
  203. base::BindOnce(std::move(callback), result, checkbox_checked));
  204. content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE,
  205. thread);
  206. }
  207. } // namespace
  208. int ShowMessageBoxSync(NativeWindow* parent,
  209. MessageBoxType type,
  210. const std::vector<std::string>& buttons,
  211. int default_id,
  212. int cancel_id,
  213. int options,
  214. const std::string& title,
  215. const std::string& message,
  216. const std::string& detail,
  217. const gfx::ImageSkia& icon) {
  218. atom::UnresponsiveSuppressor suppressor;
  219. return ShowTaskDialogUTF8(parent, type, buttons, default_id, cancel_id,
  220. options, title, message, detail, "", nullptr, icon);
  221. }
  222. void ShowMessageBox(NativeWindow* parent,
  223. MessageBoxType type,
  224. const std::vector<std::string>& buttons,
  225. int default_id,
  226. int cancel_id,
  227. int options,
  228. const std::string& title,
  229. const std::string& message,
  230. const std::string& detail,
  231. const std::string& checkbox_label,
  232. bool checkbox_checked,
  233. const gfx::ImageSkia& icon,
  234. MessageBoxCallback callback) {
  235. auto thread =
  236. std::make_unique<base::Thread>(ATOM_PRODUCT_NAME "MessageBoxThread");
  237. thread->init_com_with_mta(false);
  238. if (!thread->Start()) {
  239. std::move(callback).Run(cancel_id, checkbox_checked);
  240. return;
  241. }
  242. base::Thread* unretained = thread.release();
  243. unretained->task_runner()->PostTask(
  244. FROM_HERE,
  245. base::BindOnce(&RunMessageBoxInNewThread, base::Unretained(unretained),
  246. parent, type, buttons, default_id, cancel_id, options,
  247. title, message, detail, checkbox_label, checkbox_checked,
  248. icon, std::move(callback)));
  249. }
  250. void ShowErrorBox(const base::string16& title, const base::string16& content) {
  251. atom::UnresponsiveSuppressor suppressor;
  252. ShowTaskDialogUTF16(nullptr, MESSAGE_BOX_TYPE_ERROR, {}, -1, 0, 0, L"Error",
  253. title, content, L"", nullptr, gfx::ImageSkia());
  254. }
  255. } // namespace atom