haikuwebkit/Tools/HaikuLauncher/LauncherWindow.cpp

470 lines
15 KiB
C++

/*
* Copyright (C) 2007 Andrea Anzani <andrea.anzani@gmail.com>
* Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com>
* Copyright (C) 2009 Maxime Simon <simon.maxime@gmail.com>
* Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
* Copyright (C) 2010 Michael Lotz <mmlr@mlotz.ch>
* Copyright (C) 2010 Rene Gollent <rene@gollent.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "LauncherWindow.h"
#include "AuthenticationPanel.h"
#include "LauncherApp.h"
#include "WebPage.h"
#include "WebView.h"
#include "WebViewConstants.h"
#include <interface/StringView.h>
#include <Button.h>
#include <Entry.h>
#include <FilePanel.h>
#include <GridLayoutBuilder.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <Path.h>
#include <SeparatorView.h>
#include <SpaceLayoutItem.h>
#include <StatusBar.h>
#include <StringView.h>
#include <TextControl.h>
#include <stdio.h>
enum {
OPEN_LOCATION = 'open',
OPEN_INSPECTOR = 'insp',
SAVE_PAGE = 'save',
GO_BACK = 'goba',
GO_FORWARD = 'gofo',
STOP = 'stop',
GOTO_URL = 'goul',
RELOAD = 'reld',
TEXT_SIZE_INCREASE = 'tsin',
TEXT_SIZE_DECREASE = 'tsdc',
TEXT_SIZE_RESET = 'tsrs',
};
LauncherWindow::LauncherWindow(BRect frame, ToolbarPolicy toolbarPolicy)
: BWebWindow(frame, "HaikuLauncher",
B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS)
{
init(new BWebView("web view"), toolbarPolicy);
}
LauncherWindow::LauncherWindow(BRect frame, BWebView* webView,
ToolbarPolicy toolbarPolicy)
: BWebWindow(frame, "HaikuLauncher",
B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS)
{
init(webView, toolbarPolicy);
}
LauncherWindow::~LauncherWindow()
{
delete m_saveFilePanel;
}
void LauncherWindow::DispatchMessage(BMessage* message, BHandler* target)
{
if (m_url && message->what == B_KEY_DOWN && target == m_url->TextView()) {
// Handle B_RETURN in the URL text control. This is the easiest
// way to react *only* when the user presses the return key in the
// address bar, as opposed to trying to load whatever is in there when
// the text control just goes out of focus.
const char* bytes;
if (message->FindString("bytes", &bytes) == B_OK
&& bytes[0] == B_RETURN) {
// Do it in such a way that the user sees the Go-button go down.
m_goButton->SetValue(B_CONTROL_ON);
UpdateIfNeeded();
m_goButton->Invoke();
snooze(1000);
m_goButton->SetValue(B_CONTROL_OFF);
}
}
BWebWindow::DispatchMessage(message, target);
}
void LauncherWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case OPEN_LOCATION:
if (m_url) {
if (m_url->TextView()->IsFocus())
m_url->TextView()->SelectAll();
else
m_url->MakeFocus(true);
}
break;
case OPEN_INSPECTOR: {
// FIXME: wouldn't the view better be in the same window?
BRect frame = Frame();
frame.OffsetBy(20, 20);
LauncherWindow* inspectorWindow = new LauncherWindow(frame);
inspectorWindow->Show();
CurrentWebView()->SetInspectorView(inspectorWindow->CurrentWebView());
break;
}
case SAVE_PAGE: {
BMessage* message = new BMessage(B_SAVE_REQUESTED);
message->AddPointer("page", CurrentWebView()->WebPage());
if (m_saveFilePanel == NULL) {
m_saveFilePanel = new BFilePanel(B_SAVE_PANEL, NULL, NULL,
B_DIRECTORY_NODE, false);
}
m_saveFilePanel->SetSaveText(CurrentWebView()->WebPage()->MainFrameTitle());
m_saveFilePanel->SetMessage(message);
m_saveFilePanel->Show();
break;
}
case RELOAD:
CurrentWebView()->Reload();
break;
case GOTO_URL: {
BString url;
if (message->FindString("url", &url) != B_OK)
url = m_url->Text();
CurrentWebView()->LoadURL(url.String());
break;
}
case GO_BACK:
CurrentWebView()->GoBack();
break;
case GO_FORWARD:
CurrentWebView()->GoForward();
break;
case STOP:
CurrentWebView()->StopLoading();
break;
case B_SIMPLE_DATA: {
// User possibly dropped files on this window.
// If there is more than one entry_ref, let the app handle it (open one
// new page per ref). If there is one ref, open it in this window.
type_code type;
int32 countFound;
if (message->GetInfo("refs", &type, &countFound) != B_OK
|| type != B_REF_TYPE) {
break;
}
if (countFound > 1) {
message->what = B_REFS_RECEIVED;
be_app->PostMessage(message);
break;
}
entry_ref ref;
if (message->FindRef("refs", &ref) != B_OK)
break;
BEntry entry(&ref, true);
BPath path;
if (!entry.Exists() || entry.GetPath(&path) != B_OK)
break;
CurrentWebView()->LoadURL(path.Path());
break;
}
case TEXT_SIZE_INCREASE:
CurrentWebView()->IncreaseZoomFactor(true);
break;
case TEXT_SIZE_DECREASE:
CurrentWebView()->DecreaseZoomFactor(true);
break;
case TEXT_SIZE_RESET:
CurrentWebView()->ResetZoomFactor();
break;
default:
BWebWindow::MessageReceived(message);
break;
}
}
bool LauncherWindow::QuitRequested()
{
BMessage message(WINDOW_CLOSED);
message.AddRect("window frame", Frame());
be_app->PostMessage(&message);
return BWebWindow::QuitRequested();
}
// #pragma mark - Notification API
void LauncherWindow::NavigationRequested(const BString& url, BWebView* view)
{
}
void LauncherWindow::NewWindowRequested(const BString& url, bool primaryAction)
{
// Always open new windows in the application thread, since
// creating a BWebView will try to grab the application lock.
// But our own WebPage may already try to lock us from within
// the application thread -> dead-lock. Thus we can't wait for
// a reply here.
BMessage message(NEW_WINDOW);
message.AddString("url", url);
be_app->PostMessage(&message);
}
void LauncherWindow::NewPageCreated(BWebView* view, BRect windowFrame,
bool modalDialog, bool resizable, bool activate)
{
if (!windowFrame.IsValid())
windowFrame = Frame().OffsetByCopy(10, 10);
LauncherWindow* window = new LauncherWindow(windowFrame, view, HaveToolbar);
window->Show();
}
void LauncherWindow::LoadNegotiating(const BString& url, BWebView* view)
{
BString status("Requesting: ");
status << url;
StatusChanged(status, view);
}
void LauncherWindow::LoadCommitted(const BString& url, BWebView* view)
{
// This hook is invoked when the load is commited.
if (m_url)
m_url->SetText(url.String());
BString status("Loading: ");
status << url;
StatusChanged(status, view);
NavigationCapabilitiesChanged(m_BackButton->IsEnabled(),
m_ForwardButton->IsEnabled(), true, view);
}
void LauncherWindow::LoadProgress(float progress, BWebView* view)
{
if (m_loadingProgressBar) {
if (progress < 100 && m_loadingProgressBar->IsHidden())
m_loadingProgressBar->Show();
m_loadingProgressBar->SetTo(progress);
}
}
void LauncherWindow::LoadFailed(const BString& url, BWebView* view)
{
BString status(url);
status << " failed.";
StatusChanged(status, view);
if (m_loadingProgressBar && !m_loadingProgressBar->IsHidden())
m_loadingProgressBar->Hide();
}
void LauncherWindow::LoadFinished(const BString& url, BWebView* view)
{
// Update the URL again to handle cases where LoadCommitted is not called
// (for example when navigating to anchors in the same page).
if (m_url)
m_url->SetText(url.String());
BString status(url);
status << " finished.";
StatusChanged(status, view);
if (m_loadingProgressBar && !m_loadingProgressBar->IsHidden())
m_loadingProgressBar->Hide();
NavigationCapabilitiesChanged(m_BackButton->IsEnabled(),
m_ForwardButton->IsEnabled(), false, view);
}
void LauncherWindow::SetToolBarsVisible(bool flag, BWebView* view)
{
// TODO
}
void LauncherWindow::SetStatusBarVisible(bool flag, BWebView* view)
{
// TODO
}
void LauncherWindow::SetMenuBarVisible(bool flag, BWebView* view)
{
// TODO
}
void LauncherWindow::TitleChanged(const BString& title, BWebView* view)
{
updateTitle(title);
}
void LauncherWindow::StatusChanged(const BString& statusText, BWebView* view)
{
if (m_statusText)
m_statusText->SetText(statusText.String());
}
void LauncherWindow::NavigationCapabilitiesChanged(bool canGoBackward,
bool canGoForward, bool canStop, BWebView* view)
{
if (m_BackButton)
m_BackButton->SetEnabled(canGoBackward);
if (m_ForwardButton)
m_ForwardButton->SetEnabled(canGoForward);
if (m_StopButton)
m_StopButton->SetEnabled(canStop);
}
bool
LauncherWindow::AuthenticationChallenge(BString message, BString& inOutUser,
BString& inOutPassword, bool& inOutRememberCredentials,
uint32 failureCount, BWebView* view)
{
AuthenticationPanel* panel = new AuthenticationPanel(Frame());
// Panel auto-destructs.
bool success = panel->getAuthentication(message, inOutUser, inOutPassword,
inOutRememberCredentials, failureCount > 0, inOutUser, inOutPassword,
&inOutRememberCredentials);
return success;
}
void LauncherWindow::init(BWebView* webView, ToolbarPolicy toolbarPolicy)
{
SetCurrentWebView(webView);
if (toolbarPolicy == HaveToolbar) {
// Menu
m_menuBar = new BMenuBar("Main menu");
BMenu* menu = new BMenu("Window");
BMessage* newWindowMessage = new BMessage(NEW_WINDOW);
newWindowMessage->AddString("url", "");
BMenuItem* newItem = new BMenuItem("New", newWindowMessage, 'N');
menu->AddItem(newItem);
newItem->SetTarget(be_app);
menu->AddItem(new BMenuItem("Open location", new BMessage(OPEN_LOCATION), 'L'));
menu->AddItem(new BMenuItem("Inspect page", new BMessage(OPEN_INSPECTOR), 'I'));
menu->AddItem(new BMenuItem("Save page", new BMessage(SAVE_PAGE), 'S'));
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED), 'W', B_SHIFT_KEY));
BMenuItem* quitItem = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q');
menu->AddItem(quitItem);
quitItem->SetTarget(be_app);
m_menuBar->AddItem(menu);
menu = new BMenu("Text");
menu->AddItem(new BMenuItem("Increase size", new BMessage(TEXT_SIZE_INCREASE), '+'));
menu->AddItem(new BMenuItem("Decrease size", new BMessage(TEXT_SIZE_DECREASE), '-'));
menu->AddItem(new BMenuItem("Reset size", new BMessage(TEXT_SIZE_RESET), '0'));
m_menuBar->AddItem(menu);
// Back, Forward & Stop
m_BackButton = new BButton("Back", new BMessage(GO_BACK));
m_ForwardButton = new BButton("Forward", new BMessage(GO_FORWARD));
m_StopButton = new BButton("Stop", new BMessage(STOP));
// URL
m_url = new BTextControl("url", "", "", NULL);
// Go
m_goButton = new BButton("", "Go", new BMessage(GOTO_URL));
// Status Bar
m_statusText = new BStringView("status", "");
m_statusText->SetAlignment(B_ALIGN_LEFT);
m_statusText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
m_statusText->SetExplicitMinSize(BSize(150, 12));
// Prevent the window from growing to fit a long status message...
BFont font(be_plain_font);
font.SetSize(ceilf(font.Size() * 0.8));
m_statusText->SetFont(&font, B_FONT_SIZE);
// Loading progress bar
m_loadingProgressBar = new BStatusBar("progress");
m_loadingProgressBar->SetMaxValue(100);
m_loadingProgressBar->Hide();
m_loadingProgressBar->SetBarHeight(12);
const float kInsetSpacing = 5;
const float kElementSpacing = 7;
// Layout
AddChild(BGroupLayoutBuilder(B_VERTICAL)
.Add(m_menuBar)
.Add(BGridLayoutBuilder(kElementSpacing, kElementSpacing)
.Add(m_BackButton, 0, 0)
.Add(m_ForwardButton, 1, 0)
.Add(m_StopButton, 2, 0)
.Add(m_url, 3, 0)
.Add(m_goButton, 4, 0)
.SetInsets(kInsetSpacing, kInsetSpacing, kInsetSpacing, kInsetSpacing)
)
.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
.Add(webView)
.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
.Add(BGroupLayoutBuilder(B_HORIZONTAL, kElementSpacing)
.Add(m_statusText)
.Add(m_loadingProgressBar, 0.2)
.AddStrut(12 - kElementSpacing)
.SetInsets(kInsetSpacing, 0, kInsetSpacing, 0)
)
);
m_url->MakeFocus(true);
} else {
m_BackButton = 0;
m_ForwardButton = 0;
m_StopButton = 0;
m_goButton = 0;
m_url = 0;
m_menuBar = 0;
m_statusText = 0;
m_loadingProgressBar = 0;
AddChild(BGroupLayoutBuilder(B_VERTICAL)
.Add(webView)
);
}
m_saveFilePanel = 0;
AddShortcut('R', B_COMMAND_KEY, new BMessage(RELOAD));
be_app->PostMessage(WINDOW_OPENED);
}
void LauncherWindow::updateTitle(const BString& title)
{
BString windowTitle = title;
if (windowTitle.Length() > 0)
windowTitle << " - ";
windowTitle << "HaikuLauncher";
SetTitle(windowTitle.String());
}