summaryrefslogtreecommitdiffstats
path: root/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp')
-rw-r--r--Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp937
1 files changed, 937 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp b/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp
new file mode 100644
index 0000000..64b9fb5
--- /dev/null
+++ b/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp
@@ -0,0 +1,937 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// NOTE: This implementation is very similar to the implementation of popups in WebCore::PopupMenuWin.
+// We should try and factor out the common bits and share them.
+
+#include "WebPopupMenuProxyWin.h"
+
+#include "WebView.h"
+#include <WebCore/WebCoreInstanceHandle.h>
+#include <WebCore/ScrollbarTheme.h>
+#include <WebCore/BitmapInfo.h>
+#include <WebCore/PlatformMouseEvent.h>
+#include <windowsx.h>
+
+using namespace WebCore;
+using namespace std;
+
+namespace WebKit {
+
+static const LPCWSTR kWebKit2WebPopupMenuProxyWindowClassName = L"WebKit2WebPopupMenuProxyWindowClass";
+
+static const int defaultAnimationDuration = 200;
+static const int maxPopupHeight = 320;
+static const int popupWindowBorderWidth = 1;
+static const int separatorPadding = 4;
+static const int separatorHeight = 1;
+
+// This is used from within our custom message pump when we want to send a
+// message to the web view and not have our message stolen and sent to
+// the popup window.
+static const UINT WM_HOST_WINDOW_FIRST = WM_USER;
+static const UINT WM_HOST_WINDOW_CHAR = WM_USER + WM_CHAR;
+static const UINT WM_HOST_WINDOW_MOUSEMOVE = WM_USER + WM_MOUSEMOVE;
+
+static inline bool isASCIIPrintable(unsigned c)
+{
+ return c >= 0x20 && c <= 0x7E;
+}
+
+static void translatePoint(LPARAM& lParam, HWND from, HWND to)
+{
+ POINT pt;
+ pt.x = static_cast<short>(GET_X_LPARAM(lParam));
+ pt.y = static_cast<short>(GET_Y_LPARAM(lParam));
+ ::MapWindowPoints(from, to, &pt, 1);
+ lParam = MAKELPARAM(pt.x, pt.y);
+}
+
+LRESULT CALLBACK WebPopupMenuProxyWin::WebPopupMenuProxyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ LONG_PTR longPtr = ::GetWindowLongPtr(hWnd, 0);
+
+ if (WebPopupMenuProxyWin* popupMenuProxy = reinterpret_cast<WebPopupMenuProxyWin*>(longPtr))
+ return popupMenuProxy->wndProc(hWnd, message, wParam, lParam);
+
+ if (message == WM_CREATE) {
+ LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
+
+ // Associate the WebView with the window.
+ ::SetWindowLongPtr(hWnd, 0, (LONG_PTR)createStruct->lpCreateParams);
+ return 0;
+ }
+
+ return ::DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+LRESULT WebPopupMenuProxyWin::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT lResult = 0;
+ bool handled = true;
+
+ switch (message) {
+ case WM_MOUSEACTIVATE:
+ lResult = onMouseActivate(hWnd, message, wParam, lParam, handled);
+ break;
+ case WM_SIZE:
+ lResult = onSize(hWnd, message, wParam, lParam, handled);
+ break;
+ case WM_KEYDOWN:
+ lResult = onKeyDown(hWnd, message, wParam, lParam, handled);
+ break;
+ case WM_CHAR:
+ lResult = onChar(hWnd, message, wParam, lParam, handled);
+ break;
+ case WM_MOUSEMOVE:
+ lResult = onMouseMove(hWnd, message, wParam, lParam, handled);
+ break;
+ case WM_LBUTTONDOWN:
+ lResult = onLButtonDown(hWnd, message, wParam, lParam, handled);
+ break;
+ case WM_LBUTTONUP:
+ lResult = onLButtonUp(hWnd, message, wParam, lParam, handled);
+ break;
+ case WM_MOUSEWHEEL:
+ lResult = onMouseWheel(hWnd, message, wParam, lParam, handled);
+ break;
+ case WM_PAINT:
+ lResult = onPaint(hWnd, message, wParam, lParam, handled);
+ break;
+ case WM_PRINTCLIENT:
+ lResult = onPrintClient(hWnd, message, wParam, lParam, handled);
+ break;
+ default:
+ handled = false;
+ break;
+ }
+
+ if (!handled)
+ lResult = ::DefWindowProc(hWnd, message, wParam, lParam);
+
+ return lResult;
+}
+
+bool WebPopupMenuProxyWin::registerWindowClass()
+{
+ static bool haveRegisteredWindowClass = false;
+ if (haveRegisteredWindowClass)
+ return true;
+ haveRegisteredWindowClass = true;
+
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DROPSHADOW;
+ wcex.lpfnWndProc = WebPopupMenuProxyWin::WebPopupMenuProxyWndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = sizeof(WebPopupMenuProxyWin*);
+ wcex.hInstance = instanceHandle();
+ wcex.hIcon = 0;
+ wcex.hCursor = ::LoadCursor(0, IDC_ARROW);
+ wcex.hbrBackground = 0;
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = kWebKit2WebPopupMenuProxyWindowClassName;
+ wcex.hIconSm = 0;
+
+ return !!::RegisterClassEx(&wcex);
+}
+
+WebPopupMenuProxyWin::WebPopupMenuProxyWin(WebView* webView, WebPopupMenuProxy::Client* client)
+ : WebPopupMenuProxy(client)
+ , m_webView(webView)
+ , m_newSelectedIndex(0)
+ , m_popup(0)
+ , m_DC(0)
+ , m_bmp(0)
+ , m_itemHeight(0)
+ , m_scrollOffset(0)
+ , m_wheelDelta(0)
+ , m_focusedIndex(0)
+ , m_wasClicked(false)
+ , m_scrollbarCapturingMouse(false)
+ , m_showPopup(false)
+{
+}
+
+WebPopupMenuProxyWin::~WebPopupMenuProxyWin()
+{
+ if (m_bmp)
+ ::DeleteObject(m_bmp);
+ if (m_DC)
+ ::DeleteDC(m_DC);
+ if (m_popup)
+ ::DestroyWindow(m_popup);
+ if (m_scrollbar)
+ m_scrollbar->setParent(0);
+}
+
+void WebPopupMenuProxyWin::showPopupMenu(const IntRect& rect, const Vector<WebPopupItem>& items, const PlatformPopupMenuData& data, int32_t selectedIndex)
+{
+ m_items = items;
+ m_data = data;
+ m_newSelectedIndex = selectedIndex;
+
+ calculatePositionAndSize(rect);
+ if (clientRect().isEmpty())
+ return;
+
+ HWND hostWindow = m_webView->window();
+
+ if (!m_scrollbar && visibleItems() < m_items.size()) {
+ m_scrollbar = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, SmallScrollbar);
+ m_scrollbar->styleChanged();
+ }
+
+ if (!m_popup) {
+ registerWindowClass();
+
+ DWORD exStyle = WS_EX_LTRREADING;
+
+ m_popup = ::CreateWindowEx(exStyle, kWebKit2WebPopupMenuProxyWindowClassName, TEXT("PopupMenu"),
+ WS_POPUP | WS_BORDER,
+ m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(),
+ hostWindow, 0, instanceHandle(), this);
+
+ if (!m_popup)
+ return;
+ }
+
+ BOOL shouldAnimate = FALSE;
+ ::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0);
+
+ if (shouldAnimate) {
+ RECT viewRect = {0};
+ ::GetWindowRect(hostWindow, &viewRect);
+
+ if (!::IsRectEmpty(&viewRect)) {
+ // Popups should slide into view away from the <select> box
+ // NOTE: This may have to change for Vista
+ DWORD slideDirection = (m_windowRect.y() < viewRect.top + rect.location().y()) ? AW_VER_NEGATIVE : AW_VER_POSITIVE;
+
+ ::AnimateWindow(m_popup, defaultAnimationDuration, AW_SLIDE | slideDirection);
+ }
+ } else
+ ::ShowWindow(m_popup, SW_SHOWNOACTIVATE);
+
+
+ int index = selectedIndex;
+ if (index >= 0)
+ setFocusedIndex(index);
+
+ m_showPopup = true;
+
+ // Protect the popup menu in case its owner is destroyed while we're running the message pump.
+ RefPtr<WebPopupMenuProxyWin> protect(this);
+
+ ::SetCapture(hostWindow);
+
+ MSG msg;
+ HWND activeWindow;
+
+ while (::GetMessage(&msg, 0, 0, 0)) {
+ switch (msg.message) {
+ case WM_HOST_WINDOW_MOUSEMOVE:
+ case WM_HOST_WINDOW_CHAR:
+ if (msg.hwnd == m_popup) {
+ // This message should be sent to the host window.
+ msg.hwnd = hostWindow;
+ msg.message -= WM_HOST_WINDOW_FIRST;
+ }
+ break;
+
+ // Steal mouse messages.
+ case WM_NCMOUSEMOVE:
+ case WM_NCLBUTTONDOWN:
+ case WM_NCLBUTTONUP:
+ case WM_NCLBUTTONDBLCLK:
+ case WM_NCRBUTTONDOWN:
+ case WM_NCRBUTTONUP:
+ case WM_NCRBUTTONDBLCLK:
+ case WM_NCMBUTTONDOWN:
+ case WM_NCMBUTTONUP:
+ case WM_NCMBUTTONDBLCLK:
+ case WM_MOUSEWHEEL:
+ msg.hwnd = m_popup;
+ break;
+
+ // These mouse messages use client coordinates so we need to convert them.
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_RBUTTONDBLCLK:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_MBUTTONDBLCLK: {
+ // Translate the coordinate.
+ translatePoint(msg.lParam, msg.hwnd, m_popup);
+ msg.hwnd = m_popup;
+ break;
+ }
+
+ // Steal all keyboard messages.
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_CHAR:
+ case WM_DEADCHAR:
+ case WM_SYSKEYUP:
+ case WM_SYSCHAR:
+ case WM_SYSDEADCHAR:
+ msg.hwnd = m_popup;
+ break;
+ }
+
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+
+ if (!m_showPopup)
+ break;
+ activeWindow = ::GetActiveWindow();
+ if (activeWindow != hostWindow && !::IsChild(activeWindow, hostWindow))
+ break;
+ if (::GetCapture() != hostWindow)
+ break;
+ }
+
+ if (::GetCapture() == hostWindow)
+ ::ReleaseCapture();
+
+ m_showPopup = false;
+ ::ShowWindow(m_popup, SW_HIDE);
+
+ m_client->valueChangedForPopupMenu(this, m_newSelectedIndex);
+}
+
+void WebPopupMenuProxyWin::hidePopupMenu()
+{
+ if (!m_showPopup)
+ return;
+ m_showPopup = false;
+
+ ::ShowWindow(m_popup, SW_HIDE);
+
+ // Post a WM_NULL message to wake up the message pump if necessary.
+ ::PostMessage(m_popup, WM_NULL, 0, 0);
+}
+
+void WebPopupMenuProxyWin::calculatePositionAndSize(const IntRect& rect)
+{
+ // Convert the rect (which is in view cooridates) into screen coordinates.
+ IntRect rectInScreenCoords = rect;
+ POINT location(rectInScreenCoords .location());
+ if (!::ClientToScreen(m_webView->window(), &location))
+ return;
+ rectInScreenCoords.setLocation(location);
+
+ int itemCount = m_items.size();
+ m_itemHeight = m_data.m_itemHeight;
+
+ int naturalHeight = m_itemHeight * itemCount;
+ int popupHeight = min(maxPopupHeight, naturalHeight);
+
+ // The popup should show an integral number of items (i.e. no partial items should be visible)
+ popupHeight -= popupHeight % m_itemHeight;
+
+ // Next determine its width
+ int popupWidth = m_data.m_popupWidth;
+
+ if (naturalHeight > maxPopupHeight) {
+ // We need room for a scrollbar
+ popupWidth += ScrollbarTheme::nativeTheme()->scrollbarThickness(SmallScrollbar);
+ }
+
+ popupHeight += 2 * popupWindowBorderWidth;
+
+ // The popup should be at least as wide as the control on the page
+ popupWidth = max(rectInScreenCoords.width() - m_data.m_clientInsetLeft - m_data.m_clientInsetRight, popupWidth);
+
+ // Always left-align items in the popup. This matches popup menus on the mac.
+ int popupX = rectInScreenCoords.x() + m_data.m_clientInsetLeft;
+
+ IntRect popupRect(popupX, rectInScreenCoords.bottom(), popupWidth, popupHeight);
+
+ // The popup needs to stay within the bounds of the screen and not overlap any toolbars
+ HMONITOR monitor = ::MonitorFromWindow(m_webView->window(), MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFOEX monitorInfo;
+ monitorInfo.cbSize = sizeof(MONITORINFOEX);
+ ::GetMonitorInfo(monitor, &monitorInfo);
+ FloatRect screen = monitorInfo.rcWork;
+
+ // Check that we don't go off the screen vertically
+ if (popupRect.bottom() > screen.height()) {
+ // The popup will go off the screen, so try placing it above the client
+ if (rectInScreenCoords.y() - popupRect.height() < 0) {
+ // The popup won't fit above, either, so place it whereever's bigger and resize it to fit
+ if ((rectInScreenCoords.y() + rectInScreenCoords.height() / 2) < (screen.height() / 2)) {
+ // Below is bigger
+ popupRect.setHeight(screen.height() - popupRect.y());
+ } else {
+ // Above is bigger
+ popupRect.setY(0);
+ popupRect.setHeight(rectInScreenCoords.y());
+ }
+ } else {
+ // The popup fits above, so reposition it
+ popupRect.setY(rectInScreenCoords.y() - popupRect.height());
+ }
+ }
+
+ // Check that we don't go off the screen horizontally
+ if (popupRect.x() < screen.x()) {
+ popupRect.setWidth(popupRect.width() - (screen.x() - popupRect.x()));
+ popupRect.setX(screen.x());
+ }
+
+ m_windowRect = popupRect;
+}
+
+IntRect WebPopupMenuProxyWin::clientRect() const
+{
+ IntRect clientRect = m_windowRect;
+ clientRect.inflate(-popupWindowBorderWidth);
+ clientRect.setLocation(IntPoint(0, 0));
+ return clientRect;
+}
+
+void WebPopupMenuProxyWin::invalidateItem(int index)
+{
+ if (!m_popup)
+ return;
+
+ IntRect damageRect(clientRect());
+ damageRect.setY(m_itemHeight * (index - m_scrollOffset));
+ damageRect.setHeight(m_itemHeight);
+ if (m_scrollbar)
+ damageRect.setWidth(damageRect.width() - m_scrollbar->frameRect().width());
+
+ RECT r = damageRect;
+ ::InvalidateRect(m_popup, &r, TRUE);
+}
+
+// ScrollbarClient
+
+int WebPopupMenuProxyWin::scrollSize(ScrollbarOrientation orientation) const
+{
+ return ((orientation == VerticalScrollbar) && m_scrollbar) ? (m_scrollbar->totalSize() - m_scrollbar->visibleSize()) : 0;
+}
+
+void WebPopupMenuProxyWin::setScrollOffsetFromAnimation(const IntPoint& offset)
+{
+ if (m_scrollbar)
+ m_scrollbar->setValue(offset.y(), Scrollbar::FromScrollAnimator);
+}
+
+void WebPopupMenuProxyWin::valueChanged(Scrollbar* scrollBar)
+{
+ ASSERT(m_scrollbar);
+
+ if (!m_popup)
+ return;
+
+ int offset = scrollBar->value();
+
+ if (m_scrollOffset == offset)
+ return;
+
+ int scrolledLines = m_scrollOffset - offset;
+ m_scrollOffset = offset;
+
+ UINT flags = SW_INVALIDATE;
+
+#ifdef CAN_SET_SMOOTH_SCROLLING_DURATION
+ BOOL shouldSmoothScroll = FALSE;
+ ::SystemParametersInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &shouldSmoothScroll, 0);
+ if (shouldSmoothScroll)
+ flags |= MAKEWORD(SW_SMOOTHSCROLL, smoothScrollAnimationDuration);
+#endif
+
+ IntRect listRect = clientRect();
+ if (m_scrollbar)
+ listRect.setWidth(listRect.width() - m_scrollbar->frameRect().width());
+ RECT r = listRect;
+ ::ScrollWindowEx(m_popup, 0, scrolledLines * m_itemHeight, &r, 0, 0, 0, flags);
+ if (m_scrollbar) {
+ r = m_scrollbar->frameRect();
+ ::InvalidateRect(m_popup, &r, TRUE);
+ }
+ ::UpdateWindow(m_popup);
+}
+
+void WebPopupMenuProxyWin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
+{
+ IntRect scrollRect = rect;
+ scrollRect.move(scrollbar->x(), scrollbar->y());
+ RECT r = scrollRect;
+ ::InvalidateRect(m_popup, &r, false);
+}
+
+// Message pump messages.
+
+LRESULT WebPopupMenuProxyWin::onMouseActivate(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled)
+{
+ handled = true;
+ return MA_NOACTIVATE;
+}
+
+LRESULT WebPopupMenuProxyWin::onSize(HWND hWnd, UINT message, WPARAM, LPARAM lParam, bool& handled)
+{
+ handled = true;
+ if (!scrollbar())
+ return 0;
+
+ IntSize size(LOWORD(lParam), HIWORD(lParam));
+ scrollbar()->setFrameRect(IntRect(size.width() - scrollbar()->width(), 0, scrollbar()->width(), size.height()));
+
+ int visibleItems = this->visibleItems();
+ scrollbar()->setEnabled(visibleItems < m_items.size());
+ scrollbar()->setSteps(1, max(1, visibleItems - 1));
+ scrollbar()->setProportion(visibleItems, m_items.size());
+ return 0;
+}
+
+LRESULT WebPopupMenuProxyWin::onKeyDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled)
+{
+ handled = true;
+
+ LRESULT lResult = 0;
+ switch (LOWORD(wParam)) {
+ case VK_DOWN:
+ case VK_RIGHT:
+ down();
+ break;
+ case VK_UP:
+ case VK_LEFT:
+ up();
+ break;
+ case VK_HOME:
+ focusFirst();
+ break;
+ case VK_END:
+ focusLast();
+ break;
+ case VK_PRIOR:
+ if (focusedIndex() != scrollOffset()) {
+ // Set the selection to the first visible item
+ int firstVisibleItem = scrollOffset();
+ up(focusedIndex() - firstVisibleItem);
+ } else {
+ // The first visible item is selected, so move the selection back one page
+ up(visibleItems());
+ }
+ break;
+ case VK_NEXT: {
+ int lastVisibleItem = scrollOffset() + visibleItems() - 1;
+ if (focusedIndex() != lastVisibleItem) {
+ // Set the selection to the last visible item
+ down(lastVisibleItem - focusedIndex());
+ } else {
+ // The last visible item is selected, so move the selection forward one page
+ down(visibleItems());
+ }
+ break;
+ }
+ case VK_TAB:
+ ::SendMessage(m_webView->window(), message, wParam, lParam);
+ hide();
+ break;
+ case VK_ESCAPE:
+ hide();
+ break;
+ default:
+ if (isASCIIPrintable(wParam)) {
+ // Send the keydown to the WebView so it can be used for type-to-select.
+ // Since we know that the virtual key is ASCII printable, it's OK to convert this to
+ // a WM_CHAR message. (We don't want to call TranslateMessage because that will post a
+ // WM_CHAR message that will be stolen and redirected to the popup HWND.
+ ::PostMessage(m_popup, WM_HOST_WINDOW_CHAR, wParam, lParam);
+ } else
+ lResult = 1;
+ break;
+ }
+
+ return lResult;
+}
+
+LRESULT WebPopupMenuProxyWin::onChar(HWND hWnd, UINT message, WPARAM wParam, LPARAM, bool& handled)
+{
+ handled = true;
+
+ LRESULT lResult = 0;
+ int index;
+ switch (wParam) {
+ case 0x0D: // Enter/Return
+ hide();
+ index = focusedIndex();
+ ASSERT(index >= 0);
+ // FIXME: Do we need to send back the index right away?
+ m_newSelectedIndex = index;
+ break;
+ case 0x1B: // Escape
+ hide();
+ break;
+ case 0x09: // TAB
+ case 0x08: // Backspace
+ case 0x0A: // Linefeed
+ default: // Character
+ lResult = 1;
+ break;
+ }
+
+ return lResult;
+}
+
+LRESULT WebPopupMenuProxyWin::onMouseMove(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled)
+{
+ handled = true;
+
+ IntPoint mousePoint(MAKEPOINTS(lParam));
+ if (scrollbar()) {
+ IntRect scrollBarRect = scrollbar()->frameRect();
+ if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
+ // Put the point into coordinates relative to the scroll bar
+ mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
+ PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
+ scrollbar()->mouseMoved(event);
+ return 0;
+ }
+ }
+
+ BOOL shouldHotTrack = FALSE;
+ ::SystemParametersInfo(SPI_GETHOTTRACKING, 0, &shouldHotTrack, 0);
+
+ RECT bounds;
+ ::GetClientRect(m_popup, &bounds);
+ if (!::PtInRect(&bounds, mousePoint) && !(wParam & MK_LBUTTON)) {
+ // When the mouse is not inside the popup menu and the left button isn't down, just
+ // repost the message to the web view.
+
+ // Translate the coordinate.
+ translatePoint(lParam, m_popup, m_webView->window());
+
+ ::PostMessage(m_popup, WM_HOST_WINDOW_MOUSEMOVE, wParam, lParam);
+ return 0;
+ }
+
+ if ((shouldHotTrack || wParam & MK_LBUTTON) && ::PtInRect(&bounds, mousePoint))
+ setFocusedIndex(listIndexAtPoint(mousePoint), true);
+
+ return 0;
+}
+
+LRESULT WebPopupMenuProxyWin::onLButtonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled)
+{
+ handled = true;
+
+ IntPoint mousePoint(MAKEPOINTS(lParam));
+ if (scrollbar()) {
+ IntRect scrollBarRect = scrollbar()->frameRect();
+ if (scrollBarRect.contains(mousePoint)) {
+ // Put the point into coordinates relative to the scroll bar
+ mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
+ PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
+ scrollbar()->mouseDown(event);
+ setScrollbarCapturingMouse(true);
+ return 0;
+ }
+ }
+
+ // If the mouse is inside the window, update the focused index. Otherwise,
+ // hide the popup.
+ RECT bounds;
+ ::GetClientRect(m_popup, &bounds);
+ if (::PtInRect(&bounds, mousePoint))
+ setFocusedIndex(listIndexAtPoint(mousePoint), true);
+ else
+ hide();
+
+ return 0;
+}
+
+
+LRESULT WebPopupMenuProxyWin::onLButtonUp(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled)
+{
+ handled = true;
+
+ IntPoint mousePoint(MAKEPOINTS(lParam));
+ if (scrollbar()) {
+ IntRect scrollBarRect = scrollbar()->frameRect();
+ if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
+ setScrollbarCapturingMouse(false);
+ // Put the point into coordinates relative to the scroll bar
+ mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
+ PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
+ scrollbar()->mouseUp();
+ // FIXME: This is a hack to work around Scrollbar not invalidating correctly when it doesn't have a parent widget
+ RECT r = scrollBarRect;
+ ::InvalidateRect(m_popup, &r, TRUE);
+ return 0;
+ }
+ }
+ // Only hide the popup if the mouse is inside the popup window.
+ RECT bounds;
+ ::GetClientRect(m_popup, &bounds);
+ if (::PtInRect(&bounds, mousePoint)) {
+ hide();
+ int index = focusedIndex();
+ if (index >= 0) {
+ // FIXME: Do we need to send back the index right away?
+ m_newSelectedIndex = index;
+ }
+ }
+
+ return 0;
+}
+
+LRESULT WebPopupMenuProxyWin::onMouseWheel(HWND hWnd, UINT message, WPARAM wParam, LPARAM, bool& handled)
+{
+ handled = true;
+
+ if (!scrollbar())
+ return 0;
+
+ int i = 0;
+ for (incrementWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam)); abs(wheelDelta()) >= WHEEL_DELTA; reduceWheelDelta(WHEEL_DELTA)) {
+ if (wheelDelta() > 0)
+ ++i;
+ else
+ --i;
+ }
+ scrollbar()->scroll(i > 0 ? ScrollUp : ScrollDown, ScrollByLine, abs(i));
+ return 0;
+}
+
+LRESULT WebPopupMenuProxyWin::onPaint(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled)
+{
+ handled = true;
+
+ PAINTSTRUCT paintStruct;
+ ::BeginPaint(m_popup, &paintStruct);
+ paint(paintStruct.rcPaint, paintStruct.hdc);
+ ::EndPaint(m_popup, &paintStruct);
+
+ return 0;
+}
+
+LRESULT WebPopupMenuProxyWin::onPrintClient(HWND hWnd, UINT, WPARAM wParam, LPARAM, bool& handled)
+{
+ handled = true;
+
+ HDC hdc = reinterpret_cast<HDC>(wParam);
+ paint(clientRect(), hdc);
+
+ return 0;
+}
+
+bool WebPopupMenuProxyWin::down(unsigned lines)
+{
+ int size = m_items.size();
+
+ int lastSelectableIndex, selectedListIndex;
+ lastSelectableIndex = selectedListIndex = focusedIndex();
+ for (int i = selectedListIndex + 1; i >= 0 && i < size; ++i) {
+ if (m_items[i].m_isEnabled) {
+ lastSelectableIndex = i;
+ if (i >= selectedListIndex + (int)lines)
+ break;
+ }
+ }
+
+ return setFocusedIndex(lastSelectableIndex);
+}
+
+bool WebPopupMenuProxyWin::up(unsigned lines)
+{
+ int size = m_items.size();
+
+ int lastSelectableIndex, selectedListIndex;
+ lastSelectableIndex = selectedListIndex = focusedIndex();
+ for (int i = selectedListIndex - 1; i >= 0 && i < size; --i) {
+ if (m_items[i].m_isEnabled) {
+ lastSelectableIndex = i;
+ if (i <= selectedListIndex - (int)lines)
+ break;
+ }
+ }
+
+ return setFocusedIndex(lastSelectableIndex);
+}
+
+void WebPopupMenuProxyWin::paint(const IntRect& damageRect, HDC hdc)
+{
+ if (!m_popup)
+ return;
+
+ if (!m_DC) {
+ m_DC = ::CreateCompatibleDC(::GetDC(m_popup));
+ if (!m_DC)
+ return;
+ }
+
+ if (m_bmp) {
+ bool keepBitmap = false;
+ BITMAP bitmap;
+ if (::GetObject(m_bmp, sizeof(bitmap), &bitmap))
+ keepBitmap = bitmap.bmWidth == clientRect().width() && bitmap.bmHeight == clientRect().height();
+ if (!keepBitmap) {
+ ::DeleteObject(m_bmp);
+ m_bmp = 0;
+ }
+ }
+
+ if (!m_bmp) {
+ BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size());
+ void* pixels = 0;
+ m_bmp = ::CreateDIBSection(m_DC, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
+ if (!m_bmp)
+ return;
+ ::SelectObject(m_DC, m_bmp);
+ }
+
+ GraphicsContext context(m_DC);
+
+ IntRect translatedDamageRect = damageRect;
+ translatedDamageRect.move(IntSize(0, m_scrollOffset * m_itemHeight));
+ m_data.m_notSelectedBackingStore->paint(context, damageRect.location(), translatedDamageRect);
+
+ IntRect selectedIndexRectInBackingStore(0, focusedIndex() * m_itemHeight, m_data.m_selectedBackingStore->size().width(), m_itemHeight);
+ IntPoint selectedIndexDstPoint = selectedIndexRectInBackingStore.location();
+ selectedIndexDstPoint.move(0, -m_scrollOffset * m_itemHeight);
+
+ m_data.m_selectedBackingStore->paint(context, selectedIndexDstPoint, selectedIndexRectInBackingStore);
+
+ if (m_scrollbar)
+ m_scrollbar->paint(&context, damageRect);
+
+ HDC localDC = hdc ? hdc : ::GetDC(m_popup);
+
+ ::BitBlt(localDC, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC, damageRect.x(), damageRect.y(), SRCCOPY);
+
+ if (!hdc)
+ ::ReleaseDC(m_popup, localDC);
+}
+
+bool WebPopupMenuProxyWin::setFocusedIndex(int i, bool hotTracking)
+{
+ if (i < 0 || i >= m_items.size() || i == focusedIndex())
+ return false;
+
+ if (!m_items[i].m_isEnabled)
+ return false;
+
+ invalidateItem(focusedIndex());
+ invalidateItem(i);
+
+ m_focusedIndex = i;
+
+ if (!hotTracking)
+ m_client->setTextFromItemForPopupMenu(this, i);
+
+ if (!scrollToRevealSelection())
+ ::UpdateWindow(m_popup);
+
+ return true;
+}
+
+int WebPopupMenuProxyWin::visibleItems() const
+{
+ return clientRect().height() / m_itemHeight;
+}
+
+int WebPopupMenuProxyWin::listIndexAtPoint(const IntPoint& point) const
+{
+ return m_scrollOffset + point.y() / m_itemHeight;
+}
+
+int WebPopupMenuProxyWin::focusedIndex() const
+{
+ return m_focusedIndex;
+}
+
+void WebPopupMenuProxyWin::focusFirst()
+{
+ int size = m_items.size();
+
+ for (int i = 0; i < size; ++i) {
+ if (m_items[i].m_isEnabled) {
+ setFocusedIndex(i);
+ break;
+ }
+ }
+}
+
+void WebPopupMenuProxyWin::focusLast()
+{
+ int size = m_items.size();
+
+ for (int i = size - 1; i > 0; --i) {
+ if (m_items[i].m_isEnabled) {
+ setFocusedIndex(i);
+ break;
+ }
+ }
+}
+
+
+void WebPopupMenuProxyWin::incrementWheelDelta(int delta)
+{
+ m_wheelDelta += delta;
+}
+
+void WebPopupMenuProxyWin::reduceWheelDelta(int delta)
+{
+ ASSERT(delta >= 0);
+ ASSERT(delta <= abs(m_wheelDelta));
+
+ if (m_wheelDelta > 0)
+ m_wheelDelta -= delta;
+ else if (m_wheelDelta < 0)
+ m_wheelDelta += delta;
+ else
+ return;
+}
+
+bool WebPopupMenuProxyWin::scrollToRevealSelection()
+{
+ if (!m_scrollbar)
+ return false;
+
+ int index = focusedIndex();
+
+ if (index < m_scrollOffset) {
+ m_scrollbar->setValue(index, Scrollbar::NotFromScrollAnimator);
+ return true;
+ }
+
+ if (index >= m_scrollOffset + visibleItems()) {
+ m_scrollbar->setValue(index - visibleItems() + 1, Scrollbar::NotFromScrollAnimator);
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace WebKit