summaryrefslogtreecommitdiffstats
path: root/chrome/views/dialog_client_view.cc
diff options
context:
space:
mode:
authorbeng@google.com <beng@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-31 00:42:17 +0000
committerbeng@google.com <beng@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-31 00:42:17 +0000
commitb2a4b9f96997dc3b929b5b9930325a030a8294e3 (patch)
tree3b18dd4002b9f6b681dd8256783f9d308591568f /chrome/views/dialog_client_view.cc
parentcd5241ff75071da8e4345e81ebc220e17505a306 (diff)
downloadchromium_src-b2a4b9f96997dc3b929b5b9930325a030a8294e3.zip
chromium_src-b2a4b9f96997dc3b929b5b9930325a030a8294e3.tar.gz
chromium_src-b2a4b9f96997dc3b929b5b9930325a030a8294e3.tar.bz2
Move dialog-specific aspects of ClientView into a new subclass DialogClientView.
ClientView becomes a generic representation of the View that occupies the "client area" of a window. All Windows now require a Client View (though they can use the default). Adjust WindowDelegate to provide a method for constructing the ClientView for a Window. The DialogDelegate overrides this to construct the DialogClientView. In the future, other specialized delegates will construct ClientViews specific to them, e.g. WizardDelegate would construct WizardClientView. Adjust the Window Init method to set up this new required Client View, and make some tweaks to CustomFrameWindow to make all this work. Remove all traces of dialog specific handling in Window into DialogClientView. B=1280060 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/views/dialog_client_view.cc')
-rw-r--r--chrome/views/dialog_client_view.cc410
1 files changed, 410 insertions, 0 deletions
diff --git a/chrome/views/dialog_client_view.cc b/chrome/views/dialog_client_view.cc
new file mode 100644
index 0000000..0ce9293
--- /dev/null
+++ b/chrome/views/dialog_client_view.cc
@@ -0,0 +1,410 @@
+// 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/dialog_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/dialog_delegate.h"
+#include "chrome/views/window.h"
+#include "generated_resources.h"
+
+namespace ChromeViews {
+
+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));
+}
+
+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());
+}
+
+// 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) {
+ }
+
+ // Overridden 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
+
+// static
+ChromeFont DialogClientView::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;
+
+///////////////////////////////////////////////////////////////////////////////
+// DialogClientView, public:
+
+DialogClientView::DialogClientView(Window* owner, View* contents_view)
+ : ClientView(owner, contents_view),
+ ok_button_(NULL),
+ cancel_button_(NULL),
+ extra_view_(NULL),
+ accepted_(false) {
+ 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);
+ ok_button_ = new DialogButton(
+ window(), 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(
+ window(), 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 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() {
+ 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 {
+ 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;
+}
+
+int DialogClientView::NonClientHitTest(const gfx::Point& point) {
+ if (size_box_bounds_.Contains(point.x() - GetX(), point.y() - GetY()))
+ 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
+ // ViewContainer's HWND.
+ ShowDialogButtons();
+ ClientView::ViewHierarchyChanged(is_add, parent, child);
+ UpdateDialogButtons();
+ Layout();
+ }
+}
+
+void DialogClientView::DidChangeBounds(const CRect& prev, const CRect& next) {
+ Layout();
+}
+
+void DialogClientView::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 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()->window_delegate()->CanResize() ||
+ window()->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 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_->GetHeight() + kDialogButtonContentSpacing;
+ return ok_button_->GetHeight() + kDialogButtonContentSpacing;
+ }
+ return 0;
+}
+
+void DialogClientView::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 DialogClientView::LayoutContentsView() {
+ CRect lb;
+ GetLocalBounds(&lb, false);
+ lb.bottom = std::max(0, static_cast<int>(lb.bottom - GetButtonsHeight()));
+ contents_view()->SetBounds(lb);
+ contents_view()->Layout();
+}
+
+DialogDelegate* DialogClientView::GetDialogDelegate() const {
+ DialogDelegate* dd = window()->window_delegate()->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;
+ }
+}
+
+}