netsurf/frontends/windows/drawable.c
Vincent Sanders 95b8d12950 implement windows clipboard functionality
This allows clipboard to operate (cut, copy, paste and delete) in the
 win32 front end. The clipboard is set and read in windows unicode
 mode and then converted to/from utf-8 for the browser core.
2019-05-09 23:11:05 +01:00

672 lines
14 KiB
C

/*
* Copyright 2011 Vincent Sanders <vince@simtec.co.uk>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/**
* \file
* win32 implementation of drawable window showing browser context
*/
#include <stdbool.h>
#include <stdint.h>
#include "utils/config.h"
#include <windows.h>
#include <windowsx.h>
#include "utils/errors.h"
#include "utils/log.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/local_history.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;
NSLOG(netsurf, INFO, "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) {
struct rect rect;
rect.x0 = rect.x1 = gw->scrollx;
rect.y0 = rect.y1 = gw->scrolly + gw->requestscrolly + si.nPos - mem;
win32_window_set_scroll(gw, &rect);
}
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;
NSLOG(netsurf, INFO, "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) {
struct rect rect;
rect.x0 = rect.x1 = gw->scrollx + gw->requestscrollx + si.nPos - mem;
rect.y0 = rect.y1 = gw->scrolly;
win32_window_set_scroll(gw, &rect);
}
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;
NSLOG(netsurf, INFO, "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) {
NSLOG(netsurf, INFO,
"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)) {
nsw32_local_history_hide();
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;
NSLOG(netsurf, INFO, "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)) {
NSLOG(netsurf, INFO, "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) {
NSLOG(netsurf, INFO,
"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);
nsw32_local_history_hide();
return 0;
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);
case WM_PASTE:
browser_window_key_press(gw->bw, NS_KEY_PASTE);
return 0;
case WM_COPY:
browser_window_key_press(gw->bw, NS_KEY_COPY_SELECTION);
return 0;
case WM_CUT:
browser_window_key_press(gw->bw, NS_KEY_CUT_SELECTION);
return 0;
case WM_CLEAR:
/**
* \todo win32 clear operation deletes the contents of
* the selection but ns clear selection only
* removes the highlight.
*/
browser_window_key_press(gw->bw, NS_KEY_CLEAR_SELECTION);
return 0;
}
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");
NSLOG(netsurf, INFO, "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;
}