Merge pull request #3714 from atom/chrome47
[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>
8 #include <commctrl.h>
9
10 #include <map>
11 #include <vector>
12
13 #include "atom/browser/browser.h"
14 #include "atom/browser/native_window_views.h"
15 #include "base/callback.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/thread.h"
19 #include "base/win/scoped_gdi_object.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "ui/gfx/icon_util.h"
22
23 namespace atom {
24
25 namespace {
26
27 // Small command ID values are already taken by Windows, we have to start from
28 // a large number to avoid conflicts with Windows.
29 const int kIDStart = 100;
30
31 // Get the common ID from button's name.
32 struct CommonButtonID {
33   int button;
34   int id;
35 };
36 CommonButtonID GetCommonID(const base::string16& button) {
37   base::string16 lower = base::ToLowerASCII(button);
38   if (lower == L"ok")
39     return { TDCBF_OK_BUTTON, IDOK };
40   else if (lower == L"yes")
41     return { TDCBF_YES_BUTTON, IDYES };
42   else if (lower == L"no")
43     return { TDCBF_NO_BUTTON, IDNO };
44   else if (lower == L"cancel")
45     return { TDCBF_CANCEL_BUTTON, IDCANCEL };
46   else if (lower == L"retry")
47     return { TDCBF_RETRY_BUTTON, IDRETRY };
48   else if (lower == L"close")
49     return { TDCBF_CLOSE_BUTTON, IDCLOSE };
50   return { -1, -1 };
51 }
52
53 // Determine whether the buttons are common buttons, if so map common ID
54 // to button ID.
55 void MapToCommonID(const std::vector<base::string16>& buttons,
56                    std::map<int, int>* id_map,
57                    TASKDIALOG_COMMON_BUTTON_FLAGS* button_flags,
58                    std::vector<TASKDIALOG_BUTTON>* dialog_buttons) {
59   for (size_t i = 0; i < buttons.size(); ++i) {
60     auto common = GetCommonID(buttons[i]);
61     if (common.button != -1) {
62       // It is a common button.
63       (*id_map)[common.id] = i;
64       (*button_flags) |= common.button;
65     } else {
66       // It is a custom button.
67       dialog_buttons->push_back({i + kIDStart, buttons[i].c_str()});
68     }
69   }
70 }
71
72 int ShowMessageBoxUTF16(HWND parent,
73                         MessageBoxType type,
74                         const std::vector<base::string16>& buttons,
75                         int cancel_id,
76                         int options,
77                         const base::string16& title,
78                         const base::string16& message,
79                         const base::string16& detail,
80                         const gfx::ImageSkia& icon) {
81   TASKDIALOG_FLAGS flags =
82       TDF_SIZE_TO_CONTENT |  // Show all content.
83       TDF_ALLOW_DIALOG_CANCELLATION;  // Allow canceling the dialog.
84
85   TASKDIALOGCONFIG config = { 0 };
86   config.cbSize     = sizeof(config);
87   config.hwndParent = parent;
88   config.hInstance  = GetModuleHandle(NULL);
89   config.dwFlags    = flags;
90
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
99   base::win::ScopedHICON hicon;
100   if (!icon.isNull()) {
101     hicon.Set(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 MESSAGE_BOX_TYPE_INFORMATION:
108       case MESSAGE_BOX_TYPE_QUESTION:
109         config.pszMainIcon = TD_INFORMATION_ICON;
110         break;
111       case MESSAGE_BOX_TYPE_WARNING:
112         config.pszMainIcon = TD_WARNING_ICON;
113         break;
114       case MESSAGE_BOX_TYPE_ERROR:
115         config.pszMainIcon = TD_ERROR_ICON;
116         break;
117     }
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
128   // Iterate through the buttons, put common buttons in dwCommonButtons
129   // and custom buttons in pButtons.
130   std::map<int, int> id_map;
131   std::vector<TASKDIALOG_BUTTON> dialog_buttons;
132   if (options & MESSAGE_BOX_NO_LINK) {
133     for (size_t i = 0; i < buttons.size(); ++i)
134       dialog_buttons.push_back({i + kIDStart, buttons[i].c_str()});
135   } else {
136     MapToCommonID(buttons, &id_map, &config.dwCommonButtons, &dialog_buttons);
137   }
138   if (dialog_buttons.size() > 0) {
139     config.pButtons = &dialog_buttons.front();
140     config.cButtons = dialog_buttons.size();
141     if (!(options & MESSAGE_BOX_NO_LINK))
142       config.dwFlags |= TDF_USE_COMMAND_LINKS;  // custom buttons as links.
143   }
144
145   int id = 0;
146   TaskDialogIndirect(&config, &id, NULL, NULL);
147   if (id_map.find(id) != id_map.end())  // common button.
148     return id_map[id];
149   else if (id >= kIDStart)  // custom button.
150     return id - kIDStart;
151   else
152     return cancel_id;
153 }
154
155 void RunMessageBoxInNewThread(base::Thread* thread,
156                               NativeWindow* parent,
157                               MessageBoxType type,
158                               const std::vector<std::string>& buttons,
159                               int cancel_id,
160                               int options,
161                               const std::string& title,
162                               const std::string& message,
163                               const std::string& detail,
164                               const gfx::ImageSkia& icon,
165                               const MessageBoxCallback& callback) {
166   int result = ShowMessageBox(parent, type, buttons, cancel_id, options, title,
167                               message, detail, icon);
168   content::BrowserThread::PostTask(
169       content::BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
170   content::BrowserThread::DeleteSoon(
171       content::BrowserThread::UI, FROM_HERE, thread);
172 }
173
174 }  // namespace
175
176 int ShowMessageBox(NativeWindow* parent,
177                    MessageBoxType type,
178                    const std::vector<std::string>& buttons,
179                    int cancel_id,
180                    int options,
181                    const std::string& title,
182                    const std::string& message,
183                    const std::string& detail,
184                    const gfx::ImageSkia& icon) {
185   std::vector<base::string16> utf16_buttons;
186   for (const auto& button : buttons)
187     utf16_buttons.push_back(base::UTF8ToUTF16(button));
188
189   HWND hwnd_parent = parent ?
190       static_cast<atom::NativeWindowViews*>(parent)->GetAcceleratedWidget() :
191       NULL;
192
193   NativeWindow::DialogScope dialog_scope(parent);
194   return ShowMessageBoxUTF16(hwnd_parent,
195                              type,
196                              utf16_buttons,
197                              cancel_id,
198                              options,
199                              base::UTF8ToUTF16(title),
200                              base::UTF8ToUTF16(message),
201                              base::UTF8ToUTF16(detail),
202                              icon);
203 }
204
205 void ShowMessageBox(NativeWindow* parent,
206                     MessageBoxType type,
207                     const std::vector<std::string>& buttons,
208                     int cancel_id,
209                     int options,
210                     const std::string& title,
211                     const std::string& message,
212                     const std::string& detail,
213                     const gfx::ImageSkia& icon,
214                     const MessageBoxCallback& callback) {
215   scoped_ptr<base::Thread> thread(
216       new base::Thread(ATOM_PRODUCT_NAME "MessageBoxThread"));
217   thread->init_com_with_mta(false);
218   if (!thread->Start()) {
219     callback.Run(cancel_id);
220     return;
221   }
222
223   base::Thread* unretained = thread.release();
224   unretained->message_loop()->PostTask(
225       FROM_HERE,
226       base::Bind(&RunMessageBoxInNewThread, base::Unretained(unretained),
227                  parent, type, buttons, cancel_id, options, title, message,
228                  detail, icon, callback));
229 }
230
231 void ShowErrorBox(const base::string16& title, const base::string16& content) {
232   ShowMessageBoxUTF16(NULL, MESSAGE_BOX_TYPE_ERROR, {}, 0, 0, L"Error", title,
233                       content, gfx::ImageSkia());
234 }
235
236 }  // namespace atom