Home | History | Annotate | Download | only in combobox
      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