summaryrefslogtreecommitdiffstats
path: root/chrome/views/controls/button
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/views/controls/button')
-rw-r--r--chrome/views/controls/button/button.cc71
-rw-r--r--chrome/views/controls/button/button.h74
-rw-r--r--chrome/views/controls/button/button_dropdown.cc193
-rw-r--r--chrome/views/controls/button/button_dropdown.h72
-rw-r--r--chrome/views/controls/button/checkbox.cc181
-rw-r--r--chrome/views/controls/button/checkbox.h85
-rw-r--r--chrome/views/controls/button/custom_button.cc239
-rw-r--r--chrome/views/controls/button/custom_button.h94
-rw-r--r--chrome/views/controls/button/image_button.cc157
-rw-r--r--chrome/views/controls/button/image_button.h101
-rw-r--r--chrome/views/controls/button/menu_button.cc254
-rw-r--r--chrome/views/controls/button/menu_button.h98
-rw-r--r--chrome/views/controls/button/native_button.cc212
-rw-r--r--chrome/views/controls/button/native_button.h146
-rw-r--r--chrome/views/controls/button/radio_button.cc123
-rw-r--r--chrome/views/controls/button/radio_button.h60
-rw-r--r--chrome/views/controls/button/text_button.cc323
-rw-r--r--chrome/views/controls/button/text_button.h138
18 files changed, 2621 insertions, 0 deletions
diff --git a/chrome/views/controls/button/button.cc b/chrome/views/controls/button/button.cc
new file mode 100644
index 0000000..5c28138
--- /dev/null
+++ b/chrome/views/controls/button/button.cc
@@ -0,0 +1,71 @@
+// 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/controls/button/button.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+// Button, public:
+
+Button::~Button() {
+}
+
+void Button::SetTooltipText(const std::wstring& tooltip_text) {
+ tooltip_text_ = tooltip_text;
+ TooltipTextChanged();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Button, View overrides:
+
+bool Button::GetTooltipText(int x, int y, std::wstring* tooltip) {
+ if (!tooltip_text_.empty()) {
+ *tooltip = tooltip_text_;
+ return true;
+ }
+ return false;
+}
+
+bool Button::GetAccessibleKeyboardShortcut(std::wstring* shortcut) {
+ if (!accessible_shortcut_.empty()) {
+ *shortcut = accessible_shortcut_;
+ return true;
+ }
+ return false;
+}
+
+bool Button::GetAccessibleName(std::wstring* name) {
+ if (!accessible_name_.empty()) {
+ *name = accessible_name_;
+ return true;
+ }
+ return false;
+}
+
+void Button::SetAccessibleKeyboardShortcut(const std::wstring& shortcut) {
+ accessible_shortcut_.assign(shortcut);
+}
+
+void Button::SetAccessibleName(const std::wstring& name) {
+ accessible_name_.assign(name);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Button, protected:
+
+Button::Button(ButtonListener* listener)
+ : listener_(listener),
+ tag_(-1),
+ mouse_event_flags_(0) {
+}
+
+void Button::NotifyClick(int mouse_event_flags) {
+ mouse_event_flags_ = mouse_event_flags;
+ listener_->ButtonPressed(this);
+ // NOTE: don't attempt to reset mouse_event_flags_ as the listener may have
+ // deleted us.
+}
+
+} // namespace views
diff --git a/chrome/views/controls/button/button.h b/chrome/views/controls/button/button.h
new file mode 100644
index 0000000..1f6514b
--- /dev/null
+++ b/chrome/views/controls/button/button.h
@@ -0,0 +1,74 @@
+// 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.
+
+#ifndef CHROME_VIEWS_CONTROLS_BUTTON_BUTTON_H_
+#define CHROME_VIEWS_CONTROLS_BUTTON_BUTTON_H_
+
+#include "chrome/views/view.h"
+
+namespace views {
+
+class Button;
+
+// An interface implemented by an object to let it know that a button was
+// pressed.
+class ButtonListener {
+ public:
+ virtual void ButtonPressed(Button* sender) = 0;
+};
+
+// A View representing a button. Depending on the specific type, the button
+// could be implemented by a native control or custom rendered.
+class Button : public View {
+ public:
+ virtual ~Button();
+
+ void SetTooltipText(const std::wstring& tooltip_text);
+
+ int tag() const { return tag_; }
+ void set_tag(int tag) { tag_ = tag; }
+
+ int mouse_event_flags() const { return mouse_event_flags_; }
+
+ // Overridden from View:
+ virtual bool GetTooltipText(int x, int y, std::wstring* tooltip);
+ virtual bool GetAccessibleKeyboardShortcut(std::wstring* shortcut);
+ virtual bool GetAccessibleName(std::wstring* name);
+ virtual void SetAccessibleKeyboardShortcut(const std::wstring& shortcut);
+ virtual void SetAccessibleName(const std::wstring& name);
+
+ protected:
+ // Construct the Button with a Listener. The listener can be NULL, as long as
+ // the specific button implementation makes sure to not call NotifyClick. This
+ // can be true of buttons that don't have a listener - e.g. menubuttons where
+ // there's no default action.
+ explicit Button(ButtonListener* listener);
+
+ // Cause the button to notify the listener that a click occurred.
+ virtual void NotifyClick(int mouse_event_flags);
+
+ private:
+ // The text shown in a tooltip.
+ std::wstring tooltip_text_;
+
+ // Accessibility data.
+ std::wstring accessible_shortcut_;
+ std::wstring accessible_name_;
+
+ // The button's listener. Notified when clicked.
+ ButtonListener* listener_;
+
+ // The id tag associated with this button. Used to disambiguate buttons in
+ // the ButtonListener implementation.
+ int tag_;
+
+ // Event flags present when the button was clicked.
+ int mouse_event_flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(Button);
+};
+
+} // namespace views
+
+#endif // CHROME_VIEWS_CONTROLS_BUTTON_BUTTON_H_
diff --git a/chrome/views/controls/button/button_dropdown.cc b/chrome/views/controls/button/button_dropdown.cc
new file mode 100644
index 0000000..c2ea7cc
--- /dev/null
+++ b/chrome/views/controls/button/button_dropdown.cc
@@ -0,0 +1,193 @@
+// 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/controls/button/button_dropdown.h"
+
+#include "base/message_loop.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/views/controls/menu/view_menu_delegate.h"
+#include "chrome/views/widget/widget.h"
+#include "grit/generated_resources.h"
+
+namespace views {
+
+// How long to wait before showing the menu
+static const int kMenuTimerDelay = 500;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown - constructors, destructors, initialization, cleanup
+//
+////////////////////////////////////////////////////////////////////////////////
+
+ButtonDropDown::ButtonDropDown(ButtonListener* listener,
+ Menu::Delegate* menu_delegate)
+ : ImageButton(listener),
+ menu_delegate_(menu_delegate),
+ y_position_on_lbuttondown_(0),
+ show_menu_factory_(this) {
+}
+
+ButtonDropDown::~ButtonDropDown() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown - Events
+//
+////////////////////////////////////////////////////////////////////////////////
+
+bool ButtonDropDown::OnMousePressed(const MouseEvent& e) {
+ if (IsEnabled() && e.IsLeftMouseButton() && HitTest(e.location())) {
+ // Store the y pos of the mouse coordinates so we can use them later to
+ // determine if the user dragged the mouse down (which should pop up the
+ // drag down menu immediately, instead of waiting for the timer)
+ y_position_on_lbuttondown_ = e.y();
+
+ // Schedule a task that will show the menu.
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ show_menu_factory_.NewRunnableMethod(&ButtonDropDown::ShowDropDownMenu,
+ GetWidget()->GetNativeView()),
+ kMenuTimerDelay);
+ }
+
+ return ImageButton::OnMousePressed(e);
+}
+
+void ButtonDropDown::OnMouseReleased(const MouseEvent& e, bool canceled) {
+ ImageButton::OnMouseReleased(e, canceled);
+
+ if (canceled)
+ return;
+
+ if (e.IsLeftMouseButton())
+ show_menu_factory_.RevokeAll();
+
+ if (IsEnabled() && e.IsRightMouseButton() && HitTest(e.location())) {
+ show_menu_factory_.RevokeAll();
+ // Make the button look depressed while the menu is open.
+ // NOTE: SetState() schedules a paint, but it won't occur until after the
+ // context menu message loop has terminated, so we PaintNow() to
+ // update the appearance synchronously.
+ SetState(BS_PUSHED);
+ PaintNow();
+ ShowDropDownMenu(GetWidget()->GetNativeView());
+ }
+}
+
+bool ButtonDropDown::OnMouseDragged(const MouseEvent& e) {
+ bool result = ImageButton::OnMouseDragged(e);
+
+ if (!show_menu_factory_.empty()) {
+ // SM_CYDRAG is a pixel value for minimum dragging distance before operation
+ // counts as a drag, and not just as a click and accidental move of a mouse.
+ // See http://msdn2.microsoft.com/en-us/library/ms724385.aspx for details.
+ int dragging_threshold = GetSystemMetrics(SM_CYDRAG);
+
+ // If the mouse is dragged to a y position lower than where it was when
+ // clicked then we should not wait for the menu to appear but show
+ // it immediately.
+ if (e.y() > y_position_on_lbuttondown_ + dragging_threshold) {
+ show_menu_factory_.RevokeAll();
+ ShowDropDownMenu(GetWidget()->GetNativeView());
+ }
+ }
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown - Menu functions
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void ButtonDropDown::ShowContextMenu(int x, int y, bool is_mouse_gesture) {
+ show_menu_factory_.RevokeAll();
+ // Make the button look depressed while the menu is open.
+ // NOTE: SetState() schedules a paint, but it won't occur until after the
+ // context menu message loop has terminated, so we PaintNow() to
+ // update the appearance synchronously.
+ SetState(BS_PUSHED);
+ PaintNow();
+ ShowDropDownMenu(GetWidget()->GetNativeView());
+ SetState(BS_HOT);
+}
+
+void ButtonDropDown::ShowDropDownMenu(HWND window) {
+ if (menu_delegate_) {
+ gfx::Rect lb = GetLocalBounds(true);
+
+ // Both the menu position and the menu anchor type change if the UI layout
+ // is right-to-left.
+ gfx::Point menu_position(lb.origin());
+ menu_position.Offset(0, lb.height() - 1);
+ if (UILayoutIsRightToLeft())
+ menu_position.Offset(lb.width() - 1, 0);
+
+ Menu::AnchorPoint anchor = Menu::TOPLEFT;
+ if (UILayoutIsRightToLeft())
+ anchor = Menu::TOPRIGHT;
+
+ View::ConvertPointToScreen(this, &menu_position);
+
+ int left_bound = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ if (menu_position.x() < left_bound)
+ menu_position.set_x(left_bound);
+
+ Menu menu(menu_delegate_, anchor, window);
+
+ // ID's for AppendMenu is 1-based because RunMenu will ignore the user
+ // selection if id=0 is selected (0 = NO-OP) so we add 1 here and subtract 1
+ // in the handlers above to get the actual index
+ int item_count = menu_delegate_->GetItemCount();
+ for (int i = 0; i < item_count; i++) {
+ if (menu_delegate_->IsItemSeparator(i + 1)) {
+ menu.AppendSeparator();
+ } else {
+ if (menu_delegate_->HasIcon(i + 1))
+ menu.AppendMenuItemWithIcon(i + 1, L"", SkBitmap());
+ else
+ menu.AppendMenuItem(i+1, L"", Menu::NORMAL);
+ }
+ }
+
+ menu.RunMenuAt(menu_position.x(), menu_position.y());
+
+ // Need to explicitly clear mouse handler so that events get sent
+ // properly after the menu finishes running. If we don't do this, then
+ // the first click to other parts of the UI is eaten.
+ SetMouseHandler(NULL);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown - Accessibility
+//
+////////////////////////////////////////////////////////////////////////////////
+
+bool ButtonDropDown::GetAccessibleDefaultAction(std::wstring* action) {
+ DCHECK(action);
+
+ action->assign(l10n_util::GetString(IDS_ACCACTION_PRESS));
+ return true;
+}
+
+bool ButtonDropDown::GetAccessibleRole(VARIANT* role) {
+ DCHECK(role);
+
+ role->vt = VT_I4;
+ role->lVal = ROLE_SYSTEM_BUTTONDROPDOWN;
+ return true;
+}
+
+bool ButtonDropDown::GetAccessibleState(VARIANT* state) {
+ DCHECK(state);
+
+ state->lVal |= STATE_SYSTEM_HASPOPUP;
+ return true;
+}
+
+} // namespace views
diff --git a/chrome/views/controls/button/button_dropdown.h b/chrome/views/controls/button/button_dropdown.h
new file mode 100644
index 0000000..b46158b
--- /dev/null
+++ b/chrome/views/controls/button/button_dropdown.h
@@ -0,0 +1,72 @@
+// 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.
+
+#ifndef CHROME_VIEWS_CONTROLS_BUTTON_BUTTON_DROPDOWN_H_
+#define CHROME_VIEWS_CONTROLS_BUTTON_BUTTON_DROPDOWN_H_
+
+#include "base/task.h"
+#include "chrome/views/controls/button/image_button.h"
+#include "chrome/views/controls/menu/menu.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ButtonDropDown
+//
+// A button class that when pressed (and held) or pressed (and drag down) will
+// display a menu
+//
+////////////////////////////////////////////////////////////////////////////////
+class ButtonDropDown : public ImageButton {
+ public:
+ ButtonDropDown(ButtonListener* listener, Menu::Delegate* menu_delegate);
+ virtual ~ButtonDropDown();
+
+ // Returns the MSAA default action of the current view. The string returned
+ // describes the default action that will occur when executing
+ // IAccessible::DoDefaultAction.
+ bool GetAccessibleDefaultAction(std::wstring* action);
+
+ // Returns the MSAA role of the current view. The role is what assistive
+ // technologies (ATs) use to determine what behavior to expect from a given
+ // control.
+ bool GetAccessibleRole(VARIANT* role);
+
+ // Returns the MSAA state of the current view. Sets the input VARIANT
+ // appropriately, and returns true if a change was performed successfully.
+ // Overriden from View.
+ virtual bool GetAccessibleState(VARIANT* state);
+
+ private:
+ // Overridden from Button
+ virtual bool OnMousePressed(const MouseEvent& e);
+ virtual void OnMouseReleased(const MouseEvent& e, bool canceled);
+ virtual bool OnMouseDragged(const MouseEvent& e);
+
+ // Overridden from View. Used to display the right-click menu, as triggered
+ // by the keyboard, for instance. Using the member function ShowDropDownMenu
+ // for the actual display.
+ virtual void ShowContextMenu(int x,
+ int y,
+ bool is_mouse_gesture);
+
+ // Internal function to show the dropdown menu
+ void ShowDropDownMenu(HWND window);
+
+ // Specifies who to delegate populating the menu
+ Menu::Delegate* menu_delegate_;
+
+ // Y position of mouse when left mouse button is pressed
+ int y_position_on_lbuttondown_;
+
+ // A factory for tasks that show the dropdown context menu for the button.
+ ScopedRunnableMethodFactory<ButtonDropDown> show_menu_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ButtonDropDown);
+};
+
+} // namespace views
+
+#endif // CHROME_VIEWS_CONTROLS_BUTTON_BUTTON_DROPDOWN_H_
diff --git a/chrome/views/controls/button/checkbox.cc b/chrome/views/controls/button/checkbox.cc
new file mode 100644
index 0000000..c5463de
--- /dev/null
+++ b/chrome/views/controls/button/checkbox.cc
@@ -0,0 +1,181 @@
+// 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/controls/button/checkbox.h"
+
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/views/controls/button/checkbox.h"
+#include "chrome/views/controls/hwnd_view.h"
+#include "chrome/views/controls/label.h"
+
+// FIXME(ACW) there got be a better way to find out the check box sizes
+static int kCheckBoxWidth = 13;
+static int kCheckBoxHeight = 13;
+static int kCheckBoxToLabel = 4;
+
+namespace views {
+
+// Horizontal focus padding.
+const int CheckBox::kFocusPaddingHorizontal = 2;
+const int CheckBox::kFocusPaddingVertical = 1;
+
+const char CheckBox::kViewClassName[] = "chrome/views/CheckBox";
+
+CheckBox::CheckBox(const std::wstring& label)
+ : NativeButton(label),
+ is_selected_(false) {
+ // Note: we paint the label as a floating view
+ SetMinSizeFromDLUs(gfx::Size(0, 0));
+ label_ = new Label(label);
+ label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+}
+
+CheckBox::~CheckBox() {
+ delete label_;
+}
+
+void CheckBox::SetMultiLine(bool multi_line) {
+ label_->SetMultiLine(multi_line);
+}
+
+// static
+int CheckBox::GetTextIndent() {
+ return kCheckBoxWidth + kCheckBoxToLabel + kFocusPaddingHorizontal;
+}
+
+void CheckBox::SetIsSelected(bool f) {
+ if (f != is_selected_) {
+ is_selected_ = f;
+ UpdateNativeButton();
+ }
+}
+
+bool CheckBox::IsSelected() const {
+ return is_selected_;
+}
+
+std::string CheckBox::GetClassName() const {
+ return kViewClassName;
+}
+
+void CheckBox::Layout() {
+ int label_x = GetTextIndent();
+ label_->SetBounds(label_x, 0, std::max(0, width() - label_x), height());
+ if (hwnd_view_) {
+ int first_line_height = label_->GetFont().height();
+ hwnd_view_->SetBounds(0, ((first_line_height - kCheckBoxHeight) / 2) + 1,
+ kCheckBoxWidth, kCheckBoxHeight);
+ hwnd_view_->UpdateHWNDBounds();
+ }
+}
+
+void CheckBox::ComputeTextRect(gfx::Rect* out) {
+ gfx::Size s = label_->GetPreferredSize();
+ out->set_x(GetTextIndent());
+ out->set_y(kFocusPaddingVertical);
+ int new_width = std::min(width() - (kCheckBoxWidth + kCheckBoxToLabel),
+ s.width());
+ out->set_width(std::max(0, new_width));
+ out->set_height(s.height());
+}
+
+void CheckBox::Paint(ChromeCanvas* canvas) {
+ gfx::Rect r;
+ ComputeTextRect(&r);
+ // Paint the focus border if any.
+ if (HasFocus()) {
+ // Mirror left point for rectangle to draw focus for RTL text.
+ canvas->DrawFocusRect(MirroredLeftPointForRect(r) - kFocusPaddingHorizontal,
+ r.y() - kFocusPaddingVertical,
+ r.width() + kFocusPaddingHorizontal * 2,
+ r.height() + kFocusPaddingVertical * 2);
+ }
+ PaintFloatingView(canvas, label_, r.x(), r.y(), r.width(), r.height());
+}
+
+void CheckBox::SetEnabled(bool enabled) {
+ if (enabled_ == enabled)
+ return;
+ NativeButton::SetEnabled(enabled);
+ label_->SetEnabled(enabled);
+}
+
+HWND CheckBox::CreateNativeControl(HWND parent_container) {
+ HWND r = ::CreateWindowEx(WS_EX_TRANSPARENT | GetAdditionalExStyle(),
+ L"BUTTON",
+ L"",
+ WS_CHILD | BS_CHECKBOX | WS_VISIBLE,
+ 0, 0, width(), height(),
+ parent_container, NULL, NULL, NULL);
+ ConfigureNativeButton(r);
+ return r;
+}
+
+void CheckBox::ConfigureNativeButton(HWND hwnd) {
+ ::SendMessage(hwnd,
+ static_cast<UINT>(BM_SETCHECK),
+ static_cast<WPARAM>(is_selected_ ? BST_CHECKED : BST_UNCHECKED),
+ 0);
+ label_->SetText(GetLabel());
+}
+
+gfx::Size CheckBox::GetPreferredSize() {
+ gfx::Size prefsize = label_->GetPreferredSize();
+ prefsize.set_height(std::max(prefsize.height() + kFocusPaddingVertical * 2,
+ kCheckBoxHeight));
+ prefsize.Enlarge(GetTextIndent() * 2, 0);
+ return prefsize;
+}
+
+LRESULT CheckBox::OnCommand(UINT code, int id, HWND source) {
+ if (code == BN_CLICKED)
+ SetIsSelected(!is_selected_);
+
+ return NativeButton::OnCommand(code, id, source);
+}
+
+void CheckBox::HighlightButton(bool f) {
+ ::SendMessage(GetNativeControlHWND(),
+ static_cast<UINT>(BM_SETSTATE),
+ static_cast<WPARAM>(f),
+ 0);
+}
+
+bool CheckBox::LabelHitTest(const MouseEvent& event) {
+ CPoint p(event.x(), event.y());
+ gfx::Rect r;
+ ComputeTextRect(&r);
+ return r.Contains(event.x(), event.y());
+}
+
+void CheckBox::OnMouseEntered(const MouseEvent& event) {
+ HighlightButton(LabelHitTest(event));
+}
+
+void CheckBox::OnMouseMoved(const MouseEvent& event) {
+ HighlightButton(LabelHitTest(event));
+}
+
+void CheckBox::OnMouseExited(const MouseEvent& event) {
+ HighlightButton(false);
+}
+
+bool CheckBox::OnMousePressed(const MouseEvent& event) {
+ HighlightButton(LabelHitTest(event));
+ return true;
+}
+
+bool CheckBox::OnMouseDragged(const MouseEvent& event) {
+ HighlightButton(LabelHitTest(event));
+ return true;
+}
+
+void CheckBox::OnMouseReleased(const MouseEvent& event,
+ bool canceled) {
+ HighlightButton(false);
+ if (!canceled && LabelHitTest(event))
+ OnCommand(BN_CLICKED, 0, GetNativeControlHWND());
+}
+
+} // namespace views
diff --git a/chrome/views/controls/button/checkbox.h b/chrome/views/controls/button/checkbox.h
new file mode 100644
index 0000000..78c006b
--- /dev/null
+++ b/chrome/views/controls/button/checkbox.h
@@ -0,0 +1,85 @@
+// 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.
+
+#ifndef CHROME_VIEWS_CONTROLS_BUTTON_CHECKBOX_H_
+#define CHROME_VIEWS_CONTROLS_BUTTON_CHECKBOX_H_
+
+#include "base/gfx/rect.h"
+#include "chrome/views/controls/button/native_button.h"
+
+namespace views {
+
+class Label;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// CheckBox implements a check box button. It uses the standard windows control
+// for the check item but not for the label. We have to do this because windows
+// always wants to draw a background under the label. I tried to intercept
+// WM_CTLCOLORSTATIC and return a NULL_BRUSH and setting the BkMode to
+// transparent as well as other things. The background was always drawn as solid
+// black.
+//
+// The label is implemented with a views::Label
+//
+////////////////////////////////////////////////////////////////////////////////
+class CheckBox : public NativeButton {
+ public:
+ static const char kViewClassName[];
+ static const int kFocusPaddingHorizontal;
+ static const int kFocusPaddingVertical;
+
+ explicit CheckBox(const std::wstring& label);
+ virtual ~CheckBox();
+
+ // Allows the label to wrap across multiple lines if |multi_line| is true.
+ // If false, the text is cropped.
+ void SetMultiLine(bool multi_line);
+
+ // Returns the x position of the text. This can also be used to indent
+ // subsequent dependent controls.
+ static int GetTextIndent();
+
+ virtual void SetIsSelected(bool f);
+ bool IsSelected() const;
+
+ virtual std::string GetClassName() const;
+
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+
+ virtual bool OnMousePressed(const MouseEvent& event);
+ virtual bool OnMouseDragged(const MouseEvent& event);
+ virtual void OnMouseReleased(const MouseEvent& event, bool canceled);
+ virtual void OnMouseEntered(const MouseEvent& event);
+ virtual void OnMouseMoved(const MouseEvent& event);
+ virtual void OnMouseExited(const MouseEvent& event);
+
+ virtual void Paint(ChromeCanvas* canvas);
+
+ // Overriden to forward to the label too.
+ virtual void SetEnabled(bool enabled);
+
+ protected:
+
+ virtual HWND CreateNativeControl(HWND parent_container);
+ virtual void ConfigureNativeButton(HWND hwnd);
+ virtual LRESULT OnCommand(UINT code, int id, HWND source);
+
+ Label* label_;
+ private:
+
+ void HighlightButton(bool f);
+ bool LabelHitTest(const MouseEvent& event);
+ void ComputeTextRect(gfx::Rect* out);
+
+ bool is_selected_;
+
+
+ DISALLOW_EVIL_CONSTRUCTORS(CheckBox);
+};
+
+} // namespace views
+
+#endif // CHROME_VIEWS_CONTROLS_BUTTON_CHECKBOX_H_
diff --git a/chrome/views/controls/button/custom_button.cc b/chrome/views/controls/button/custom_button.cc
new file mode 100644
index 0000000..04e0a50
--- /dev/null
+++ b/chrome/views/controls/button/custom_button.cc
@@ -0,0 +1,239 @@
+// 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/controls/button/custom_button.h"
+
+#include "base/base_drag_source.h"
+#include "chrome/browser/drag_utils.h"
+#include "chrome/common/drag_drop_types.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/os_exchange_data.h"
+#include "chrome/common/throb_animation.h"
+
+namespace views {
+
+// How long the hover animation takes if uninterrupted.
+static const int kHoverFadeDurationMs = 150;
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, public:
+
+CustomButton::~CustomButton() {
+}
+
+void CustomButton::SetState(ButtonState state) {
+ if (state != state_) {
+ if (animate_on_state_change_ || !hover_animation_->IsAnimating()) {
+ animate_on_state_change_ = true;
+ if (state_ == BS_NORMAL && state == BS_HOT) {
+ // Button is hovered from a normal state, start hover animation.
+ hover_animation_->Show();
+ } else if (state_ == BS_HOT && state == BS_NORMAL) {
+ // Button is returning to a normal state from hover, start hover
+ // fade animation.
+ hover_animation_->Hide();
+ } else {
+ hover_animation_->Stop();
+ }
+ }
+
+ state_ = state;
+ SchedulePaint();
+ }
+}
+
+void CustomButton::StartThrobbing(int cycles_til_stop) {
+ animate_on_state_change_ = false;
+ hover_animation_->StartThrobbing(cycles_til_stop);
+}
+
+void CustomButton::SetAnimationDuration(int duration) {
+ hover_animation_->SetSlideDuration(duration);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, View overrides:
+
+void CustomButton::SetEnabled(bool enabled) {
+ if (enabled && state_ == BS_DISABLED) {
+ SetState(BS_NORMAL);
+ } else if (!enabled && state_ != BS_DISABLED) {
+ SetState(BS_DISABLED);
+ }
+}
+
+bool CustomButton::IsEnabled() const {
+ return state_ != BS_DISABLED;
+}
+
+bool CustomButton::IsFocusable() const {
+ return (state_ != BS_DISABLED) && View::IsFocusable();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, protected:
+
+CustomButton::CustomButton(ButtonListener* listener)
+ : Button(listener),
+ state_(BS_NORMAL),
+ animate_on_state_change_(true) {
+ hover_animation_.reset(new ThrobAnimation(this));
+ hover_animation_->SetSlideDuration(kHoverFadeDurationMs);
+}
+
+bool CustomButton::IsTriggerableEvent(const MouseEvent& e) {
+ return e.IsLeftMouseButton();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, View overrides (protected):
+
+bool CustomButton::AcceleratorPressed(const Accelerator& accelerator) {
+ if (enabled_) {
+ SetState(BS_NORMAL);
+ NotifyClick(0);
+ return true;
+ }
+ return false;
+}
+
+bool CustomButton::OnMousePressed(const MouseEvent& e) {
+ if (state_ != BS_DISABLED) {
+ if (IsTriggerableEvent(e) && HitTest(e.location()))
+ SetState(BS_PUSHED);
+ RequestFocus();
+ }
+ return true;
+}
+
+bool CustomButton::OnMouseDragged(const MouseEvent& e) {
+ if (state_ != BS_DISABLED) {
+ if (!HitTest(e.location()))
+ SetState(BS_NORMAL);
+ else if (IsTriggerableEvent(e))
+ SetState(BS_PUSHED);
+ else
+ SetState(BS_HOT);
+ }
+ return true;
+}
+
+void CustomButton::OnMouseReleased(const MouseEvent& e, bool canceled) {
+ if (InDrag()) {
+ // Starting a drag results in a MouseReleased, we need to ignore it.
+ return;
+ }
+
+ if (state_ != BS_DISABLED) {
+ if (canceled || !HitTest(e.location())) {
+ SetState(BS_NORMAL);
+ } else {
+ SetState(BS_HOT);
+ if (IsTriggerableEvent(e)) {
+ NotifyClick(e.GetFlags());
+ // We may be deleted at this point (by the listener's notification
+ // handler) so no more doing anything, just return.
+ return;
+ }
+ }
+ }
+}
+
+void CustomButton::OnMouseEntered(const MouseEvent& e) {
+ if (state_ != BS_DISABLED)
+ SetState(BS_HOT);
+}
+
+void CustomButton::OnMouseMoved(const MouseEvent& e) {
+ if (state_ != BS_DISABLED) {
+ if (HitTest(e.location())) {
+ SetState(BS_HOT);
+ } else {
+ SetState(BS_NORMAL);
+ }
+ }
+}
+
+void CustomButton::OnMouseExited(const MouseEvent& e) {
+ // Starting a drag results in a MouseExited, we need to ignore it.
+ if (state_ != BS_DISABLED && !InDrag())
+ SetState(BS_NORMAL);
+}
+
+bool CustomButton::OnKeyPressed(const KeyEvent& e) {
+ if (state_ != BS_DISABLED) {
+ // Space sets button state to pushed. Enter clicks the button. This matches
+ // the Windows native behavior of buttons, where Space clicks the button
+ // on KeyRelease and Enter clicks the button on KeyPressed.
+ if (e.GetCharacter() == VK_SPACE) {
+ SetState(BS_PUSHED);
+ return true;
+ } else if (e.GetCharacter() == VK_RETURN) {
+ SetState(BS_NORMAL);
+ NotifyClick(0);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CustomButton::OnKeyReleased(const KeyEvent& e) {
+ if (state_ != BS_DISABLED) {
+ if (e.GetCharacter() == VK_SPACE) {
+ SetState(BS_NORMAL);
+ NotifyClick(0);
+ return true;
+ }
+ }
+ return false;
+}
+
+void CustomButton::OnDragDone() {
+ SetState(BS_NORMAL);
+}
+
+void CustomButton::ShowContextMenu(int x, int y, bool is_mouse_gesture) {
+ if (GetContextMenuController()) {
+ // We're about to show the context menu. Showing the context menu likely
+ // means we won't get a mouse exited and reset state. Reset it now to be
+ // sure.
+ if (state_ != BS_DISABLED)
+ SetState(BS_NORMAL);
+ View::ShowContextMenu(x, y, is_mouse_gesture);
+ }
+}
+
+void CustomButton::ViewHierarchyChanged(bool is_add, View *parent,
+ View *child) {
+ if (!is_add && state_ != BS_DISABLED)
+ SetState(BS_NORMAL);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, AnimationDelegate implementation:
+
+void CustomButton::AnimationProgressed(const Animation* animation) {
+ SchedulePaint();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CustomButton, private:
+
+void CustomButton::SetHighlighted(bool highlighted) {
+ if (highlighted && state_ != BS_DISABLED) {
+ SetState(BS_HOT);
+ } else if (!highlighted && state_ != BS_DISABLED) {
+ SetState(BS_NORMAL);
+ }
+}
+
+bool CustomButton::IsHighlighted() const {
+ return state_ == BS_HOT;
+}
+
+bool CustomButton::IsPushed() const {
+ return state_ == BS_PUSHED;
+}
+
+} // namespace views
diff --git a/chrome/views/controls/button/custom_button.h b/chrome/views/controls/button/custom_button.h
new file mode 100644
index 0000000..cb4db5d
--- /dev/null
+++ b/chrome/views/controls/button/custom_button.h
@@ -0,0 +1,94 @@
+// 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.
+
+#ifndef CHROME_VIEWS_CONTROLS_BUTTON_CUSTOM_BUTTON_H_
+#define CHROME_VIEWS_CONTROLS_BUTTON_CUSTOM_BUTTON_H_
+
+#include "chrome/common/animation.h"
+#include "chrome/views/controls/button/button.h"
+
+class ThrobAnimation;
+
+namespace views {
+
+// A button with custom rendering. The common base class of IconButton and
+// TextButton.
+class CustomButton : public Button,
+ public AnimationDelegate {
+ public:
+ virtual ~CustomButton();
+
+ // Possible states
+ enum ButtonState {
+ BS_NORMAL = 0,
+ BS_HOT,
+ BS_PUSHED,
+ BS_DISABLED,
+ BS_COUNT
+ };
+
+ // Get/sets the current display state of the button.
+ ButtonState state() const { return state_; }
+ void SetState(ButtonState state);
+
+ // Starts throbbing. See HoverAnimation for a description of cycles_til_stop.
+ void StartThrobbing(int cycles_til_stop);
+
+ // Set how long the hover animation will last for.
+ void SetAnimationDuration(int duration);
+
+ // Overridden from View:
+ virtual void SetEnabled(bool enabled);
+ virtual bool IsEnabled() const;
+ virtual bool IsFocusable() const;
+
+ protected:
+ // Construct the Button with a Listener. See comment for Button's ctor.
+ explicit CustomButton(ButtonListener* listener);
+
+ // Returns true if the event is one that can trigger notifying the listener.
+ // This implementation returns true if the left mouse button is down.
+ virtual bool IsTriggerableEvent(const MouseEvent& e);
+
+ // Overridden from View:
+ virtual bool AcceleratorPressed(const Accelerator& accelerator);
+ virtual bool OnMousePressed(const MouseEvent& e);
+ virtual bool OnMouseDragged(const MouseEvent& e);
+ virtual void OnMouseReleased(const MouseEvent& e, bool canceled);
+ virtual void OnMouseEntered(const MouseEvent& e);
+ virtual void OnMouseMoved(const MouseEvent& e);
+ virtual void OnMouseExited(const MouseEvent& e);
+ virtual bool OnKeyPressed(const KeyEvent& e);
+ virtual bool OnKeyReleased(const KeyEvent& e);
+ virtual void OnDragDone();
+ virtual void ShowContextMenu(int x, int y, bool is_mouse_gesture);
+ virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child);
+
+ // Overridden from AnimationDelegate:
+ virtual void AnimationProgressed(const Animation* animation);
+
+ // The button state (defined in implementation)
+ ButtonState state_;
+
+ // Hover animation.
+ scoped_ptr<ThrobAnimation> hover_animation_;
+
+ private:
+ // Set / test whether the button is highlighted (in the hover state).
+ void SetHighlighted(bool highlighted);
+ bool IsHighlighted() const;
+
+ // Returns whether the button is pushed.
+ bool IsPushed() const;
+
+ // Should we animate when the state changes? Defaults to true, but false while
+ // throbbing.
+ bool animate_on_state_change_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomButton);
+};
+
+} // namespace views
+
+#endif // CHROME_VIEWS_CONTROLS_BUTTON_CUSTOM_BUTTON_H_
diff --git a/chrome/views/controls/button/image_button.cc b/chrome/views/controls/button/image_button.cc
new file mode 100644
index 0000000..235cc83
--- /dev/null
+++ b/chrome/views/controls/button/image_button.cc
@@ -0,0 +1,157 @@
+// 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/controls/button/image_button.h"
+
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/throb_animation.h"
+#include "grit/generated_resources.h"
+#include "skia/ext/image_operations.h"
+
+namespace views {
+
+static const int kDefaultWidth = 16; // Default button width if no theme.
+static const int kDefaultHeight = 14; // Default button height if no theme.
+
+////////////////////////////////////////////////////////////////////////////////
+// ImageButton, public:
+
+ImageButton::ImageButton(ButtonListener* listener)
+ : CustomButton(listener),
+ h_alignment_(ALIGN_LEFT),
+ v_alignment_(ALIGN_TOP) {
+ // By default, we request that the ChromeCanvas passed to our View::Paint()
+ // implementation is flipped horizontally so that the button's bitmaps are
+ // mirrored when the UI directionality is right-to-left.
+ EnableCanvasFlippingForRTLUI(true);
+}
+
+ImageButton::~ImageButton() {
+}
+
+void ImageButton::SetImage(ButtonState aState, SkBitmap* anImage) {
+ images_[aState] = anImage ? *anImage : SkBitmap();
+}
+
+void ImageButton::SetImageAlignment(HorizontalAlignment h_align,
+ VerticalAlignment v_align) {
+ h_alignment_ = h_align;
+ v_alignment_ = v_align;
+ SchedulePaint();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ImageButton, View overrides:
+
+gfx::Size ImageButton::GetPreferredSize() {
+ if (!images_[BS_NORMAL].isNull())
+ return gfx::Size(images_[BS_NORMAL].width(), images_[BS_NORMAL].height());
+ return gfx::Size(kDefaultWidth, kDefaultHeight);
+}
+
+void ImageButton::Paint(ChromeCanvas* canvas) {
+ // Call the base class first to paint any background/borders.
+ View::Paint(canvas);
+
+ SkBitmap img = GetImageToPaint();
+
+ if (!img.isNull()) {
+ int x = 0, y = 0;
+
+ if (h_alignment_ == ALIGN_CENTER)
+ x = (width() - img.width()) / 2;
+ else if (h_alignment_ == ALIGN_RIGHT)
+ x = width() - img.width();
+
+ if (v_alignment_ == ALIGN_MIDDLE)
+ y = (height() - img.height()) / 2;
+ else if (v_alignment_ == ALIGN_BOTTOM)
+ y = height() - img.height();
+
+ canvas->DrawBitmapInt(img, x, y);
+ }
+ PaintFocusBorder(canvas);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ImageButton, protected:
+
+SkBitmap ImageButton::GetImageToPaint() {
+ SkBitmap img;
+
+ if (!images_[BS_HOT].isNull() && hover_animation_->IsAnimating()) {
+ img = skia::ImageOperations::CreateBlendedBitmap(images_[BS_NORMAL],
+ images_[BS_HOT], hover_animation_->GetCurrentValue());
+ } else {
+ img = images_[state_];
+ }
+
+ return !img.isNull() ? img : images_[BS_NORMAL];
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToggleImageButton, public:
+
+ToggleImageButton::ToggleImageButton(ButtonListener* listener)
+ : ImageButton(listener),
+ toggled_(false) {
+}
+
+ToggleImageButton::~ToggleImageButton() {
+}
+
+void ToggleImageButton::SetToggled(bool toggled) {
+ if (toggled == toggled_)
+ return;
+
+ for (int i = 0; i < BS_COUNT; ++i) {
+ SkBitmap temp = images_[i];
+ images_[i] = alternate_images_[i];
+ alternate_images_[i] = temp;
+ }
+ toggled_ = toggled;
+ SchedulePaint();
+}
+
+void ToggleImageButton::SetToggledImage(ButtonState state, SkBitmap* image) {
+ if (toggled_) {
+ images_[state] = image ? *image : SkBitmap();
+ if (state_ == state)
+ SchedulePaint();
+ } else {
+ alternate_images_[state] = image ? *image : SkBitmap();
+ }
+}
+
+void ToggleImageButton::SetToggledTooltipText(const std::wstring& tooltip) {
+ toggled_tooltip_text_.assign(tooltip);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToggleImageButton, ImageButton overrides:
+
+void ToggleImageButton::SetImage(ButtonState state, SkBitmap* image) {
+ if (toggled_) {
+ alternate_images_[state] = image ? *image : SkBitmap();
+ } else {
+ images_[state] = image ? *image : SkBitmap();
+ if (state_ == state)
+ SchedulePaint();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToggleImageButton, View overrides:
+
+bool ToggleImageButton::GetTooltipText(int x, int y, std::wstring* tooltip) {
+ if (!toggled_ || toggled_tooltip_text_.empty())
+ return Button::GetTooltipText(x, y, tooltip);
+
+ *tooltip = toggled_tooltip_text_;
+ return true;
+}
+
+} // namespace views
diff --git a/chrome/views/controls/button/image_button.h b/chrome/views/controls/button/image_button.h
new file mode 100644
index 0000000..6d304ba
--- /dev/null
+++ b/chrome/views/controls/button/image_button.h
@@ -0,0 +1,101 @@
+// 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.
+
+#ifndef CHROME_VIEWS_CONTROLS_BUTTON_IMAGE_BUTTON_H_
+#define CHROME_VIEWS_CONTROLS_BUTTON_IMAGE_BUTTON_H_
+
+#include "chrome/views/controls/button/custom_button.h"
+#include "skia/include/SkBitmap.h"
+
+namespace views {
+
+// An image button.
+class ImageButton : public CustomButton {
+ public:
+ explicit ImageButton(ButtonListener* listener);
+ virtual ~ImageButton();
+
+ // Set the image the button should use for the provided state.
+ virtual void SetImage(ButtonState aState, SkBitmap* anImage);
+
+ enum HorizontalAlignment { ALIGN_LEFT = 0,
+ ALIGN_CENTER,
+ ALIGN_RIGHT, };
+
+ enum VerticalAlignment { ALIGN_TOP = 0,
+ ALIGN_MIDDLE,
+ ALIGN_BOTTOM };
+
+ // Sets how the image is laid out within the button's bounds.
+ void SetImageAlignment(HorizontalAlignment h_align,
+ VerticalAlignment v_align);
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Paint(ChromeCanvas* canvas);
+
+ protected:
+ // Returns the image to paint. This is invoked from paint and returns a value
+ // from images.
+ virtual SkBitmap GetImageToPaint();
+
+ // The images used to render the different states of this button.
+ SkBitmap images_[BS_COUNT];
+
+ private:
+ // Image alignment.
+ HorizontalAlignment h_alignment_;
+ VerticalAlignment v_alignment_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageButton);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ToggleImageButton
+//
+// A toggle-able ImageButton. It swaps out its graphics when toggled.
+//
+////////////////////////////////////////////////////////////////////////////////
+class ToggleImageButton : public ImageButton {
+ public:
+ ToggleImageButton(ButtonListener* listener);
+ virtual ~ToggleImageButton();
+
+ // Change the toggled state.
+ void SetToggled(bool toggled);
+
+ // Like Button::SetImage(), but to set the graphics used for the
+ // "has been toggled" state. Must be called for each button state
+ // before the button is toggled.
+ void SetToggledImage(ButtonState state, SkBitmap* image);
+
+ // Set the tooltip text displayed when the button is toggled.
+ void SetToggledTooltipText(const std::wstring& tooltip);
+
+ // Overridden from ImageButton:
+ virtual void SetImage(ButtonState aState, SkBitmap* anImage);
+
+ // Overridden from View:
+ virtual bool GetTooltipText(int x, int y, std::wstring* tooltip);
+
+ private:
+ // The parent class's images_ member is used for the current images,
+ // and this array is used to hold the alternative images.
+ // We swap between the two when toggling.
+ SkBitmap alternate_images_[BS_COUNT];
+
+ // True if the button is currently toggled.
+ bool toggled_;
+
+ // The parent class's tooltip_text_ is displayed when not toggled, and
+ // this one is shown when toggled.
+ std::wstring toggled_tooltip_text_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ToggleImageButton);
+};
+
+} // namespace views
+
+#endif // CHROME_VIEWS_CONTROLS_BUTTON_IMAGE_BUTTON_H_
diff --git a/chrome/views/controls/button/menu_button.cc b/chrome/views/controls/button/menu_button.cc
new file mode 100644
index 0000000..0bcd2ae
--- /dev/null
+++ b/chrome/views/controls/button/menu_button.cc
@@ -0,0 +1,254 @@
+// 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/controls/button/menu_button.h"
+
+#include "chrome/common/drag_drop_types.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/win_util.h"
+#include "chrome/views/controls/button/button.h"
+#include "chrome/views/controls/menu/view_menu_delegate.h"
+#include "chrome/views/event.h"
+#include "chrome/views/widget/root_view.h"
+#include "chrome/views/widget/widget.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace views {
+
+// The amount of time, in milliseconds, we wait before allowing another mouse
+// pressed event to show the menu.
+static const int64 kMinimumTimeBetweenButtonClicks = 100;
+
+// The down arrow used to differentiate the menu button from normal
+// text buttons.
+static const SkBitmap* kMenuMarker = NULL;
+
+// How much padding to put on the left and right of the menu marker.
+static const int kMenuMarkerPaddingLeft = 3;
+static const int kMenuMarkerPaddingRight = -1;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton - constructors, destructors, initialization
+//
+////////////////////////////////////////////////////////////////////////////////
+
+MenuButton::MenuButton(ButtonListener* listener,
+ const std::wstring& text,
+ ViewMenuDelegate* menu_delegate,
+ bool show_menu_marker)
+ : TextButton(listener, text),
+ menu_visible_(false),
+ menu_closed_time_(),
+ menu_delegate_(menu_delegate),
+ show_menu_marker_(show_menu_marker) {
+ if (kMenuMarker == NULL) {
+ kMenuMarker = ResourceBundle::GetSharedInstance()
+ .GetBitmapNamed(IDR_MENU_DROPARROW);
+ }
+ set_alignment(TextButton::ALIGN_LEFT);
+}
+
+MenuButton::~MenuButton() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton - Public APIs
+//
+////////////////////////////////////////////////////////////////////////////////
+
+gfx::Size MenuButton::GetPreferredSize() {
+ gfx::Size prefsize = TextButton::GetPreferredSize();
+ if (show_menu_marker_) {
+ prefsize.Enlarge(kMenuMarker->width() + kMenuMarkerPaddingLeft +
+ kMenuMarkerPaddingRight,
+ 0);
+ }
+ return prefsize;
+}
+
+void MenuButton::Paint(ChromeCanvas* canvas, bool for_drag) {
+ TextButton::Paint(canvas, for_drag);
+
+ if (show_menu_marker_) {
+ gfx::Insets insets = GetInsets();
+
+ // We can not use the views' mirroring infrastructure for mirroring a
+ // MenuButton control (see TextButton::Paint() for a detailed explanation
+ // regarding why we can not flip the canvas). Therefore, we need to
+ // manually mirror the position of the down arrow.
+ gfx::Rect arrow_bounds(width() - insets.right() -
+ kMenuMarker->width() - kMenuMarkerPaddingRight,
+ height() / 2 - kMenuMarker->height() / 2,
+ kMenuMarker->width(),
+ kMenuMarker->height());
+ arrow_bounds.set_x(MirroredLeftPointForRect(arrow_bounds));
+ canvas->DrawBitmapInt(*kMenuMarker, arrow_bounds.x(), arrow_bounds.y());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton - Events
+//
+////////////////////////////////////////////////////////////////////////////////
+
+int MenuButton::GetMaximumScreenXCoordinate() {
+ Widget* widget = GetWidget();
+
+ if (!widget) {
+ NOTREACHED();
+ return 0;
+ }
+
+ HWND hwnd = widget->GetNativeView();
+ CRect t;
+ ::GetWindowRect(hwnd, &t);
+
+ gfx::Rect r(t);
+ gfx::Rect monitor_rect = win_util::GetMonitorBoundsForRect(r);
+ return monitor_rect.x() + monitor_rect.width() - 1;
+}
+
+bool MenuButton::Activate() {
+ SetState(BS_PUSHED);
+ // We need to synchronously paint here because subsequently we enter a
+ // menu modal loop which will stop this window from updating and
+ // receiving the paint message that should be spawned by SetState until
+ // after the menu closes.
+ PaintNow();
+ if (menu_delegate_) {
+ gfx::Rect lb = GetLocalBounds(true);
+
+ // The position of the menu depends on whether or not the locale is
+ // right-to-left.
+ gfx::Point menu_position(lb.right(), lb.bottom());
+ if (UILayoutIsRightToLeft())
+ menu_position.set_x(lb.x());
+
+ View::ConvertPointToScreen(this, &menu_position);
+ if (UILayoutIsRightToLeft())
+ menu_position.Offset(2, -4);
+ else
+ menu_position.Offset(-2, -4);
+
+ int max_x_coordinate = GetMaximumScreenXCoordinate();
+ if (max_x_coordinate && max_x_coordinate <= menu_position.x())
+ menu_position.set_x(max_x_coordinate - 1);
+
+ // We're about to show the menu from a mouse press. By showing from the
+ // mouse press event we block RootView in mouse dispatching. This also
+ // appears to cause RootView to get a mouse pressed BEFORE the mouse
+ // release is seen, which means RootView sends us another mouse press no
+ // matter where the user pressed. To force RootView to recalculate the
+ // mouse target during the mouse press we explicitly set the mouse handler
+ // to NULL.
+ GetRootView()->SetMouseHandler(NULL);
+
+ menu_visible_ = true;
+ menu_delegate_->RunMenu(this, menu_position.ToPOINT(),
+ GetWidget()->GetNativeView());
+ menu_visible_ = false;
+ menu_closed_time_ = Time::Now();
+
+ // Now that the menu has closed, we need to manually reset state to
+ // "normal" since the menu modal loop will have prevented normal
+ // mouse move messages from getting to this View. We set "normal"
+ // and not "hot" because the likelihood is that the mouse is now
+ // somewhere else (user clicked elsewhere on screen to close the menu
+ // or selected an item) and we will inevitably refresh the hot state
+ // in the event the mouse _is_ over the view.
+ SetState(BS_NORMAL);
+
+ // We must return false here so that the RootView does not get stuck
+ // sending all mouse pressed events to us instead of the appropriate
+ // target.
+ return false;
+ }
+ return true;
+}
+
+bool MenuButton::OnMousePressed(const MouseEvent& e) {
+ RequestFocus();
+ if (state() != BS_DISABLED) {
+ // If we're draggable (GetDragOperations returns a non-zero value), then
+ // don't pop on press, instead wait for release.
+ if (e.IsOnlyLeftMouseButton() && HitTest(e.location()) &&
+ GetDragOperations(e.x(), e.y()) == DragDropTypes::DRAG_NONE) {
+ TimeDelta delta = Time::Now() - menu_closed_time_;
+ int64 delta_in_milliseconds = delta.InMilliseconds();
+ if (delta_in_milliseconds > kMinimumTimeBetweenButtonClicks) {
+ return Activate();
+ }
+ }
+ }
+ return true;
+}
+
+void MenuButton::OnMouseReleased(const MouseEvent& e,
+ bool canceled) {
+ if (GetDragOperations(e.x(), e.y()) != DragDropTypes::DRAG_NONE &&
+ state() != BS_DISABLED && !canceled && !InDrag() &&
+ e.IsOnlyLeftMouseButton() && HitTest(e.location())) {
+ Activate();
+ } else {
+ TextButton::OnMouseReleased(e, canceled);
+ }
+}
+
+// When the space bar or the enter key is pressed we need to show the menu.
+bool MenuButton::OnKeyReleased(const KeyEvent& e) {
+ if ((e.GetCharacter() == VK_SPACE) || (e.GetCharacter() == VK_RETURN)) {
+ return Activate();
+ }
+ return true;
+}
+
+// The reason we override View::OnMouseExited is because we get this event when
+// we display the menu. If we don't override this method then
+// BaseButton::OnMouseExited will get the event and will set the button's state
+// to BS_NORMAL instead of keeping the state BM_PUSHED. This, in turn, will
+// cause the button to appear depressed while the menu is displayed.
+void MenuButton::OnMouseExited(const MouseEvent& event) {
+ if ((state_ != BS_DISABLED) && (!menu_visible_) && (!InDrag())) {
+ SetState(BS_NORMAL);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton - accessibility
+//
+////////////////////////////////////////////////////////////////////////////////
+
+bool MenuButton::GetAccessibleDefaultAction(std::wstring* action) {
+ DCHECK(action);
+
+ action->assign(l10n_util::GetString(IDS_ACCACTION_PRESS));
+ return true;
+}
+
+bool MenuButton::GetAccessibleRole(VARIANT* role) {
+ DCHECK(role);
+
+ role->vt = VT_I4;
+ role->lVal = ROLE_SYSTEM_BUTTONDROPDOWN;
+ return true;
+}
+
+bool MenuButton::GetAccessibleState(VARIANT* state) {
+ DCHECK(state);
+
+ state->lVal |= STATE_SYSTEM_HASPOPUP;
+ return true;
+}
+
+} // namespace views
diff --git a/chrome/views/controls/button/menu_button.h b/chrome/views/controls/button/menu_button.h
new file mode 100644
index 0000000..458853e
--- /dev/null
+++ b/chrome/views/controls/button/menu_button.h
@@ -0,0 +1,98 @@
+// 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.
+
+#ifndef CHROME_VIEWS_CONTROLS_BUTTON_MENU_BUTTON_H_
+#define CHROME_VIEWS_CONTROLS_BUTTON_MENU_BUTTON_H_
+
+#include <windows.h>
+
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/views/background.h"
+#include "chrome/views/controls/button/text_button.h"
+#include "base/time.h"
+
+namespace views {
+
+class MouseEvent;
+class ViewMenuDelegate;
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MenuButton
+//
+// A button that shows a menu when the left mouse button is pushed
+//
+////////////////////////////////////////////////////////////////////////////////
+class MenuButton : public TextButton {
+ public:
+ //
+ // Create a Button
+ MenuButton(ButtonListener* listener,
+ const std::wstring& text,
+ ViewMenuDelegate* menu_delegate,
+ bool show_menu_marker);
+ virtual ~MenuButton();
+
+ // Activate the button (called when the button is pressed).
+ virtual bool Activate();
+
+ // Overridden to take into account the potential use of a drop marker.
+ virtual gfx::Size GetPreferredSize();
+ virtual void Paint(ChromeCanvas* canvas, bool for_drag);
+
+ // These methods are overriden to implement a simple push button
+ // behavior
+ virtual bool OnMousePressed(const MouseEvent& e);
+ void OnMouseReleased(const MouseEvent& e, bool canceled);
+ virtual bool OnKeyReleased(const KeyEvent& e);
+ virtual void OnMouseExited(const MouseEvent& event);
+
+ // Returns the MSAA default action of the current view. The string returned
+ // describes the default action that will occur when executing
+ // IAccessible::DoDefaultAction.
+ bool GetAccessibleDefaultAction(std::wstring* action);
+
+ // Returns the MSAA role of the current view. The role is what assistive
+ // technologies (ATs) use to determine what behavior to expect from a given
+ // control.
+ bool GetAccessibleRole(VARIANT* role);
+
+ // Returns the MSAA state of the current view. Sets the input VARIANT
+ // appropriately, and returns true if a change was performed successfully.
+ // Overriden from View.
+ virtual bool GetAccessibleState(VARIANT* state);
+
+ protected:
+ // true if the menu is currently visible.
+ bool menu_visible_;
+
+ private:
+
+ // Compute the maximum X coordinate for the current screen. MenuButtons
+ // use this to make sure a menu is never shown off screen.
+ int GetMaximumScreenXCoordinate();
+
+ DISALLOW_EVIL_CONSTRUCTORS(MenuButton);
+
+ // We use a time object in order to keep track of when the menu was closed.
+ // The time is used for simulating menu behavior for the menu button; that
+ // is, if the menu is shown and the button is pressed, we need to close the
+ // menu. There is no clean way to get the second click event because the
+ // menu is displayed using a modal loop and, unlike regular menus in Windows,
+ // the button is not part of the displayed menu.
+ base::Time menu_closed_time_;
+
+ // The associated menu's resource identifier.
+ ViewMenuDelegate* menu_delegate_;
+
+ // Whether or not we're showing a drop marker.
+ bool show_menu_marker_;
+
+ friend class TextButtonBackground;
+};
+
+} // namespace views
+
+#endif // CHROME_VIEWS_CONTROLS_BUTTON_MENU_BUTTON_H_
diff --git a/chrome/views/controls/button/native_button.cc b/chrome/views/controls/button/native_button.cc
new file mode 100644
index 0000000..4da39407
--- /dev/null
+++ b/chrome/views/controls/button/native_button.cc
@@ -0,0 +1,212 @@
+// 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/controls/button/native_button.h"
+
+#include "base/logging.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/background.h"
+
+namespace views {
+
+const char NativeButton::kViewClassName[] = "chrome/views/NativeButton";
+
+NativeButton::NativeButton(const std::wstring& label)
+ : enforce_dlu_min_size_(true) {
+ Init(label, false);
+}
+
+NativeButton::NativeButton(const std::wstring& label, bool is_default)
+ : enforce_dlu_min_size_(true) {
+ Init(label, is_default);
+}
+
+NativeButton::~NativeButton() {
+}
+
+std::string NativeButton::GetClassName() const {
+ return kViewClassName;
+}
+
+void NativeButton::SetListener(Listener *l) {
+ listener_ = l;
+}
+
+void NativeButton::SetPadding(CSize size) {
+ padding_ = size;
+}
+
+gfx::Size NativeButton::GetPreferredSize() {
+ HWND hwnd = GetNativeControlHWND();
+ if (hwnd) {
+ SIZE sz = {0, 0};
+ ::SendMessage(hwnd,
+ BCM_GETIDEALSIZE,
+ 0,
+ reinterpret_cast<LPARAM>(&sz));
+ sz.cx += 2 * padding_.cx;
+ sz.cy += 2 * padding_.cy;
+
+ if (enforce_dlu_min_size_) {
+ if (min_dlu_size_.width()) {
+ sz.cx =
+ std::max(static_cast<int>(sz.cx),
+ font_.horizontal_dlus_to_pixels(min_dlu_size_.width()));
+ }
+ if (min_dlu_size_.height())
+ sz.cy = std::max(static_cast<int>(sz.cy),
+ font_.vertical_dlus_to_pixels(min_dlu_size_.height()));
+ }
+ return gfx::Size(sz.cx, sz.cy);
+ }
+ return gfx::Size();
+}
+
+void NativeButton::SetLabel(const std::wstring& l) {
+ // Even though we create a flipped HWND for a native button when the locale
+ // is right-to-left, Windows does not render text for the button using a
+ // right-to-left context (perhaps because the parent HWND is not flipped).
+ // The result is that RTL strings containing punctuation marks are not
+ // displayed properly. For example, the string "...ABC" (where A, B and C are
+ // Hebrew characters) is displayed as "ABC..." which is incorrect.
+ //
+ // In order to overcome this problem, we mark the localized Hebrew strings as
+ // RTL strings explicitly (using the appropriate Unicode formatting) so that
+ // Windows displays the text correctly regardless of the HWND hierarchy.
+ std::wstring localized_label;
+ if (l10n_util::AdjustStringForLocaleDirection(l, &localized_label))
+ label_.assign(localized_label);
+ else
+ label_.assign(l);
+
+ SetAccessibleName(l);
+ UpdateNativeButton();
+}
+
+const std::wstring NativeButton::GetLabel() const {
+ return label_;
+}
+
+HWND NativeButton::CreateNativeControl(HWND parent_container) {
+ DWORD flags = WS_CHILD | BS_PUSHBUTTON;
+ if (is_default_)
+ flags |= BS_DEFPUSHBUTTON;
+ HWND r = ::CreateWindowEx(GetAdditionalExStyle(), L"BUTTON", L"", flags, 0, 0,
+ width(), height(), parent_container, NULL,
+ NULL, NULL);
+ SendMessage(r, WM_SETFONT, reinterpret_cast<WPARAM>(font_.hfont()), FALSE);
+ ConfigureNativeButton(r);
+ return r;
+}
+
+LRESULT NativeButton::OnNotify(int w_param, LPNMHDR l_param) {
+ return 0;
+}
+
+LRESULT NativeButton::OnCommand(UINT code, int id, HWND source) {
+ if (code == BN_CLICKED)
+ Clicked();
+ return 0;
+}
+
+void NativeButton::UpdateNativeButton() {
+ HWND hwnd = GetNativeControlHWND();
+ if (hwnd)
+ ConfigureNativeButton(hwnd);
+}
+
+void NativeButton::ConfigureNativeButton(HWND hwnd) {
+ ::SetWindowText(hwnd, label_.c_str());
+}
+
+void NativeButton::SetDefaultButton(bool is_default_button) {
+ if (is_default_button == is_default_)
+ return;
+ is_default_ = is_default_button;
+ if (is_default_button)
+ AddAccelerator(Accelerator(VK_RETURN, false, false, false));
+ else
+ RemoveAccelerator(Accelerator(VK_RETURN, false, false, false));
+ SendMessage(GetNativeControlHWND(), BM_SETSTYLE,
+ is_default_button ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON, true);
+}
+
+bool NativeButton::AcceleratorPressed(const Accelerator& accelerator) {
+ if (enabled_) {
+ Clicked();
+ return true;
+ }
+ return false;
+}
+
+bool NativeButton::GetAccessibleRole(VARIANT* role) {
+ DCHECK(role);
+
+ role->vt = VT_I4;
+ role->lVal = ROLE_SYSTEM_PUSHBUTTON;
+ return true;
+}
+
+bool NativeButton::GetAccessibleName(std::wstring* name) {
+ if (!accessible_name_.empty()) {
+ *name = accessible_name_;
+ return true;
+ }
+ return false;
+}
+
+void NativeButton::SetAccessibleName(const std::wstring& name) {
+ accessible_name_.assign(name);
+}
+
+void NativeButton::Init(const std::wstring& label, bool is_default) {
+ // Marking the string as an RTL string if the locale is RTL. Refer to
+ // the comments in NativeButton::SetLabel for more details.
+ std::wstring localized_label;
+ if (l10n_util::AdjustStringForLocaleDirection(label, &localized_label))
+ label_.assign(localized_label);
+ else
+ label_.assign(label);
+
+ l10n_util::AdjustStringForLocaleDirection(label, &label_);
+ listener_ = NULL;
+ SetAccessibleName(label);
+ // The padding of 8 is a bit arbitrary, there appears to be no way to
+ // get a recommended padding, and this value varies greatly among windows
+ // dialogs.
+ //
+ // The min size in DLUs comes from
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch14e.asp
+ padding_ = CSize(8, 0);
+ is_default_ = is_default;
+ min_dlu_size_.SetSize(50, 14);
+ SetFocusable(true);
+ if (is_default)
+ AddAccelerator(Accelerator(VK_RETURN, false, false, false));
+}
+
+void NativeButton::Clicked() {
+ DCHECK(enabled_);
+ // Give the focus to the button.
+ RequestFocus();
+
+ if (listener_)
+ listener_->ButtonPressed(this);
+}
+
+bool NativeButton::NotifyOnKeyDown() const {
+ return true;
+}
+
+bool NativeButton::OnKeyDown(int virtual_key_code) {
+ if (virtual_key_code == VK_RETURN) {
+ Clicked();
+ return true;
+ }
+ return false;
+}
+
+} // namespace views
diff --git a/chrome/views/controls/button/native_button.h b/chrome/views/controls/button/native_button.h
new file mode 100644
index 0000000..07d7340
--- /dev/null
+++ b/chrome/views/controls/button/native_button.h
@@ -0,0 +1,146 @@
+// 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.
+
+#ifndef CHROME_VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_H_
+#define CHROME_VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_H_
+
+#include <string>
+
+#include "base/gfx/size.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/views/controls/native_control.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NativeButton is a wrapper for a windows native button
+// TODO(beng): (Cleanup) this should derive also from some button-like base to
+// get all the listenery-stuff for free and to work with
+// AddManagedButton.
+//
+////////////////////////////////////////////////////////////////////////////////
+class NativeButton : public NativeControl {
+ public:
+ explicit NativeButton(const std::wstring& label);
+ NativeButton(const std::wstring& label, bool is_default);
+ virtual ~NativeButton();
+
+ virtual gfx::Size GetPreferredSize();
+
+ void SetLabel(const std::wstring& l);
+ const std::wstring GetLabel() const;
+
+ std::string GetClassName() const;
+
+ class Listener {
+ public:
+ //
+ // This is invoked once the button is released.
+ virtual void ButtonPressed(NativeButton* sender) = 0;
+ };
+
+ //
+ // The the listener, the object that receives a notification when this
+ // button is pressed.
+ void SetListener(Listener* listener);
+
+ // Set the font used by this button.
+ void SetFont(const ChromeFont& font) { font_ = font; }
+
+ // Adds some internal padding to the button. The |size| specified is applied
+ // on both sides of the button for each directions
+ void SetPadding(CSize size);
+
+ // Sets/unsets this button as the default button. The default button is the
+ // one triggered when enter is pressed. It is displayed with a blue border.
+ void SetDefaultButton(bool is_default_button);
+ bool IsDefaultButton() const { return is_default_; }
+
+ virtual LRESULT OnNotify(int w_param, LPNMHDR l_param);
+ virtual LRESULT OnCommand(UINT code, int id, HWND source);
+
+ // Invoked when the accelerator associated with the button is pressed.
+ virtual bool AcceleratorPressed(const Accelerator& accelerator);
+
+ // Sets the minimum size of the button from the specified size (in dialog
+ // units). If the width/height is non-zero, the preferred size of the button
+ // is max(preferred size of the content + padding, dlus converted to pixels).
+ //
+ // The default is 50, 14.
+ void SetMinSizeFromDLUs(const gfx::Size& dlu_size) {
+ min_dlu_size_ = dlu_size;
+ }
+
+ // Returns the MSAA role of the current view. The role is what assistive
+ // technologies (ATs) use to determine what behavior to expect from a given
+ // control.
+ bool GetAccessibleRole(VARIANT* role);
+
+ // Returns a brief, identifying string, containing a unique, readable name.
+ bool GetAccessibleName(std::wstring* name);
+
+ // Assigns an accessible string name.
+ void SetAccessibleName(const std::wstring& name);
+
+ // Sets whether the min size of this button should follow the Windows
+ // guidelines. Default is true. Set this to false if you want slim buttons.
+ void set_enforce_dlu_min_size(bool value) { enforce_dlu_min_size_ = value; }
+
+ // The view class name.
+ static const char kViewClassName[];
+
+ protected:
+
+ virtual HWND CreateNativeControl(HWND parent_container);
+
+ // Sub-classes can call this method to cause the native button to reflect
+ // the current state
+ virtual void UpdateNativeButton();
+
+ // Sub-classes must override this method to properly configure the native
+ // button given the current state
+ virtual void ConfigureNativeButton(HWND hwnd);
+
+ // Overridden from NativeControl so we can activate the button when Enter is
+ // pressed.
+ virtual bool NotifyOnKeyDown() const;
+ virtual bool OnKeyDown(int virtual_key_code);
+
+ private:
+ NativeButton() {}
+
+ // Initializes the button. If |is_default| is true, this appears like the
+ // "default" button, and will register Enter as its keyboard accelerator.
+ void Init(const std::wstring& label, bool is_default);
+
+ void Clicked();
+
+ // Whether the button preferred size should follow the Microsoft layout
+ // guidelines. Default is true.
+ bool enforce_dlu_min_size_;
+
+ std::wstring label_;
+ ChromeFont font_;
+ Listener* listener_;
+
+ CSize padding_;
+
+ // True if the button should be rendered to appear like the "default" button
+ // in the containing dialog box. Default buttons register Enter as their
+ // accelerator.
+ bool is_default_;
+
+ // Minimum size, in dlus (see SetMinSizeFromDLUs).
+ gfx::Size min_dlu_size_;
+
+ // Storage of strings needed for accessibility.
+ std::wstring accessible_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeButton);
+};
+
+} // namespace views
+
+#endif // CHROME_VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_H_
diff --git a/chrome/views/controls/button/radio_button.cc b/chrome/views/controls/button/radio_button.cc
new file mode 100644
index 0000000..758c747
--- /dev/null
+++ b/chrome/views/controls/button/radio_button.cc
@@ -0,0 +1,123 @@
+// 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/controls/button/radio_button.h"
+
+#include "chrome/views/controls/label.h"
+#include "chrome/views/controls/hwnd_view.h"
+#include "chrome/views/widget/root_view.h"
+
+namespace views {
+
+// FIXME(ACW) there got be a better way to find out the check box sizes
+static int kRadioWidth = 13;
+static int kRadioHeight = 13;
+static int kRadioToLabel = 4;
+
+const char RadioButton::kViewClassName[] = "chrome/views/RadioButton";
+
+RadioButton::RadioButton(const std::wstring& label, int group_id)
+ : CheckBox(label) {
+ SetGroup(group_id);
+}
+
+RadioButton::~RadioButton() {
+}
+
+HWND RadioButton::CreateNativeControl(HWND parent_container) {
+ HWND r = ::CreateWindowEx(GetAdditionalExStyle(),
+ L"BUTTON",
+ L"",
+ WS_CHILD | BS_RADIOBUTTON ,
+ 0, 0, width(), height(),
+ parent_container, NULL, NULL, NULL);
+ ConfigureNativeButton(r);
+ return r;
+}
+
+LRESULT RadioButton::OnCommand(UINT code, int id, HWND source) {
+ // Radio buttons can't be toggled off once selected except by clicking on
+ // another radio button within the same group, so we override this from
+ // CheckBox to prevent this from happening.
+ if (code == BN_CLICKED) {
+ RequestFocus();
+ if (!IsSelected()) {
+ SetIsSelected(true);
+ return NativeButton::OnCommand(code, id, source);
+ }
+ }
+ return 0;
+}
+
+// static
+int RadioButton::GetTextIndent() {
+ return kRadioWidth + kRadioToLabel + kFocusPaddingHorizontal;
+}
+
+
+std::string RadioButton::GetClassName() const {
+ return kViewClassName;
+}
+
+gfx::Size RadioButton::GetPreferredSize() {
+ gfx::Size prefsize = label_->GetPreferredSize();
+ prefsize.set_height(std::max(prefsize.height() + kFocusPaddingVertical * 2,
+ kRadioHeight));
+ prefsize.Enlarge(kRadioToLabel + kRadioWidth + kFocusPaddingHorizontal * 2,
+ 0);
+ return prefsize;
+}
+
+void RadioButton::Layout() {
+ int label_x = GetTextIndent();
+ label_->SetBounds(label_x, 0, width() - label_x, height());
+ if (hwnd_view_) {
+ int first_line_height = label_->GetFont().height();
+ hwnd_view_->SetBounds(0, ((first_line_height - kRadioHeight) / 2) + 1,
+ kRadioWidth, kRadioHeight);
+ hwnd_view_->UpdateHWNDBounds();
+ }
+}
+
+void RadioButton::SetIsSelected(bool f) {
+ if (f != IsSelected()) {
+ if (f) {
+ // We can't just get the root view here because sometimes the radio
+ // button isn't attached to a root view (e.g., if it's part of a tab page
+ // that is currently not active).
+ View* container = GetParent();
+ while (container && container->GetParent())
+ container = container->GetParent();
+ if (container) {
+ std::vector<View*> other;
+ container->GetViewsWithGroup(GetGroup(), &other);
+ std::vector<View*>::iterator i;
+ for (i = other.begin(); i != other.end(); ++i) {
+ if (*i != this) {
+ RadioButton* peer = static_cast<RadioButton*>(*i);
+ peer->SetIsSelected(false);
+ }
+ }
+ }
+ }
+ CheckBox::SetIsSelected(f);
+ }
+}
+
+View* RadioButton::GetSelectedViewForGroup(int group_id) {
+ std::vector<View*> views;
+ GetRootView()->GetViewsWithGroup(group_id, &views);
+ if (views.empty())
+ return NULL;
+
+ for (std::vector<View*>::const_iterator iter = views.begin();
+ iter != views.end(); ++iter) {
+ RadioButton* radio_button = static_cast<RadioButton*>(*iter);
+ if (radio_button->IsSelected())
+ return radio_button;
+ }
+ return NULL;
+}
+
+} // namespace views
diff --git a/chrome/views/controls/button/radio_button.h b/chrome/views/controls/button/radio_button.h
new file mode 100644
index 0000000..dbae232
--- /dev/null
+++ b/chrome/views/controls/button/radio_button.h
@@ -0,0 +1,60 @@
+// 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.
+
+#ifndef CHROME_VIEWS_CONTROLS_BUTTON_RADIO_BUTTON_H_
+#define CHROME_VIEWS_CONTROLS_BUTTON_RADIO_BUTTON_H_
+
+#include "chrome/views/controls/button/checkbox.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// A wrapper for windows's native radio button. Radio buttons can be mutually
+// exclusive with other radio buttons.
+//
+////////////////////////////////////////////////////////////////////////////////
+class RadioButton : public CheckBox {
+ public:
+ // The view class name.
+ static const char kViewClassName[];
+
+ // Create a radio button with the provided label and group id.
+ // The group id is used to identify all the other radio buttons which are in
+ // mutual exclusion with this radio button. Note: RadioButton assumes that
+ // all views with that group id are RadioButton. It is an error to give
+ // that group id to another view subclass which is not a radio button or
+ // a radio button subclass.
+ RadioButton(const std::wstring& label, int group_id);
+ virtual ~RadioButton();
+
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+
+ virtual std::string GetClassName() const;
+
+ // Overridden to properly perform mutual exclusion.
+ virtual void SetIsSelected(bool f);
+
+ virtual View* GetSelectedViewForGroup(int group_id);
+
+ // When focusing a RadioButton with Tab/Shift-Tab, only the selected button
+ // from the group should be accessible.
+ virtual bool IsGroupFocusTraversable() const { return false; }
+
+ protected:
+ virtual HWND CreateNativeControl(HWND parent_container);
+ virtual LRESULT OnCommand(UINT code, int id, HWND source);
+
+ private:
+ // Get the horizontal distance of the start of the text from the left of the
+ // control.
+ static int GetTextIndent();
+
+ DISALLOW_EVIL_CONSTRUCTORS(RadioButton);
+};
+
+} // namespace views
+
+#endif // CHROME_VIEWS_CONTROLS_BUTTON_RADIO_BUTTON_H_
diff --git a/chrome/views/controls/button/text_button.cc b/chrome/views/controls/button/text_button.cc
new file mode 100644
index 0000000..c5f1ae6
--- /dev/null
+++ b/chrome/views/controls/button/text_button.cc
@@ -0,0 +1,323 @@
+// 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/controls/button/text_button.h"
+
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/throb_animation.h"
+#include "chrome/common/win_util.h"
+#include "chrome/views/controls/button/button.h"
+#include "chrome/views/event.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+
+namespace views {
+
+// Padding between the icon and text.
+static const int kIconTextPadding = 5;
+
+// Preferred padding between text and edge
+static const int kPreferredPaddingHorizontal = 6;
+static const int kPreferredPaddingVertical = 5;
+
+static const SkColor kEnabledColor = SkColorSetRGB(6, 45, 117);
+static const SkColor kHighlightColor = SkColorSetARGB(200, 255, 255, 255);
+static const SkColor kDisabledColor = SkColorSetRGB(161, 161, 146);
+
+// How long the hover fade animation should last.
+static const int kHoverAnimationDurationMs = 170;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TextButtonBorder - constructors, destructors, initialization
+//
+////////////////////////////////////////////////////////////////////////////////
+
+TextButtonBorder::TextButtonBorder() {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ hot_set_.top_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_LEFT_H);
+ hot_set_.top = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_H);
+ hot_set_.top_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_RIGHT_H);
+ hot_set_.left = rb.GetBitmapNamed(IDR_TEXTBUTTON_LEFT_H);
+ hot_set_.center = rb.GetBitmapNamed(IDR_TEXTBUTTON_CENTER_H);
+ hot_set_.right = rb.GetBitmapNamed(IDR_TEXTBUTTON_RIGHT_H);
+ hot_set_.bottom_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_LEFT_H);
+ hot_set_.bottom = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_H);
+ hot_set_.bottom_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_RIGHT_H);
+
+ pushed_set_.top_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_LEFT_P);
+ pushed_set_.top = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_P);
+ pushed_set_.top_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_RIGHT_P);
+ pushed_set_.left = rb.GetBitmapNamed(IDR_TEXTBUTTON_LEFT_P);
+ pushed_set_.center = rb.GetBitmapNamed(IDR_TEXTBUTTON_CENTER_P);
+ pushed_set_.right = rb.GetBitmapNamed(IDR_TEXTBUTTON_RIGHT_P);
+ pushed_set_.bottom_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_LEFT_P);
+ pushed_set_.bottom = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_P);
+ pushed_set_.bottom_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_RIGHT_P);
+}
+
+TextButtonBorder::~TextButtonBorder() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TextButtonBackground - painting
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void TextButtonBorder::Paint(const View& view, ChromeCanvas* canvas) const {
+ const TextButton* mb = static_cast<const TextButton*>(&view);
+ int state = mb->state();
+
+ // TextButton takes care of deciding when to call Paint.
+ const MBBImageSet* set = &hot_set_;
+ if (state == TextButton::BS_PUSHED)
+ set = &pushed_set_;
+
+ if (set) {
+ gfx::Rect bounds = view.bounds();
+
+ // Draw the top left image
+ canvas->DrawBitmapInt(*set->top_left, 0, 0);
+
+ // Tile the top image
+ canvas->TileImageInt(
+ *set->top,
+ set->top_left->width(),
+ 0,
+ bounds.width() - set->top_right->width() - set->top_left->width(),
+ set->top->height());
+
+ // Draw the top right image
+ canvas->DrawBitmapInt(*set->top_right,
+ bounds.width() - set->top_right->width(), 0);
+
+ // Tile the left image
+ canvas->TileImageInt(
+ *set->left,
+ 0,
+ set->top_left->height(),
+ set->top_left->width(),
+ bounds.height() - set->top->height() - set->bottom_left->height());
+
+ // Tile the center image
+ canvas->TileImageInt(
+ *set->center,
+ set->left->width(),
+ set->top->height(),
+ bounds.width() - set->right->width() - set->left->width(),
+ bounds.height() - set->bottom->height() - set->top->height());
+
+ // Tile the right image
+ canvas->TileImageInt(
+ *set->right,
+ bounds.width() - set->right->width(),
+ set->top_right->height(),
+ bounds.width(),
+ bounds.height() - set->bottom_right->height() -
+ set->top_right->height());
+
+ // Draw the bottom left image
+ canvas->DrawBitmapInt(*set->bottom_left,
+ 0,
+ bounds.height() - set->bottom_left->height());
+
+ // Tile the bottom image
+ canvas->TileImageInt(
+ *set->bottom,
+ set->bottom_left->width(),
+ bounds.height() - set->bottom->height(),
+ bounds.width() - set->bottom_right->width() -
+ set->bottom_left->width(),
+ set->bottom->height());
+
+ // Draw the bottom right image
+ canvas->DrawBitmapInt(*set->bottom_right,
+ bounds.width() - set->bottom_right->width(),
+ bounds.height() - set->bottom_right->height());
+ } else {
+ // Do nothing
+ }
+}
+
+void TextButtonBorder::GetInsets(gfx::Insets* insets) const {
+ insets->Set(kPreferredPaddingVertical, kPreferredPaddingHorizontal,
+ kPreferredPaddingVertical, kPreferredPaddingHorizontal);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TextButton, public:
+
+TextButton::TextButton(ButtonListener* listener, const std::wstring& text)
+ : CustomButton(listener),
+ font_(ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::BaseFont)),
+ color_(kEnabledColor),
+ max_width_(0),
+ alignment_(ALIGN_LEFT) {
+ SetText(text);
+ set_border(new TextButtonBorder);
+ SetAnimationDuration(kHoverAnimationDurationMs);
+}
+
+TextButton::~TextButton() {
+}
+
+void TextButton::SetText(const std::wstring& text) {
+ text_ = text;
+ // Update our new current and max text size
+ text_size_.SetSize(font_.GetStringWidth(text_), font_.height());
+ max_text_size_.SetSize(std::max(max_text_size_.width(), text_size_.width()),
+ std::max(max_text_size_.height(),
+ text_size_.height()));
+}
+
+void TextButton::SetIcon(const SkBitmap& icon) {
+ icon_ = icon;
+}
+
+void TextButton::ClearMaxTextSize() {
+ max_text_size_ = text_size_;
+}
+
+void TextButton::Paint(ChromeCanvas* canvas, bool for_drag) {
+ if (!for_drag) {
+ PaintBackground(canvas);
+
+ if (hover_animation_->IsAnimating()) {
+ // Draw the hover bitmap into an offscreen buffer, then blend it
+ // back into the current canvas.
+ canvas->saveLayerAlpha(NULL,
+ static_cast<int>(hover_animation_->GetCurrentValue() * 255),
+ SkCanvas::kARGB_NoClipLayer_SaveFlag);
+ canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode);
+ PaintBorder(canvas);
+ canvas->restore();
+ } else if (state_ == BS_HOT || state_ == BS_PUSHED) {
+ PaintBorder(canvas);
+ }
+
+ PaintFocusBorder(canvas);
+ }
+
+ gfx::Insets insets = GetInsets();
+ int available_width = width() - insets.width();
+ int available_height = height() - insets.height();
+ // Use the actual text (not max) size to properly center the text.
+ int content_width = text_size_.width();
+ if (icon_.width() > 0) {
+ content_width += icon_.width();
+ if (!text_.empty())
+ content_width += kIconTextPadding;
+ }
+ // Place the icon along the left edge.
+ int icon_x;
+ if (alignment_ == ALIGN_LEFT) {
+ icon_x = insets.left();
+ } else if (alignment_ == ALIGN_RIGHT) {
+ icon_x = available_width - content_width;
+ } else {
+ icon_x =
+ std::max(0, (available_width - content_width) / 2) + insets.left();
+ }
+ int text_x = icon_x;
+ if (icon_.width() > 0)
+ text_x += icon_.width() + kIconTextPadding;
+ const int text_width = std::min(text_size_.width(),
+ width() - insets.right() - text_x);
+ int text_y = (available_height - text_size_.height()) / 2 + insets.top();
+
+ if (text_width > 0) {
+ // Because the text button can (at times) draw multiple elements on the
+ // canvas, we can not mirror the button by simply flipping the canvas as
+ // doing this will mirror the text itself. Flipping the canvas will also
+ // make the icons look wrong because icons are almost always represented as
+ // direction insentisive bitmaps and such bitmaps should never be flipped
+ // horizontally.
+ //
+ // Due to the above, we must perform the flipping manually for RTL UIs.
+ gfx::Rect text_bounds(text_x, text_y, text_width, text_size_.height());
+ text_bounds.set_x(MirroredLeftPointForRect(text_bounds));
+
+ if (for_drag) {
+ canvas->DrawStringWithHalo(text_, font_, color_, kHighlightColor,
+ text_bounds.x(),
+ text_bounds.y(),
+ text_bounds.width(),
+ text_bounds.height(),
+ l10n_util::DefaultCanvasTextAlignment());
+ } else {
+ // Draw bevel highlight
+ canvas->DrawStringInt(text_,
+ font_,
+ kHighlightColor,
+ text_bounds.x() + 1,
+ text_bounds.y() + 1,
+ text_bounds.width(),
+ text_bounds.height());
+
+ canvas->DrawStringInt(text_,
+ font_,
+ color_,
+ text_bounds.x(),
+ text_bounds.y(),
+ text_bounds.width(),
+ text_bounds.height());
+ }
+ }
+
+ if (icon_.width() > 0) {
+ int icon_y = (available_height - icon_.height()) / 2 + insets.top();
+
+ // Mirroring the icon position if necessary.
+ gfx::Rect icon_bounds(icon_x, icon_y, icon_.width(), icon_.height());
+ icon_bounds.set_x(MirroredLeftPointForRect(icon_bounds));
+ canvas->DrawBitmapInt(icon_, icon_bounds.x(), icon_bounds.y());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TextButton, View overrides:
+
+gfx::Size TextButton::GetPreferredSize() {
+ gfx::Insets insets = GetInsets();
+
+ // Use the max size to set the button boundaries.
+ gfx::Size prefsize(max_text_size_.width() + icon_.width() + insets.width(),
+ std::max(max_text_size_.height(), icon_.height()) +
+ insets.height());
+
+ if (icon_.width() > 0 && !text_.empty())
+ prefsize.Enlarge(kIconTextPadding, 0);
+
+ if (max_width_ > 0)
+ prefsize.set_width(std::min(max_width_, prefsize.width()));
+
+ return prefsize;
+}
+
+gfx::Size TextButton::GetMinimumSize() {
+ return max_text_size_;
+}
+
+void TextButton::SetEnabled(bool enabled) {
+ if (enabled == IsEnabled())
+ return;
+ CustomButton::SetEnabled(enabled);
+ color_ = enabled ? kEnabledColor : kDisabledColor;
+ SchedulePaint();
+}
+
+bool TextButton::OnMousePressed(const MouseEvent& e) {
+ return true;
+}
+
+void TextButton::Paint(ChromeCanvas* canvas) {
+ Paint(canvas, false);
+}
+
+} // namespace views
diff --git a/chrome/views/controls/button/text_button.h b/chrome/views/controls/button/text_button.h
new file mode 100644
index 0000000..6ef4b52
--- /dev/null
+++ b/chrome/views/controls/button/text_button.h
@@ -0,0 +1,138 @@
+// 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.
+
+#ifndef CHROME_VIEWS_CONTROLS_BUTTON_TEXT_BUTTON_H_
+#define CHROME_VIEWS_CONTROLS_BUTTON_TEXT_BUTTON_H_
+
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/views/border.h"
+#include "chrome/views/controls/button/custom_button.h"
+#include "skia/include/SkBitmap.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TextButtonBorder
+//
+// A Border subclass that paints a TextButton's background layer -
+// basically the button frame in the hot/pushed states.
+//
+////////////////////////////////////////////////////////////////////////////////
+class TextButtonBorder : public Border {
+ public:
+ TextButtonBorder();
+ virtual ~TextButtonBorder();
+
+ // Render the background for the provided view
+ virtual void Paint(const View& view, ChromeCanvas* canvas) const;
+
+ // Returns the insets for the border.
+ virtual void GetInsets(gfx::Insets* insets) const;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(TextButtonBorder);
+
+ // Images
+ struct MBBImageSet {
+ SkBitmap* top_left;
+ SkBitmap* top;
+ SkBitmap* top_right;
+ SkBitmap* left;
+ SkBitmap* center;
+ SkBitmap* right;
+ SkBitmap* bottom_left;
+ SkBitmap* bottom;
+ SkBitmap* bottom_right;
+ };
+ MBBImageSet hot_set_;
+ MBBImageSet pushed_set_;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TextButton
+//
+// A button which displays text and/or and icon that can be changed in
+// response to actions. TextButton reserves space for the largest string
+// passed to SetText. To reset the cached max size invoke ClearMaxTextSize.
+//
+////////////////////////////////////////////////////////////////////////////////
+class TextButton : public CustomButton {
+ public:
+ TextButton(ButtonListener* listener, const std::wstring& text);
+ virtual ~TextButton();
+
+ // Call SetText once per string in your set of possible values at button
+ // creation time, so that it can contain the largest of them and avoid
+ // resizing the button when the text changes.
+ virtual void SetText(const std::wstring& text);
+ std::wstring text() const { return text_; }
+
+ typedef enum TextAlignment {
+ ALIGN_LEFT,
+ ALIGN_CENTER,
+ ALIGN_RIGHT
+ };
+
+ void set_alignment(TextAlignment alignment) { alignment_ = alignment; }
+
+ // Sets the icon.
+ void SetIcon(const SkBitmap& icon);
+ SkBitmap icon() const { return icon_; }
+
+ // TextButton remembers the maximum display size of the text passed to
+ // SetText. This method resets the cached maximum display size to the
+ // current size.
+ void ClearMaxTextSize();
+
+ void set_max_width(int max_width) { max_width_ = max_width; }
+
+ // Paint the button into the specified canvas. If |for_drag| is true, the
+ // function paints a drag image representation into the canvas.
+ virtual void Paint(ChromeCanvas* canvas, bool for_drag);
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+ virtual gfx::Size GetMinimumSize();
+ virtual void SetEnabled(bool enabled);
+
+ protected:
+ virtual bool OnMousePressed(const MouseEvent& e);
+ virtual void Paint(ChromeCanvas* canvas);
+
+ private:
+ // The text string that is displayed in the button.
+ std::wstring text_;
+
+ // The size of the text string.
+ gfx::Size text_size_;
+
+ // Track the size of the largest text string seen so far, so that
+ // changing text_ will not resize the button boundary.
+ gfx::Size max_text_size_;
+
+ // The alignment of the text string within the button.
+ TextAlignment alignment_;
+
+ // The font used to paint the text.
+ ChromeFont font_;
+
+ // Text color.
+ SkColor color_;
+
+ // An icon displayed with the text.
+ SkBitmap icon_;
+
+ // The width of the button will never be larger than this value. A value <= 0
+ // indicates the width is not constrained.
+ int max_width_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TextButton);
+};
+
+} // namespace views
+
+#endif // CHROME_VIEWS_CONTROLS_BUTTON_TEXT_BUTTON_H_