diff options
Diffstat (limited to 'WebCore/platform/win/PopupMenuWin.cpp')
-rw-r--r-- | WebCore/platform/win/PopupMenuWin.cpp | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/WebCore/platform/win/PopupMenuWin.cpp b/WebCore/platform/win/PopupMenuWin.cpp new file mode 100644 index 0000000..466d881 --- /dev/null +++ b/WebCore/platform/win/PopupMenuWin.cpp @@ -0,0 +1,826 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "PopupMenu.h" + +#include "Document.h" +#include "FloatRect.h" +#include "FontSelector.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PlatformScreen.h" +#include "PlatformScrollBar.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "SimpleFontData.h" +#include <tchar.h> +#include <windows.h> + +using std::min; + +namespace WebCore { + +using namespace HTMLNames; + +// Default Window animation duration in milliseconds +static const int defaultAnimationDuration = 200; +// Maximum height of a popup window +static const int maxPopupHeight = 320; + +static const int popupWindowAlphaPercent = 95; + +const int optionSpacingMiddle = 1; +const int popupWindowBorderWidth = 1; + +static LPCTSTR kPopupWindowClassName = _T("PopupWindowClass"); +static ATOM registerPopup(); +static LRESULT CALLBACK PopupWndProc(HWND, UINT, WPARAM, LPARAM); + +// FIXME: Remove this as soon as practical. +static inline bool isASCIIPrintable(unsigned c) +{ + return c >= 0x20 && c <= 0x7E; +} + +PopupMenu::PopupMenu(PopupMenuClient* client) + : RefCounted<PopupMenu>(0) + , m_popupClient(client) + , m_scrollBar(0) + , m_popup(0) + , m_DC(0) + , m_bmp(0) + , m_wasClicked(false) + , m_itemHeight(0) + , m_scrollOffset(0) + , m_wheelDelta(0) + , m_focusedIndex(0) + , m_scrollbarCapturingMouse(false) +{ +} + +PopupMenu::~PopupMenu() +{ + if (m_bmp) + ::DeleteObject(m_bmp); + if (m_DC) + ::DeleteObject(m_DC); + if (m_popup) + ::DestroyWindow(m_popup); +} + +void PopupMenu::show(const IntRect& r, FrameView* v, int index) +{ + calculatePositionAndSize(r, v); + if (clientRect().isEmpty()) + return; + + if (!m_popup) { + registerPopup(); + + DWORD exStyle = WS_EX_LAYERED | WS_EX_LTRREADING; + + // Even though we already know our size and location at this point, we pass (0,0,0,0) as our size/location here. + // We need to wait until after the call to ::SetWindowLongPtr to set our size so that in our WM_SIZE handler we can get access to the PopupMenu object + m_popup = ::CreateWindowEx(exStyle, kPopupWindowClassName, _T("PopupMenu"), + WS_POPUP | WS_BORDER, + 0, 0, 0, 0, + v->containingWindow(), 0, 0, 0); + + if (!m_popup) + return; + + ::SetWindowLongPtr(m_popup, 0, (LONG_PTR)this); + ::SetLayeredWindowAttributes(m_popup, 0, (255 * popupWindowAlphaPercent) / 100, LWA_ALPHA); + } + + if (!m_scrollBar) + if (visibleItems() < client()->listSize()) { + // We need a scroll bar + m_scrollBar = new PlatformScrollbar(this, VerticalScrollbar, SmallScrollbar); + m_scrollBar->setContainingWindow(m_popup); + } + + ::SetWindowPos(m_popup, HWND_TOP, m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), 0); + + // Determine whether we should animate our popups + // Note: Must use 'BOOL' and 'FALSE' instead of 'bool' and 'false' to avoid stack corruption with SystemParametersInfo + BOOL shouldAnimate = FALSE; +#ifdef CAN_ANIMATE_TRANSPARENT_WINDOWS_SMOOTHLY + ::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0); +#endif + + if (shouldAnimate) { + RECT viewRect = {0}; + ::GetWindowRect(v->containingWindow(), &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 + v->contentsToWindow(r.location()).y()) ? AW_VER_NEGATIVE : AW_VER_POSITIVE; + + ::AnimateWindow(m_popup, defaultAnimationDuration, AW_SLIDE | slideDirection | AW_ACTIVATE); + } + } else + ::ShowWindow(m_popup, SW_SHOWNORMAL); + ::SetCapture(m_popup); + + if (client()) { + int index = client()->selectedIndex(); + if (index >= 0) + setFocusedIndex(index); + } +} + +void PopupMenu::hide() +{ + ::ShowWindow(m_popup, SW_HIDE); +} + +const int endOfLinePadding = 2; +void PopupMenu::calculatePositionAndSize(const IntRect& r, FrameView* v) +{ + // r is in absolute document coordinates, but we want to be in screen coordinates + + // First, move to WebView coordinates + IntRect rScreenCoords(v->contentsToWindow(r.location()), r.size()); + + // Then, translate to screen coordinates + POINT location(rScreenCoords.location()); + if (!::ClientToScreen(v->containingWindow(), &location)) + return; + + rScreenCoords.setLocation(location); + + // First, determine the popup's height + int itemCount = client()->listSize(); + m_itemHeight = client()->clientStyle()->font().height() + optionSpacingMiddle; + 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 = 0; + for (int i = 0; i < itemCount; ++i) { + String text = client()->itemText(i); + if (text.isEmpty()) + continue; + + popupWidth = max(popupWidth, client()->clientStyle()->font().width(TextRun(text.characters(), text.length()))); + } + + if (naturalHeight > maxPopupHeight) + // We need room for a scrollbar + popupWidth += PlatformScrollbar::verticalScrollbarWidth(SmallScrollbar); + + // Add padding to align the popup text with the <select> text + // Note: We can't add paddingRight() because that value includes the width + // of the dropdown button, so we must use our own endOfLinePadding constant. + popupWidth += max(0, endOfLinePadding - client()->clientInsetRight()) + max(0, client()->clientPaddingLeft() - client()->clientInsetLeft()); + + // Leave room for the border + popupWidth += 2 * popupWindowBorderWidth; + popupHeight += 2 * popupWindowBorderWidth; + + // The popup should be at least as wide as the control on the page + popupWidth = max(rScreenCoords.width() - client()->clientInsetLeft() - client()->clientInsetRight(), popupWidth); + + // Always left-align items in the popup. This matches popup menus on the mac. + int popupX = rScreenCoords.x() + client()->clientInsetLeft(); + + IntRect popupRect(popupX, rScreenCoords.bottom(), popupWidth, popupHeight); + + // The popup needs to stay within the bounds of the screen and not overlap any toolbars + FloatRect screen = screenAvailableRect(v); + + // 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 (rScreenCoords.y() - popupRect.height() < 0) { + // The popup won't fit above, either, so place it whereever's bigger and resize it to fit + if ((rScreenCoords.y() + rScreenCoords.height() / 2) < (screen.height() / 2)) { + // Below is bigger + popupRect.setHeight(screen.height() - popupRect.y()); + } else { + // Above is bigger + popupRect.setY(0); + popupRect.setHeight(rScreenCoords.y()); + } + } else { + // The popup fits above, so reposition it + popupRect.setY(rScreenCoords.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; + return; +} + +bool PopupMenu::setFocusedIndex(int i, bool hotTracking) +{ + if (i < 0 || i >= client()->listSize() || i == focusedIndex()) + return false; + + if (!client()->itemIsEnabled(i)) + return false; + + invalidateItem(focusedIndex()); + invalidateItem(i); + + m_focusedIndex = i; + + if (!hotTracking) + client()->setTextFromItem(i); + + if (!scrollToRevealSelection()) + ::UpdateWindow(m_popup); + + return true; +} + +int PopupMenu::visibleItems() const +{ + return clientRect().height() / m_itemHeight; +} + +int PopupMenu::listIndexAtPoint(const IntPoint& point) const +{ + return m_scrollOffset + point.y() / m_itemHeight; +} + +int PopupMenu::focusedIndex() const +{ + return m_focusedIndex; +} + +void PopupMenu::focusFirst() +{ + if (!client()) + return; + + int size = client()->listSize(); + + for (int i = 0; i < size; ++i) + if (client()->itemIsEnabled(i)) { + setFocusedIndex(i); + break; + } +} + +void PopupMenu::focusLast() +{ + if (!client()) + return; + + int size = client()->listSize(); + + for (int i = size - 1; i > 0; --i) + if (client()->itemIsEnabled(i)) { + setFocusedIndex(i); + break; + } +} + +bool PopupMenu::down(unsigned lines) +{ + if (!client()) + return false; + + int size = client()->listSize(); + + int lastSelectableIndex, selectedListIndex; + lastSelectableIndex = selectedListIndex = focusedIndex(); + for (int i = selectedListIndex + 1; i >= 0 && i < size; ++i) + if (client()->itemIsEnabled(i)) { + lastSelectableIndex = i; + if (i >= selectedListIndex + (int)lines) + break; + } + + return setFocusedIndex(lastSelectableIndex); +} + +bool PopupMenu::up(unsigned lines) +{ + if (!client()) + return false; + + int size = client()->listSize(); + + int lastSelectableIndex, selectedListIndex; + lastSelectableIndex = selectedListIndex = focusedIndex(); + for (int i = selectedListIndex - 1; i >= 0 && i < size; --i) + if (client()->itemIsEnabled(i)) { + lastSelectableIndex = i; + if (i <= selectedListIndex - (int)lines) + break; + } + + return setFocusedIndex(lastSelectableIndex); +} + +void PopupMenu::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->frameGeometry().width()); + + RECT r = damageRect; + ::InvalidateRect(m_popup, &r, TRUE); +} + +IntRect PopupMenu::clientRect() const +{ + IntRect clientRect = m_windowRect; + clientRect.inflate(-popupWindowBorderWidth); + clientRect.setLocation(IntPoint(0, 0)); + return clientRect; +} + +void PopupMenu::incrementWheelDelta(int delta) +{ + m_wheelDelta += delta; +} + +void PopupMenu::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 PopupMenu::scrollToRevealSelection() +{ + if (!m_scrollBar) + return false; + + int index = focusedIndex(); + + if (index < m_scrollOffset) { + m_scrollBar->setValue(index); + return true; + } + + if (index >= m_scrollOffset + visibleItems()) { + m_scrollBar->setValue(index - visibleItems() + 1); + return true; + } + + return false; +} + +void PopupMenu::updateFromElement() +{ + if (!m_popup) + return; + + m_focusedIndex = client()->selectedIndex(); + + ::InvalidateRect(m_popup, 0, TRUE); + if (!scrollToRevealSelection()) + ::UpdateWindow(m_popup); +} + +bool PopupMenu::itemWritingDirectionIsNatural() +{ + return true; +} + +const int separatorPadding = 4; +const int separatorHeight = 1; +void PopupMenu::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.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = clientRect().width(); + bitmapInfo.bmiHeader.biHeight = -clientRect().height(); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + bitmapInfo.bmiHeader.biSizeImage = 0; + bitmapInfo.bmiHeader.biXPelsPerMeter = 0; + bitmapInfo.bmiHeader.biYPelsPerMeter = 0; + bitmapInfo.bmiHeader.biClrUsed = 0; + bitmapInfo.bmiHeader.biClrImportant = 0; + + 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); + + int itemCount = client()->listSize(); + + // listRect is the damageRect translated into the coordinates of the entire menu list (which is itemCount * m_itemHeight pixels tall) + IntRect listRect = damageRect; + listRect.move(IntSize(0, m_scrollOffset * m_itemHeight)); + + RenderStyle* clientStyle = client()->clientStyle(); + + for (int y = listRect.y(); y < listRect.bottom(); y += m_itemHeight) { + int index = y / m_itemHeight; + + Color optionBackgroundColor, optionTextColor; + RenderStyle* itemStyle = client()->itemStyle(index); + if (index == focusedIndex()) { + optionBackgroundColor = theme()->activeListBoxSelectionBackgroundColor(); + optionTextColor = theme()->activeListBoxSelectionForegroundColor(); + } else { + optionBackgroundColor = client()->itemBackgroundColor(index); + optionTextColor = itemStyle->color(); + } + + // itemRect is in client coordinates + IntRect itemRect(0, (index - m_scrollOffset) * m_itemHeight, damageRect.width(), m_itemHeight); + + // Draw the background for this menu item + if (itemStyle->visibility() != HIDDEN) + context.fillRect(itemRect, optionBackgroundColor); + + if (client()->itemIsSeparator(index)) { + IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight); + context.fillRect(separatorRect, optionTextColor); + continue; + } + + String itemText = client()->itemText(index); + + unsigned length = itemText.length(); + const UChar* string = itemText.characters(); + TextRun textRun(string, length, false, 0, 0, itemText.defaultWritingDirection() == WTF::Unicode::RightToLeft); + + context.setFillColor(optionTextColor); + + Font itemFont = client()->clientStyle()->font(); + if (client()->itemIsLabel(index)) { + FontDescription d = itemFont.fontDescription(); + d.setBold(true); + itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); + itemFont.update(m_popupClient->fontSelector()); + } + context.setFont(itemFont); + + // Draw the item text + if (itemStyle->visibility() != HIDDEN) { + int textX = max(0, client()->clientPaddingLeft() - client()->clientInsetLeft()); + int textY = itemRect.y() + itemFont.ascent() + (itemRect.height() - itemFont.height()) / 2; + context.drawBidiText(textRun, IntPoint(textX, textY)); + } + } + + if (m_scrollBar) + m_scrollBar->paint(&context, damageRect); + + if (!hdc) + hdc = ::GetDC(m_popup); + + ::BitBlt(hdc, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC, damageRect.x(), damageRect.y(), SRCCOPY); +} + +void PopupMenu::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->frameGeometry().width()); + RECT r = listRect; + ::ScrollWindowEx(m_popup, 0, scrolledLines * m_itemHeight, &r, 0, 0, 0, flags); + if (m_scrollBar) { + r = m_scrollBar->frameGeometry(); + ::InvalidateRect(m_popup, &r, TRUE); + } + ::UpdateWindow(m_popup); +} + +IntRect PopupMenu::windowClipRect() const +{ + return m_windowRect; +} + +static ATOM registerPopup() +{ + static bool haveRegisteredWindowClass = false; + + if (haveRegisteredWindowClass) + return true; + + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = 0; + wcex.lpfnWndProc = PopupWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = sizeof(PopupMenu*); // For the PopupMenu pointer + wcex.hInstance = Page::instanceHandle(); + wcex.hIcon = 0; + wcex.hCursor = LoadCursor(0, IDC_ARROW); + wcex.hbrBackground = 0; + wcex.lpszMenuName = 0; + wcex.lpszClassName = kPopupWindowClassName; + wcex.hIconSm = 0; + + haveRegisteredWindowClass = true; + + return ::RegisterClassEx(&wcex); +} + +const int smoothScrollAnimationDuration = 5000; +static LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LRESULT lResult = 0; + LONG_PTR longPtr = GetWindowLongPtr(hWnd, 0); + PopupMenu* popup = reinterpret_cast<PopupMenu*>(longPtr); + + switch (message) { + case WM_SIZE: + if (popup && popup->scrollBar()) { + IntSize size(LOWORD(lParam), HIWORD(lParam)); + popup->scrollBar()->setRect(IntRect(size.width() - popup->scrollBar()->width(), 0, popup->scrollBar()->width(), size.height())); + + int visibleItems = popup->visibleItems(); + popup->scrollBar()->setEnabled(visibleItems < popup->client()->listSize()); + popup->scrollBar()->setSteps(1, max(1, visibleItems - 1)); + popup->scrollBar()->setProportion(visibleItems, popup->client()->listSize()); + } + break; + case WM_ACTIVATE: + if (popup && popup->client() && wParam == WA_INACTIVE) + popup->client()->hidePopup(); + break; + case WM_KILLFOCUS: + if (popup && popup->client() && (HWND)wParam != popup->popupHandle()) + // Focus is going elsewhere, so hide + popup->client()->hidePopup(); + break; + case WM_KEYDOWN: + if (popup && popup->client()) { + lResult = 0; + switch (LOWORD(wParam)) { + case VK_DOWN: + case VK_RIGHT: + popup->down(); + break; + case VK_UP: + case VK_LEFT: + popup->up(); + break; + case VK_HOME: + popup->focusFirst(); + break; + case VK_END: + popup->focusLast(); + break; + case VK_PRIOR: + if (popup->focusedIndex() != popup->scrollOffset()) { + // Set the selection to the first visible item + int firstVisibleItem = popup->scrollOffset(); + popup->up(popup->focusedIndex() - firstVisibleItem); + } else + // The first visible item is selected, so move the selection back one page + popup->up(popup->visibleItems()); + break; + case VK_NEXT: + if (popup) { + int lastVisibleItem = popup->scrollOffset() + popup->visibleItems() - 1; + if (popup->focusedIndex() != lastVisibleItem) { + // Set the selection to the last visible item + popup->down(lastVisibleItem - popup->focusedIndex()); + } else + // The last visible item is selected, so move the selection forward one page + popup->down(popup->visibleItems()); + } + break; + case VK_TAB: + ::SendMessage(popup->client()->clientDocument()->view()->containingWindow(), message, wParam, lParam); + popup->client()->hidePopup(); + break; + default: + if (isASCIIPrintable(wParam)) + // Send the keydown to the WebView so it can be used for type-to-select. + ::PostMessage(popup->client()->clientDocument()->view()->containingWindow(), message, wParam, lParam); + else + lResult = 1; + break; + } + } + break; + case WM_CHAR: + if (popup && popup->client()) { + lResult = 0; + int index; + switch (wParam) { + case 0x0D: // Enter/Return + popup->client()->hidePopup(); + index = popup->focusedIndex(); + ASSERT(index >= 0); + popup->client()->valueChanged(index); + break; + case 0x1B: // Escape + popup->client()->hidePopup(); + break; + case 0x09: // TAB + case 0x08: // Backspace + case 0x0A: // Linefeed + default: // Character + lResult = 1; + break; + } + } + break; + case WM_MOUSEMOVE: + if (popup) { + IntPoint mousePoint(MAKEPOINTS(lParam)); + if (popup->scrollBar()) { + IntRect scrollBarRect = popup->scrollBar()->frameGeometry(); + if (popup->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())); + popup->scrollBar()->handleMouseMoveEvent(event); + break; + } + } + + BOOL shouldHotTrack = FALSE; + ::SystemParametersInfo(SPI_GETHOTTRACKING, 0, &shouldHotTrack, 0); + + RECT bounds; + GetClientRect(popup->popupHandle(), &bounds); + if ((shouldHotTrack || wParam & MK_LBUTTON) && ::PtInRect(&bounds, mousePoint)) + popup->setFocusedIndex(popup->listIndexAtPoint(mousePoint), true); + + // Release capture if the left button isn't down, and the mousePoint is outside the popup window. + // This way, the WebView will get future mouse events in the rest of the window. + if (!(wParam & MK_LBUTTON) && !::PtInRect(&bounds, mousePoint)) { + ::ReleaseCapture(); + break; + } + } + break; + case WM_LBUTTONDOWN: + if (popup) { + ::SetCapture(popup->popupHandle()); + IntPoint mousePoint(MAKEPOINTS(lParam)); + if (popup->scrollBar()) { + IntRect scrollBarRect = popup->scrollBar()->frameGeometry(); + 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())); + popup->scrollBar()->handleMousePressEvent(event); + popup->setScrollbarCapturingMouse(true); + break; + } + } + + popup->setFocusedIndex(popup->listIndexAtPoint(mousePoint), true); + } + break; + case WM_LBUTTONUP: + if (popup) { + IntPoint mousePoint(MAKEPOINTS(lParam)); + if (popup->scrollBar()) { + ::ReleaseCapture(); + IntRect scrollBarRect = popup->scrollBar()->frameGeometry(); + if (popup->scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) { + popup->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())); + popup->scrollBar()->handleMouseReleaseEvent(event); + // FIXME: This is a hack to work around PlatformScrollbar not invalidating correctly when it doesn't have a parent widget + RECT r = scrollBarRect; + ::InvalidateRect(popup->popupHandle(), &r, TRUE); + break; + } + } + // Only release capture and hide the popup if the mouse is inside the popup window. + RECT bounds; + GetClientRect(popup->popupHandle(), &bounds); + if (popup->client() && ::PtInRect(&bounds, mousePoint)) { + ::ReleaseCapture(); + popup->client()->hidePopup(); + int index = popup->focusedIndex(); + if (index >= 0) + popup->client()->valueChanged(index); + } + } + break; + case WM_MOUSEWHEEL: + if (popup && popup->scrollBar()) { + int i = 0; + for (popup->incrementWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam)); abs(popup->wheelDelta()) >= WHEEL_DELTA; popup->reduceWheelDelta(WHEEL_DELTA)) + if (popup->wheelDelta() > 0) + ++i; + else + --i; + + popup->scrollBar()->scroll(i > 0 ? ScrollUp : ScrollDown, ScrollByLine, abs(i)); + } + break; + case WM_PAINT: + if (popup) { + PAINTSTRUCT paintInfo; + ::BeginPaint(popup->popupHandle(), &paintInfo); + popup->paint(paintInfo.rcPaint, paintInfo.hdc); + ::EndPaint(popup->popupHandle(), &paintInfo); + lResult = 0; + } + break; + case WM_PRINTCLIENT: + if (popup) + popup->paint(popup->clientRect(), (HDC)wParam); + break; + default: + lResult = DefWindowProc(hWnd, message, wParam, lParam); + } + + return lResult; +} + +} |