2006-05-01 Steve Falkenburg <sfalken@microsoft.com>
[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     d->mainFrame->loadHTMLString("<p style=\"background-color: #00FF00\">Testing</p><img src=\"http://webkit.opendarwin.org/images/icon-gold.png\" alt=\"Face\"><div style=\"border: solid blue\" contenteditable=\"true\">div with blue border</div><ul><li>foo<li>bar<li>baz</ul>");
112 }
113
114 WebView::~WebView()
115 {
116     delete d;
117 }
118
119 HWND WebView::windowHandle()
120 {
121     return d->windowHandle;
122 }
123
124 WebFrame* WebView::mainFrame()
125 {
126     return d->mainFrame;
127 }
128
129 void WebView::mouseMoved(WPARAM wParam, LPARAM lParam)
130 {
131     PlatformMouseEvent mouseEvent(windowHandle(), wParam, lParam, 0);
132     d->mainFrame->toPrivate()->impl()->view()->handleMouseMoveEvent(mouseEvent);
133 }
134
135 void WebView::mouseDown(WPARAM wParam, LPARAM lParam)
136 {
137     PlatformMouseEvent mouseEvent(windowHandle(), wParam, lParam, 1);
138     d->mainFrame->toPrivate()->impl()->view()->handleMousePressEvent(mouseEvent);
139 }
140
141 void WebView::mouseUp(WPARAM wParam, LPARAM lParam)
142 {
143     PlatformMouseEvent mouseEvent(windowHandle(), wParam, lParam, 1);
144     d->mainFrame->toPrivate()->impl()->view()->handleMouseReleaseEvent(mouseEvent);
145 }
146
147 void WebView::mouseDoubleClick(WPARAM wParam, LPARAM lParam)
148 {
149     PlatformMouseEvent mouseEvent(windowHandle(), wParam, lParam, 2);
150     d->mainFrame->toPrivate()->impl()->view()->handleMouseReleaseEvent(mouseEvent);
151 }
152
153 bool WebView::keyPress(WPARAM wParam, LPARAM lParam)
154 {
155     PlatformKeyboardEvent keyEvent(windowHandle(), wParam, lParam);
156
157     FrameWin* frame = static_cast<FrameWin*>(d->mainFrame->toPrivate()->impl());
158     bool handled = frame->keyPress(keyEvent);
159     if (!handled && !keyEvent.isKeyUp()) {
160         Node* start = frame->selection().start().node();
161         if (start && start->isContentEditable()) {
162             switch(keyEvent.WindowsKeyCode()) {
163             case VK_BACK:
164                 TypingCommand::deleteKeyPressed(frame->document());
165                 break;
166             case VK_DELETE:
167                 TypingCommand::forwardDeleteKeyPressed(frame->document());
168                 break;
169             case VK_LEFT:
170                 frame->selection().modify(SelectionController::MOVE, SelectionController::LEFT, CharacterGranularity);
171                 break;
172             case VK_RIGHT:
173                 frame->selection().modify(SelectionController::MOVE, SelectionController::RIGHT, CharacterGranularity);
174                 break;
175             case VK_UP:
176                 frame->selection().modify(SelectionController::MOVE, SelectionController::BACKWARD, ParagraphGranularity);
177                 break;
178             case VK_DOWN:
179                 frame->selection().modify(SelectionController::MOVE, SelectionController::FORWARD, ParagraphGranularity);
180                 break;
181             default:
182                 nextCharIsInputText = true;
183             }
184             handled = true;
185         }
186     }
187     return handled;
188 }
189
190 #define LINE_SCROLL_SIZE 30
191
192 static int calculateScrollDelta(WPARAM wParam, int oldPosition, int pageSize)
193 {
194     switch (LOWORD(wParam)) {
195         case SB_PAGEUP: 
196             return -(pageSize - LINE_SCROLL_SIZE); 
197          case SB_PAGEDOWN: 
198             return (pageSize - LINE_SCROLL_SIZE); 
199         case SB_LINEUP: 
200             return -LINE_SCROLL_SIZE;
201         case SB_LINEDOWN: 
202             return LINE_SCROLL_SIZE;
203         case SB_THUMBPOSITION: 
204         case SB_THUMBTRACK:
205             return HIWORD(wParam) - oldPosition; 
206     }
207     return 0;
208 }
209
210 static int scrollMessageForKey(WPARAM keyCode)
211 {
212     switch (keyCode) {
213     case VK_UP:
214         return SB_LINEUP;
215     case VK_PRIOR: 
216         return SB_PAGEUP;
217     case VK_NEXT:
218         return SB_PAGEDOWN;
219     case VK_DOWN:
220         return SB_LINEDOWN;
221     case VK_HOME:
222         return SB_TOP;
223     case VK_END:
224         return SB_BOTTOM;
225     case VK_SPACE:
226         return (GetKeyState(VK_SHIFT) & 0x8000) ? SB_PAGEUP : SB_PAGEDOWN;
227     }
228     return -1;
229 }
230
231 LRESULT CALLBACK WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
232 {
233     int wmId, wmEvent;
234     WebView* webview = (WebView*)GetWindowLongPtr(hWnd, 0);
235     switch (message)
236     {
237     case WM_PAINT:
238         webview->mainFrame()->toPrivate()->paint();
239         break;
240     case WM_DESTROY:
241         // Do nothing?
242         break;
243     case WM_MOUSEMOVE:
244         webview->mouseMoved(wParam, lParam);
245         break;
246     case WM_LBUTTONDOWN:
247         // Make ourselves the focused window before doing anything else
248         // FIXME: I'm not sure if this is the "right" way to do this
249         // but w/o this call, we never become focused since we don't allow
250         // the default handling of mouse events.
251         SetFocus(hWnd);
252     case WM_MBUTTONDOWN:
253     case WM_RBUTTONDOWN:
254         webview->mouseDown(wParam, lParam);
255         break;
256     case WM_LBUTTONUP:
257     case WM_MBUTTONUP:
258     case WM_RBUTTONUP:
259         webview->mouseUp(wParam, lParam);
260         break;
261     case WM_LBUTTONDBLCLK:
262     case WM_MBUTTONDBLCLK:
263     case WM_RBUTTONDBLCLK:
264         webview->mouseDoubleClick(wParam, lParam);
265         break;
266     case WM_HSCROLL: {
267         ScrollView* view = webview->mainFrame()->toPrivate()->impl()->view();
268         view->scrollBy(calculateScrollDelta(wParam, view->contentsX(), view->visibleWidth()), 0);
269         webview->mainFrame()->toPrivate()->impl()->sendScrollEvent();
270         break;
271     }
272     case WM_VSCROLL: {
273         ScrollView* view = webview->mainFrame()->toPrivate()->impl()->view();
274         view->scrollBy(0, calculateScrollDelta(wParam, view->contentsY(), view->visibleHeight()));
275         webview->mainFrame()->toPrivate()->impl()->sendScrollEvent();
276         break;
277     }
278     case WM_KEYDOWN: {
279         // FIXME: First we should send key events up through the DOM
280         // to form controls, etc.  If they are not handled, we fall
281         // through to the top level webview and do things like scrolling
282         if (webview->keyPress(wParam, lParam))
283             break;
284         WORD wScrollNotify = scrollMessageForKey(wParam);
285         if (wScrollNotify != -1)
286             SendMessage(hWnd, WM_VSCROLL, MAKELONG(wScrollNotify, 0), 0L);
287         break;
288     }
289     case WM_CHAR: {
290         // FIXME: We need to use WM_UNICHAR to support international text.
291         if (nextCharIsInputText) {
292             TypingCommand::insertText(webview->mainFrame()->toPrivate()->impl()->document(), QChar(wParam), 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 };