diff options
Diffstat (limited to 'chrome/views/client_view.cc')
-rw-r--r-- | chrome/views/client_view.cc | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/chrome/views/client_view.cc b/chrome/views/client_view.cc new file mode 100644 index 0000000..2ae6870 --- /dev/null +++ b/chrome/views/client_view.cc @@ -0,0 +1,425 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> +#include <uxtheme.h> +#include <vsstyle.h> + +#include "chrome/views/client_view.h" + +#include "base/gfx/native_theme.h" +#include "chrome/browser/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.h" +#include "generated_resources.h" + +namespace { + +// Updates any of the standard buttons according to the delegate. +void UpdateButtonHelper(ChromeViews::NativeButton* button_view, + ChromeViews::DialogDelegate* delegate, + ChromeViews::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)); +} + +} // namespace + +namespace ChromeViews { + +// static +ChromeFont ClientView::dialog_button_font_; +static const int kDialogMinButtonWidth = 75; +static const int kDialogButtonLabelSpacing = 16; +static const int kDialogButtonContentSpacing = 0; + +// 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; + +namespace { + +// DialogButton ---------------------------------------------------------------- + +// DialogButtons is used for the ok/cancel buttons of the window. DialogButton +// forwrds 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) { + } + + // Overriden to forward to the delegate. + virtual bool AcceleratorPressed(const Accelerator& accelerator) { + if (!owner_->window_delegate()->AsDialogDelegate()-> + AreAcceleratorsEnabled(type_)) { + return false; + } + return NativeButton::AcceleratorPressed(accelerator); + } + + private: + Window* owner_; + const DialogDelegate::DialogButton type_; + + DISALLOW_EVIL_CONSTRUCTORS(DialogButton); +}; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// ClientView, public: +ClientView::ClientView(Window* owner, View* contents_view) + : ok_button_(NULL), + cancel_button_(NULL), + extra_view_(NULL), + owner_(owner), + contents_view_(contents_view) { + DCHECK(owner_); + InitClass(); +} + +ClientView::~ClientView() { +} + +void ClientView::ShowDialogButtons() { + if (!owner_->window_delegate()) + return; + + DialogDelegate* dd = owner_->window_delegate()->AsDialogDelegate(); + if (!dd) + return; + + 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); + ok_button_ = new DialogButton( + owner_, DialogDelegate::DIALOGBUTTON_OK, + label, + (dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_OK) != 0); + ok_button_->SetListener(this); + ok_button_->SetGroup(kButtonGroup); + if (!cancel_button_) + 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); + } + } + cancel_button_ = new DialogButton( + owner_, DialogDelegate::DIALOGBUTTON_CANCEL, + label, + (dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_CANCEL) + != 0); + cancel_button_->SetListener(this); + cancel_button_->SetGroup(kButtonGroup); + cancel_button_->AddAccelerator(Accelerator(VK_ESCAPE, false, false, false)); + AddChildView(cancel_button_); + } + + ChromeViews::View* extra_view = dd->GetExtraView(); + if (extra_view && !extra_view_) { + extra_view_ = extra_view; + extra_view_->SetGroup(kButtonGroup); + AddChildView(extra_view_); + } + 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)); + } +} + +// Changing dialog labels will change button widths. +void ClientView::UpdateDialogButtons() { + if (!owner_->window_delegate()) + return; + + DialogDelegate* dd = owner_->window_delegate()->AsDialogDelegate(); + if (!dd) + return; + + 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(); +} + +bool ClientView::PointIsInSizeBox(const gfx::Point& point) { + CPoint temp = point.ToPOINT(); + View::ConvertPointFromViewContainer(this, &temp); + return size_box_bounds_.Contains(temp.x, temp.y); +} + +//////////////////////////////////////////////////////////////////////////////// +// ClientView, View overrides: + +static 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->GetWidth(), view->GetHeight()); +} + +void ClientView::Paint(ChromeCanvas* canvas) { + if (!owner_->window_delegate()) + return; + + if (owner_->window_delegate()->AsDialogDelegate()) { + FillViewWithSysColor(canvas, this, GetSysColor(COLOR_3DFACE)); + } else { + // TODO(beng): (Cleanup) this should be COLOR_WINDOW but until the App + // Install wizard is updated to use the DialogDelegate somehow, + // we'll just use this value here. + FillViewWithSysColor(canvas, this, GetSysColor(COLOR_3DFACE)); + } +} + +void ClientView::PaintChildren(ChromeCanvas* canvas) { + View::PaintChildren(canvas); + if (!owner_->IsMaximized() && !owner_->IsMinimized()) + PaintSizeBox(canvas); +} + +void ClientView::Layout() { + if (has_dialog_buttons()) + LayoutDialogButtons(); + LayoutContentsView(); +} + +void ClientView::ViewHierarchyChanged(bool is_add, View* parent, View* child) { + if (is_add && child == this) { + // Only add the contents_view_ once, and only when we ourselves are added + // to the view hierarchy, since some contents_view_s assume that when they + // are added to the hierarchy a HWND exists, when it may not, since we are + // not yet added... + if (contents_view_ && contents_view_->GetParent() != this) + AddChildView(contents_view_); + // Can only add and update the dialog buttons _after_ they are added to the + // view hierarchy since they are native controls and require the + // ViewContainer's HWND. + ShowDialogButtons(); + UpdateDialogButtons(); + Layout(); + } +} + +void ClientView::DidChangeBounds(const CRect& prev, const CRect& next) { + Layout(); +} + +void ClientView::GetPreferredSize(CSize* out) { + DCHECK(out); + contents_view_->GetPreferredSize(out); + int button_height = 0; + if (has_dialog_buttons()) { + if (cancel_button_) + button_height = cancel_button_->GetHeight(); + else + button_height = ok_button_->GetHeight(); + // Account for padding above and below the button. + button_height += kDialogButtonContentSpacing + kButtonVEdgeMargin; + } + out->cy += button_height; +} + +bool ClientView::AcceleratorPressed(const Accelerator& accelerator) { + DCHECK(accelerator.GetKeyCode() == VK_ESCAPE); // We only expect Escape key. + owner_->Close(); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// ClientView, NativeButton::Listener implementation: + +void ClientView::ButtonPressed(NativeButton* sender) { + if (sender == ok_button_) { + owner_->AcceptWindow(); + } else if (sender == cancel_button_) { + owner_->CancelWindow(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// ClientView, private: + +void ClientView::PaintSizeBox(ChromeCanvas* canvas) { + if (!owner_->window_delegate()) + return; + + if (owner_->window_delegate()->CanResize() || + owner_->window_delegate()->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... + CRect gripper_bounds; + GetLocalBounds(&gripper_bounds, false); + gripper_bounds.left = gripper_bounds.right - gripper_size.cx; + gripper_bounds.top = gripper_bounds.bottom - gripper_size.cy; + size_box_bounds_ = gripper_bounds; + gfx::NativeTheme::instance()->PaintStatusGripper( + dc, SP_PANE, 1, 0, gripper_bounds); + canvas->endPlatformPaint(); + } +} + +int ClientView::GetButtonWidth(DialogDelegate::DialogButton button) { + if (!owner_->window_delegate()) + return kDialogMinButtonWidth; + + DialogDelegate* dd = owner_->window_delegate()->AsDialogDelegate(); + DCHECK(dd); + + std::wstring button_label = dd->GetDialogButtonLabel(button); + int string_width = dialog_button_font_.GetStringWidth(button_label); + return std::max(string_width + kDialogButtonLabelSpacing, + kDialogMinButtonWidth); +} + +void ClientView::LayoutDialogButtons() { + CRect extra_bounds; + if (cancel_button_) { + CSize ps; + cancel_button_->GetPreferredSize(&ps); + CRect lb; + GetLocalBounds(&lb, false); + int button_width = GetButtonWidth(DialogDelegate::DIALOGBUTTON_CANCEL); + CRect bounds; + bounds.left = lb.right - button_width - kButtonHEdgeMargin; + bounds.top = lb.bottom - ps.cy - kButtonVEdgeMargin; + bounds.right = bounds.left + button_width; + bounds.bottom = bounds.top + ps.cy; + cancel_button_->SetBounds(bounds); + // The extra view bounds are dependent on this button. + extra_bounds.right = bounds.left; + extra_bounds.top = bounds.top; + } + if (ok_button_) { + CSize ps; + ok_button_->GetPreferredSize(&ps); + CRect lb; + GetLocalBounds(&lb, false); + int button_width = GetButtonWidth(DialogDelegate::DIALOGBUTTON_OK); + int ok_button_right = lb.right - kButtonHEdgeMargin; + if (cancel_button_) + ok_button_right = cancel_button_->GetX() - kRelatedButtonHSpacing; + CRect bounds; + bounds.left = ok_button_right - button_width; + bounds.top = lb.bottom - ps.cy - kButtonVEdgeMargin; + bounds.right = ok_button_right; + bounds.bottom = bounds.top + ps.cy; + ok_button_->SetBounds(bounds); + // The extra view bounds are dependent on this button. + extra_bounds.right = bounds.left; + extra_bounds.top = bounds.top; + } + if (extra_view_) { + CSize ps; + extra_view_->GetPreferredSize(&ps); + CRect lb; + GetLocalBounds(&lb, false); + extra_bounds.left = lb.left + kButtonHEdgeMargin; + extra_bounds.bottom = extra_bounds.top + ps.cy; + extra_view_->SetBounds(extra_bounds); + } +} + +void ClientView::LayoutContentsView() { + // We acquire a |contents_view_| ptr when we are constructed, but this can be + // NULL (for testing purposes). Also, we explicitly don't immediately insert + // the contents_view_ into the hierarchy until we ourselves are inserted, + // because the contents_view_ may have initialization that relies on a HWND + // at the time it is inserted into _any_ hierarchy. So this check is to + // ensure the contents_view_ is in a valid state as a child of this window + // before trying to lay it out to our size. + if (contents_view_ && contents_view_->GetParent() == this) { + int button_height = 0; + if (has_dialog_buttons()) { + if (cancel_button_) + button_height = cancel_button_->GetHeight(); + else + button_height = ok_button_->GetHeight(); + // Account for padding above and below the button. + button_height += kDialogButtonContentSpacing + kButtonVEdgeMargin; + } + + CRect lb; + GetLocalBounds(&lb, false); + lb.bottom = std::max(0, static_cast<int>(lb.bottom - button_height)); + contents_view_->SetBounds(lb); + contents_view_->Layout(); + } +} + +// static +void ClientView::InitClass() { + static bool initialized = false; + if (!initialized) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + dialog_button_font_ = rb.GetFont(ResourceBundle::BaseFont); + initialized = true; + } +} + +} |