Reviewed by Darin.
[WebKit-https.git] / WebKitWin / WebView.cpp
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "stdafx.h"
27 #include "config.h"
28
29 #include "WebFrame.h"
30 #include "WebFramePrivate.h"
31
32 #include "Document.h"
33 #include "FrameView.h"
34 #include "IntRect.h"
35 #include "PlatformKeyboardEvent.h"
36 #include "PlatformMouseEvent.h"
37 #include "SelectionController.h"
38 #include "TypingCommand.h"
39 #include "WebView.h"
40
41 using namespace WebCore;
42
43 namespace WebKit {
44
45 class WebView::WebViewPrivate {
46 public:
47     WebViewPrivate() {}
48     ~WebViewPrivate()
49     {
50         delete mainFrame;
51     }
52
53     WebFrame* mainFrame;
54     HWND windowHandle;
55 };
56
57 const LPCWSTR kWebViewWindowClassName = L"WebViewWindowClass";
58 static bool nextCharIsInputText = false;
59
60 LRESULT CALLBACK WebViewWndProc(HWND, UINT, WPARAM, LPARAM);
61
62 static ATOM registerWebViewWithInstance(HINSTANCE hInstance)
63 {
64     static bool haveRegisteredWindowClass = false;
65     if (haveRegisteredWindowClass)
66         return true;
67
68     WNDCLASSEX wcex;
69
70     wcex.cbSize = sizeof(WNDCLASSEX);
71
72     wcex.style          = CS_DBLCLKS;
73     wcex.lpfnWndProc    = WebViewWndProc;
74     wcex.cbClsExtra     = 0;
75     wcex.cbWndExtra     = 4; // 4 bytes for the WebView pointer
76     wcex.hInstance      = hInstance;
77     wcex.hIcon          = 0;
78     wcex.hCursor        = LoadCursor(0, IDC_ARROW);
79     wcex.hbrBackground  = 0;
80     wcex.lpszMenuName   = 0;
81     wcex.lpszClassName  = kWebViewWindowClassName;
82     wcex.hIconSm        = 0;
83
84     return RegisterClassEx(&wcex);
85 }
86
87 // FIXME: This should eventually just use the DLL instance, I think.
88 WebView* WebView::createWebView(HINSTANCE hInstance, HWND parent, WebHost* host)
89 {
90     // Save away our instace handle for WebCore to use.
91     Widget::instanceHandle = hInstance;
92
93     registerWebViewWithInstance(hInstance);
94
95     HWND hWnd = CreateWindow(kWebViewWindowClassName, 0, WS_CHILD | WS_HSCROLL | WS_VSCROLL,
96        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, parent, 0, hInstance, 0);
97
98     if (!hWnd)
99         return 0;
100
101     WebView* newWebView = new WebView(hWnd, host);
102     SetWindowLongPtr(hWnd, 0, (LONG)newWebView);
103     return newWebView;
104 }
105
106 WebView::WebView(HWND hWnd, WebHost* host)
107 {
108     d = new WebViewPrivate();
109     d->windowHandle = hWnd;
110     d->mainFrame = WebFrame::createWebFrame("dummy", this, host);
111 }
112
113 WebView::~WebView()
114 {
115     delete d;
116 }
117
118 HWND WebView::windowHandle()
119 {
120     return d->windowHandle;
121 }
122
123 WebFrame* WebView::mainFrame()
124 {
125     return d->mainFrame;
126 }
127
128 void WebView::mouseMoved(WPARAM wParam, LPARAM lParam)
129 {
130     PlatformMouseEvent mouseEvent(windowHandle(), wParam, lParam, 0);
131     d->mainFrame->toPrivate()->impl()->view()->handleMouseMoveEvent(mouseEvent);
132 }
133
134 void WebView::mouseDown(WPARAM wParam, LPARAM lParam)
135 {
136     PlatformMouseEvent mouseEvent(windowHandle(), wParam, lParam, 1);
137     d->mainFrame->toPrivate()->impl()->view()->handleMousePressEvent(mouseEvent);
138 }
139
140 void WebView::mouseUp(WPARAM wParam, LPARAM lParam)
141 {
142     PlatformMouseEvent mouseEvent(windowHandle(), wParam, lParam, 1);
143     d->mainFrame->toPrivate()->impl()->view()->handleMouseReleaseEvent(mouseEvent);
144 }
145
146 void WebView::mouseDoubleClick(WPARAM wParam, LPARAM lParam)
147 {
148     PlatformMouseEvent mouseEvent(windowHandle(), wParam, lParam, 2);
149     d->mainFrame->toPrivate()->impl()->view()->handleMouseReleaseEvent(mouseEvent);
150 }
151
152 bool WebView::keyPress(WPARAM wParam, LPARAM lParam)
153 {
154     PlatformKeyboardEvent keyEvent(windowHandle(), wParam, lParam);
155
156     FrameWin* frame = static_cast<FrameWin*>(d->mainFrame->toPrivate()->impl());
157     bool handled = frame->keyPress(keyEvent);
158     if (!handled && !keyEvent.isKeyUp()) {
159         Node* start = frame->selection().start().node();
160         if (start && start->isContentEditable()) {
161             switch(keyEvent.WindowsKeyCode()) {
162             case VK_BACK:
163                 TypingCommand::deleteKeyPressed(frame->document());
164                 break;
165             case VK_DELETE:
166                 TypingCommand::forwardDeleteKeyPressed(frame->document());
167                 break;
168             case VK_LEFT:
169                 frame->selection().modify(SelectionController::MOVE, SelectionController::LEFT, CharacterGranularity);
170                 break;
171             case VK_RIGHT:
172                 frame->selection().modify(SelectionController::MOVE, SelectionController::RIGHT, CharacterGranularity);
173                 break;
174             case VK_UP:
175                 frame->selection().modify(SelectionController::MOVE, SelectionController::BACKWARD, ParagraphGranularity);
176                 break;
177             case VK_DOWN:
178                 frame->selection().modify(SelectionController::MOVE, SelectionController::FORWARD, ParagraphGranularity);
179                 break;
180             default:
181                 nextCharIsInputText = true;
182             }
183             handled = true;
184         }
185     }
186     return handled;
187 }
188
189 #define LINE_SCROLL_SIZE 30
190
191 static int calculateScrollDelta(WPARAM wParam, int oldPosition, int pageSize)
192 {
193     switch (LOWORD(wParam)) {
194         case SB_PAGEUP: 
195             return -(pageSize - LINE_SCROLL_SIZE); 
196          case SB_PAGEDOWN: 
197             return (pageSize - LINE_SCROLL_SIZE); 
198         case SB_LINEUP: 
199             return -LINE_SCROLL_SIZE;
200         case SB_LINEDOWN: 
201             return LINE_SCROLL_SIZE;
202         case SB_THUMBPOSITION: 
203         case SB_THUMBTRACK:
204             return HIWORD(wParam) - oldPosition; 
205     }
206     return 0;
207 }
208
209 static int scrollMessageForKey(WPARAM keyCode)
210 {
211     switch (keyCode) {
212     case VK_UP:
213         return SB_LINEUP;
214     case VK_PRIOR: 
215         return SB_PAGEUP;
216     case VK_NEXT:
217         return SB_PAGEDOWN;
218     case VK_DOWN:
219         return SB_LINEDOWN;
220     case VK_HOME:
221         return SB_TOP;
222     case VK_END:
223         return SB_BOTTOM;
224     case VK_SPACE:
225         return (GetKeyState(VK_SHIFT) & 0x8000) ? SB_PAGEUP : SB_PAGEDOWN;
226     }
227     return -1;
228 }
229
230 LRESULT CALLBACK WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
231 {
232     int wmId, wmEvent;
233     WebView* webview = (WebView*)GetWindowLongPtr(hWnd, 0);
234     switch (message)
235     {
236     case WM_PAINT:
237         webview->mainFrame()->toPrivate()->paint();
238         break;
239     case WM_DESTROY:
240         // Do nothing?
241         break;
242     case WM_MOUSEMOVE:
243         webview->mouseMoved(wParam, lParam);
244         break;
245     case WM_LBUTTONDOWN:
246         // Make ourselves the focused window before doing anything else
247         // FIXME: I'm not sure if this is the "right" way to do this
248         // but w/o this call, we never become focused since we don't allow
249         // the default handling of mouse events.
250         SetFocus(hWnd);
251     case WM_MBUTTONDOWN:
252     case WM_RBUTTONDOWN:
253         webview->mouseDown(wParam, lParam);
254         break;
255     case WM_LBUTTONUP:
256     case WM_MBUTTONUP:
257     case WM_RBUTTONUP:
258         webview->mouseUp(wParam, lParam);
259         break;
260     case WM_LBUTTONDBLCLK:
261     case WM_MBUTTONDBLCLK:
262     case WM_RBUTTONDBLCLK:
263         webview->mouseDoubleClick(wParam, lParam);
264         break;
265     case WM_HSCROLL: {
266         ScrollView* view = webview->mainFrame()->toPrivate()->impl()->view();
267         view->scrollBy(calculateScrollDelta(wParam, view->contentsX(), view->visibleWidth()), 0);
268         webview->mainFrame()->toPrivate()->impl()->sendScrollEvent();
269         break;
270     }
271     case WM_VSCROLL: {
272         ScrollView* view = webview->mainFrame()->toPrivate()->impl()->view();
273         view->scrollBy(0, calculateScrollDelta(wParam, view->contentsY(), view->visibleHeight()));
274         webview->mainFrame()->toPrivate()->impl()->sendScrollEvent();
275         break;
276     }
277     case WM_KEYDOWN: {
278         // FIXME: First we should send key events up through the DOM
279         // to form controls, etc.  If they are not handled, we fall
280         // through to the top level webview and do things like scrolling
281         if (webview->keyPress(wParam, lParam))
282             break;
283         WORD wScrollNotify = scrollMessageForKey(wParam);
284         if (wScrollNotify != -1)
285             SendMessage(hWnd, WM_VSCROLL, MAKELONG(wScrollNotify, 0), 0L);
286         break;
287     }
288     case WM_CHAR: {
289         // FIXME: We need to use WM_UNICHAR to support international text.
290         if (nextCharIsInputText) {
291             UChar c = wParam;
292             TypingCommand::insertText(webview->mainFrame()->toPrivate()->impl()->document(), String(&c, 1), false);
293             nextCharIsInputText = false;
294         }
295         break;
296     }
297     case WM_KEYUP: {
298         webview->keyPress(wParam, lParam);
299         break;
300     }
301     case WM_SIZE:
302         if (!webview)
303             break;
304         webview->mainFrame()->toPrivate()->impl()->sendResizeEvent();
305         break;
306     case WM_SETFOCUS:
307         webview->mainFrame()->toPrivate()->impl()->setWindowHasFocus(true);
308         webview->mainFrame()->toPrivate()->impl()->setDisplaysWithFocusAttributes(true);
309         break;
310     case WM_KILLFOCUS:
311         webview->mainFrame()->toPrivate()->impl()->setWindowHasFocus(false);
312         webview->mainFrame()->toPrivate()->impl()->setDisplaysWithFocusAttributes(false);
313         break;
314     default:
315         return DefWindowProc(hWnd, message, wParam, lParam);
316     }
317     return 0;
318 }
319
320 };