1/*
2 * Copyright (C) 2010 Apple 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 INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "StdAfx.h"
27#include "BrowserWindow.h"
28#include "MiniBrowser.h"
29#include "Resource.h"
30
31#include <assert.h>
32#include <commctrl.h>
33#include <shlwapi.h>
34#include <vector>
35#include <wininet.h>
36
37using namespace std;
38
39BrowserWindow::BrowserWindow()
40    : m_window(0)
41    , m_rebarWindow(0)
42    , m_comboBoxWindow(0)
43{
44}
45
46LRESULT CALLBACK BrowserWindow::BrowserWindowWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
47{
48    LONG_PTR longPtr = ::GetWindowLongPtr(window, 0);
49
50    if (BrowserWindow* browserView = reinterpret_cast<BrowserWindow*>(longPtr))
51        return browserView->wndProc(window, message, wParam, lParam);
52
53    if (message == WM_CREATE) {
54        LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
55        BrowserWindow* browserWindow = static_cast<BrowserWindow*>(createStruct->lpCreateParams);
56        browserWindow->m_window = window;
57
58        ::SetWindowLongPtr(window, 0, (LONG_PTR)browserWindow);
59
60        browserWindow->onCreate(createStruct);
61        return 0;
62    }
63
64    return ::DefWindowProc(window, message, wParam, lParam);
65}
66
67LRESULT BrowserWindow::wndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
68{
69    LRESULT lResult = 0;
70    bool handled = true;
71
72    switch (message) {
73    case WM_ERASEBKGND:
74        lResult = 1;
75        break;
76
77    case WM_COMMAND:
78        lResult = onCommand(LOWORD(wParam), handled);
79        break;
80
81    case WM_SIZE:
82        onSize(LOWORD(lParam), HIWORD(lParam));
83        break;
84
85    case WM_DESTROY:
86        onDestroy();
87        break;
88
89    case WM_NCDESTROY:
90        onNCDestroy();
91        break;
92
93    default:
94        handled = false;
95    }
96
97    if (!handled)
98        lResult = ::DefWindowProc(window, message, wParam, lParam);
99
100    return lResult;
101}
102
103void BrowserWindow::createWindow(int x, int y, int width, int height)
104{
105    assert(!m_window);
106
107    // Register the class.
108    WNDCLASSEX windowClass = { 0 };
109    windowClass.cbSize = sizeof(windowClass);
110    windowClass.style = 0;
111    windowClass.lpfnWndProc = BrowserWindowWndProc;
112    windowClass.cbClsExtra = 0;
113    windowClass.cbWndExtra = sizeof(this);
114    windowClass.hInstance = MiniBrowser::shared().instance();
115    windowClass.hIcon = 0;
116    windowClass.hCursor = ::LoadCursor(0, IDC_ARROW);
117    windowClass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
118    windowClass.lpszMenuName = MAKEINTRESOURCE(IDR_MAINFRAME);
119    windowClass.lpszClassName = L"MiniBrowser";
120    windowClass.hIconSm = 0;
121
122    ::RegisterClassEx(&windowClass);
123
124    ::CreateWindowW(L"MiniBrowser", L"MiniBrowser", WS_OVERLAPPEDWINDOW, x, y, width, height, 0, 0, MiniBrowser::shared().instance(), this);
125}
126
127void BrowserWindow::showWindow()
128{
129    assert(m_window);
130
131    ::ShowWindow(m_window, SW_SHOWNORMAL);
132}
133
134void BrowserWindow::goToURL(const std::wstring& url)
135{
136    m_browserView.goToURL(url);
137}
138
139void BrowserWindow::onCreate(LPCREATESTRUCT createStruct)
140{
141    // Register our window.
142    MiniBrowser::shared().registerWindow(this);
143
144    // Create the rebar control.
145    m_rebarWindow = ::CreateWindowEx(0, REBARCLASSNAME, 0, WS_VISIBLE | WS_BORDER | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN | RBS_VARHEIGHT | RBS_BANDBORDERS,
146                                     0, 0, 0, 0, m_window, 0, createStruct->hInstance, 0);
147    REBARINFO rebarInfo = { 0 };
148    rebarInfo.cbSize = sizeof(rebarInfo);
149    rebarInfo.fMask = 0;
150    ::SendMessage(m_rebarWindow, RB_SETBARINFO, 0, (LPARAM)&rebarInfo);
151
152    // Create the combo box control.
153    m_comboBoxWindow = ::CreateWindowEx(0, L"combobox", 0, WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CBS_AUTOHSCROLL | CBS_DROPDOWN,
154                                        0, 0, 0, 0, m_rebarWindow, 0, createStruct->hInstance, 0);
155    SendMessage(m_comboBoxWindow, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(TRUE, 0));
156
157    REBARBANDINFO bandInfo;
158    bandInfo.cbSize = sizeof(bandInfo);
159    bandInfo.fMask = RBBIM_STYLE | RBBIM_TEXT | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE;
160    bandInfo.fStyle = RBBS_CHILDEDGE | RBBS_GRIPPERALWAYS;
161    bandInfo.lpText = L"Address";
162    bandInfo.hwndChild = m_comboBoxWindow;
163
164    RECT comboBoxRect;
165    ::GetWindowRect(m_comboBoxWindow, &comboBoxRect);
166    bandInfo.cx = 100;
167    bandInfo.cxMinChild = comboBoxRect.right - comboBoxRect.left;
168    bandInfo.cyMinChild = comboBoxRect.bottom - comboBoxRect.top;
169
170    // Add the band to the rebar.
171    int result = ::SendMessage(m_rebarWindow, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&bandInfo);
172
173    // Create the browser view.
174    RECT webViewRect = { 0, 0, 0, 0};
175    m_browserView.create(webViewRect, this);
176}
177
178void BrowserWindow::onDestroy()
179{
180    MiniBrowser::shared().unregisterWindow(this);
181
182    // FIXME: Should we close the browser view here?
183}
184
185void BrowserWindow::onNCDestroy()
186{
187    delete this;
188}
189
190void BrowserWindow::onSize(int width, int height)
191{
192    RECT rebarRect;
193    ::GetClientRect(m_rebarWindow, &rebarRect);
194
195    // Resize the rebar.
196    ::MoveWindow(m_rebarWindow, 0, 0, width, rebarRect.bottom - rebarRect.top, true);
197
198    RECT webViewRect;
199    webViewRect.top = rebarRect.bottom;
200    webViewRect.left = 0;
201    webViewRect.right = width;
202    webViewRect.bottom = height;
203    m_browserView.setFrame(webViewRect);
204}
205
206LRESULT BrowserWindow::onCommand(int commandID, bool& handled)
207{
208    switch (commandID) {
209    case ID_FILE_NEW_WINDOW:
210        MiniBrowser::shared().createNewWindow();
211        break;
212    case ID_FILE_CLOSE:
213        ::PostMessage(m_window, WM_CLOSE, 0, 0);
214        break;
215    case ID_DEBUG_SHOW_WEB_VIEW: {
216        HMENU menu = ::GetMenu(m_window);
217        bool shouldHide = ::GetMenuState(menu, ID_DEBUG_SHOW_WEB_VIEW, MF_BYCOMMAND) & MF_CHECKED;
218
219        ::CheckMenuItem(menu, ID_DEBUG_SHOW_WEB_VIEW, MF_BYCOMMAND | (shouldHide ? MF_UNCHECKED : MF_CHECKED));
220
221        // Show or hide the web view.
222        HWND webViewWindow = WKViewGetWindow(m_browserView.webView());
223        ::ShowWindow(webViewWindow, shouldHide ? SW_HIDE : SW_SHOW);
224        break;
225    }
226    default:
227        handled = false;
228    }
229
230    return 0;
231}
232
233bool BrowserWindow::handleMessage(const MSG* message)
234{
235    if (message->hwnd != m_comboBoxWindow && !::IsChild(m_comboBoxWindow, message->hwnd))
236        return false;
237
238    // Look for a WM_KEYDOWN message.
239    if (message->message != WM_KEYDOWN)
240        return false;
241
242    // Look for the VK_RETURN key.
243    if (message->wParam != VK_RETURN)
244        return false;
245
246    std::vector<WCHAR> buffer;
247    int textLength = ::GetWindowTextLength(m_comboBoxWindow);
248
249    buffer.resize(textLength + 1);
250    ::GetWindowText(m_comboBoxWindow, &buffer[0], buffer.size());
251
252    std::wstring url(&buffer[0], buffer.size() - 1);
253
254    if (url.find(L"http://"))
255        url = L"http://" + url;
256
257    m_browserView.goToURL(url);
258
259    // We handled this message.
260    return true;
261}
262