Merge pull request #2377 from CtrlVP/master
[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/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"
21
22 namespace atom {
23
24 namespace {
25
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;
29
30 // Get the common ID from button's name.
31 struct CommonButtonID {
32   int button;
33   int id;
34 };
35 CommonButtonID GetCommonID(const base::string16& button) {
36   base::string16 lower = base::StringToLowerASCII(button);
37   if (lower == L"ok")
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 };
49   return { -1, -1 };
50 }
51
52 // Determine whether the buttons are common buttons, if so map common ID
53 // to button 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;
64     } else {
65       // It is a custom button.
66       dialog_buttons->push_back({i + kIDStart, buttons[i].c_str()});
67     }
68   }
69 }
70
71 int ShowMessageBoxUTF16(HWND parent,
72                         MessageBoxType type,
73                         const std::vector<base::string16>& buttons,
74                         int cancel_id,
75                         int options,
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.
81   if (cancel_id != 0)
82     flags |= TDF_ALLOW_DIALOG_CANCELLATION;  // allow dialog to be cancelled.
83
84   TASKDIALOGCONFIG config = { 0 };
85   config.cbSize     = sizeof(config);
86   config.hwndParent = parent;
87   config.hInstance  = GetModuleHandle(NULL);
88   config.dwFlags    = flags;
89
90   if (!title.empty())
91     config.pszWindowTitle = title.c_str();
92
93   base::win::ScopedHICON hicon;
94   if (!icon.isNull()) {
95     hicon.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap()));
96     config.dwFlags |= TDF_USE_HICON_MAIN;
97     config.hMainIcon = hicon.Get();
98   } else {
99     // Show icon according to dialog's type.
100     switch (type) {
101       case MESSAGE_BOX_TYPE_INFORMATION:
102       case MESSAGE_BOX_TYPE_QUESTION:
103         config.pszMainIcon = TD_INFORMATION_ICON;
104         break;
105       case MESSAGE_BOX_TYPE_WARNING:
106         config.pszMainIcon = TD_WARNING_ICON;
107         break;
108       case MESSAGE_BOX_TYPE_ERROR:
109         config.pszMainIcon = TD_ERROR_ICON;
110         break;
111     }
112   }
113
114   // If "detail" is empty then don't make message hilighted.
115   if (detail.empty()) {
116     config.pszContent = message.c_str();
117   } else {
118     config.pszMainInstruction = message.c_str();
119     config.pszContent = detail.c_str();
120   }
121
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()});
129   } else {
130     MapToCommonID(buttons, &id_map, &config.dwCommonButtons, &dialog_buttons);
131   }
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.
137   }
138
139   int id = 0;
140   TaskDialogIndirect(&config, &id, NULL, NULL);
141   if (id_map.find(id) != id_map.end())  // common button.
142     return id_map[id];
143   else if (id >= kIDStart)  // custom button.
144     return id - kIDStart;
145   else
146     return cancel_id;
147 }
148
149 void RunMessageBoxInNewThread(base::Thread* thread,
150                               NativeWindow* parent,
151                               MessageBoxType type,
152                               const std::vector<std::string>& buttons,
153                               int cancel_id,
154                               int options,
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);
166 }
167
168 }  // namespace
169
170 int ShowMessageBox(NativeWindow* parent,
171                    MessageBoxType type,
172                    const std::vector<std::string>& buttons,
173                    int cancel_id,
174                    int options,
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));
182
183   HWND hwnd_parent = parent ?
184       static_cast<atom::NativeWindowViews*>(parent)->GetAcceleratedWidget() :
185       NULL;
186
187   NativeWindow::DialogScope dialog_scope(parent);
188   return ShowMessageBoxUTF16(hwnd_parent,
189                              type,
190                              utf16_buttons,
191                              cancel_id,
192                              options,
193                              base::UTF8ToUTF16(title),
194                              base::UTF8ToUTF16(message),
195                              base::UTF8ToUTF16(detail),
196                              icon);
197 }
198
199 void ShowMessageBox(NativeWindow* parent,
200                     MessageBoxType type,
201                     const std::vector<std::string>& buttons,
202                     int cancel_id,
203                     int options,
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);
214     return;
215   }
216
217   base::Thread* unretained = thread.release();
218   unretained->message_loop()->PostTask(
219       FROM_HERE,
220       base::Bind(&RunMessageBoxInNewThread, base::Unretained(unretained),
221                  parent, type, buttons, cancel_id, options, title, message,
222                  detail, icon, callback));
223 }
224
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());
228 }
229
230 }  // namespace atom