2013-07-05 01:59:53 +00:00
|
|
|
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
2013-08-13 08:51:47 +00:00
|
|
|
#include "browser/ui/message_box.h"
|
2013-07-05 01:59:53 +00:00
|
|
|
|
2013-09-22 09:11:09 +00:00
|
|
|
#include "base/callback.h"
|
2013-12-17 06:01:40 +00:00
|
|
|
#include "base/message_loop/message_loop.h"
|
2013-07-24 10:34:50 +00:00
|
|
|
#include "base/run_loop.h"
|
2013-12-17 06:01:40 +00:00
|
|
|
#include "base/strings/string_util.h"
|
|
|
|
#include "base/strings/utf_string_conversions.h"
|
2013-07-24 10:34:50 +00:00
|
|
|
#include "browser/native_window.h"
|
2013-07-25 07:30:44 +00:00
|
|
|
#include "skia/ext/skia_utils_win.h"
|
2013-07-25 07:22:44 +00:00
|
|
|
#include "ui/views/controls/button/label_button.h"
|
2013-07-24 10:34:50 +00:00
|
|
|
#include "ui/views/controls/message_box_view.h"
|
|
|
|
#include "ui/views/layout/grid_layout.h"
|
2013-07-25 08:19:40 +00:00
|
|
|
#include "ui/views/layout/layout_constants.h"
|
2013-07-24 10:34:50 +00:00
|
|
|
#include "ui/views/widget/widget.h"
|
|
|
|
#include "ui/views/widget/widget_delegate.h"
|
|
|
|
|
2013-07-05 01:59:53 +00:00
|
|
|
namespace atom {
|
|
|
|
|
2013-07-24 10:34:50 +00:00
|
|
|
namespace {
|
|
|
|
|
2013-07-25 13:04:33 +00:00
|
|
|
// The group used by the buttons. This name is chosen voluntarily big not to
|
|
|
|
// conflict with other groups that could be in the dialog content.
|
|
|
|
const int kButtonGroup = 1127;
|
|
|
|
|
2013-07-24 10:34:50 +00:00
|
|
|
class MessageDialog : public base::MessageLoop::Dispatcher,
|
|
|
|
public views::WidgetDelegate,
|
2013-07-25 07:22:44 +00:00
|
|
|
public views::View,
|
2013-07-24 10:34:50 +00:00
|
|
|
public views::ButtonListener {
|
|
|
|
public:
|
|
|
|
MessageDialog(NativeWindow* parent_window,
|
|
|
|
MessageBoxType type,
|
|
|
|
const std::vector<std::string>& buttons,
|
|
|
|
const std::string& title,
|
|
|
|
const std::string& message,
|
|
|
|
const std::string& detail);
|
|
|
|
virtual ~MessageDialog();
|
|
|
|
|
2013-09-24 08:11:23 +00:00
|
|
|
void Show();
|
|
|
|
|
|
|
|
int GetResult() const;
|
|
|
|
|
|
|
|
void set_callback(const MessageBoxCallback& callback) {
|
|
|
|
delete_on_close_ = true;
|
|
|
|
callback_ = callback;
|
|
|
|
}
|
2013-07-25 08:56:02 +00:00
|
|
|
|
2013-07-24 10:34:50 +00:00
|
|
|
private:
|
|
|
|
// Overridden from MessageLoop::Dispatcher:
|
|
|
|
virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
|
|
|
|
|
2013-07-25 09:06:08 +00:00
|
|
|
// Overridden from views::WidgetDelegate:
|
|
|
|
virtual string16 GetWindowTitle() const;
|
2013-07-24 10:34:50 +00:00
|
|
|
virtual void WindowClosing() OVERRIDE;
|
|
|
|
virtual views::Widget* GetWidget() OVERRIDE;
|
|
|
|
virtual const views::Widget* GetWidget() const OVERRIDE;
|
2013-07-25 07:22:44 +00:00
|
|
|
virtual views::View* GetContentsView() OVERRIDE;
|
2013-07-25 13:04:33 +00:00
|
|
|
virtual views::View* GetInitiallyFocusedView() OVERRIDE;
|
2013-07-24 10:51:03 +00:00
|
|
|
virtual ui::ModalType GetModalType() const OVERRIDE;
|
2013-07-24 10:34:50 +00:00
|
|
|
|
2013-07-25 08:19:40 +00:00
|
|
|
// Overridden from views::View:
|
|
|
|
virtual gfx::Size GetPreferredSize() OVERRIDE;
|
|
|
|
virtual void Layout() OVERRIDE;
|
2013-07-25 13:04:33 +00:00
|
|
|
virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
|
2013-07-25 08:19:40 +00:00
|
|
|
|
2013-07-24 10:34:50 +00:00
|
|
|
// Overridden from views::ButtonListener:
|
|
|
|
virtual void ButtonPressed(views::Button* sender,
|
|
|
|
const ui::Event& event) OVERRIDE;
|
|
|
|
|
|
|
|
bool should_close_;
|
2013-09-24 08:11:23 +00:00
|
|
|
bool delete_on_close_;
|
2013-07-25 08:56:02 +00:00
|
|
|
int result_;
|
2013-07-25 09:06:08 +00:00
|
|
|
string16 title_;
|
2013-07-24 10:34:50 +00:00
|
|
|
views::Widget* widget_;
|
|
|
|
views::MessageBoxView* message_box_view_;
|
2014-02-10 12:07:38 +00:00
|
|
|
scoped_ptr<NativeWindow::DialogScope> dialog_scope_;
|
2013-07-25 08:19:40 +00:00
|
|
|
std::vector<views::LabelButton*> buttons_;
|
2013-09-24 08:11:23 +00:00
|
|
|
MessageBoxCallback callback_;
|
2013-07-24 10:34:50 +00:00
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(MessageDialog);
|
|
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// MessageDialog, public:
|
|
|
|
|
|
|
|
MessageDialog::MessageDialog(NativeWindow* parent_window,
|
|
|
|
MessageBoxType type,
|
|
|
|
const std::vector<std::string>& buttons,
|
|
|
|
const std::string& title,
|
|
|
|
const std::string& message,
|
|
|
|
const std::string& detail)
|
2013-07-25 07:22:44 +00:00
|
|
|
: should_close_(false),
|
2013-09-24 08:11:23 +00:00
|
|
|
delete_on_close_(false),
|
2013-07-25 08:56:02 +00:00
|
|
|
result_(-1),
|
2013-07-25 09:06:08 +00:00
|
|
|
title_(UTF8ToUTF16(title)),
|
2013-07-25 07:22:44 +00:00
|
|
|
widget_(NULL),
|
2014-02-10 12:07:38 +00:00
|
|
|
message_box_view_(NULL),
|
|
|
|
dialog_scope_(new NativeWindow::DialogScope(parent_window)) {
|
2013-08-16 08:33:32 +00:00
|
|
|
DCHECK_GT(buttons.size(), 0u);
|
2013-07-25 07:22:44 +00:00
|
|
|
set_owned_by_client();
|
|
|
|
|
2013-07-24 10:34:50 +00:00
|
|
|
views::MessageBoxView::InitParams params(UTF8ToUTF16(title));
|
2013-07-25 09:06:08 +00:00
|
|
|
params.message = UTF8ToUTF16(message + "\n" + detail);
|
2013-07-24 10:34:50 +00:00
|
|
|
message_box_view_ = new views::MessageBoxView(params);
|
2013-07-25 08:19:40 +00:00
|
|
|
AddChildView(message_box_view_);
|
2013-07-24 10:34:50 +00:00
|
|
|
|
2013-07-25 08:19:40 +00:00
|
|
|
for (size_t i = 0; i < buttons.size(); ++i) {
|
2013-07-25 07:22:44 +00:00
|
|
|
views::LabelButton* button = new views::LabelButton(
|
|
|
|
this, UTF8ToUTF16(buttons[i]));
|
|
|
|
button->set_tag(i);
|
2013-07-25 07:47:12 +00:00
|
|
|
button->set_min_size(gfx::Size(60, 20));
|
2013-07-25 07:22:44 +00:00
|
|
|
button->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
|
2013-07-25 13:04:33 +00:00
|
|
|
button->SetGroup(kButtonGroup);
|
2013-07-25 07:22:44 +00:00
|
|
|
|
2013-07-25 08:19:40 +00:00
|
|
|
buttons_.push_back(button);
|
|
|
|
AddChildView(button);
|
|
|
|
}
|
2013-07-25 13:04:33 +00:00
|
|
|
|
|
|
|
// First button is always default button.
|
2013-07-25 08:56:02 +00:00
|
|
|
buttons_[0]->SetIsDefault(true);
|
2013-07-25 13:04:33 +00:00
|
|
|
buttons_[0]->AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
|
2013-07-25 07:47:12 +00:00
|
|
|
|
2013-07-24 10:34:50 +00:00
|
|
|
views::Widget::InitParams widget_params;
|
|
|
|
widget_params.delegate = this;
|
2013-07-25 13:04:33 +00:00
|
|
|
widget_params.top_level = true;
|
2013-07-24 10:34:50 +00:00
|
|
|
if (parent_window)
|
|
|
|
widget_params.parent = parent_window->GetNativeWindow();
|
|
|
|
widget_ = new views::Widget;
|
2013-07-25 07:22:44 +00:00
|
|
|
widget_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_NATIVE);
|
2013-07-24 10:34:50 +00:00
|
|
|
widget_->Init(widget_params);
|
|
|
|
|
2013-07-25 13:04:33 +00:00
|
|
|
// Bind to ESC.
|
|
|
|
AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
|
|
|
|
|
2013-07-25 07:30:44 +00:00
|
|
|
set_background(views::Background::CreateSolidBackground(
|
|
|
|
skia::COLORREFToSkColor(GetSysColor(COLOR_WINDOW))));
|
2013-07-24 10:34:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MessageDialog::~MessageDialog() {
|
|
|
|
}
|
|
|
|
|
2013-09-24 08:11:23 +00:00
|
|
|
void MessageDialog::Show() {
|
|
|
|
widget_->Show();
|
|
|
|
}
|
|
|
|
|
|
|
|
int MessageDialog::GetResult() const {
|
|
|
|
// When the dialog is closed without choosing anything, we think the user
|
|
|
|
// chose 'Cancel', otherwise we think the default behavior is chosen.
|
|
|
|
if (result_ == -1) {
|
|
|
|
for (size_t i = 0; i < buttons_.size(); ++i)
|
|
|
|
if (LowerCaseEqualsASCII(buttons_[i]->GetText(), "cancel")) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return result_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-24 10:34:50 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// MessageDialog, private:
|
|
|
|
|
|
|
|
bool MessageDialog::Dispatch(const base::NativeEvent& event) {
|
|
|
|
TranslateMessage(&event);
|
|
|
|
DispatchMessage(&event);
|
|
|
|
return !should_close_;
|
|
|
|
}
|
|
|
|
|
2013-07-25 09:06:08 +00:00
|
|
|
string16 MessageDialog::GetWindowTitle() const {
|
|
|
|
return title_;
|
|
|
|
}
|
|
|
|
|
2013-07-24 10:34:50 +00:00
|
|
|
void MessageDialog::WindowClosing() {
|
|
|
|
should_close_ = true;
|
2014-02-10 12:07:38 +00:00
|
|
|
dialog_scope_.reset();
|
2013-09-24 08:11:23 +00:00
|
|
|
|
|
|
|
if (delete_on_close_) {
|
|
|
|
callback_.Run(GetResult());
|
|
|
|
base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
|
|
|
|
}
|
2013-07-24 10:34:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
views::Widget* MessageDialog::GetWidget() {
|
|
|
|
return widget_;
|
|
|
|
}
|
|
|
|
|
|
|
|
const views::Widget* MessageDialog::GetWidget() const {
|
|
|
|
return widget_;
|
|
|
|
}
|
|
|
|
|
2013-07-25 07:22:44 +00:00
|
|
|
views::View* MessageDialog::GetContentsView() {
|
|
|
|
return this;
|
2013-07-24 10:34:50 +00:00
|
|
|
}
|
|
|
|
|
2013-07-25 13:04:33 +00:00
|
|
|
views::View* MessageDialog::GetInitiallyFocusedView() {
|
|
|
|
if (buttons_.size() > 0)
|
|
|
|
return buttons_[0];
|
|
|
|
else
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2013-07-24 10:51:03 +00:00
|
|
|
ui::ModalType MessageDialog::GetModalType() const {
|
|
|
|
return ui::MODAL_TYPE_WINDOW;
|
|
|
|
}
|
|
|
|
|
2013-07-25 08:19:40 +00:00
|
|
|
gfx::Size MessageDialog::GetPreferredSize() {
|
|
|
|
gfx::Size size(0, buttons_[0]->GetPreferredSize().height());
|
|
|
|
for (size_t i = 0; i < buttons_.size(); ++i)
|
|
|
|
size.Enlarge(buttons_[i]->GetPreferredSize().width(), 0);
|
|
|
|
|
|
|
|
// Button spaces.
|
|
|
|
size.Enlarge(views::kRelatedButtonHSpacing * (buttons_.size() - 1),
|
|
|
|
views::kRelatedControlVerticalSpacing);
|
|
|
|
|
|
|
|
// The message box view.
|
|
|
|
gfx::Size contents_size = message_box_view_->GetPreferredSize();
|
|
|
|
size.Enlarge(0, contents_size.height());
|
|
|
|
if (contents_size.width() > size.width())
|
|
|
|
size.set_width(contents_size.width());
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MessageDialog::Layout() {
|
|
|
|
gfx::Rect bounds = GetContentsBounds();
|
|
|
|
|
|
|
|
// Layout the row containing the buttons.
|
|
|
|
int x = bounds.width();
|
|
|
|
int height = buttons_[0]->GetPreferredSize().height() +
|
|
|
|
views::kRelatedControlVerticalSpacing;
|
2013-08-30 19:08:04 +00:00
|
|
|
|
|
|
|
// NB: We iterate through the buttons backwards here because
|
|
|
|
// Mac and Windows buttons are laid out in opposite order.
|
|
|
|
for (int i = buttons_.size() - 1; i >= 0; --i) {
|
2013-07-25 08:19:40 +00:00
|
|
|
gfx::Size size = buttons_[i]->GetPreferredSize();
|
|
|
|
x -= size.width() + views::kRelatedButtonHSpacing;
|
|
|
|
|
|
|
|
buttons_[i]->SetBounds(x, bounds.height() - height,
|
|
|
|
size.width(), size.height());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Layout the message box view.
|
|
|
|
message_box_view_->SetBounds(bounds.x(), bounds.y(), bounds.width(),
|
|
|
|
bounds.height() - height);
|
|
|
|
}
|
|
|
|
|
2013-07-25 13:04:33 +00:00
|
|
|
bool MessageDialog::AcceleratorPressed(const ui::Accelerator& accelerator) {
|
|
|
|
DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE);
|
|
|
|
widget_->Close();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-24 10:34:50 +00:00
|
|
|
void MessageDialog::ButtonPressed(views::Button* sender,
|
|
|
|
const ui::Event& event) {
|
2013-07-25 08:56:02 +00:00
|
|
|
result_ = sender->tag();
|
|
|
|
widget_->Close();
|
2013-07-24 10:34:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2013-07-05 01:59:53 +00:00
|
|
|
int ShowMessageBox(NativeWindow* parent_window,
|
|
|
|
MessageBoxType type,
|
|
|
|
const std::vector<std::string>& buttons,
|
|
|
|
const std::string& title,
|
|
|
|
const std::string& message,
|
|
|
|
const std::string& detail) {
|
2013-07-24 10:34:50 +00:00
|
|
|
MessageDialog dialog(parent_window, type, buttons, title, message, detail);
|
2013-09-24 08:11:23 +00:00
|
|
|
dialog.Show();
|
2013-07-24 10:34:50 +00:00
|
|
|
{
|
|
|
|
base::MessageLoop::ScopedNestableTaskAllower allow(
|
|
|
|
base::MessageLoopForUI::current());
|
|
|
|
base::RunLoop run_loop(&dialog);
|
|
|
|
run_loop.Run();
|
|
|
|
}
|
2013-07-25 08:56:02 +00:00
|
|
|
|
2013-09-24 08:11:23 +00:00
|
|
|
return dialog.GetResult();
|
2013-07-05 01:59:53 +00:00
|
|
|
}
|
|
|
|
|
2013-09-22 09:11:09 +00:00
|
|
|
void ShowMessageBox(NativeWindow* parent_window,
|
|
|
|
MessageBoxType type,
|
|
|
|
const std::vector<std::string>& buttons,
|
|
|
|
const std::string& title,
|
|
|
|
const std::string& message,
|
|
|
|
const std::string& detail,
|
|
|
|
const MessageBoxCallback& callback) {
|
2013-09-24 08:11:23 +00:00
|
|
|
// The dialog would be deleted when the dialog is closed.
|
|
|
|
MessageDialog* dialog = new MessageDialog(
|
|
|
|
parent_window, type, buttons, title, message, detail);
|
|
|
|
dialog->set_callback(callback);
|
|
|
|
dialog->Show();
|
2013-09-22 09:11:09 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 01:59:53 +00:00
|
|
|
} // namespace atom
|