15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/button/radio_button.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/accessibility/ax_view_state.h"
990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
1003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "ui/resources/grit/ui_resources.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace views {
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const char RadioButton::kViewClassName[] = "RadioButton";
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)RadioButton::RadioButton(const base::string16& label, int group_id)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : Checkbox(label) {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetGroup(group_id);
2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
2390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Unchecked/Unfocused images.
2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(false, false, STATE_NORMAL,
2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO));
2790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(false, false, STATE_HOVERED,
2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_HOVER));
2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(false, false, STATE_PRESSED,
3090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_PRESSED));
3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(false, false, STATE_DISABLED,
3290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_DISABLED));
3390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Checked/Unfocused images.
3590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(true, false, STATE_NORMAL,
3690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_CHECKED));
3790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(true, false, STATE_HOVERED,
3890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_CHECKED_HOVER));
3990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(true, false, STATE_PRESSED,
4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_CHECKED_PRESSED));
4190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(true, false, STATE_DISABLED,
4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_CHECKED_DISABLED));
4390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Unchecked/Focused images.
4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(false, true, STATE_NORMAL,
4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_FOCUSED));
4790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(false, true, STATE_HOVERED,
4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_FOCUSED_HOVER));
4990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(false, true, STATE_PRESSED,
5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_FOCUSED_PRESSED));
5190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
5290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Checked/Focused images.
5390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(true, true, STATE_NORMAL,
5490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_FOCUSED_CHECKED));
5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(true, true, STATE_HOVERED,
5690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_FOCUSED_CHECKED_HOVER));
5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetCustomImage(true, true, STATE_PRESSED,
5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 *rb.GetImageSkiaNamed(IDR_RADIO_FOCUSED_CHECKED_PRESSED));
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RadioButton::~RadioButton() {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RadioButton::SetChecked(bool checked) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (checked == RadioButton::checked())
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (checked) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We can't just get the root view here because sometimes the radio
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // button isn't attached to a root view (e.g., if it's part of a tab page
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // that is currently not active).
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    View* container = parent();
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (container && container->parent())
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      container = container->parent();
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (container) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Views other;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      container->GetViewsInGroup(GetGroup(), &other);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (Views::iterator i(other.begin()); i != other.end(); ++i) {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (*i != this) {
79b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          if (strcmp((*i)->GetClassName(), kViewClassName)) {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            NOTREACHED() << "radio-button-nt has same group as other non "
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            "radio-button-nt views.";
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            continue;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          RadioButton* peer = static_cast<RadioButton*>(*i);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          peer->SetChecked(false);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Checkbox::SetChecked(checked);
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
93b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char* RadioButton::GetClassName() const {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return kViewClassName;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void RadioButton::GetAccessibleState(ui::AXViewState* state) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Checkbox::GetAccessibleState(state);
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state->role = ui::AX_ROLE_RADIO_BUTTON;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)View* RadioButton::GetSelectedViewForGroup(int group) {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Views views;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetWidget()->GetRootView()->GetViewsInGroup(group, &views);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (views.empty())
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (Views::const_iterator i(views.begin()); i != views.end(); ++i) {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // REVIEW: why don't we check the runtime type like is done above?
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RadioButton* radio_button = static_cast<RadioButton*>(*i);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (radio_button->checked())
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return radio_button;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool RadioButton::IsGroupFocusTraversable() const {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When focusing a radio button with tab/shift+tab, only the selected button
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // from the group should be focused.
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RadioButton::OnFocus() {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Checkbox::OnFocus();
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetChecked(true);
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), 0, 0);
12790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  LabelButton::NotifyClick(event);
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RadioButton::NotifyClick(const ui::Event& event) {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the checked state to true only if we are unchecked, since we can't
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // be toggled on and off like a checkbox.
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!checked())
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetChecked(true);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequestFocus();
13690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  LabelButton::NotifyClick(event);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ui::NativeTheme::Part RadioButton::GetThemePart() const {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::NativeTheme::kRadio;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace views
144