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