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.
5 #include "atom/browser/ui/message_box.h"
13 #include "atom/browser/native_window_views.h"
14 #include "base/callback.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/threading/thread.h"
18 #include "base/win/scoped_gdi_object.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "ui/gfx/icon_util.h"
26 // Small command ID values are already taken by Windows, we have to start from
27 // a large number to avoid conflicts with Windows.
28 const int kIDStart = 100;
30 // Get the common ID from button's name.
31 struct CommonButtonID {
35 CommonButtonID GetCommonID(const base::string16& button) {
36 base::string16 lower = base::StringToLowerASCII(button);
38 return { TDCBF_OK_BUTTON, IDOK };
39 else if (lower == L"yes")
40 return { TDCBF_YES_BUTTON, IDYES };
41 else if (lower == L"no")
42 return { TDCBF_NO_BUTTON, IDNO };
43 else if (lower == L"cancel")
44 return { TDCBF_CANCEL_BUTTON, IDCANCEL };
45 else if (lower == L"retry")
46 return { TDCBF_RETRY_BUTTON, IDRETRY };
47 else if (lower == L"close")
48 return { TDCBF_CLOSE_BUTTON, IDCLOSE };
52 // Determine whether the buttons are common buttons, if so map common ID
54 void MapToCommonID(const std::vector<base::string16>& buttons,
55 std::map<int, int>* id_map,
56 TASKDIALOG_COMMON_BUTTON_FLAGS* button_flags,
57 std::vector<TASKDIALOG_BUTTON>* dialog_buttons) {
58 for (size_t i = 0; i < buttons.size(); ++i) {
59 auto common = GetCommonID(buttons[i]);
60 if (common.button != -1) {
61 // It is a common button.
62 (*id_map)[common.id] = i;
63 (*button_flags) |= common.button;
65 // It is a custom button.
66 dialog_buttons->push_back({i + kIDStart, buttons[i].c_str()});
71 int ShowMessageBoxUTF16(HWND parent,
73 const std::vector<base::string16>& buttons,
76 const base::string16& title,
77 const base::string16& message,
78 const base::string16& detail,
79 const gfx::ImageSkia& icon) {
80 TASKDIALOG_FLAGS flags = TDF_SIZE_TO_CONTENT; // show all content.
82 flags |= TDF_ALLOW_DIALOG_CANCELLATION; // allow dialog to be cancelled.
84 TASKDIALOGCONFIG config = { 0 };
85 config.cbSize = sizeof(config);
86 config.hwndParent = parent;
87 config.hInstance = GetModuleHandle(NULL);
88 config.dwFlags = flags;
91 config.pszWindowTitle = title.c_str();
93 base::win::ScopedHICON hicon;
95 hicon.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap()));
96 config.dwFlags |= TDF_USE_HICON_MAIN;
97 config.hMainIcon = hicon.Get();
99 // Show icon according to dialog's type.
101 case MESSAGE_BOX_TYPE_INFORMATION:
102 case MESSAGE_BOX_TYPE_QUESTION:
103 config.pszMainIcon = TD_INFORMATION_ICON;
105 case MESSAGE_BOX_TYPE_WARNING:
106 config.pszMainIcon = TD_WARNING_ICON;
108 case MESSAGE_BOX_TYPE_ERROR:
109 config.pszMainIcon = TD_ERROR_ICON;
114 // If "detail" is empty then don't make message hilighted.
115 if (detail.empty()) {
116 config.pszContent = message.c_str();
118 config.pszMainInstruction = message.c_str();
119 config.pszContent = detail.c_str();
122 // Iterate through the buttons, put common buttons in dwCommonButtons
123 // and custom buttons in pButtons.
124 std::map<int, int> id_map;
125 std::vector<TASKDIALOG_BUTTON> dialog_buttons;
126 if (options & MESSAGE_BOX_NO_LINK) {
127 for (size_t i = 0; i < buttons.size(); ++i)
128 dialog_buttons.push_back({i + kIDStart, buttons[i].c_str()});
130 MapToCommonID(buttons, &id_map, &config.dwCommonButtons, &dialog_buttons);
132 if (dialog_buttons.size() > 0) {
133 config.pButtons = &dialog_buttons.front();
134 config.cButtons = dialog_buttons.size();
135 if (!(options & MESSAGE_BOX_NO_LINK))
136 config.dwFlags |= TDF_USE_COMMAND_LINKS; // custom buttons as links.
140 TaskDialogIndirect(&config, &id, NULL, NULL);
141 if (id_map.find(id) != id_map.end()) // common button.
143 else if (id >= kIDStart) // custom button.
144 return id - kIDStart;
149 void RunMessageBoxInNewThread(base::Thread* thread,
150 NativeWindow* parent,
152 const std::vector<std::string>& buttons,
155 const std::string& title,
156 const std::string& message,
157 const std::string& detail,
158 const gfx::ImageSkia& icon,
159 const MessageBoxCallback& callback) {
160 int result = ShowMessageBox(parent, type, buttons, cancel_id, options, title,
161 message, detail, icon);
162 content::BrowserThread::PostTask(
163 content::BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
164 content::BrowserThread::DeleteSoon(
165 content::BrowserThread::UI, FROM_HERE, thread);
170 int ShowMessageBox(NativeWindow* parent,
172 const std::vector<std::string>& buttons,
175 const std::string& title,
176 const std::string& message,
177 const std::string& detail,
178 const gfx::ImageSkia& icon) {
179 std::vector<base::string16> utf16_buttons;
180 for (const auto& button : buttons)
181 utf16_buttons.push_back(base::UTF8ToUTF16(button));
183 HWND hwnd_parent = parent ?
184 static_cast<atom::NativeWindowViews*>(parent)->GetAcceleratedWidget() :
187 NativeWindow::DialogScope dialog_scope(parent);
188 return ShowMessageBoxUTF16(hwnd_parent,
193 base::UTF8ToUTF16(title),
194 base::UTF8ToUTF16(message),
195 base::UTF8ToUTF16(detail),
199 void ShowMessageBox(NativeWindow* parent,
201 const std::vector<std::string>& buttons,
204 const std::string& title,
205 const std::string& message,
206 const std::string& detail,
207 const gfx::ImageSkia& icon,
208 const MessageBoxCallback& callback) {
209 scoped_ptr<base::Thread> thread(
210 new base::Thread(ATOM_PRODUCT_NAME "MessageBoxThread"));
211 thread->init_com_with_mta(false);
212 if (!thread->Start()) {
213 callback.Run(cancel_id);
217 base::Thread* unretained = thread.release();
218 unretained->message_loop()->PostTask(
220 base::Bind(&RunMessageBoxInNewThread, base::Unretained(unretained),
221 parent, type, buttons, cancel_id, options, title, message,
222 detail, icon, callback));
225 void ShowErrorBox(const base::string16& title, const base::string16& content) {
226 ShowMessageBoxUTF16(NULL, MESSAGE_BOX_TYPE_ERROR, {}, 0, 0, L"Error", title,
227 content, gfx::ImageSkia());