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