diff options
Diffstat (limited to 'chrome/views/window/dialog_client_view.cc')
-rw-r--r-- | chrome/views/window/dialog_client_view.cc | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/chrome/views/window/dialog_client_view.cc b/chrome/views/window/dialog_client_view.cc new file mode 100644 index 0000000..dbc3b179 --- /dev/null +++ b/chrome/views/window/dialog_client_view.cc @@ -0,0 +1,434 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/views/window/dialog_client_view.h" + +#include <windows.h> +#include <uxtheme.h> +#include <vsstyle.h> + +#include "base/gfx/native_theme.h" +#include "chrome/browser/views/standard_layout.h" +#include "chrome/common/gfx/chrome_canvas.h" +#include "chrome/common/gfx/chrome_font.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/win_util.h" +#include "chrome/views/window/dialog_delegate.h" +#include "chrome/views/window/window.h" +#include "grit/generated_resources.h" + +namespace views { + +namespace { + +// Updates any of the standard buttons according to the delegate. +void UpdateButtonHelper(NativeButton* button_view, + DialogDelegate* delegate, + DialogDelegate::DialogButton button) { + std::wstring label = delegate->GetDialogButtonLabel(button); + if (!label.empty()) + button_view->SetLabel(label); + button_view->SetEnabled(delegate->IsDialogButtonEnabled(button)); + button_view->SetVisible(delegate->IsDialogButtonVisible(button)); +} + +void FillViewWithSysColor(ChromeCanvas* canvas, View* view, COLORREF color) { + SkColor sk_color = + SkColorSetRGB(GetRValue(color), GetGValue(color), GetBValue(color)); + canvas->FillRectInt(sk_color, 0, 0, view->width(), view->height()); +} + +// DialogButton ---------------------------------------------------------------- + +// DialogButtons is used for the ok/cancel buttons of the window. DialogButton +// forwards AcceleratorPressed to the delegate. + +class DialogButton : public NativeButton { + public: + DialogButton(Window* owner, + DialogDelegate::DialogButton type, + const std::wstring& title, + bool is_default) + : NativeButton(title, is_default), owner_(owner), type_(type) { + } + + // Overridden to forward to the delegate. + virtual bool AcceleratorPressed(const Accelerator& accelerator) { + if (!owner_->GetDelegate()->AsDialogDelegate()-> + AreAcceleratorsEnabled(type_)) { + return false; + } + return NativeButton::AcceleratorPressed(accelerator); + } + + private: + Window* owner_; + const DialogDelegate::DialogButton type_; + + DISALLOW_COPY_AND_ASSIGN(DialogButton); +}; + +} // namespace + +// static +ChromeFont DialogClientView::dialog_button_font_; +static const int kDialogMinButtonWidth = 75; +static const int kDialogButtonLabelSpacing = 16; +static const int kDialogButtonContentSpacing = 5; + +// 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. +static const int kButtonGroup = 6666; + +/////////////////////////////////////////////////////////////////////////////// +// DialogClientView, public: + +DialogClientView::DialogClientView(Window* owner, View* contents_view) + : ClientView(owner, contents_view), + ok_button_(NULL), + cancel_button_(NULL), + extra_view_(NULL), + accepted_(false), + default_button_(NULL) { + InitClass(); +} + +DialogClientView::~DialogClientView() { +} + +void DialogClientView::ShowDialogButtons() { + DialogDelegate* dd = GetDialogDelegate(); + int buttons = dd->GetDialogButtons(); + if (buttons & DialogDelegate::DIALOGBUTTON_OK && !ok_button_) { + std::wstring label = + dd->GetDialogButtonLabel(DialogDelegate::DIALOGBUTTON_OK); + if (label.empty()) + label = l10n_util::GetString(IDS_OK); + bool is_default_button = + (dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_OK) != 0; + ok_button_ = new DialogButton(window(), DialogDelegate::DIALOGBUTTON_OK, + label, is_default_button); + ok_button_->SetListener(this); + ok_button_->SetGroup(kButtonGroup); + if (is_default_button) + default_button_ = ok_button_; + if (!(buttons & DialogDelegate::DIALOGBUTTON_CANCEL)) + ok_button_->AddAccelerator(Accelerator(VK_ESCAPE, false, false, false)); + AddChildView(ok_button_); + } + if (buttons & DialogDelegate::DIALOGBUTTON_CANCEL && !cancel_button_) { + std::wstring label = + dd->GetDialogButtonLabel(DialogDelegate::DIALOGBUTTON_CANCEL); + if (label.empty()) { + if (buttons & DialogDelegate::DIALOGBUTTON_OK) { + label = l10n_util::GetString(IDS_CANCEL); + } else { + label = l10n_util::GetString(IDS_CLOSE); + } + } + bool is_default_button = + (dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_CANCEL) + != 0; + cancel_button_ = new DialogButton(window(), + DialogDelegate::DIALOGBUTTON_CANCEL, + label, is_default_button); + cancel_button_->SetListener(this); + cancel_button_->SetGroup(kButtonGroup); + cancel_button_->AddAccelerator(Accelerator(VK_ESCAPE, false, false, false)); + if (is_default_button) + default_button_ = ok_button_; + AddChildView(cancel_button_); + } + if (!buttons) { + // Register the escape key as an accelerator which will close the window + // if there are no dialog buttons. + AddAccelerator(Accelerator(VK_ESCAPE, false, false, false)); + } +} + +void DialogClientView::SetDefaultButton(NativeButton* new_default_button) { + if (default_button_ && default_button_ != new_default_button) { + default_button_->SetDefaultButton(false); + default_button_ = NULL; + } + + if (new_default_button) { + default_button_ = new_default_button; + default_button_->SetDefaultButton(true); + } +} + +void DialogClientView::FocusWillChange(View* focused_before, + View* focused_now) { + NativeButton* new_default_button = NULL; + if (focused_now && + focused_now->GetClassName() == NativeButton::kViewClassName) { + new_default_button = static_cast<NativeButton*>(focused_now); + } else { + // The focused view is not a button, get the default button from the + // delegate. + DialogDelegate* dd = GetDialogDelegate(); + if ((dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_OK) != 0) + new_default_button = ok_button_; + if ((dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_CANCEL) + != 0) + new_default_button = cancel_button_; + } + SetDefaultButton(new_default_button); +} + +// Changing dialog labels will change button widths. +void DialogClientView::UpdateDialogButtons() { + DialogDelegate* dd = GetDialogDelegate(); + int buttons = dd->GetDialogButtons(); + + if (buttons & DialogDelegate::DIALOGBUTTON_OK) + UpdateButtonHelper(ok_button_, dd, DialogDelegate::DIALOGBUTTON_OK); + + if (buttons & DialogDelegate::DIALOGBUTTON_CANCEL) + UpdateButtonHelper(cancel_button_, dd, DialogDelegate::DIALOGBUTTON_CANCEL); + + LayoutDialogButtons(); + SchedulePaint(); +} + +void DialogClientView::AcceptWindow() { + if (accepted_) { + // We should only get into AcceptWindow once. + NOTREACHED(); + return; + } + accepted_ = true; + if (GetDialogDelegate()->Accept(false)) + window()->Close(); +} + +void DialogClientView::CancelWindow() { + // Call the standard Close handler, which checks with the delegate before + // proceeding. This checking _isn't_ done here, but in the WM_CLOSE handler, + // so that the close box on the window also shares this code path. + window()->Close(); +} + +/////////////////////////////////////////////////////////////////////////////// +// DialogClientView, ClientView overrides: + +bool DialogClientView::CanClose() const { + if (!accepted_) { + DialogDelegate* dd = GetDialogDelegate(); + int buttons = dd->GetDialogButtons(); + if (buttons & DialogDelegate::DIALOGBUTTON_CANCEL) + return dd->Cancel(); + if (buttons & DialogDelegate::DIALOGBUTTON_OK) + return dd->Accept(true); + } + return true; +} + +void DialogClientView::WindowClosing() { + FocusManager* focus_manager = GetFocusManager(); + DCHECK(focus_manager); + if (focus_manager) + focus_manager->RemoveFocusChangeListener(this); + ClientView::WindowClosing(); +} + +int DialogClientView::NonClientHitTest(const gfx::Point& point) { + if (size_box_bounds_.Contains(point.x() - x(), point.y() - y())) + return HTBOTTOMRIGHT; + return ClientView::NonClientHitTest(point); +} + +//////////////////////////////////////////////////////////////////////////////// +// DialogClientView, View overrides: + +void DialogClientView::Paint(ChromeCanvas* canvas) { + FillViewWithSysColor(canvas, this, GetSysColor(COLOR_3DFACE)); +} + +void DialogClientView::PaintChildren(ChromeCanvas* canvas) { + View::PaintChildren(canvas); + if (!window()->IsMaximized() && !window()->IsMinimized()) + PaintSizeBox(canvas); +} + +void DialogClientView::Layout() { + if (has_dialog_buttons()) + LayoutDialogButtons(); + LayoutContentsView(); +} + +void DialogClientView::ViewHierarchyChanged(bool is_add, View* parent, + View* child) { + if (is_add && child == this) { + // Can only add and update the dialog buttons _after_ they are added to the + // view hierarchy since they are native controls and require the + // Container's HWND. + ShowDialogButtons(); + ClientView::ViewHierarchyChanged(is_add, parent, child); + + FocusManager* focus_manager = GetFocusManager(); + // Listen for focus change events so we can update the default button. + DCHECK(focus_manager); // bug #1291225: crash reports seem to indicate it + // can be NULL. + if (focus_manager) + focus_manager->AddFocusChangeListener(this); + + // The "extra view" must be created and installed after the contents view + // has been inserted into the view hierarchy. + CreateExtraView(); + UpdateDialogButtons(); + Layout(); + } +} + +gfx::Size DialogClientView::GetPreferredSize() { + gfx::Size prefsize = contents_view()->GetPreferredSize(); + int button_height = 0; + if (has_dialog_buttons()) { + if (cancel_button_) + button_height = cancel_button_->height(); + else + button_height = ok_button_->height(); + // Account for padding above and below the button. + button_height += kDialogButtonContentSpacing + kButtonVEdgeMargin; + } + prefsize.Enlarge(0, button_height); + return prefsize; +} + +bool DialogClientView::AcceleratorPressed(const Accelerator& accelerator) { + DCHECK(accelerator.GetKeyCode() == VK_ESCAPE); // We only expect Escape key. + window()->Close(); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// DialogClientView, NativeButton::Listener implementation: + +void DialogClientView::ButtonPressed(NativeButton* sender) { + if (sender == ok_button_) { + AcceptWindow(); + } else if (sender == cancel_button_) { + CancelWindow(); + } else { + NOTREACHED(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// DialogClientView, private: + +void DialogClientView::PaintSizeBox(ChromeCanvas* canvas) { + if (window()->GetDelegate()->CanResize() || + window()->GetDelegate()->CanMaximize()) { + HDC dc = canvas->beginPlatformPaint(); + SIZE gripper_size = { 0, 0 }; + gfx::NativeTheme::instance()->GetThemePartSize( + gfx::NativeTheme::STATUS, dc, SP_GRIPPER, 1, NULL, TS_TRUE, + &gripper_size); + + // TODO(beng): (http://b/1085509) In "classic" rendering mode, there isn't + // a theme-supplied gripper. We should probably improvise + // something, which would also require changing |gripper_size| + // to have different default values, too... + size_box_bounds_ = GetLocalBounds(false); + size_box_bounds_.set_x(size_box_bounds_.right() - gripper_size.cx); + size_box_bounds_.set_y(size_box_bounds_.bottom() - gripper_size.cy); + RECT native_bounds = size_box_bounds_.ToRECT(); + gfx::NativeTheme::instance()->PaintStatusGripper( + dc, SP_PANE, 1, 0, &native_bounds); + canvas->endPlatformPaint(); + } +} + +int DialogClientView::GetButtonWidth(int button) const { + DialogDelegate* dd = GetDialogDelegate(); + std::wstring button_label = dd->GetDialogButtonLabel( + static_cast<DialogDelegate::DialogButton>(button)); + int string_width = dialog_button_font_.GetStringWidth(button_label); + return std::max(string_width + kDialogButtonLabelSpacing, + kDialogMinButtonWidth); +} + +int DialogClientView::GetButtonsHeight() const { + if (has_dialog_buttons()) { + if (cancel_button_) + return cancel_button_->height() + kDialogButtonContentSpacing; + return ok_button_->height() + kDialogButtonContentSpacing; + } + return 0; +} + +void DialogClientView::LayoutDialogButtons() { + gfx::Rect extra_bounds; + if (cancel_button_) { + gfx::Size ps = cancel_button_->GetPreferredSize(); + gfx::Rect lb = GetLocalBounds(false); + int button_width = GetButtonWidth(DialogDelegate::DIALOGBUTTON_CANCEL); + int button_x = lb.right() - button_width - kButtonHEdgeMargin; + int button_y = lb.bottom() - ps.height() - kButtonVEdgeMargin; + cancel_button_->SetBounds(button_x, button_y, button_width, ps.height()); + // The extra view bounds are dependent on this button. + extra_bounds.set_width(std::max(0, cancel_button_->x())); + extra_bounds.set_y(cancel_button_->y()); + } + if (ok_button_) { + gfx::Size ps = ok_button_->GetPreferredSize(); + gfx::Rect lb = GetLocalBounds(false); + int button_width = GetButtonWidth(DialogDelegate::DIALOGBUTTON_OK); + int ok_button_right = lb.right() - kButtonHEdgeMargin; + if (cancel_button_) + ok_button_right = cancel_button_->x() - kRelatedButtonHSpacing; + int button_x = ok_button_right - button_width; + int button_y = lb.bottom() - ps.height() - kButtonVEdgeMargin; + ok_button_->SetBounds(button_x, button_y, ok_button_right - button_x, + ps.height()); + // The extra view bounds are dependent on this button. + extra_bounds.set_width(std::max(0, ok_button_->x())); + extra_bounds.set_y(ok_button_->y()); + } + if (extra_view_) { + gfx::Size ps = extra_view_->GetPreferredSize(); + gfx::Rect lb = GetLocalBounds(false); + extra_bounds.set_x(lb.x() + kButtonHEdgeMargin); + extra_bounds.set_height(ps.height()); + extra_view_->SetBounds(extra_bounds); + } +} + +void DialogClientView::LayoutContentsView() { + gfx::Rect lb = GetLocalBounds(false); + lb.set_height(std::max(0, lb.height() - GetButtonsHeight())); + contents_view()->SetBounds(lb); + contents_view()->Layout(); +} + +void DialogClientView::CreateExtraView() { + View* extra_view = GetDialogDelegate()->GetExtraView(); + if (extra_view && !extra_view_) { + extra_view_ = extra_view; + extra_view_->SetGroup(kButtonGroup); + AddChildView(extra_view_); + } +} + +DialogDelegate* DialogClientView::GetDialogDelegate() const { + DialogDelegate* dd = window()->GetDelegate()->AsDialogDelegate(); + DCHECK(dd); + return dd; +} + +// static +void DialogClientView::InitClass() { + static bool initialized = false; + if (!initialized) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + dialog_button_font_ = rb.GetFont(ResourceBundle::BaseFont); + initialized = true; + } +} + +} // namespace views |