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/native_combobox_win.h" 6 7 #include "base/i18n/rtl.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "ui/base/events/event.h" 10 #include "ui/base/models/combobox_model.h" 11 #include "ui/base/resource/resource_bundle.h" 12 #include "ui/base/win/hwnd_util.h" 13 #include "ui/gfx/font.h" 14 #include "ui/native_theme/native_theme_win.h" 15 #include "ui/views/controls/combobox/combobox.h" 16 #include "ui/views/controls/combobox/native_combobox_views.h" 17 #include "ui/views/widget/widget.h" 18 19 namespace { 20 21 // Limit how small a combobox can be. 22 const int kMinComboboxWidth = 148; 23 24 // Add a couple extra pixels to the widths of comboboxes and combobox 25 // dropdowns so that text isn't too crowded. 26 const int kComboboxExtraPaddingX = 6; 27 28 } // namespace 29 30 namespace views { 31 32 //////////////////////////////////////////////////////////////////////////////// 33 // NativeComboboxWin, public: 34 35 NativeComboboxWin::NativeComboboxWin(Combobox* combobox) 36 : combobox_(combobox), 37 content_width_(0) { 38 // Associates the actual HWND with the combobox so it is the one considered as 39 // having the focus (not the wrapper) when the HWND is focused directly (with 40 // a click for example). 41 set_focus_view(combobox); 42 } 43 44 NativeComboboxWin::~NativeComboboxWin() { 45 } 46 47 //////////////////////////////////////////////////////////////////////////////// 48 // NativeComboboxWin, NativeComboboxWrapper implementation: 49 50 void NativeComboboxWin::UpdateFromModel() { 51 SendMessage(native_view(), CB_RESETCONTENT, 0, 0); 52 const gfx::Font& font = Combobox::GetFont(); 53 int max_width = 0; 54 int num_items = combobox_->model()->GetItemCount(); 55 for (int i = 0; i < num_items; ++i) { 56 string16 text = combobox_->model()->GetItemAt(i); 57 58 // Inserting the Unicode formatting characters if necessary so that the 59 // text is displayed correctly in right-to-left UIs. 60 base::i18n::AdjustStringForLocaleDirection(&text); 61 62 SendMessage(native_view(), CB_ADDSTRING, 0, 63 reinterpret_cast<LPARAM>(UTF16ToWide(text).c_str())); 64 max_width = std::max(max_width, font.GetStringWidth(text)); 65 } 66 content_width_ = max_width; 67 68 if (num_items > 0) { 69 SendMessage(native_view(), CB_SETCURSEL, combobox_->selected_index(), 0); 70 71 // Set the width for the drop down while accounting for the scrollbar and 72 // borders. 73 if (num_items > ComboBox_GetMinVisible(native_view())) 74 max_width += GetSystemMetrics(SM_CXVSCROLL); 75 // SM_CXEDGE would not be correct here, since the dropdown is flat, not 3D. 76 int kComboboxDropdownBorderSize = 1; 77 max_width += 2 * kComboboxDropdownBorderSize + kComboboxExtraPaddingX; 78 SendMessage(native_view(), CB_SETDROPPEDWIDTH, max_width, 0); 79 } 80 } 81 82 void NativeComboboxWin::UpdateSelectedIndex() { 83 // Note that we use CB_SETCURSEL and not CB_SELECTSTRING because on RTL 84 // locales the strings we get from our ComboBox::Model might be augmented 85 // with Unicode directionality marks before we insert them into the combobox 86 // and therefore we can not assume that the string we get from 87 // ui::ComboboxModel can be safely searched for and selected (which is what 88 // CB_SELECTSTRING does). 89 SendMessage(native_view(), CB_SETCURSEL, combobox_->selected_index(), 0); 90 } 91 92 void NativeComboboxWin::UpdateEnabled() { 93 SetEnabled(combobox_->enabled()); 94 } 95 96 int NativeComboboxWin::GetSelectedIndex() const { 97 LRESULT selected_index = SendMessage(native_view(), CB_GETCURSEL, 0, 0); 98 return selected_index != CB_ERR ? selected_index : -1; 99 } 100 101 bool NativeComboboxWin::IsDropdownOpen() const { 102 return SendMessage(native_view(), CB_GETDROPPEDSTATE, 0, 0) != 0; 103 } 104 105 gfx::Size NativeComboboxWin::GetPreferredSize() { 106 COMBOBOXINFO cbi = { 0 }; 107 cbi.cbSize = sizeof(cbi); 108 // Note: Don't use CB_GETCOMBOBOXINFO since that crashes on WOW64 systems 109 // when you have a global message hook installed. 110 GetComboBoxInfo(native_view(), &cbi); 111 gfx::Rect rect_item(cbi.rcItem); 112 gfx::Rect rect_button(cbi.rcButton); 113 gfx::Size border = ui::NativeThemeWin::instance()->GetThemeBorderSize( 114 ui::NativeThemeWin::MENULIST); 115 116 // The padding value of '3' is the xy offset from the corner of the control 117 // to the corner of rcItem. It does not seem to be queryable from the theme. 118 // It is consistent on all versions of Windows from 2K to Vista, and is 119 // invariant with respect to the combobox border size. We could conceivably 120 // get this number from rect_item.x, but it seems fragile to depend on 121 // position here, inside of the layout code. 122 const int kItemOffset = 3; 123 int item_to_button_distance = std::max(kItemOffset - border.width(), 0); 124 125 // The cx computation can be read as measuring from left to right. 126 int pref_width = std::max(kItemOffset + content_width_ + 127 kComboboxExtraPaddingX + 128 item_to_button_distance + rect_button.width() + 129 border.width(), kMinComboboxWidth); 130 // The two arguments to ::max below should be typically be equal. 131 int pref_height = std::max(rect_item.height() + 2 * kItemOffset, 132 rect_button.height() + 2 * border.height()); 133 return gfx::Size(pref_width, pref_height); 134 } 135 136 View* NativeComboboxWin::GetView() { 137 return this; 138 } 139 140 void NativeComboboxWin::SetFocus() { 141 OnFocus(); 142 } 143 144 void NativeComboboxWin::ValidityStateChanged() { 145 // TODO(estade): implement. 146 } 147 148 bool NativeComboboxWin::HandleKeyPressed(const ui::KeyEvent& event) { 149 return false; 150 } 151 152 bool NativeComboboxWin::HandleKeyReleased(const ui::KeyEvent& event) { 153 return false; 154 } 155 156 void NativeComboboxWin::HandleFocus() { 157 } 158 159 void NativeComboboxWin::HandleBlur() { 160 } 161 162 gfx::NativeView NativeComboboxWin::GetTestingHandle() const { 163 return native_view(); 164 } 165 166 //////////////////////////////////////////////////////////////////////////////// 167 // NativeComboboxWin, NativeControlWin overrides: 168 169 bool NativeComboboxWin::ProcessMessage(UINT message, 170 WPARAM w_param, 171 LPARAM l_param, 172 LRESULT* result) { 173 if (message == WM_COMMAND && HIWORD(w_param) == CBN_SELCHANGE) { 174 combobox_->SelectionChanged(); 175 *result = 0; 176 return true; 177 } 178 return NativeControlWin::ProcessMessage(message, w_param, l_param, result); 179 } 180 181 void NativeComboboxWin::CreateNativeControl() { 182 // It's ok to add WS_VSCROLL. The scrollbar will show up only when necessary 183 // as long as we don't use CBS_DISABLENOSCROLL. 184 // See http://msdn.microsoft.com/en-us/library/7h63bxbe(VS.80).aspx 185 DWORD flags = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 186 CBS_DROPDOWNLIST | WS_VSCROLL; 187 HWND control_hwnd = ::CreateWindowEx(GetAdditionalExStyle(), L"COMBOBOX", L"", 188 flags, 0, 0, width(), height(), 189 GetWidget()->GetNativeView(), NULL, NULL, 190 NULL); 191 ui::CheckWindowCreated(control_hwnd); 192 NativeControlCreated(control_hwnd); 193 } 194 195 void NativeComboboxWin::NativeControlCreated(HWND native_control) { 196 NativeControlWin::NativeControlCreated(native_control); 197 198 UpdateFont(); 199 } 200 201 //////////////////////////////////////////////////////////////////////////////// 202 // NativeComboboxWin, private: 203 204 void NativeComboboxWin::UpdateFont() { 205 HFONT font = Combobox::GetFont().GetNativeFont(); 206 SendMessage(native_view(), WM_SETFONT, reinterpret_cast<WPARAM>(font), FALSE); 207 } 208 209 //////////////////////////////////////////////////////////////////////////////// 210 // NativeComboboxWrapper, public: 211 212 // static 213 NativeComboboxWrapper* NativeComboboxWrapper::CreateWrapper( 214 Combobox* combobox) { 215 return new NativeComboboxViews(combobox); 216 } 217 218 } // namespace views 219