普通文本  |  227行  |  7.45 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/native_control_win.h"

#include <windowsx.h>

#include "base/logging.h"
#include "ui/base/accessibility/accessibility_types.h"
#include "ui/base/l10n/l10n_util_win.h"
#include "ui/base/view_prop.h"
#include "ui/gfx/win/hwnd_util.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/widget/widget.h"

using ui::ViewProp;

const char kNativeControlWinKey[] = "__NATIVE_CONTROL_WIN__";

namespace views {

////////////////////////////////////////////////////////////////////////////////
// NativeControlWin, public:

NativeControlWin::NativeControlWin() : original_wndproc_(NULL) {
}

NativeControlWin::~NativeControlWin() {
  HWND hwnd = native_view();
  if (hwnd) {
    // Destroy the hwnd if it still exists. Otherwise we won't have shut things
    // down correctly, leading to leaking and crashing if another message
    // comes in for the hwnd.
    Detach();
    DestroyWindow(hwnd);
  }
}

bool NativeControlWin::ProcessMessage(UINT message,
                                      WPARAM w_param,
                                      LPARAM l_param,
                                      LRESULT* result) {
  switch (message) {
    case WM_CONTEXTMENU:
      ShowContextMenu(gfx::Point(GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param)));
      *result = 0;
      return true;
    case WM_CTLCOLORBTN:
    case WM_CTLCOLORSTATIC:
      *result = GetControlColor(message, reinterpret_cast<HDC>(w_param),
                                native_view());
      return true;
  }
  return false;
}

////////////////////////////////////////////////////////////////////////////////
// NativeControlWin, View overrides:

void NativeControlWin::OnEnabledChanged() {
  View::OnEnabledChanged();
  if (native_view())
    EnableWindow(native_view(), enabled());
}

void NativeControlWin::ViewHierarchyChanged(
    const ViewHierarchyChangedDetails& details) {
  // Call the base class to hide the view if we're being removed.
  NativeViewHost::ViewHierarchyChanged(details);

  // Create the HWND when we're added to a valid Widget. Many controls need a
  // parent HWND to function properly.
  if (details.is_add && GetWidget() && !native_view())
    CreateNativeControl();
}

void NativeControlWin::VisibilityChanged(View* starting_from, bool is_visible) {
  // We might get called due to visibility changes at any point in the
  // hierarchy, lets check whether we are really visible or not.
  bool is_drawn = IsDrawn();
  if (!is_drawn && native_view()) {
    // We destroy the child control HWND when we become invisible because of the
    // performance cost of maintaining many HWNDs.
    HWND hwnd = native_view();
    Detach();
    DestroyWindow(hwnd);
  } else if (is_drawn && !native_view()) {
    if (GetWidget())
      CreateNativeControl();
  }
  if (is_drawn) {
    // The view becomes visible after native control is created.
    // Layout now.
    Layout();
  }
}

void NativeControlWin::OnFocus() {
  DCHECK(native_view());
  SetFocus(native_view());

  // Since we are being wrapped by a view, accessibility should receive
  // the super class as the focused view.
  View* parent_view = parent();

  // Due to some controls not behaving as expected without having
  // a native win32 control, we don't always send a native (MSAA)
  // focus notification.
  bool send_native_event =
      strcmp(parent_view->GetClassName(), Combobox::kViewClassName) &&
      parent_view->HasFocus();

  // Send the accessibility focus notification.
  parent_view->NotifyAccessibilityEvent(
      ui::AccessibilityTypes::EVENT_FOCUS, send_native_event);
}

////////////////////////////////////////////////////////////////////////////////
// NativeControlWin, protected:

void NativeControlWin::ShowContextMenu(const gfx::Point& location) {
  if (!context_menu_controller())
    return;

  if (location.x() == -1 && location.y() == -1) {
    View::ShowContextMenu(GetKeyboardContextMenuLocation(),
                          ui::MENU_SOURCE_KEYBOARD);
  } else {
    View::ShowContextMenu(location, ui::MENU_SOURCE_MOUSE);
  }
}

void NativeControlWin::NativeControlCreated(HWND native_control) {
  // Associate this object with the control's HWND so that NativeWidgetWin can
  // find this object when it receives messages from it.
  props_.push_back(new ViewProp(native_control, kNativeControlWinKey, this));
  props_.push_back(ChildWindowMessageProcessor::Register(native_control, this));

  // Subclass so we get WM_KEYDOWN and WM_SETFOCUS messages.
  original_wndproc_ = gfx::SetWindowProc(
      native_control, &NativeControlWin::NativeControlWndProc);

  Attach(native_control);
  // native_view() is now valid.

  // Update the newly created HWND with any resident enabled state.
  EnableWindow(native_view(), enabled());

  // This message ensures that the focus border is shown.
  SendMessage(native_view(), WM_CHANGEUISTATE,
              MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
}

DWORD NativeControlWin::GetAdditionalExStyle() const {
  // If the UI for the view is mirrored, we should make sure we add the
  // extended window style for a right-to-left layout so the subclass creates
  // a mirrored HWND for the underlying control.
  DWORD ex_style = 0;
  if (base::i18n::IsRTL())
    ex_style |= l10n_util::GetExtendedStyles();

  return ex_style;
}

DWORD NativeControlWin::GetAdditionalRTLStyle() const {
  // If the UI for the view is mirrored, we should make sure we add the
  // extended window style for a right-to-left layout so the subclass creates
  // a mirrored HWND for the underlying control.
  DWORD ex_style = 0;
  if (base::i18n::IsRTL())
    ex_style |= l10n_util::GetExtendedTooltipStyles();

  return ex_style;
}

////////////////////////////////////////////////////////////////////////////////
// NativeControlWin, private:

LRESULT NativeControlWin::GetControlColor(UINT message, HDC dc, HWND sender) {
  View *ancestor = this;
  while (ancestor) {
    const Background* background = ancestor->background();
    if (background) {
      HBRUSH brush = background->GetNativeControlBrush();
      if (brush)
        return reinterpret_cast<LRESULT>(brush);
    }
    ancestor = ancestor->parent();
  }

  // COLOR_BTNFACE is the default for dialog box backgrounds.
  return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE));
}

// static
LRESULT NativeControlWin::NativeControlWndProc(HWND window,
                                               UINT message,
                                               WPARAM w_param,
                                               LPARAM l_param) {
  NativeControlWin* native_control = reinterpret_cast<NativeControlWin*>(
      ViewProp::GetValue(window, kNativeControlWinKey));
  DCHECK(native_control);

  if (message == WM_KEYDOWN &&
      native_control->OnKeyDown(static_cast<int>(w_param))) {
      return 0;
  } else if (message == WM_SETFOCUS) {
    // Let the focus manager know that the focus changed.
    FocusManager* focus_manager = native_control->GetFocusManager();
    if (focus_manager) {
      focus_manager->SetFocusedView(native_control->focus_view());
    } else {
      NOTREACHED();
    }
  } else if (message == WM_DESTROY) {
    native_control->props_.clear();
    gfx::SetWindowProc(window, native_control->original_wndproc_);
  }

  return CallWindowProc(native_control->original_wndproc_, window, message,
                        w_param, l_param);
}

}  // namespace views