2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2007-2009 Torch Mobile Inc.
4 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
24 #include "PopupMenuWin.h"
26 #include "BitmapInfo.h"
28 #include "FloatRect.h"
29 #include "FontSelector.h"
31 #include "FrameView.h"
32 #include "GraphicsContext.h"
33 #include "HTMLNames.h"
34 #include "HostWindow.h"
36 #include "PlatformMouseEvent.h"
37 #include "PlatformScreen.h"
38 #include "RenderTheme.h"
39 #include "RenderView.h"
40 #include "Scrollbar.h"
41 #include "ScrollbarTheme.h"
42 #include "SimpleFontData.h"
44 #include "WebCoreInstanceHandle.h"
49 #define MAKEPOINTS(l) (*((POINTS FAR *)&(l)))
52 #define HIGH_BIT_MASK_SHORT 0x8000
58 using namespace HTMLNames;
60 // Default Window animation duration in milliseconds
61 static const int defaultAnimationDuration = 200;
62 // Maximum height of a popup window
63 static const int maxPopupHeight = 320;
65 const int optionSpacingMiddle = 1;
66 const int popupWindowBorderWidth = 1;
68 static LPCWSTR kPopupWindowClassName = L"PopupWindowClass";
70 // This is used from within our custom message pump when we want to send a
71 // message to the web view and not have our message stolen and sent to
73 static const UINT WM_HOST_WINDOW_FIRST = WM_USER;
74 static const UINT WM_HOST_WINDOW_CHAR = WM_USER + WM_CHAR;
75 static const UINT WM_HOST_WINDOW_MOUSEMOVE = WM_USER + WM_MOUSEMOVE;
77 // FIXME: Remove this as soon as practical.
78 static inline bool isASCIIPrintable(unsigned c)
80 return c >= 0x20 && c <= 0x7E;
83 static void translatePoint(LPARAM& lParam, HWND from, HWND to)
86 pt.x = (short)GET_X_LPARAM(lParam);
87 pt.y = (short)GET_Y_LPARAM(lParam);
88 ::MapWindowPoints(from, to, &pt, 1);
89 lParam = MAKELPARAM(pt.x, pt.y);
92 PopupMenuWin::PopupMenuWin(PopupMenuClient* client)
93 : m_popupClient(client)
103 , m_scrollbarCapturingMouse(false)
108 PopupMenuWin::~PopupMenuWin()
111 ::DeleteObject(m_bmp);
115 ::DestroyWindow(m_popup);
117 m_scrollbar->setParent(0);
120 void PopupMenuWin::disconnectClient()
125 LPCWSTR PopupMenuWin::popupClassName()
127 return kPopupWindowClassName;
130 void PopupMenuWin::show(const IntRect& r, FrameView* view, int index)
132 calculatePositionAndSize(r, view);
133 if (clientRect().isEmpty())
136 HWND hostWindow = view->hostWindow()->platformPageClient();
138 if (!m_scrollbar && visibleItems() < client()->listSize()) {
139 // We need a scroll bar
140 m_scrollbar = client()->createScrollbar(this, VerticalScrollbar, SmallScrollbar);
141 m_scrollbar->styleChanged();
147 DWORD exStyle = WS_EX_LTRREADING;
149 m_popup = ::CreateWindowExW(exStyle, kPopupWindowClassName, L"PopupMenu",
150 WS_POPUP | WS_BORDER,
151 m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(),
152 hostWindow, 0, WebCore::instanceHandle(), this);
157 // We need to reposition the popup window.
158 ::MoveWindow(m_popup, m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), false);
161 // Determine whether we should animate our popups
162 // Note: Must use 'BOOL' and 'FALSE' instead of 'bool' and 'false' to avoid stack corruption with SystemParametersInfo
163 BOOL shouldAnimate = FALSE;
165 ::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0);
169 ::GetWindowRect(hostWindow, &viewRect);
171 if (!::IsRectEmpty(&viewRect)) {
172 // Popups should slide into view away from the <select> box
173 // NOTE: This may have to change for Vista
174 DWORD slideDirection = (m_windowRect.y() < viewRect.top + view->contentsToWindow(r.location()).y()) ? AW_VER_NEGATIVE : AW_VER_POSITIVE;
176 ::AnimateWindow(m_popup, defaultAnimationDuration, AW_SLIDE | slideDirection);
180 ::ShowWindow(m_popup, SW_SHOWNOACTIVATE);
183 int index = client()->selectedIndex();
185 setFocusedIndex(index);
190 // Protect the popup menu in case its owner is destroyed while we're running the message pump.
191 RefPtr<PopupMenu> protect(this);
193 ::SetCapture(hostWindow);
198 while (::GetMessage(&msg, 0, 0, 0)) {
199 switch (msg.message) {
200 case WM_HOST_WINDOW_MOUSEMOVE:
201 case WM_HOST_WINDOW_CHAR:
202 if (msg.hwnd == m_popup) {
203 // This message should be sent to the host window.
204 msg.hwnd = hostWindow;
205 msg.message -= WM_HOST_WINDOW_FIRST;
209 // Steal mouse messages.
212 case WM_NCLBUTTONDOWN:
214 case WM_NCLBUTTONDBLCLK:
215 case WM_NCRBUTTONDOWN:
217 case WM_NCRBUTTONDBLCLK:
218 case WM_NCMBUTTONDOWN:
220 case WM_NCMBUTTONDBLCLK:
226 // These mouse messages use client coordinates so we need to convert them.
230 case WM_LBUTTONDBLCLK:
233 case WM_RBUTTONDBLCLK:
236 case WM_MBUTTONDBLCLK: {
237 // Translate the coordinate.
238 translatePoint(msg.lParam, msg.hwnd, m_popup);
244 // Steal all keyboard messages.
257 ::TranslateMessage(&msg);
258 ::DispatchMessage(&msg);
265 activeWindow = ::GetActiveWindow();
266 if (activeWindow != hostWindow && !::IsChild(activeWindow, hostWindow))
268 if (::GetCapture() != hostWindow)
272 if (::GetCapture() == hostWindow)
275 // We're done, hide the popup if necessary.
279 void PopupMenuWin::hide()
286 ::ShowWindow(m_popup, SW_HIDE);
289 client()->popupDidHide();
291 // Post a WM_NULL message to wake up the message pump if necessary.
292 ::PostMessage(m_popup, WM_NULL, 0, 0);
295 void PopupMenuWin::calculatePositionAndSize(const IntRect& r, FrameView* v)
297 // r is in absolute document coordinates, but we want to be in screen coordinates
299 // First, move to WebView coordinates
300 IntRect rScreenCoords(v->contentsToWindow(r.location()), r.size());
302 // Then, translate to screen coordinates
303 POINT location(rScreenCoords.location());
304 if (!::ClientToScreen(v->hostWindow()->platformPageClient(), &location))
307 rScreenCoords.setLocation(location);
309 // First, determine the popup's height
310 int itemCount = client()->listSize();
311 m_itemHeight = client()->menuStyle().font().fontMetrics().height() + optionSpacingMiddle;
312 int naturalHeight = m_itemHeight * itemCount;
313 int popupHeight = min(maxPopupHeight, naturalHeight);
314 // The popup should show an integral number of items (i.e. no partial items should be visible)
315 popupHeight -= popupHeight % m_itemHeight;
317 // Next determine its width
319 for (int i = 0; i < itemCount; ++i) {
320 String text = client()->itemText(i);
324 Font itemFont = client()->menuStyle().font();
325 if (client()->itemIsLabel(i)) {
326 FontDescription d = itemFont.fontDescription();
327 d.setWeight(d.bolderWeight());
328 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
329 itemFont.update(m_popupClient->fontSelector());
332 popupWidth = max(popupWidth, static_cast<int>(ceilf(itemFont.width(TextRun(text.characters(), text.length())))));
335 if (naturalHeight > maxPopupHeight)
336 // We need room for a scrollbar
337 popupWidth += ScrollbarTheme::nativeTheme()->scrollbarThickness(SmallScrollbar);
339 // Add padding to align the popup text with the <select> text
340 popupWidth += max(0, client()->clientPaddingRight() - client()->clientInsetRight()) + max(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
342 // Leave room for the border
343 popupWidth += 2 * popupWindowBorderWidth;
344 popupHeight += 2 * popupWindowBorderWidth;
346 // The popup should be at least as wide as the control on the page
347 popupWidth = max(rScreenCoords.width() - client()->clientInsetLeft() - client()->clientInsetRight(), popupWidth);
349 // Always left-align items in the popup. This matches popup menus on the mac.
350 int popupX = rScreenCoords.x() + client()->clientInsetLeft();
352 IntRect popupRect(popupX, rScreenCoords.maxY(), popupWidth, popupHeight);
354 // The popup needs to stay within the bounds of the screen and not overlap any toolbars
355 FloatRect screen = screenAvailableRect(v);
357 // Check that we don't go off the screen vertically
358 if (popupRect.maxY() > screen.height()) {
359 // The popup will go off the screen, so try placing it above the client
360 if (rScreenCoords.y() - popupRect.height() < 0) {
361 // The popup won't fit above, either, so place it whereever's bigger and resize it to fit
362 if ((rScreenCoords.y() + rScreenCoords.height() / 2) < (screen.height() / 2)) {
364 popupRect.setHeight(screen.height() - popupRect.y());
368 popupRect.setHeight(rScreenCoords.y());
371 // The popup fits above, so reposition it
372 popupRect.setY(rScreenCoords.y() - popupRect.height());
376 // Check that we don't go off the screen horizontally
377 if (popupRect.x() < screen.x()) {
378 popupRect.setWidth(popupRect.width() - (screen.x() - popupRect.x()));
379 popupRect.setX(screen.x());
381 m_windowRect = popupRect;
385 bool PopupMenuWin::setFocusedIndex(int i, bool hotTracking)
387 if (i < 0 || i >= client()->listSize() || i == focusedIndex())
390 if (!client()->itemIsEnabled(i))
393 invalidateItem(focusedIndex());
399 client()->setTextFromItem(i);
401 if (!scrollToRevealSelection())
402 ::UpdateWindow(m_popup);
407 int PopupMenuWin::visibleItems() const
409 return clientRect().height() / m_itemHeight;
412 int PopupMenuWin::listIndexAtPoint(const IntPoint& point) const
414 return m_scrollOffset + point.y() / m_itemHeight;
417 int PopupMenuWin::focusedIndex() const
419 return m_focusedIndex;
422 void PopupMenuWin::focusFirst()
427 int size = client()->listSize();
429 for (int i = 0; i < size; ++i)
430 if (client()->itemIsEnabled(i)) {
436 void PopupMenuWin::focusLast()
441 int size = client()->listSize();
443 for (int i = size - 1; i > 0; --i)
444 if (client()->itemIsEnabled(i)) {
450 bool PopupMenuWin::down(unsigned lines)
455 int size = client()->listSize();
457 int lastSelectableIndex, selectedListIndex;
458 lastSelectableIndex = selectedListIndex = focusedIndex();
459 for (int i = selectedListIndex + 1; i >= 0 && i < size; ++i)
460 if (client()->itemIsEnabled(i)) {
461 lastSelectableIndex = i;
462 if (i >= selectedListIndex + (int)lines)
466 return setFocusedIndex(lastSelectableIndex);
469 bool PopupMenuWin::up(unsigned lines)
474 int size = client()->listSize();
476 int lastSelectableIndex, selectedListIndex;
477 lastSelectableIndex = selectedListIndex = focusedIndex();
478 for (int i = selectedListIndex - 1; i >= 0 && i < size; --i)
479 if (client()->itemIsEnabled(i)) {
480 lastSelectableIndex = i;
481 if (i <= selectedListIndex - (int)lines)
485 return setFocusedIndex(lastSelectableIndex);
488 void PopupMenuWin::invalidateItem(int index)
493 IntRect damageRect(clientRect());
494 damageRect.setY(m_itemHeight * (index - m_scrollOffset));
495 damageRect.setHeight(m_itemHeight);
497 damageRect.setWidth(damageRect.width() - m_scrollbar->frameRect().width());
500 ::InvalidateRect(m_popup, &r, TRUE);
503 IntRect PopupMenuWin::clientRect() const
505 IntRect clientRect = m_windowRect;
506 clientRect.inflate(-popupWindowBorderWidth);
507 clientRect.setLocation(IntPoint(0, 0));
511 void PopupMenuWin::incrementWheelDelta(int delta)
513 m_wheelDelta += delta;
516 void PopupMenuWin::reduceWheelDelta(int delta)
519 ASSERT(delta <= abs(m_wheelDelta));
521 if (m_wheelDelta > 0)
522 m_wheelDelta -= delta;
523 else if (m_wheelDelta < 0)
524 m_wheelDelta += delta;
529 bool PopupMenuWin::scrollToRevealSelection()
534 int index = focusedIndex();
536 if (index < m_scrollOffset) {
537 ScrollableArea::scrollToYOffsetWithoutAnimation(index);
541 if (index >= m_scrollOffset + visibleItems()) {
542 ScrollableArea::scrollToYOffsetWithoutAnimation(index - visibleItems() + 1);
549 void PopupMenuWin::updateFromElement()
554 m_focusedIndex = client()->selectedIndex();
556 ::InvalidateRect(m_popup, 0, TRUE);
557 if (!scrollToRevealSelection())
558 ::UpdateWindow(m_popup);
561 const int separatorPadding = 4;
562 const int separatorHeight = 1;
563 void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc)
569 m_DC = ::CreateCompatibleDC(::GetDC(m_popup));
575 bool keepBitmap = false;
577 if (GetObject(m_bmp, sizeof(bitmap), &bitmap))
578 keepBitmap = bitmap.bmWidth == clientRect().width()
579 && bitmap.bmHeight == clientRect().height();
587 BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size(), BitmapInfo::BitCount16);
589 BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size());
592 m_bmp = ::CreateDIBSection(m_DC, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
596 ::SelectObject(m_DC, m_bmp);
599 GraphicsContext context(m_DC);
601 int itemCount = client()->listSize();
603 // listRect is the damageRect translated into the coordinates of the entire menu list (which is itemCount * m_itemHeight pixels tall)
604 IntRect listRect = damageRect;
605 listRect.move(IntSize(0, m_scrollOffset * m_itemHeight));
607 for (int y = listRect.y(); y < listRect.maxY(); y += m_itemHeight) {
608 int index = y / m_itemHeight;
610 Color optionBackgroundColor, optionTextColor;
611 PopupMenuStyle itemStyle = client()->itemStyle(index);
612 if (index == focusedIndex()) {
613 optionBackgroundColor = RenderTheme::defaultTheme()->activeListBoxSelectionBackgroundColor();
614 optionTextColor = RenderTheme::defaultTheme()->activeListBoxSelectionForegroundColor();
616 optionBackgroundColor = itemStyle.backgroundColor();
617 optionTextColor = itemStyle.foregroundColor();
620 // itemRect is in client coordinates
621 IntRect itemRect(0, (index - m_scrollOffset) * m_itemHeight, damageRect.width(), m_itemHeight);
623 // Draw the background for this menu item
624 if (itemStyle.isVisible())
625 context.fillRect(itemRect, optionBackgroundColor, ColorSpaceDeviceRGB);
627 if (client()->itemIsSeparator(index)) {
628 IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight);
629 context.fillRect(separatorRect, optionTextColor, ColorSpaceDeviceRGB);
633 String itemText = client()->itemText(index);
635 unsigned length = itemText.length();
636 const UChar* string = itemText.characters();
637 TextDirection direction = (itemText.defaultWritingDirection() == WTF::Unicode::RightToLeft) ? RTL : LTR;
638 TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, direction);
640 context.setFillColor(optionTextColor, ColorSpaceDeviceRGB);
642 Font itemFont = client()->menuStyle().font();
643 if (client()->itemIsLabel(index)) {
644 FontDescription d = itemFont.fontDescription();
645 d.setWeight(d.bolderWeight());
646 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
647 itemFont.update(m_popupClient->fontSelector());
650 // Draw the item text
651 if (itemStyle.isVisible()) {
652 int textX = max(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
653 if (RenderTheme::defaultTheme()->popupOptionSupportsTextIndent() && itemStyle.textDirection() == LTR)
654 textX += itemStyle.textIndent().calcMinValue(itemRect.width());
655 int textY = itemRect.y() + itemFont.fontMetrics().ascent() + (itemRect.height() - itemFont.fontMetrics().height()) / 2;
656 context.drawBidiText(itemFont, textRun, IntPoint(textX, textY));
661 m_scrollbar->paint(&context, damageRect);
663 HDC localDC = hdc ? hdc : ::GetDC(m_popup);
665 ::BitBlt(localDC, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC, damageRect.x(), damageRect.y(), SRCCOPY);
668 ::ReleaseDC(m_popup, localDC);
671 int PopupMenuWin::scrollSize(ScrollbarOrientation orientation) const
673 return ((orientation == VerticalScrollbar) && m_scrollbar) ? (m_scrollbar->totalSize() - m_scrollbar->visibleSize()) : 0;
676 int PopupMenuWin::scrollPosition(Scrollbar*) const
678 return m_scrollOffset;
681 void PopupMenuWin::setScrollOffset(const IntPoint& offset)
683 scrollTo(offset.y());
686 void PopupMenuWin::scrollTo(int offset)
693 if (m_scrollOffset == offset)
696 int scrolledLines = m_scrollOffset - offset;
697 m_scrollOffset = offset;
699 UINT flags = SW_INVALIDATE;
701 #ifdef CAN_SET_SMOOTH_SCROLLING_DURATION
702 BOOL shouldSmoothScroll = FALSE;
703 ::SystemParametersInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &shouldSmoothScroll, 0);
704 if (shouldSmoothScroll)
705 flags |= MAKEWORD(SW_SMOOTHSCROLL, smoothScrollAnimationDuration);
708 IntRect listRect = clientRect();
710 listRect.setWidth(listRect.width() - m_scrollbar->frameRect().width());
712 ::ScrollWindowEx(m_popup, 0, scrolledLines * m_itemHeight, &r, 0, 0, 0, flags);
714 r = m_scrollbar->frameRect();
715 ::InvalidateRect(m_popup, &r, TRUE);
717 ::UpdateWindow(m_popup);
720 void PopupMenuWin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
722 IntRect scrollRect = rect;
723 scrollRect.move(scrollbar->x(), scrollbar->y());
725 ::InvalidateRect(m_popup, &r, false);
728 void PopupMenuWin::registerClass()
730 static bool haveRegisteredWindowClass = false;
732 if (haveRegisteredWindowClass)
739 wcex.cbSize = sizeof(WNDCLASSEX);
741 wcex.style = CS_DROPSHADOW;
744 wcex.lpfnWndProc = PopupMenuWndProc;
746 wcex.cbWndExtra = sizeof(PopupMenu*); // For the PopupMenu pointer
747 wcex.hInstance = WebCore::instanceHandle();
749 wcex.hCursor = LoadCursor(0, IDC_ARROW);
750 wcex.hbrBackground = 0;
751 wcex.lpszMenuName = 0;
752 wcex.lpszClassName = kPopupWindowClassName;
754 haveRegisteredWindowClass = true;
757 RegisterClass(&wcex);
759 RegisterClassEx(&wcex);
764 LRESULT CALLBACK PopupMenuWin::PopupMenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
767 LONG longPtr = GetWindowLong(hWnd, 0);
769 LONG_PTR longPtr = GetWindowLongPtr(hWnd, 0);
772 if (PopupMenuWin* popup = reinterpret_cast<PopupMenuWin*>(longPtr))
773 return popup->wndProc(hWnd, message, wParam, lParam);
775 if (message == WM_CREATE) {
776 LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
778 // Associate the PopupMenu with the window.
780 ::SetWindowLong(hWnd, 0, (LONG)createStruct->lpCreateParams);
782 ::SetWindowLongPtr(hWnd, 0, (LONG_PTR)createStruct->lpCreateParams);
787 return ::DefWindowProc(hWnd, message, wParam, lParam);
790 const int smoothScrollAnimationDuration = 5000;
792 LRESULT PopupMenuWin::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
798 case WM_MOUSEACTIVATE:
799 return MA_NOACTIVATE;
805 IntSize size(LOWORD(lParam), HIWORD(lParam));
806 scrollbar()->setFrameRect(IntRect(size.width() - scrollbar()->width(), 0, scrollbar()->width(), size.height()));
808 int visibleItems = this->visibleItems();
809 scrollbar()->setEnabled(visibleItems < client()->listSize());
810 scrollbar()->setSteps(1, max(1, visibleItems - 1));
811 scrollbar()->setProportion(visibleItems, client()->listSize());
820 bool altKeyPressed = GetKeyState(VK_MENU) & HIGH_BIT_MASK_SHORT;
821 bool ctrlKeyPressed = GetKeyState(VK_CONTROL) & HIGH_BIT_MASK_SHORT;
824 switch (LOWORD(wParam)) {
826 if (!altKeyPressed && !ctrlKeyPressed) {
827 int index = focusedIndex();
829 client()->valueChanged(index);
836 int index = focusedIndex();
838 client()->valueChanged(index);
848 int index = focusedIndex();
850 client()->valueChanged(index);
865 if (focusedIndex() != scrollOffset()) {
866 // Set the selection to the first visible item
867 int firstVisibleItem = scrollOffset();
868 up(focusedIndex() - firstVisibleItem);
870 // The first visible item is selected, so move the selection back one page
875 int lastVisibleItem = scrollOffset() + visibleItems() - 1;
876 if (focusedIndex() != lastVisibleItem) {
877 // Set the selection to the last visible item
878 down(lastVisibleItem - focusedIndex());
880 // The last visible item is selected, so move the selection forward one page
881 down(visibleItems());
886 ::SendMessage(client()->hostWindow()->platformPageClient(), message, wParam, lParam);
893 if (isASCIIPrintable(wParam))
894 // Send the keydown to the WebView so it can be used for type-to-select.
895 // Since we know that the virtual key is ASCII printable, it's OK to convert this to
896 // a WM_CHAR message. (We don't want to call TranslateMessage because that will post a
897 // WM_CHAR message that will be stolen and redirected to the popup HWND.
898 ::PostMessage(m_popup, WM_HOST_WINDOW_CHAR, wParam, lParam);
912 case 0x0D: // Enter/Return
914 index = focusedIndex();
916 client()->valueChanged(index);
922 case 0x08: // Backspace
923 case 0x0A: // Linefeed
924 default: // Character
931 IntPoint mousePoint(MAKEPOINTS(lParam));
933 IntRect scrollBarRect = scrollbar()->frameRect();
934 if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
935 // Put the point into coordinates relative to the scroll bar
936 mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
937 PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
938 scrollbar()->mouseMoved(event);
943 BOOL shouldHotTrack = FALSE;
945 ::SystemParametersInfo(SPI_GETHOTTRACKING, 0, &shouldHotTrack, 0);
949 GetClientRect(popupHandle(), &bounds);
950 if (!::PtInRect(&bounds, mousePoint) && !(wParam & MK_LBUTTON) && client()) {
951 // When the mouse is not inside the popup menu and the left button isn't down, just
952 // repost the message to the web view.
954 // Translate the coordinate.
955 translatePoint(lParam, m_popup, client()->hostWindow()->platformPageClient());
957 ::PostMessage(m_popup, WM_HOST_WINDOW_MOUSEMOVE, wParam, lParam);
961 if ((shouldHotTrack || wParam & MK_LBUTTON) && ::PtInRect(&bounds, mousePoint))
962 setFocusedIndex(listIndexAtPoint(mousePoint), true);
966 case WM_LBUTTONDOWN: {
967 IntPoint mousePoint(MAKEPOINTS(lParam));
969 IntRect scrollBarRect = scrollbar()->frameRect();
970 if (scrollBarRect.contains(mousePoint)) {
971 // Put the point into coordinates relative to the scroll bar
972 mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
973 PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
974 scrollbar()->mouseDown(event);
975 setScrollbarCapturingMouse(true);
980 // If the mouse is inside the window, update the focused index. Otherwise,
983 GetClientRect(m_popup, &bounds);
984 if (::PtInRect(&bounds, mousePoint))
985 setFocusedIndex(listIndexAtPoint(mousePoint), true);
991 IntPoint mousePoint(MAKEPOINTS(lParam));
993 IntRect scrollBarRect = scrollbar()->frameRect();
994 if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
995 setScrollbarCapturingMouse(false);
996 // Put the point into coordinates relative to the scroll bar
997 mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
998 PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
999 scrollbar()->mouseUp();
1000 // FIXME: This is a hack to work around Scrollbar not invalidating correctly when it doesn't have a parent widget
1001 RECT r = scrollBarRect;
1002 ::InvalidateRect(popupHandle(), &r, TRUE);
1006 // Only hide the popup if the mouse is inside the popup window.
1008 GetClientRect(popupHandle(), &bounds);
1009 if (client() && ::PtInRect(&bounds, mousePoint)) {
1011 int index = focusedIndex();
1013 client()->valueChanged(index);
1018 case WM_MOUSEWHEEL: {
1023 for (incrementWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam)); abs(wheelDelta()) >= WHEEL_DELTA; reduceWheelDelta(WHEEL_DELTA)) {
1024 if (wheelDelta() > 0)
1030 ScrollableArea::scroll(i > 0 ? ScrollUp : ScrollDown, ScrollByLine, abs(i));
1035 PAINTSTRUCT paintInfo;
1036 ::BeginPaint(popupHandle(), &paintInfo);
1037 paint(paintInfo.rcPaint, paintInfo.hdc);
1038 ::EndPaint(popupHandle(), &paintInfo);
1043 case WM_PRINTCLIENT:
1044 paint(clientRect(), (HDC)wParam);
1048 lResult = DefWindowProc(hWnd, message, wParam, lParam);