1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/views/controls/combobox/combobox.h" 6 7 #include "base/logging.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "ui/base/accessibility/accessible_view_state.h" 10 #include "ui/base/events/event.h" 11 #include "ui/base/keycodes/keyboard_codes.h" 12 #include "ui/base/models/combobox_model.h" 13 #include "ui/base/resource/resource_bundle.h" 14 #include "ui/views/controls/combobox/combobox_listener.h" 15 #include "ui/views/controls/native/native_view_host.h" 16 #include "ui/views/controls/prefix_selector.h" 17 #include "ui/views/ime/input_method.h" 18 #include "ui/views/widget/widget.h" 19 20 namespace views { 21 22 // static 23 const char Combobox::kViewClassName[] = "Combobox"; 24 25 //////////////////////////////////////////////////////////////////////////////// 26 // Combobox, public: 27 28 Combobox::Combobox(ui::ComboboxModel* model) 29 : native_wrapper_(NULL), 30 model_(model), 31 listener_(NULL), 32 selected_index_(model_->GetDefaultIndex()), 33 invalid_(false) { 34 set_focusable(true); 35 } 36 37 Combobox::~Combobox() { 38 } 39 40 // static 41 const gfx::Font& Combobox::GetFont() { 42 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 43 return rb.GetFont(ui::ResourceBundle::BaseFont); 44 } 45 46 void Combobox::ModelChanged() { 47 selected_index_ = std::min(0, model_->GetItemCount()); 48 if (native_wrapper_) 49 native_wrapper_->UpdateFromModel(); 50 PreferredSizeChanged(); 51 } 52 53 void Combobox::SetSelectedIndex(int index) { 54 selected_index_ = index; 55 if (native_wrapper_) 56 native_wrapper_->UpdateSelectedIndex(); 57 } 58 59 void Combobox::SelectionChanged() { 60 selected_index_ = native_wrapper_->GetSelectedIndex(); 61 if (listener_) 62 listener_->OnSelectedIndexChanged(this); 63 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false); 64 } 65 66 void Combobox::SetAccessibleName(const string16& name) { 67 accessible_name_ = name; 68 } 69 70 void Combobox::SetInvalid(bool invalid) { 71 invalid_ = invalid; 72 if (native_wrapper_) 73 native_wrapper_->ValidityStateChanged(); 74 } 75 76 ui::TextInputClient* Combobox::GetTextInputClient() { 77 if (!selector_) 78 selector_.reset(new PrefixSelector(this)); 79 return selector_.get(); 80 } 81 82 int Combobox::GetRowCount() { 83 return model()->GetItemCount(); 84 } 85 86 int Combobox::GetSelectedRow() { 87 return selected_index_; 88 } 89 90 void Combobox::SetSelectedRow(int row) { 91 SetSelectedIndex(row); 92 } 93 94 string16 Combobox::GetTextForRow(int row) { 95 return model()->GetItemAt(row); 96 } 97 98 //////////////////////////////////////////////////////////////////////////////// 99 // Combobox, View overrides: 100 101 gfx::Size Combobox::GetPreferredSize() { 102 if (native_wrapper_) 103 return native_wrapper_->GetPreferredSize(); 104 return gfx::Size(); 105 } 106 107 void Combobox::Layout() { 108 if (native_wrapper_) { 109 native_wrapper_->GetView()->SetBounds(0, 0, width(), height()); 110 native_wrapper_->GetView()->Layout(); 111 } 112 } 113 114 void Combobox::OnEnabledChanged() { 115 View::OnEnabledChanged(); 116 if (native_wrapper_) 117 native_wrapper_->UpdateEnabled(); 118 } 119 120 // VKEY_ESCAPE should be handled by this view when the drop down list is active. 121 // In other words, the list should be closed instead of the dialog. 122 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { 123 if (e.key_code() != ui::VKEY_ESCAPE || 124 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) { 125 return false; 126 } 127 return native_wrapper_ && native_wrapper_->IsDropdownOpen(); 128 } 129 130 void Combobox::OnPaintFocusBorder(gfx::Canvas* canvas) { 131 if (NativeViewHost::kRenderNativeControlFocus) 132 View::OnPaintFocusBorder(canvas); 133 } 134 135 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { 136 return native_wrapper_ && native_wrapper_->HandleKeyPressed(e); 137 } 138 139 bool Combobox::OnKeyReleased(const ui::KeyEvent& e) { 140 return native_wrapper_ && native_wrapper_->HandleKeyReleased(e); 141 } 142 143 void Combobox::OnFocus() { 144 GetInputMethod()->OnFocus(); 145 // Forward the focus to the wrapper. 146 if (native_wrapper_) { 147 native_wrapper_->SetFocus(); 148 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, true); 149 } else { 150 View::OnFocus(); // Will focus the RootView window (so we still get 151 // keyboard messages). 152 } 153 } 154 155 void Combobox::OnBlur() { 156 GetInputMethod()->OnBlur(); 157 if (selector_) 158 selector_->OnViewBlur(); 159 if (native_wrapper_) 160 native_wrapper_->HandleBlur(); 161 } 162 163 void Combobox::GetAccessibleState(ui::AccessibleViewState* state) { 164 state->role = ui::AccessibilityTypes::ROLE_COMBOBOX; 165 state->name = accessible_name_; 166 state->value = model_->GetItemAt(selected_index_); 167 state->index = selected_index_; 168 state->count = model_->GetItemCount(); 169 } 170 171 void Combobox::ViewHierarchyChanged( 172 const ViewHierarchyChangedDetails& details) { 173 if (details.is_add && !native_wrapper_ && GetWidget()) { 174 // The native wrapper's lifetime will be managed by the view hierarchy after 175 // we call AddChildView. 176 native_wrapper_ = NativeComboboxWrapper::CreateWrapper(this); 177 AddChildView(native_wrapper_->GetView()); 178 // The underlying native widget may not be created until the wrapper is 179 // parented. For this reason the wrapper is only updated after adding its 180 // view. 181 native_wrapper_->UpdateFromModel(); 182 native_wrapper_->UpdateSelectedIndex(); 183 native_wrapper_->UpdateEnabled(); 184 } 185 } 186 187 const char* Combobox::GetClassName() const { 188 return kViewClassName; 189 } 190 191 } // namespace views 192