• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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.h"
6 
7 #include <atlbase.h>
8 #include <atlapp.h>
9 #include <atlcrack.h>
10 #include <atlframe.h>
11 #include <atlmisc.h>
12 
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "ui/base/accessibility/accessibility_types.h"
16 #include "ui/base/l10n/l10n_util_win.h"
17 #include "ui/base/view_prop.h"
18 #include "ui/events/keycodes/keyboard_code_conversion_win.h"
19 #include "ui/events/keycodes/keyboard_codes.h"
20 #include "ui/gfx/win/hwnd_util.h"
21 #include "ui/views/background.h"
22 #include "ui/views/controls/native/native_view_host.h"
23 #include "ui/views/focus/focus_manager.h"
24 #include "ui/views/widget/widget.h"
25 
26 using ui::ViewProp;
27 
28 namespace views {
29 
30 // Maps to the NativeControl.
31 static const char* const kNativeControlKey = "__NATIVE_CONTROL__";
32 
33 class NativeControlContainer : public CWindowImpl<NativeControlContainer,
34                                CWindow,
35                                CWinTraits<WS_CHILD | WS_CLIPSIBLINGS |
36                                           WS_CLIPCHILDREN>> {
37  public:
NativeControlContainer(NativeControl * parent)38   explicit NativeControlContainer(NativeControl* parent)
39       : parent_(parent),
40         control_(NULL),
41         original_handler_(NULL) {
42   }
43 
Init()44   void Init() {
45     Create(parent_->GetWidget()->GetNativeView());
46     ::ShowWindow(m_hWnd, SW_SHOW);
47   }
48 
~NativeControlContainer()49   virtual ~NativeControlContainer() {
50   }
51 
52   // NOTE: If you add a new message, be sure and verify parent_ is valid before
53   // calling into parent_.
54   DECLARE_FRAME_WND_CLASS(L"ChromeViewsNativeControlContainer", NULL);
55   BEGIN_MSG_MAP(NativeControlContainer);
56     MSG_WM_CREATE(OnCreate);
57     MSG_WM_ERASEBKGND(OnEraseBkgnd);
58     MSG_WM_PAINT(OnPaint);
59     MSG_WM_SIZE(OnSize);
60     MSG_WM_NOTIFY(OnNotify);
61     MSG_WM_COMMAND(OnCommand);
62     MSG_WM_DESTROY(OnDestroy);
63     MSG_WM_CONTEXTMENU(OnContextMenu);
64     MSG_WM_CTLCOLORBTN(OnCtlColorBtn);
65     MSG_WM_CTLCOLORSTATIC(OnCtlColorStatic)
66   END_MSG_MAP();
67 
GetControl()68   HWND GetControl() {
69     return control_;
70   }
71 
72   // Called when the parent is getting deleted. This control stays around until
73   // it gets the OnFinalMessage call.
ResetParent()74   void ResetParent() {
75     parent_ = NULL;
76   }
77 
OnFinalMessage(HWND hwnd)78   void OnFinalMessage(HWND hwnd) {
79     if (parent_)
80       parent_->NativeControlDestroyed();
81     delete this;
82   }
83 
84  private:
85   friend class NativeControl;
86 
OnCreate(LPCREATESTRUCT create_struct)87   LRESULT OnCreate(LPCREATESTRUCT create_struct) {
88     control_ = parent_->CreateNativeControl(m_hWnd);
89 
90     // We subclass the control hwnd so we get the WM_KEYDOWN messages.
91     original_handler_ = gfx::SetWindowProc(
92         control_, &NativeControl::NativeControlWndProc);
93     prop_.reset(new ViewProp(control_, kNativeControlKey , parent_));
94 
95     ::ShowWindow(control_, SW_SHOW);
96     return 1;
97   }
98 
OnEraseBkgnd(HDC dc)99   LRESULT OnEraseBkgnd(HDC dc) {
100     return 1;
101   }
102 
OnPaint(HDC ignore)103   void OnPaint(HDC ignore) {
104     PAINTSTRUCT ps;
105     HDC dc = ::BeginPaint(*this, &ps);
106     ::EndPaint(*this, &ps);
107   }
108 
OnSize(int type,const CSize & sz)109   void OnSize(int type, const CSize& sz) {
110     ::MoveWindow(control_, 0, 0, sz.cx, sz.cy, TRUE);
111   }
112 
OnCommand(UINT code,int id,HWND source)113   LRESULT OnCommand(UINT code, int id, HWND source) {
114     return parent_ ? parent_->OnCommand(code, id, source) : 0;
115   }
116 
OnNotify(int w_param,LPNMHDR l_param)117   LRESULT OnNotify(int w_param, LPNMHDR l_param) {
118     if (parent_)
119       return parent_->OnNotify(w_param, l_param);
120     else
121       return 0;
122   }
123 
OnDestroy()124   void OnDestroy() {
125     if (parent_)
126       parent_->OnDestroy();
127   }
128 
OnContextMenu(HWND window,const POINT & location)129   void OnContextMenu(HWND window, const POINT& location) {
130     if (parent_)
131       parent_->OnContextMenu(location);
132   }
133 
134   // We need to find an ancestor with a non-null background, and
135   // ask it for a (solid color) brush that approximates
136   // the background.  The caller will use this when drawing
137   // the native control as a background color, particularly
138   // for radiobuttons and XP style pushbuttons.
OnCtlColor(UINT msg,HDC dc,HWND control)139   LRESULT OnCtlColor(UINT msg, HDC dc, HWND control) {
140     const View *ancestor = parent_;
141     while (ancestor) {
142       const Background *background = ancestor->background();
143       if (background) {
144         HBRUSH brush = background->GetNativeControlBrush();
145         if (brush)
146           return reinterpret_cast<LRESULT>(brush);
147       }
148       ancestor = ancestor->parent();
149     }
150 
151     // COLOR_BTNFACE is the default for dialog box backgrounds.
152     return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE));
153   }
154 
OnCtlColorBtn(HDC dc,HWND control)155   LRESULT OnCtlColorBtn(HDC dc, HWND control) {
156     return OnCtlColor(WM_CTLCOLORBTN, dc, control);
157   }
158 
OnCtlColorStatic(HDC dc,HWND control)159   LRESULT OnCtlColorStatic(HDC dc, HWND control) {
160     return OnCtlColor(WM_CTLCOLORSTATIC, dc, control);
161   }
162 
163   NativeControl* parent_;
164   HWND control_;
165 
166   // Message handler that was set before we reset it.
167   WNDPROC original_handler_;
168 
169   scoped_ptr<ViewProp> prop_;
170 
171   DISALLOW_COPY_AND_ASSIGN(NativeControlContainer);
172 };
173 
NativeControl()174 NativeControl::NativeControl() : hwnd_view_(NULL),
175                                  container_(NULL),
176                                  fixed_width_(-1),
177                                  horizontal_alignment_(CENTER),
178                                  fixed_height_(-1),
179                                  vertical_alignment_(CENTER) {
180   SetFocusable(true);
181 }
182 
~NativeControl()183 NativeControl::~NativeControl() {
184   if (container_) {
185     container_->ResetParent();
186     ::DestroyWindow(*container_);
187   }
188 }
189 
ValidateNativeControl()190 void NativeControl::ValidateNativeControl() {
191   if (hwnd_view_ == NULL) {
192     hwnd_view_ = new NativeViewHost;
193     AddChildView(hwnd_view_);
194   }
195 
196   if (!container_ && visible()) {
197     container_ = new NativeControlContainer(this);
198     container_->Init();
199     hwnd_view_->Attach(*container_);
200     if (!enabled())
201       EnableWindow(GetNativeControlHWND(), enabled());
202 
203     // This message ensures that the focus border is shown.
204     ::SendMessage(container_->GetControl(),
205                   WM_CHANGEUISTATE,
206                   MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS),
207                   0);
208   }
209 }
210 
ViewHierarchyChanged(const ViewHierarchyChangedDetails & details)211 void NativeControl::ViewHierarchyChanged(
212     const ViewHierarchyChangedDetails& details) {
213   if (details.is_add && details.parent != this && !container_ && GetWidget()) {
214     ValidateNativeControl();
215     Layout();
216   }
217 }
218 
Layout()219 void NativeControl::Layout() {
220   if (!container_ && GetWidget())
221     ValidateNativeControl();
222 
223   if (hwnd_view_) {
224     gfx::Rect lb = GetLocalBounds();
225 
226     int x = lb.x();
227     int y = lb.y();
228     int width = lb.width();
229     int height = lb.height();
230     if (fixed_width_ > 0) {
231       width = std::min(fixed_width_, width);
232       switch (horizontal_alignment_) {
233         case LEADING:
234           // Nothing to do.
235           break;
236         case CENTER:
237           x += (lb.width() - width) / 2;
238           break;
239         case TRAILING:
240           x = x + lb.width() - width;
241           break;
242         default:
243           NOTREACHED();
244       }
245     }
246 
247     if (fixed_height_ > 0) {
248       height = std::min(fixed_height_, height);
249       switch (vertical_alignment_) {
250         case LEADING:
251           // Nothing to do.
252           break;
253         case CENTER:
254           y += (lb.height() - height) / 2;
255           break;
256         case TRAILING:
257           y = y + lb.height() - height;
258           break;
259         default:
260           NOTREACHED();
261       }
262     }
263 
264     hwnd_view_->SetBounds(x, y, width, height);
265   }
266 }
267 
OnContextMenu(const POINT & location)268 void NativeControl::OnContextMenu(const POINT& location) {
269   if (!context_menu_controller())
270     return;
271 
272   if (location.x == -1 && location.y == -1) {
273     ShowContextMenu(GetKeyboardContextMenuLocation(),
274                     ui::MENU_SOURCE_KEYBOARD);
275   } else {
276     ShowContextMenu(gfx::Point(location), ui::MENU_SOURCE_MOUSE);
277   }
278 }
279 
OnFocus()280 void NativeControl::OnFocus() {
281   if (container_) {
282     DCHECK(container_->GetControl());
283     ::SetFocus(container_->GetControl());
284     NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, false);
285   }
286 }
287 
GetNativeControlHWND()288 HWND NativeControl::GetNativeControlHWND() {
289   if (container_)
290     return container_->GetControl();
291   else
292     return NULL;
293 }
294 
NativeControlDestroyed()295 void NativeControl::NativeControlDestroyed() {
296   if (hwnd_view_)
297     hwnd_view_->Detach();
298   container_ = NULL;
299 }
300 
SetVisible(bool is_visible)301 void NativeControl::SetVisible(bool is_visible) {
302   if (is_visible != visible()) {
303     View::SetVisible(is_visible);
304     if (!is_visible && container_)
305       ::DestroyWindow(*container_);
306     else if (is_visible && !container_)
307       ValidateNativeControl();
308   }
309 }
310 
OnEnabledChanged()311 void NativeControl::OnEnabledChanged() {
312   View::OnEnabledChanged();
313   if (GetNativeControlHWND())
314     EnableWindow(GetNativeControlHWND(), enabled());
315 }
316 
OnPaint(gfx::Canvas * canvas)317 void NativeControl::OnPaint(gfx::Canvas* canvas) {
318 }
319 
VisibilityChanged(View * starting_from,bool is_visible)320 void NativeControl::VisibilityChanged(View* starting_from, bool is_visible) {
321   SetVisible(is_visible);
322 }
323 
SetFixedWidth(int width,Alignment alignment)324 void NativeControl::SetFixedWidth(int width, Alignment alignment) {
325   DCHECK_GT(width, 0);
326   fixed_width_ = width;
327   horizontal_alignment_ = alignment;
328 }
329 
SetFixedHeight(int height,Alignment alignment)330 void NativeControl::SetFixedHeight(int height, Alignment alignment) {
331   DCHECK_GT(height, 0);
332   fixed_height_ = height;
333   vertical_alignment_ = alignment;
334 }
335 
GetAdditionalExStyle() const336 DWORD NativeControl::GetAdditionalExStyle() const {
337   // If the UI for the view is mirrored, we should make sure we add the
338   // extended window style for a right-to-left layout so the subclass creates
339   // a mirrored HWND for the underlying control.
340   DWORD ex_style = 0;
341   if (base::i18n::IsRTL())
342     ex_style |= l10n_util::GetExtendedStyles();
343 
344   return ex_style;
345 }
346 
GetAdditionalRTLStyle() const347 DWORD NativeControl::GetAdditionalRTLStyle() const {
348   // If the UI for the view is mirrored, we should make sure we add the
349   // extended window style for a right-to-left layout so the subclass creates
350   // a mirrored HWND for the underlying control.
351   DWORD ex_style = 0;
352   if (base::i18n::IsRTL())
353     ex_style |= l10n_util::GetExtendedTooltipStyles();
354 
355   return ex_style;
356 }
357 
358 // static
NativeControlWndProc(HWND window,UINT message,WPARAM w_param,LPARAM l_param)359 LRESULT CALLBACK NativeControl::NativeControlWndProc(HWND window,
360                                                      UINT message,
361                                                      WPARAM w_param,
362                                                      LPARAM l_param) {
363   NativeControl* native_control = static_cast<NativeControl*>(
364       ViewProp::GetValue(window, kNativeControlKey));
365   DCHECK(native_control);
366   WNDPROC original_handler = native_control->container_->original_handler_;
367   DCHECK(original_handler);
368 
369   if (message == WM_KEYDOWN &&
370       native_control->OnKeyDown(ui::KeyboardCodeForWindowsKeyCode(w_param))) {
371     return 0;
372   } else if (message == WM_SETFOCUS) {
373     // Let the focus manager know that the focus changed.
374     FocusManager* focus_manager = native_control->GetFocusManager();
375     if (focus_manager) {
376       focus_manager->SetFocusedView(native_control);
377     } else {
378       NOTREACHED();
379     }
380   } else if (message == WM_DESTROY) {
381     gfx::SetWindowProc(window, reinterpret_cast<WNDPROC>(original_handler));
382     native_control->container_->prop_.reset();
383   }
384 
385   return CallWindowProc(reinterpret_cast<WNDPROC>(original_handler), window,
386                         message, w_param, l_param);
387 }
388 
389 }  // namespace views
390