/* * Copyright 2011 Vincent Sanders * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "utils/config.h" #include #include #include "utils/errors.h" #include "utils/log.h" #include "utils/utils.h" #include "netsurf/browser_window.h" #include "netsurf/plotters.h" #include "netsurf/keypress.h" #include "windows/windbg.h" #include "windows/plot.h" #include "windows/window.h" #include "windows/localhistory.h" #include "windows/drawable.h" static const char windowclassname_drawable[] = "nswsdrawablewindow"; /** * Handle wheel scroll messages. */ static LRESULT nsws_drawable_wheel(struct gui_window *gw, HWND hwnd, WPARAM wparam) { int i, z = GET_WHEEL_DELTA_WPARAM(wparam) / WHEEL_DELTA; int key = LOWORD(wparam); DWORD command; unsigned int newmessage = WM_VSCROLL; if (key == MK_SHIFT) { command = (z > 0) ? SB_LINERIGHT : SB_LINELEFT; newmessage = WM_HSCROLL; } else { /* add MK_CONTROL -> zoom */ command = (z > 0) ? SB_LINEUP : SB_LINEDOWN; } z = (z < 0) ? -1 * z : z; for (i = 0; i < z; i++) { SendMessage(hwnd, newmessage, MAKELONG(command, 0), 0); } return 0; } /** * Handle vertical scroll messages. */ static LRESULT nsws_drawable_vscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam) { int width, height; SCROLLINFO si; int mem; LOG("VSCROLL %d", gw->requestscrolly); if (gw->requestscrolly != 0) return 0; si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_VERT, &si); mem = si.nPos; switch (LOWORD(wparam)) { case SB_TOP: si.nPos = si.nMin; break; case SB_BOTTOM: si.nPos = si.nMax; break; case SB_LINEUP: si.nPos -= 30; break; case SB_LINEDOWN: si.nPos += 30; break; case SB_PAGEUP: si.nPos -= gw->height; break; case SB_PAGEDOWN: si.nPos += gw->height; break; case SB_THUMBTRACK: si.nPos = si.nTrackPos; break; default: break; } si.fMask = SIF_POS; if ((gw->bw != NULL) && (browser_window_get_extents(gw->bw, true, &width, &height) == NSERROR_OK)) { si.nPos = min(si.nPos, height - gw->height); } si.nPos = max(si.nPos, 0); SetScrollInfo(hwnd, SB_VERT, &si, TRUE); GetScrollInfo(hwnd, SB_VERT, &si); if (si.nPos != mem) { win32_window_set_scroll(gw, gw->scrollx, gw->scrolly + gw->requestscrolly + si.nPos - mem); } return 0; } /** * Handle horizontal scroll messages. */ static LRESULT nsws_drawable_hscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam) { int width, height; SCROLLINFO si; int mem; LOG("HSCROLL %d", gw->requestscrollx); if (gw->requestscrollx != 0) return 0; si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_HORZ, &si); mem = si.nPos; switch (LOWORD(wparam)) { case SB_LINELEFT: si.nPos -= 30; break; case SB_LINERIGHT: si.nPos += 30; break; case SB_PAGELEFT: si.nPos -= gw->width; break; case SB_PAGERIGHT: si.nPos += gw->width; break; case SB_THUMBTRACK: si.nPos = si.nTrackPos; break; default: break; } si.fMask = SIF_POS; if ((gw->bw != NULL) && (browser_window_get_extents(gw->bw, true, &width, &height) == NSERROR_OK)) { si.nPos = min(si.nPos, width - gw->width); } si.nPos = max(si.nPos, 0); SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); GetScrollInfo(hwnd, SB_HORZ, &si); if (si.nPos != mem) { win32_window_set_scroll(gw, gw->scrollx + gw->requestscrollx + si.nPos - mem, gw->scrolly); } return 0; } /** * Handle resize events. */ static LRESULT nsws_drawable_resize(struct gui_window *gw) { browser_window_schedule_reformat(gw->bw); return 0; } /** * Handle key press messages. */ static LRESULT nsws_drawable_key(struct gui_window *gw, HWND hwnd, WPARAM wparam) { if (GetFocus() != hwnd) return 0 ; uint32_t i; bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000); bool capslock = ((GetKeyState(VK_CAPITAL) & 1) == 1); switch(wparam) { case VK_LEFT: i = NS_KEY_LEFT; if (shift) SendMessage(hwnd, WM_HSCROLL, MAKELONG(SB_LINELEFT, 0), 0); break; case VK_RIGHT: i = NS_KEY_RIGHT; if (shift) SendMessage(hwnd, WM_HSCROLL, MAKELONG(SB_LINERIGHT, 0), 0); break; case VK_UP: i = NS_KEY_UP; if (shift) SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_LINEUP, 0), 0); break; case VK_DOWN: i = NS_KEY_DOWN; if (shift) SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_LINEDOWN, 0), 0); break; case VK_HOME: i = NS_KEY_LINE_START; if (shift) SendMessage(hwnd, WM_HSCROLL, MAKELONG(SB_PAGELEFT, 0), 0); break; case VK_END: i = NS_KEY_LINE_END; if (shift) SendMessage(hwnd, WM_HSCROLL, MAKELONG(SB_PAGERIGHT, 0), 0); break; case VK_DELETE: i = NS_KEY_DELETE_RIGHT; break; case VK_NEXT: i = wparam; SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_PAGEDOWN, 0), 0); break; case VK_PRIOR: i = wparam; SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_PAGEUP, 0), 0); break; default: i = wparam; break; } if ((i >= 'A') && (i <= 'Z') && (((!capslock) && (!shift)) || ((capslock) && (shift)))) { i += 'a' - 'A'; } if (gw != NULL) browser_window_key_press(gw->bw, i); return 0; } /** * Handle paint messages. */ static LRESULT nsws_drawable_paint(struct gui_window *gw, HWND hwnd) { struct rect clip; PAINTSTRUCT ps; struct redraw_context ctx = { .interactive = true, .background_images = true, .plot = &win_plotters }; BeginPaint(hwnd, &ps); if (gw != NULL) { plot_hdc = ps.hdc; clip.x0 = ps.rcPaint.left; clip.y0 = ps.rcPaint.top; clip.x1 = ps.rcPaint.right; clip.y1 = ps.rcPaint.bottom; browser_window_redraw(gw->bw, -gw->scrollx / gw->scale, -gw->scrolly / gw->scale, &clip, &ctx); } EndPaint(hwnd, &ps); return 0; } /** * Handle mouse button up messages. */ static LRESULT nsws_drawable_mouseup(struct gui_window *gw, int x, int y, browser_mouse_state press, browser_mouse_state click) { bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000); bool ctrl = ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000); bool alt = ((GetKeyState(VK_MENU) & 0x8000) == 0x8000); if ((gw == NULL) || (gw->mouse == NULL) || (gw->bw == NULL)) return 0; LOG("state 0x%x, press 0x%x", gw->mouse->state, press); if ((gw->mouse->state & press) != 0) { gw->mouse->state &= ~press; gw->mouse->state |= click; } if (((gw->mouse->state & BROWSER_MOUSE_MOD_1) != 0) && !shift) gw->mouse->state &= ~BROWSER_MOUSE_MOD_1; if (((gw->mouse->state & BROWSER_MOUSE_MOD_2) != 0) && !ctrl) gw->mouse->state &= ~BROWSER_MOUSE_MOD_2; if (((gw->mouse->state & BROWSER_MOUSE_MOD_3) != 0) && !alt) gw->mouse->state &= ~BROWSER_MOUSE_MOD_3; if ((gw->mouse->state & click) != 0) { LOG("mouse click bw %p, state 0x%x, x %f, y %f", gw->bw, gw->mouse->state, (x + gw->scrollx) / gw->scale, (y + gw->scrolly) / gw->scale); browser_window_mouse_click(gw->bw, gw->mouse->state, (x + gw->scrollx) / gw->scale, (y + gw->scrolly) / gw->scale); } else { browser_window_mouse_track(gw->bw, 0, (x + gw->scrollx) / gw->scale, (y + gw->scrolly) / gw->scale); } gw->mouse->state = 0; return 0; } /** * Handle mouse button down messages. */ static LRESULT nsws_drawable_mousedown(struct gui_window *gw, int x, int y, browser_mouse_state button) { if ((gw == NULL) || (gw->mouse == NULL) || (gw->bw == NULL)) { nsws_localhistory_close(gw); return 0; } gw->mouse->state = button; if ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000) gw->mouse->state |= BROWSER_MOUSE_MOD_1; if ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000) gw->mouse->state |= BROWSER_MOUSE_MOD_2; if ((GetKeyState(VK_MENU) & 0x8000) == 0x8000) gw->mouse->state |= BROWSER_MOUSE_MOD_3; gw->mouse->pressed_x = (x + gw->scrollx) / gw->scale; gw->mouse->pressed_y = (y + gw->scrolly) / gw->scale; LOG("mouse click bw %p, state %x, x %f, y %f", gw->bw, gw->mouse->state, (x + gw->scrollx) / gw->scale, (y + gw->scrolly) / gw->scale); browser_window_mouse_click(gw->bw, gw->mouse->state, (x + gw->scrollx) / gw->scale, (y + gw->scrolly) / gw->scale); return 0; } /** * Handle mouse movement messages. */ static LRESULT nsws_drawable_mousemove(struct gui_window *gw, int x, int y) { bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000); bool ctrl = ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000); bool alt = ((GetKeyState(VK_MENU) & 0x8000) == 0x8000); if ((gw == NULL) || (gw->mouse == NULL) || (gw->bw == NULL)) return 0; /* scale co-ordinates */ x = (x + gw->scrollx) / gw->scale; y = (y + gw->scrolly) / gw->scale; /* if mouse button held down and pointer moved more than * minimum distance drag is happening */ if (((gw->mouse->state & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2)) != 0) && (abs(x - gw->mouse->pressed_x) >= 5) && (abs(y - gw->mouse->pressed_y) >= 5)) { LOG("Drag start state 0x%x", gw->mouse->state); if ((gw->mouse->state & BROWSER_MOUSE_PRESS_1) != 0) { browser_window_mouse_click(gw->bw, BROWSER_MOUSE_DRAG_1, gw->mouse->pressed_x, gw->mouse->pressed_y); gw->mouse->state &= ~BROWSER_MOUSE_PRESS_1; gw->mouse->state |= BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_DRAG_ON; } else if ((gw->mouse->state & BROWSER_MOUSE_PRESS_2) != 0) { browser_window_mouse_click(gw->bw, BROWSER_MOUSE_DRAG_2, gw->mouse->pressed_x, gw->mouse->pressed_y); gw->mouse->state &= ~BROWSER_MOUSE_PRESS_2; gw->mouse->state |= BROWSER_MOUSE_HOLDING_2 | BROWSER_MOUSE_DRAG_ON; } } if (((gw->mouse->state & BROWSER_MOUSE_MOD_1) != 0) && !shift) gw->mouse->state &= ~BROWSER_MOUSE_MOD_1; if (((gw->mouse->state & BROWSER_MOUSE_MOD_2) != 0) && !ctrl) gw->mouse->state &= ~BROWSER_MOUSE_MOD_2; if (((gw->mouse->state & BROWSER_MOUSE_MOD_3) != 0) && !alt) gw->mouse->state &= ~BROWSER_MOUSE_MOD_3; browser_window_mouse_track(gw->bw, gw->mouse->state, x, y); return 0; } /** * Called when activity occours within the drawable window. */ static LRESULT CALLBACK nsws_window_drawable_event_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { struct gui_window *gw; LOG_WIN_MSG(hwnd, msg, wparam, lparam); gw = nsws_get_gui_window(hwnd); if (gw == NULL) { LOG("Unable to find gui window structure for hwnd %p", hwnd); return DefWindowProc(hwnd, msg, wparam, lparam); } switch(msg) { case WM_MOUSEMOVE: return nsws_drawable_mousemove(gw, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); case WM_LBUTTONDOWN: nsws_drawable_mousedown(gw, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), BROWSER_MOUSE_PRESS_1); SetFocus(hwnd); nsws_localhistory_close(gw); return 0; break; case WM_RBUTTONDOWN: nsws_drawable_mousedown(gw, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), BROWSER_MOUSE_PRESS_2); SetFocus(hwnd); return 0; break; case WM_LBUTTONUP: return nsws_drawable_mouseup(gw, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), BROWSER_MOUSE_PRESS_1, BROWSER_MOUSE_CLICK_1); case WM_RBUTTONUP: return nsws_drawable_mouseup(gw, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), BROWSER_MOUSE_PRESS_2, BROWSER_MOUSE_CLICK_2); case WM_ERASEBKGND: /* ignore as drawable window is redrawn on paint */ return 0; case WM_PAINT: /* redraw the exposed part of the window */ return nsws_drawable_paint(gw, hwnd); case WM_KEYDOWN: return nsws_drawable_key(gw, hwnd, wparam); case WM_SIZE: return nsws_drawable_resize(gw); case WM_HSCROLL: return nsws_drawable_hscroll(gw, hwnd, wparam); case WM_VSCROLL: return nsws_drawable_vscroll(gw, hwnd, wparam); case WM_MOUSEWHEEL: return nsws_drawable_wheel(gw, hwnd, wparam); } return DefWindowProc(hwnd, msg, wparam, lparam); } /** * Create a drawable window. */ HWND nsws_window_create_drawable(HINSTANCE hinstance, HWND hparent, struct gui_window *gw) { HWND hwnd; hwnd = CreateWindow(windowclassname_drawable, NULL, WS_VISIBLE | WS_CHILD, 0, 0, 0, 0, hparent, NULL, hinstance, NULL); if (hwnd == NULL) { win_perror("WindowCreateDrawable"); LOG("Window creation failed"); return NULL; } /* set the gui window associated with this toolbar */ SetProp(hwnd, TEXT("GuiWnd"), (HANDLE)gw); return hwnd; } /** * Create the drawable window class. */ nserror nsws_create_drawable_class(HINSTANCE hinstance) { nserror ret = NSERROR_OK; WNDCLASSEX w; /* drawable area */ w.cbSize = sizeof(WNDCLASSEX); w.style = 0; w.lpfnWndProc = nsws_window_drawable_event_callback; w.cbClsExtra = 0; w.cbWndExtra = 0; w.hInstance = hinstance; w.hIcon = NULL; w.hCursor = NULL; w.hbrBackground = (HBRUSH)(COLOR_MENU + 1); w.lpszMenuName = NULL; w.lpszClassName = windowclassname_drawable; w.hIconSm = NULL; if (RegisterClassEx(&w) == 0) { win_perror("DrawableClass"); ret = NSERROR_INIT_FAILED; } return ret; }