simple_message_box_views.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/simple_message_box.h"
6
7#include "base/basictypes.h"
8#include "base/compiler_specific.h"
9#include "base/memory/ref_counted.h"
10#include "base/message_loop/message_loop.h"
11#include "base/message_loop/message_pump_dispatcher.h"
12#include "base/run_loop.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/ui/views/constrained_window_views.h"
15#include "grit/generated_resources.h"
16#include "ui/aura/client/dispatcher_client.h"
17#include "ui/aura/env.h"
18#include "ui/aura/window.h"
19#include "ui/aura/window_event_dispatcher.h"
20#include "ui/base/l10n/l10n_util.h"
21#include "ui/gfx/native_widget_types.h"
22#include "ui/views/controls/message_box_view.h"
23#include "ui/views/widget/widget.h"
24#include "ui/views/window/dialog_delegate.h"
25
26#if defined(OS_WIN)
27#include "ui/base/win/message_box_win.h"
28#include "ui/views/win/hwnd_util.h"
29#endif
30
31namespace chrome {
32
33namespace {
34
35// Multiple SimpleMessageBoxViews can show up at the same time. Each of these
36// start a nested message-loop. However, these SimpleMessageBoxViews can be
37// deleted in any order. This creates problems if a box in an inner-loop gets
38// destroyed before a box in an outer-loop. So to avoid this, ref-counting is
39// used so that the SimpleMessageBoxViews gets deleted at the right time.
40class SimpleMessageBoxViews : public views::DialogDelegate,
41                              public base::RefCounted<SimpleMessageBoxViews> {
42 public:
43  SimpleMessageBoxViews(const base::string16& title,
44                        const base::string16& message,
45                        MessageBoxType type,
46                        const base::string16& yes_text,
47                        const base::string16& no_text);
48
49  MessageBoxResult result() const { return result_; }
50
51  // Overridden from views::DialogDelegate:
52  virtual int GetDialogButtons() const OVERRIDE;
53  virtual base::string16 GetDialogButtonLabel(
54      ui::DialogButton button) const OVERRIDE;
55  virtual bool Cancel() OVERRIDE;
56  virtual bool Accept() OVERRIDE;
57
58  // Overridden from views::WidgetDelegate:
59  virtual base::string16 GetWindowTitle() const OVERRIDE;
60  virtual void DeleteDelegate() OVERRIDE;
61  virtual ui::ModalType GetModalType() const OVERRIDE;
62  virtual views::View* GetContentsView() OVERRIDE;
63  virtual views::Widget* GetWidget() OVERRIDE;
64  virtual const views::Widget* GetWidget() const OVERRIDE;
65
66 private:
67  friend class base::RefCounted<SimpleMessageBoxViews>;
68  virtual ~SimpleMessageBoxViews();
69
70  // This terminates the nested message-loop.
71  void Done();
72
73  const base::string16 window_title_;
74  const MessageBoxType type_;
75  base::string16 yes_text_;
76  base::string16 no_text_;
77  MessageBoxResult result_;
78  views::MessageBoxView* message_box_view_;
79
80  DISALLOW_COPY_AND_ASSIGN(SimpleMessageBoxViews);
81};
82
83////////////////////////////////////////////////////////////////////////////////
84// SimpleMessageBoxViews, public:
85
86SimpleMessageBoxViews::SimpleMessageBoxViews(const base::string16& title,
87                                             const base::string16& message,
88                                             MessageBoxType type,
89                                             const base::string16& yes_text,
90                                             const base::string16& no_text)
91    : window_title_(title),
92      type_(type),
93      yes_text_(yes_text),
94      no_text_(no_text),
95      result_(MESSAGE_BOX_RESULT_NO),
96      message_box_view_(new views::MessageBoxView(
97          views::MessageBoxView::InitParams(message))) {
98  AddRef();
99
100  if (yes_text_.empty()) {
101    if (type_ == MESSAGE_BOX_TYPE_QUESTION)
102      yes_text_ =
103          l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL);
104    else if (type_ == MESSAGE_BOX_TYPE_OK_CANCEL)
105      yes_text_ = l10n_util::GetStringUTF16(IDS_OK);
106    else
107      yes_text_ = l10n_util::GetStringUTF16(IDS_OK);
108  }
109
110  if (no_text_.empty()) {
111    if (type_ == MESSAGE_BOX_TYPE_QUESTION)
112      no_text_ =
113          l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL);
114    else if (type_ == MESSAGE_BOX_TYPE_OK_CANCEL)
115      no_text_ = l10n_util::GetStringUTF16(IDS_CANCEL);
116  }
117}
118
119int SimpleMessageBoxViews::GetDialogButtons() const {
120  if (type_ == MESSAGE_BOX_TYPE_QUESTION ||
121      type_ == MESSAGE_BOX_TYPE_OK_CANCEL) {
122    return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
123  }
124
125  return ui::DIALOG_BUTTON_OK;
126}
127
128base::string16 SimpleMessageBoxViews::GetDialogButtonLabel(
129    ui::DialogButton button) const {
130  if (button == ui::DIALOG_BUTTON_CANCEL)
131    return no_text_;
132  return yes_text_;
133}
134
135bool SimpleMessageBoxViews::Cancel() {
136  result_ = MESSAGE_BOX_RESULT_NO;
137  Done();
138  return true;
139}
140
141bool SimpleMessageBoxViews::Accept() {
142  result_ = MESSAGE_BOX_RESULT_YES;
143  Done();
144  return true;
145}
146
147base::string16 SimpleMessageBoxViews::GetWindowTitle() const {
148  return window_title_;
149}
150
151void SimpleMessageBoxViews::DeleteDelegate() {
152  Release();
153}
154
155ui::ModalType SimpleMessageBoxViews::GetModalType() const {
156  return ui::MODAL_TYPE_WINDOW;
157}
158
159views::View* SimpleMessageBoxViews::GetContentsView() {
160  return message_box_view_;
161}
162
163views::Widget* SimpleMessageBoxViews::GetWidget() {
164  return message_box_view_->GetWidget();
165}
166
167const views::Widget* SimpleMessageBoxViews::GetWidget() const {
168  return message_box_view_->GetWidget();
169}
170
171////////////////////////////////////////////////////////////////////////////////
172// SimpleMessageBoxViews, private:
173
174SimpleMessageBoxViews::~SimpleMessageBoxViews() {
175}
176
177void SimpleMessageBoxViews::Done() {
178  aura::Window* window = GetWidget()->GetNativeView();
179  aura::client::DispatcherClient* client =
180      aura::client::GetDispatcherClient(window->GetRootWindow());
181  client->QuitNestedMessageLoop();
182}
183
184#if defined(OS_WIN)
185UINT GetMessageBoxFlagsFromType(MessageBoxType type) {
186  UINT flags = MB_SETFOREGROUND;
187  switch (type) {
188    case MESSAGE_BOX_TYPE_INFORMATION:
189      return flags | MB_OK | MB_ICONINFORMATION;
190    case MESSAGE_BOX_TYPE_WARNING:
191      return flags | MB_OK | MB_ICONWARNING;
192    case MESSAGE_BOX_TYPE_QUESTION:
193      return flags | MB_YESNO | MB_ICONQUESTION;
194    case MESSAGE_BOX_TYPE_OK_CANCEL:
195      return flags | MB_OKCANCEL | MB_ICONWARNING;
196  }
197  NOTREACHED();
198  return flags | MB_OK | MB_ICONWARNING;
199}
200#endif
201
202MessageBoxResult ShowMessageBoxImpl(gfx::NativeWindow parent,
203                                    const base::string16& title,
204                                    const base::string16& message,
205                                    MessageBoxType type,
206                                    const base::string16& yes_text,
207                                    const base::string16& no_text) {
208#if defined(OS_WIN)
209  // GPU-based dialogs can't be used early on; fallback to a Windows MessageBox.
210  if (!ui::ContextFactory::GetInstance()) {
211    int result = ui::MessageBox(views::HWNDForNativeWindow(parent), message,
212                                title, GetMessageBoxFlagsFromType(type));
213    return (result == IDYES || result == IDOK) ?
214        MESSAGE_BOX_RESULT_YES : MESSAGE_BOX_RESULT_NO;
215  }
216#endif
217
218  scoped_refptr<SimpleMessageBoxViews> dialog(
219      new SimpleMessageBoxViews(title, message, type, yes_text, no_text));
220  CreateBrowserModalDialogViews(dialog.get(), parent)->Show();
221
222  // Use the widget's window itself so that the message loop
223  // exists when the dialog is closed by some other means than
224  // |Cancel| or |Accept|.
225  aura::Window* anchor = dialog->GetWidget()->GetNativeWindow();
226  aura::client::DispatcherClient* client =
227      aura::client::GetDispatcherClient(anchor->GetRootWindow());
228  client->RunWithDispatcher(NULL);
229  return dialog->result();
230}
231
232}  // namespace
233
234MessageBoxResult ShowMessageBox(gfx::NativeWindow parent,
235                                const base::string16& title,
236                                const base::string16& message,
237                                MessageBoxType type) {
238  return ShowMessageBoxImpl(
239      parent, title, message, type, base::string16(), base::string16());
240}
241
242MessageBoxResult ShowMessageBoxWithButtonText(gfx::NativeWindow parent,
243                                              const base::string16& title,
244                                              const base::string16& message,
245                                              const base::string16& yes_text,
246                                              const base::string16& no_text) {
247  return ShowMessageBoxImpl(
248      parent, title, message, MESSAGE_BOX_TYPE_QUESTION, yes_text, no_text);
249}
250
251}  // namespace chrome
252