Home | History | Annotate | Download | only in controls
      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/native_control_win.h"
      6 
      7 #include <windowsx.h>
      8 
      9 #include "base/logging.h"
     10 #include "ui/base/accessibility/accessibility_types.h"
     11 #include "ui/base/l10n/l10n_util_win.h"
     12 #include "ui/base/view_prop.h"
     13 #include "ui/gfx/win/hwnd_util.h"
     14 #include "ui/views/controls/combobox/combobox.h"
     15 #include "ui/views/focus/focus_manager.h"
     16 #include "ui/views/widget/widget.h"
     17 
     18 using ui::ViewProp;
     19 
     20 const char kNativeControlWinKey[] = "__NATIVE_CONTROL_WIN__";
     21 
     22 namespace views {
     23 
     24 ////////////////////////////////////////////////////////////////////////////////
     25 // NativeControlWin, public:
     26 
     27 NativeControlWin::NativeControlWin() : original_wndproc_(NULL) {
     28 }
     29 
     30 NativeControlWin::~NativeControlWin() {
     31   HWND hwnd = native_view();
     32   if (hwnd) {
     33     // Destroy the hwnd if it still exists. Otherwise we won't have shut things
     34     // down correctly, leading to leaking and crashing if another message
     35     // comes in for the hwnd.
     36     Detach();
     37     DestroyWindow(hwnd);
     38   }
     39 }
     40 
     41 bool NativeControlWin::ProcessMessage(UINT message,
     42                                       WPARAM w_param,
     43                                       LPARAM l_param,
     44                                       LRESULT* result) {
     45   switch (message) {
     46     case WM_CONTEXTMENU:
     47       ShowContextMenu(gfx::Point(GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param)));
     48       *result = 0;
     49       return true;
     50     case WM_CTLCOLORBTN:
     51     case WM_CTLCOLORSTATIC:
     52       *result = GetControlColor(message, reinterpret_cast<HDC>(w_param),
     53                                 native_view());
     54       return true;
     55   }
     56   return false;
     57 }
     58 
     59 ////////////////////////////////////////////////////////////////////////////////
     60 // NativeControlWin, View overrides:
     61 
     62 void NativeControlWin::OnEnabledChanged() {
     63   View::OnEnabledChanged();
     64   if (native_view())
     65     EnableWindow(native_view(), enabled());
     66 }
     67 
     68 void NativeControlWin::ViewHierarchyChanged(
     69     const ViewHierarchyChangedDetails& details) {
     70   // Call the base class to hide the view if we're being removed.
     71   NativeViewHost::ViewHierarchyChanged(details);
     72 
     73   // Create the HWND when we're added to a valid Widget. Many controls need a
     74   // parent HWND to function properly.
     75   if (details.is_add && GetWidget() && !native_view())
     76     CreateNativeControl();
     77 }
     78 
     79 void NativeControlWin::VisibilityChanged(View* starting_from, bool is_visible) {
     80   // We might get called due to visibility changes at any point in the
     81   // hierarchy, lets check whether we are really visible or not.
     82   bool is_drawn = IsDrawn();
     83   if (!is_drawn && native_view()) {
     84     // We destroy the child control HWND when we become invisible because of the
     85     // performance cost of maintaining many HWNDs.
     86     HWND hwnd = native_view();
     87     Detach();
     88     DestroyWindow(hwnd);
     89   } else if (is_drawn && !native_view()) {
     90     if (GetWidget())
     91       CreateNativeControl();
     92   }
     93   if (is_drawn) {
     94     // The view becomes visible after native control is created.
     95     // Layout now.
     96     Layout();
     97   }
     98 }
     99 
    100 void NativeControlWin::OnFocus() {
    101   DCHECK(native_view());
    102   SetFocus(native_view());
    103 
    104   // Since we are being wrapped by a view, accessibility should receive
    105   // the super class as the focused view.
    106   View* parent_view = parent();
    107 
    108   // Due to some controls not behaving as expected without having
    109   // a native win32 control, we don't always send a native (MSAA)
    110   // focus notification.
    111   bool send_native_event =
    112       strcmp(parent_view->GetClassName(), Combobox::kViewClassName) &&
    113       parent_view->HasFocus();
    114 
    115   // Send the accessibility focus notification.
    116   parent_view->NotifyAccessibilityEvent(
    117       ui::AccessibilityTypes::EVENT_FOCUS, send_native_event);
    118 }
    119 
    120 ////////////////////////////////////////////////////////////////////////////////
    121 // NativeControlWin, protected:
    122 
    123 void NativeControlWin::ShowContextMenu(const gfx::Point& location) {
    124   if (!context_menu_controller())
    125     return;
    126 
    127   if (location.x() == -1 && location.y() == -1) {
    128     View::ShowContextMenu(GetKeyboardContextMenuLocation(),
    129                           ui::MENU_SOURCE_KEYBOARD);
    130   } else {
    131     View::ShowContextMenu(location, ui::MENU_SOURCE_MOUSE);
    132   }
    133 }
    134 
    135 void NativeControlWin::NativeControlCreated(HWND native_control) {
    136   // Associate this object with the control's HWND so that NativeWidgetWin can
    137   // find this object when it receives messages from it.
    138   props_.push_back(new ViewProp(native_control, kNativeControlWinKey, this));
    139   props_.push_back(ChildWindowMessageProcessor::Register(native_control, this));
    140 
    141   // Subclass so we get WM_KEYDOWN and WM_SETFOCUS messages.
    142   original_wndproc_ = gfx::SetWindowProc(
    143       native_control, &NativeControlWin::NativeControlWndProc);
    144 
    145   Attach(native_control);
    146   // native_view() is now valid.
    147 
    148   // Update the newly created HWND with any resident enabled state.
    149   EnableWindow(native_view(), enabled());
    150 
    151   // This message ensures that the focus border is shown.
    152   SendMessage(native_view(), WM_CHANGEUISTATE,
    153               MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
    154 }
    155 
    156 DWORD NativeControlWin::GetAdditionalExStyle() const {
    157   // If the UI for the view is mirrored, we should make sure we add the
    158   // extended window style for a right-to-left layout so the subclass creates
    159   // a mirrored HWND for the underlying control.
    160   DWORD ex_style = 0;
    161   if (base::i18n::IsRTL())
    162     ex_style |= l10n_util::GetExtendedStyles();
    163 
    164   return ex_style;
    165 }
    166 
    167 DWORD NativeControlWin::GetAdditionalRTLStyle() const {
    168   // If the UI for the view is mirrored, we should make sure we add the
    169   // extended window style for a right-to-left layout so the subclass creates
    170   // a mirrored HWND for the underlying control.
    171   DWORD ex_style = 0;
    172   if (base::i18n::IsRTL())
    173     ex_style |= l10n_util::GetExtendedTooltipStyles();
    174 
    175   return ex_style;
    176 }
    177 
    178 ////////////////////////////////////////////////////////////////////////////////
    179 // NativeControlWin, private:
    180 
    181 LRESULT NativeControlWin::GetControlColor(UINT message, HDC dc, HWND sender) {
    182   View *ancestor = this;
    183   while (ancestor) {
    184     const Background* background = ancestor->background();
    185     if (background) {
    186       HBRUSH brush = background->GetNativeControlBrush();
    187       if (brush)
    188         return reinterpret_cast<LRESULT>(brush);
    189     }
    190     ancestor = ancestor->parent();
    191   }
    192 
    193   // COLOR_BTNFACE is the default for dialog box backgrounds.
    194   return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE));
    195 }
    196 
    197 // static
    198 LRESULT NativeControlWin::NativeControlWndProc(HWND window,
    199                                                UINT message,
    200                                                WPARAM w_param,
    201                                                LPARAM l_param) {
    202   NativeControlWin* native_control = reinterpret_cast<NativeControlWin*>(
    203       ViewProp::GetValue(window, kNativeControlWinKey));
    204   DCHECK(native_control);
    205 
    206   if (message == WM_KEYDOWN &&
    207       native_control->OnKeyDown(static_cast<int>(w_param))) {
    208       return 0;
    209   } else if (message == WM_SETFOCUS) {
    210     // Let the focus manager know that the focus changed.
    211     FocusManager* focus_manager = native_control->GetFocusManager();
    212     if (focus_manager) {
    213       focus_manager->SetFocusedView(native_control->focus_view());
    214     } else {
    215       NOTREACHED();
    216     }
    217   } else if (message == WM_DESTROY) {
    218     native_control->props_.clear();
    219     gfx::SetWindowProc(window, native_control->original_wndproc_);
    220   }
    221 
    222   return CallWindowProc(native_control->original_wndproc_, window, message,
    223                         w_param, l_param);
    224 }
    225 
    226 }  // namespace views
    227