aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandre Deckner <alexandre.deckner@uzzl.com>2012-07-03 19:20:10 +0200
committerAlexandre Deckner <alexandre.deckner@uzzl.com>2012-07-03 19:20:10 +0200
commitde3c221462b22f8d65d51cd96334620b3fd65307 (patch)
treeb7d3da5e2b87b4b18807de0464c85fe275fbc4ab
parent7f7b659ec5eb4911c6a62e86af3a379227c3eeb0 (diff)
parenta79446f47116535b146eaa022c4b6de4e01bb3ad (diff)
Merge WebPositive into the Haiku tree. History is preservedhrev44286
-rw-r--r--src/apps/webpositive/AuthenticationPanel.cpp218
-rw-r--r--src/apps/webpositive/AuthenticationPanel.h65
-rw-r--r--src/apps/webpositive/BrowserApp.cpp450
-rw-r--r--src/apps/webpositive/BrowserApp.h88
-rw-r--r--src/apps/webpositive/BrowserWindow.cpp2216
-rw-r--r--src/apps/webpositive/BrowserWindow.h257
-rw-r--r--src/apps/webpositive/BrowsingHistory.cpp381
-rw-r--r--src/apps/webpositive/BrowsingHistory.h112
-rw-r--r--src/apps/webpositive/CredentialsStorage.cpp275
-rw-r--r--src/apps/webpositive/CredentialsStorage.h98
-rw-r--r--src/apps/webpositive/DownloadProgressView.cpp838
-rw-r--r--src/apps/webpositive/DownloadProgressView.h111
-rw-r--r--src/apps/webpositive/DownloadWindow.cpp573
-rw-r--r--src/apps/webpositive/DownloadWindow.h75
-rw-r--r--src/apps/webpositive/SettingsKeys.cpp51
-rw-r--r--src/apps/webpositive/SettingsKeys.h52
-rw-r--r--src/apps/webpositive/SettingsWindow.cpp884
-rw-r--r--src/apps/webpositive/SettingsWindow.h122
-rw-r--r--src/apps/webpositive/URLInputGroup.cpp654
-rw-r--r--src/apps/webpositive/URLInputGroup.h43
-rw-r--r--src/apps/webpositive/WebPositive.rdef350
-rw-r--r--src/apps/webpositive/autocompletion/AutoCompleter.cpp253
-rw-r--r--src/apps/webpositive/autocompletion/AutoCompleter.h161
-rw-r--r--src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.cpp467
-rw-r--r--src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.h100
-rw-r--r--src/apps/webpositive/autocompletion/TextViewCompleter.cpp169
-rw-r--r--src/apps/webpositive/autocompletion/TextViewCompleter.h48
-rw-r--r--src/apps/webpositive/support/AutoLocker.h178
-rw-r--r--src/apps/webpositive/support/BaseURL.cpp18
-rw-r--r--src/apps/webpositive/support/BaseURL.h14
-rw-r--r--src/apps/webpositive/support/BitmapButton.cpp119
-rw-r--r--src/apps/webpositive/support/BitmapButton.h44
-rw-r--r--src/apps/webpositive/support/DateTime.cpp1473
-rw-r--r--src/apps/webpositive/support/DateTime.h225
-rw-r--r--src/apps/webpositive/support/FontSelectionView.cpp527
-rw-r--r--src/apps/webpositive/support/FontSelectionView.h91
-rw-r--r--src/apps/webpositive/support/HashKeys.h124
-rw-r--r--src/apps/webpositive/support/HashMap.h481
-rw-r--r--src/apps/webpositive/support/HashSet.h342
-rw-r--r--src/apps/webpositive/support/IconButton.cpp929
-rw-r--r--src/apps/webpositive/support/IconButton.h134
-rw-r--r--src/apps/webpositive/support/IconUtils.h80
-rw-r--r--src/apps/webpositive/support/NavMenu.h168
-rw-r--r--src/apps/webpositive/support/OpenHashTable.h514
-rw-r--r--src/apps/webpositive/support/SettingsMessage.cpp514
-rw-r--r--src/apps/webpositive/support/SettingsMessage.h105
-rw-r--r--src/apps/webpositive/support/SlowMenu.h76
-rw-r--r--src/apps/webpositive/support/StringForSize.cpp43
-rw-r--r--src/apps/webpositive/support/StringForSize.h23
-rw-r--r--src/apps/webpositive/support/WindowIcon.h54
-rw-r--r--src/apps/webpositive/svn_revision.cpp10
-rw-r--r--src/apps/webpositive/svn_revision.h15
-rw-r--r--src/apps/webpositive/tabview/TabContainerView.cpp542
-rw-r--r--src/apps/webpositive/tabview/TabContainerView.h103
-rw-r--r--src/apps/webpositive/tabview/TabManager.cpp940
-rw-r--r--src/apps/webpositive/tabview/TabManager.h97
-rw-r--r--src/apps/webpositive/tabview/TabView.cpp365
-rw-r--r--src/apps/webpositive/tabview/TabView.h122
58 files changed, 17581 insertions, 0 deletions
diff --git a/src/apps/webpositive/AuthenticationPanel.cpp b/src/apps/webpositive/AuthenticationPanel.cpp
new file mode 100644
index 0000000000..7dcc55260f
--- /dev/null
+++ b/src/apps/webpositive/AuthenticationPanel.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "AuthenticationPanel.h"
+
+#include <Button.h>
+#include <CheckBox.h>
+#include <ControlLook.h>
+#include <GridLayoutBuilder.h>
+#include <GroupLayoutBuilder.h>
+#include <Message.h>
+#include <Screen.h>
+#include <SeparatorView.h>
+#include <SpaceLayoutItem.h>
+#include <StringView.h>
+#include <TextControl.h>
+#include <stdio.h>
+
+static const uint32 kMsgPanelOK = 'pnok';
+static const uint32 kMsgJitter = 'jitr';
+static const uint32 kHidePassword = 'hdpw';
+
+AuthenticationPanel::AuthenticationPanel(BRect parentFrame)
+ : BWindow(BRect(-1000, -1000, -900, -900), "Authentication Required",
+ B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
+ B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE | B_NOT_ZOOMABLE
+ | B_CLOSE_ON_ESCAPE | B_AUTO_UPDATE_SIZE_LIMITS)
+ , m_parentWindowFrame(parentFrame)
+ , m_usernameTextControl(new BTextControl("user", "Username:", "", NULL))
+ , m_passwordTextControl(new BTextControl("pass", "Password:", "", NULL))
+ , m_hidePasswordCheckBox(new BCheckBox("hide", "Hide password text", new BMessage(kHidePassword)))
+ , m_rememberCredentialsCheckBox(new BCheckBox("remember", "Remember username and password for this site", NULL))
+ , m_okButton(new BButton("ok", "OK", new BMessage(kMsgPanelOK)))
+ , m_cancelButton(new BButton("cancel", "Cancel", new BMessage(B_QUIT_REQUESTED)))
+ , m_cancelled(false)
+ , m_exitSemaphore(create_sem(0, "Authentication Panel"))
+{
+}
+
+AuthenticationPanel::~AuthenticationPanel()
+{
+ delete_sem(m_exitSemaphore);
+}
+
+bool
+AuthenticationPanel::QuitRequested()
+{
+ m_cancelled = true;
+ release_sem(m_exitSemaphore);
+ return false;
+}
+
+void
+AuthenticationPanel::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ case kMsgPanelOK:
+ release_sem(m_exitSemaphore);
+ break;
+ case kHidePassword: {
+ // TODO: Toggling this is broken in BTextView. Workaround is to
+ // set the text and selection again.
+ BString text = m_passwordTextControl->Text();
+ int32 selectionStart;
+ int32 selectionEnd;
+ m_passwordTextControl->TextView()->GetSelection(&selectionStart, &selectionEnd);
+ m_passwordTextControl->TextView()->HideTyping(
+ m_hidePasswordCheckBox->Value() == B_CONTROL_ON);
+ m_passwordTextControl->SetText(text.String());
+ m_passwordTextControl->TextView()->Select(selectionStart, selectionEnd);
+ break;
+ }
+ case kMsgJitter: {
+ UpdateIfNeeded();
+ BPoint leftTop = Frame().LeftTop();
+ const float jitterOffsets[] = { -10, 0, 10, 0 };
+ const int32 jitterOffsetCount = sizeof(jitterOffsets) / sizeof(float);
+ for (int32 i = 0; i < 20; i++) {
+ float offset = jitterOffsets[i % jitterOffsetCount];
+ MoveTo(leftTop.x + offset, leftTop.y);
+ snooze(15000);
+ }
+ MoveTo(leftTop);
+ break;
+ }
+ default:
+ BWindow::MessageReceived(message);
+ }
+}
+
+bool AuthenticationPanel::getAuthentication(const BString& text,
+ const BString& previousUser, const BString& previousPass,
+ bool previousRememberCredentials, bool badPassword,
+ BString& user, BString& pass, bool* rememberCredentials)
+{
+ // Configure panel and layout controls.
+ rgb_color infoColor = ui_color(B_PANEL_TEXT_COLOR);
+ BRect textBounds(0, 0, 250, 200);
+ BTextView* textView = new BTextView(textBounds, "text", textBounds,
+ be_plain_font, &infoColor, B_FOLLOW_NONE, B_WILL_DRAW | B_SUPPORTS_LAYOUT);
+ textView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
+ textView->SetText(text.String());
+ textView->MakeEditable(false);
+ textView->MakeSelectable(false);
+
+ m_usernameTextControl->SetText(previousUser.String());
+ m_passwordTextControl->TextView()->HideTyping(true);
+ // Ignore the previous password, if it didn't work.
+ if (!badPassword)
+ m_passwordTextControl->SetText(previousPass.String());
+ m_hidePasswordCheckBox->SetValue(B_CONTROL_ON);
+ m_rememberCredentialsCheckBox->SetValue(previousRememberCredentials);
+
+ // create layout
+ SetLayout(new BGroupLayout(B_VERTICAL, 0.0));
+ float spacing = be_control_look->DefaultItemSpacing();
+ AddChild(BGroupLayoutBuilder(B_VERTICAL, 0.0)
+ .Add(BGridLayoutBuilder(0, spacing)
+ .Add(textView, 0, 0, 2)
+ .Add(m_usernameTextControl->CreateLabelLayoutItem(), 0, 1)
+ .Add(m_usernameTextControl->CreateTextViewLayoutItem(), 1, 1)
+ .Add(m_passwordTextControl->CreateLabelLayoutItem(), 0, 2)
+ .Add(m_passwordTextControl->CreateTextViewLayoutItem(), 1, 2)
+ .Add(BSpaceLayoutItem::CreateGlue(), 0, 3)
+ .Add(m_hidePasswordCheckBox, 1, 3)
+ .Add(m_rememberCredentialsCheckBox, 0, 4, 2)
+ .SetInsets(spacing, spacing, spacing, spacing)
+ )
+ .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
+ .Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing)
+ .AddGlue()
+ .Add(m_cancelButton)
+ .Add(m_okButton)
+ .SetInsets(spacing, spacing, spacing, spacing)
+ )
+ );
+
+ float textHeight = textView->LineHeight(0) * textView->CountLines();
+ textView->SetExplicitMinSize(BSize(B_SIZE_UNSET, textHeight));
+
+ SetDefaultButton(m_okButton);
+ if (badPassword && previousUser.Length())
+ m_passwordTextControl->MakeFocus(true);
+ else
+ m_usernameTextControl->MakeFocus(true);
+
+ if (m_parentWindowFrame.IsValid())
+ CenterIn(m_parentWindowFrame);
+ else
+ CenterOnScreen();
+
+ // Start AuthenticationPanel window thread
+ Show();
+
+ // Let the window jitter, if the previous password was invalid
+ if (badPassword)
+ PostMessage(kMsgJitter);
+
+ // Block calling thread
+ // Get the originating window, if it exists, to let it redraw itself.
+ BWindow* window = dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
+ if (window) {
+ status_t err;
+ for (;;) {
+ do {
+ err = acquire_sem_etc(m_exitSemaphore, 1, B_RELATIVE_TIMEOUT, 10000);
+ // We've (probably) had our time slice taken away from us
+ } while (err == B_INTERRUPTED);
+
+ if (err != B_TIMED_OUT) {
+ // Semaphore was finally released or nuked.
+ break;
+ }
+ window->UpdateIfNeeded();
+ }
+ } else {
+ // No window to update, so just hang out until we're done.
+ while (acquire_sem(m_exitSemaphore) == B_INTERRUPTED) {
+ }
+ }
+
+ // AuthenticationPanel wants to quit.
+ Lock();
+
+ user = m_usernameTextControl->Text();
+ pass = m_passwordTextControl->Text();
+ if (rememberCredentials)
+ *rememberCredentials = m_rememberCredentialsCheckBox->Value() == B_CONTROL_ON;
+
+ bool canceled = m_cancelled;
+ Quit();
+ // AuthenticationPanel object is TOAST here.
+ return !canceled;
+}
diff --git a/src/apps/webpositive/AuthenticationPanel.h b/src/apps/webpositive/AuthenticationPanel.h
new file mode 100644
index 0000000000..5af9496aad
--- /dev/null
+++ b/src/apps/webpositive/AuthenticationPanel.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+
+#ifndef AuthenticationPanel_h
+#define AuthenticationPanel_h
+
+#include <String.h>
+#include <Window.h>
+
+class BCheckBox;
+class BTextControl;
+
+class AuthenticationPanel : public BWindow {
+public:
+ AuthenticationPanel(BRect parentFrame = BRect());
+ virtual ~AuthenticationPanel();
+
+ virtual bool QuitRequested();
+
+ virtual void MessageReceived(BMessage *message);
+
+ bool getAuthentication(const BString& text, const BString& previousUser,
+ const BString& previousPass, bool previousRememberCredentials,
+ bool badPassword, BString& user, BString& pass,
+ bool* rememberCredentials);
+
+private:
+ BRect m_parentWindowFrame;
+ BTextControl* m_usernameTextControl;
+ BTextControl* m_passwordTextControl;
+ BCheckBox* m_hidePasswordCheckBox;
+ BCheckBox* m_rememberCredentialsCheckBox;
+ BButton* m_okButton;
+ BButton* m_cancelButton;
+
+ bool m_cancelled;
+
+ sem_id m_exitSemaphore;
+};
+
+#endif // AuthenticationPanel_h
diff --git a/src/apps/webpositive/BrowserApp.cpp b/src/apps/webpositive/BrowserApp.cpp
new file mode 100644
index 0000000000..1504638ae7
--- /dev/null
+++ b/src/apps/webpositive/BrowserApp.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com>
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "config.h"
+#include "BrowserApp.h"
+
+#include "BrowserWindow.h"
+#include "BrowsingHistory.h"
+#include "DownloadWindow.h"
+#include "SettingsMessage.h"
+#include "SettingsWindow.h"
+#include "svn_revision.h"
+#include "NetworkCookieJar.h"
+#include "WebPage.h"
+#include "WebSettings.h"
+#include "WebView.h"
+#include <Alert.h>
+#include <Autolock.h>
+#include <Directory.h>
+#include <Entry.h>
+#include <FindDirectory.h>
+#include <Locale.h>
+#include <Path.h>
+#include <Screen.h>
+#include <debugger.h>
+#include <stdio.h>
+
+
+const char* kApplicationSignature = "application/x-vnd.Haiku-WebPositive";
+const char* kApplicationName = "WebPositive";
+static const uint32 PRELOAD_BROWSING_HISTORY = 'plbh';
+
+#define ENABLE_NATIVE_COOKIES 0
+
+
+BrowserApp::BrowserApp()
+ :
+ BApplication(kApplicationSignature),
+ fWindowCount(0),
+ fLastWindowFrame(50, 50, 950, 750),
+ fLaunchRefsMessage(0),
+ fInitialized(false),
+ fSettings(NULL),
+ fCookies(NULL),
+ fCookieJar(NULL),
+ fDownloadWindow(NULL),
+ fSettingsWindow(NULL)
+{
+}
+
+
+BrowserApp::~BrowserApp()
+{
+ delete fLaunchRefsMessage;
+ delete fSettings;
+ delete fCookies;
+ delete fCookieJar;
+}
+
+
+void
+BrowserApp::AboutRequested()
+{
+ BString aboutText("WebPositive\n\nby Ryan Leavengood, Andrea Anzani, "
+ "Maxime Simon, Michael Lotz, Rene Gollent and Stephan Aßmus");
+ aboutText << "\n\nSVN revision: " << kSVNRevision;
+
+ BAlert* alert = new BAlert("About WebPositive", aboutText.String(),
+ "Sweet!");
+ alert->Go(NULL);
+}
+
+
+void
+BrowserApp::ArgvReceived(int32 argc, char** argv)
+{
+ BMessage message(B_REFS_RECEIVED);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp("-f", argv[i]) == 0
+ || strcmp("--fullscreen", argv[i]) == 0) {
+ message.AddBool("fullscreen", true);
+ continue;
+ }
+ const char* url = argv[i];
+ BEntry entry(argv[i], true);
+ BPath path;
+ if (entry.Exists() && entry.GetPath(&path) == B_OK)
+ url = path.Path();
+ message.AddString("url", url);
+ }
+ // Upon program launch, it will buffer a copy of the message, since
+ // ArgReceived() is called before ReadyToRun().
+ RefsReceived(&message);
+}
+
+
+void
+BrowserApp::ReadyToRun()
+{
+ // Since we will essentially run the GUI...
+ set_thread_priority(Thread(), B_DISPLAY_PRIORITY);
+
+ BWebPage::InitializeOnce();
+ BWebPage::SetCacheModel(B_WEBKIT_CACHE_MODEL_WEB_BROWSER);
+
+ BPath path;
+ if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK
+ && path.Append(kApplicationName) == B_OK
+ && create_directory(path.Path(), 0777) == B_OK) {
+
+ BWebSettings::SetPersistentStoragePath(path.Path());
+ }
+
+ BString mainSettingsPath(kApplicationName);
+ mainSettingsPath << "/Application";
+ fSettings = new SettingsMessage(B_USER_SETTINGS_DIRECTORY,
+ mainSettingsPath.String());
+#if ENABLE_NATIVE_COOKIES
+ mainSettingsPath = kApplicationName;
+ mainSettingsPath << "/Cookies";
+ fCookies = new SettingsMessage(B_USER_SETTINGS_DIRECTORY,
+ mainSettingsPath.String());
+ BMessage cookieArchive;
+ cookieArchive = fCookies->GetValue("cookies", cookieArchive);
+ fCookieJar = new BNetworkCookieJar(cookieArchive);
+ BWebPage::SetCookieJar(fCookieJar);
+#endif
+
+ fLastWindowFrame = fSettings->GetValue("window frame", fLastWindowFrame);
+ BRect defaultDownloadWindowFrame(-10, -10, 365, 265);
+ BRect downloadWindowFrame = fSettings->GetValue("downloads window frame",
+ defaultDownloadWindowFrame);
+ BRect settingsWindowFrame = fSettings->GetValue("settings window frame",
+ BRect());
+ bool showDownloads = fSettings->GetValue("show downloads", false);
+
+ fDownloadWindow = new DownloadWindow(downloadWindowFrame, showDownloads,
+ fSettings);
+ if (downloadWindowFrame == defaultDownloadWindowFrame) {
+ // Initially put download window in lower right of screen.
+ BRect screenFrame = BScreen().Frame();
+ BMessage decoratorSettings;
+ fDownloadWindow->GetDecoratorSettings(&decoratorSettings);
+ float borderWidth = 0;
+ if (decoratorSettings.FindFloat("border width", &borderWidth) != B_OK)
+ borderWidth = 5;
+ fDownloadWindow->MoveTo(screenFrame.Width() - fDownloadWindow->Frame().Width() - borderWidth,
+ screenFrame.Height() - fDownloadWindow->Frame().Height() - borderWidth);
+ }
+ fSettingsWindow = new SettingsWindow(settingsWindowFrame, fSettings);
+
+ BWebPage::SetDownloadListener(BMessenger(fDownloadWindow));
+
+ fInitialized = true;
+
+ int32 pagesCreated = 0;
+ bool fullscreen = false;
+ if (fLaunchRefsMessage) {
+ _RefsReceived(fLaunchRefsMessage, &pagesCreated, &fullscreen);
+ delete fLaunchRefsMessage;
+ fLaunchRefsMessage = NULL;
+ }
+ if (pagesCreated == 0)
+ _CreateNewWindow("", fullscreen);
+
+ PostMessage(PRELOAD_BROWSING_HISTORY);
+}
+
+
+void
+BrowserApp::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ case PRELOAD_BROWSING_HISTORY:
+ // Accessing the default instance will load the history from disk.
+ BrowsingHistory::DefaultInstance();
+ break;
+ case B_SILENT_RELAUNCH:
+ _CreateNewPage("");
+ break;
+ case NEW_WINDOW: {
+ BString url;
+ if (message->FindString("url", &url) != B_OK)
+ break;
+ _CreateNewWindow(url);
+ break;
+ }
+ case NEW_TAB: {
+ BrowserWindow* window;
+ if (message->FindPointer("window", reinterpret_cast<void**>(&window)) != B_OK)
+ break;
+ BString url;
+ message->FindString("url", &url);
+ bool select = false;
+ message->FindBool("select", &select);
+ _CreateNewTab(window, url, select);
+ break;
+ }
+ case WINDOW_OPENED:
+ fWindowCount++;
+ fDownloadWindow->SetMinimizeOnClose(false);
+ break;
+ case WINDOW_CLOSED:
+ fWindowCount--;
+ message->FindRect("window frame", &fLastWindowFrame);
+ if (fWindowCount <= 0)
+ PostMessage(B_QUIT_REQUESTED);
+ break;
+
+ case SHOW_DOWNLOAD_WINDOW:
+ _ShowWindow(message, fDownloadWindow);
+ break;
+ case SHOW_SETTINGS_WINDOW:
+ _ShowWindow(message, fSettingsWindow);
+ break;
+
+ default:
+ BApplication::MessageReceived(message);
+ break;
+ }
+}
+
+
+void
+BrowserApp::RefsReceived(BMessage* message)
+{
+ if (!fInitialized) {
+ delete fLaunchRefsMessage;
+ fLaunchRefsMessage = new BMessage(*message);
+ return;
+ }
+
+ _RefsReceived(message);
+}
+
+
+bool
+BrowserApp::QuitRequested()
+{
+ if (fDownloadWindow->DownloadsInProgress()) {
+ BAlert* alert = new BAlert("Downloads in progress",
+ "There are still downloads in progress, do you really want to "
+ "quit WebPositive now?", "Quit", "Continue downloads");
+ int32 choice = alert->Go();
+ if (choice == 1) {
+ if (fWindowCount == 0) {
+ if (fDownloadWindow->Lock()) {
+ fDownloadWindow->SetWorkspaces(1 << current_workspace());
+ if (fDownloadWindow->IsHidden())
+ fDownloadWindow->Show();
+ else
+ fDownloadWindow->Activate();
+ fDownloadWindow->SetMinimizeOnClose(true);
+ fDownloadWindow->Unlock();
+ return false;
+ }
+ } else
+ return false;
+ }
+ }
+
+ for (int i = 0; BWindow* window = WindowAt(i); i++) {
+ BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window);
+ if (!webWindow)
+ continue;
+ if (!webWindow->Lock())
+ continue;
+ if (webWindow->QuitRequested()) {
+ fLastWindowFrame = webWindow->WindowFrame();
+ webWindow->Quit();
+ i--;
+ } else {
+ webWindow->Unlock();
+ return false;
+ }
+ }
+
+ BWebPage::ShutdownOnce();
+
+ fSettings->SetValue("window frame", fLastWindowFrame);
+ if (fDownloadWindow->Lock()) {
+ fSettings->SetValue("downloads window frame", fDownloadWindow->Frame());
+ fSettings->SetValue("show downloads", !fDownloadWindow->IsHidden());
+ fDownloadWindow->Unlock();
+ }
+ if (fSettingsWindow->Lock()) {
+ fSettings->SetValue("settings window frame", fSettingsWindow->Frame());
+ fSettingsWindow->Unlock();
+ }
+
+ BMessage cookieArchive;
+ if (fCookieJar != NULL && fCookieJar->Archive(&cookieArchive) == B_OK)
+ fCookies->SetValue("cookies", cookieArchive);
+
+ return true;
+}
+
+
+void
+BrowserApp::_RefsReceived(BMessage* message, int32* _pagesCreated,
+ bool* _fullscreen)
+{
+ int32 pagesCreated = 0;
+
+ bool fullscreen;
+ if (message->FindBool("fullscreen", &fullscreen) != B_OK)
+ fullscreen = false;
+
+ entry_ref ref;
+ for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
+ BEntry entry(&ref, true);
+ if (!entry.Exists())
+ continue;
+ BPath path;
+ if (entry.GetPath(&path) != B_OK)
+ continue;
+ BString url;
+ url << path.Path();
+ _CreateNewPage(url, fullscreen);
+ pagesCreated++;
+ }
+
+ BString url;
+ for (int32 i = 0; message->FindString("url", i, &url) == B_OK; i++) {
+ _CreateNewPage(url, fullscreen);
+ pagesCreated++;
+ }
+
+ if (_pagesCreated != NULL)
+ *_pagesCreated = pagesCreated;
+ if (_fullscreen != NULL)
+ *_fullscreen = fullscreen;
+}
+
+
+void
+BrowserApp::_CreateNewPage(const BString& url, bool fullscreen)
+{
+ uint32 workspace = 1 << current_workspace();
+
+ bool loadedInWindowOnCurrentWorkspace = false;
+ for (int i = 0; BWindow* window = WindowAt(i); i++) {
+ BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window);
+ if (!webWindow)
+ continue;
+ if (webWindow->Lock()) {
+ if (webWindow->Workspaces() & workspace) {
+ if (webWindow->IsBlankTab()) {
+ if (url.Length() != 0)
+ webWindow->CurrentWebView()->LoadURL(url);
+ } else
+ webWindow->CreateNewTab(url, true);
+ webWindow->Activate();
+ webWindow->CurrentWebView()->MakeFocus(true);
+ loadedInWindowOnCurrentWorkspace = true;
+ }
+ webWindow->Unlock();
+ }
+ if (loadedInWindowOnCurrentWorkspace)
+ return;
+ }
+ _CreateNewWindow(url, fullscreen);
+}
+
+
+void
+BrowserApp::_CreateNewWindow(const BString& url, bool fullscreen)
+{
+ // Offset the window frame unless this is the first window created in the
+ // session.
+ if (fWindowCount > 0)
+ fLastWindowFrame.OffsetBy(20, 20);
+ if (!BScreen().Frame().Contains(fLastWindowFrame))
+ fLastWindowFrame.OffsetTo(50, 50);
+
+ BrowserWindow* window = new BrowserWindow(fLastWindowFrame, fSettings,
+ url);
+ if (fullscreen)
+ window->ToggleFullscreen();
+ window->Show();
+}
+
+
+void
+BrowserApp::_CreateNewTab(BrowserWindow* window, const BString& url,
+ bool select)
+{
+ if (!window->Lock())
+ return;
+ window->CreateNewTab(url, select);
+ window->Unlock();
+}
+
+
+void
+BrowserApp::_ShowWindow(const BMessage* message, BWindow* window)
+{
+ BAutolock _(window);
+ uint32 workspaces;
+ if (message->FindUInt32("workspaces", &workspaces) == B_OK)
+ window->SetWorkspaces(workspaces);
+ if (window->IsHidden())
+ window->Show();
+ else
+ window->Activate();
+}
+
+
+// #pragma mark -
+
+
+int
+main(int, char**)
+{
+ try {
+ new BrowserApp();
+ be_app->Run();
+ delete be_app;
+ } catch (...) {
+ debugger("Exception caught.");
+ }
+
+ return 0;
+}
+
diff --git a/src/apps/webpositive/BrowserApp.h b/src/apps/webpositive/BrowserApp.h
new file mode 100644
index 0000000000..05885583f6
--- /dev/null
+++ b/src/apps/webpositive/BrowserApp.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com>
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+#ifndef BROWSER_APP_H
+#define BROWSER_APP_H
+
+
+#include <Application.h>
+#include <Catalog.h>
+#include <Rect.h>
+
+class BNetworkCookieJar;
+class DownloadWindow;
+class BrowserWindow;
+class SettingsMessage;
+class SettingsWindow;
+
+
+class BrowserApp : public BApplication {
+public:
+ BrowserApp();
+ virtual ~BrowserApp();
+
+ virtual void AboutRequested();
+ virtual void ArgvReceived(int32 agrc, char** argv);
+ virtual void MessageReceived(BMessage* message);
+ virtual void RefsReceived(BMessage* message);
+ virtual void ReadyToRun();
+ virtual bool QuitRequested();
+
+private:
+ void _RefsReceived(BMessage* message,
+ int32* pagesCreated = NULL,
+ bool* fullscreen = NULL);
+ void _CreateNewPage(const BString& url,
+ bool fullscreen = false);
+ void _CreateNewWindow(const BString& url,
+ bool fullscreen = false);
+ void _CreateNewTab(BrowserWindow* window,
+ const BString& url, bool select);
+ void _ShowWindow(const BMessage* message,
+ BWindow* window);
+
+private:
+ int fWindowCount;
+ BRect fLastWindowFrame;
+ BMessage* fLaunchRefsMessage;
+ bool fInitialized;
+
+ SettingsMessage* fSettings;
+ SettingsMessage* fCookies;
+ BNetworkCookieJar* fCookieJar;
+
+ DownloadWindow* fDownloadWindow;
+ SettingsWindow* fSettingsWindow;
+};
+
+
+extern const char* kApplicationSignature;
+extern const char* kApplicationName;
+
+
+#endif // BROWSER_APP_H
+
diff --git a/src/apps/webpositive/BrowserWindow.cpp b/src/apps/webpositive/BrowserWindow.cpp
new file mode 100644
index 0000000000..c0d5ce63ef
--- /dev/null
+++ b/src/apps/webpositive/BrowserWindow.cpp
@@ -0,0 +1,2216 @@
+/*
+ * Copyright (C) 2007 Andrea Anzani <andrea.anzani@gmail.com>
+ * Copyright (C) 2007, 2010 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 "config.h"
+#include "BrowserWindow.h"
+
+#include "AuthenticationPanel.h"
+#include "BaseURL.h"
+#include "BitmapButton.h"
+#include "BrowserApp.h"
+#include "BrowsingHistory.h"
+#include "CredentialsStorage.h"
+#include "IconButton.h"
+#include "NavMenu.h"
+#include "SettingsKeys.h"
+#include "SettingsMessage.h"
+#include "TabManager.h"
+#include "URLInputGroup.h"
+#include "WebPage.h"
+#include "WebView.h"
+#include "WebViewConstants.h"
+#include "WindowIcon.h"
+#include <Alert.h>
+#include <Application.h>
+#include <Bitmap.h>
+#include <Button.h>
+#include <CheckBox.h>
+#include <Clipboard.h>
+#include <Directory.h>
+#include <Entry.h>
+#include <File.h>
+#include <FindDirectory.h>
+#include <GridLayoutBuilder.h>
+#include <GroupLayout.h>
+#include <GroupLayoutBuilder.h>
+#include <LayoutBuilder.h>
+#include <MenuBar.h>
+#include <MenuItem.h>
+#include <MessageRunner.h>
+#include <NodeInfo.h>
+#include <Path.h>
+#include <Roster.h>
+#include <Screen.h>
+#include <SeparatorView.h>
+#include <SpaceLayoutItem.h>
+#include <StatusBar.h>
+#include <StringView.h>
+#include <TextControl.h>
+
+#include <stdio.h>
+
+
+enum {
+ OPEN_LOCATION = 'open',
+ GO_BACK = 'goba',
+ GO_FORWARD = 'gofo',
+ STOP = 'stop',
+ HOME = 'home',
+ GOTO_URL = 'goul',
+ RELOAD = 'reld',
+ CLEAR_HISTORY = 'clhs',
+
+ CREATE_BOOKMARK = 'crbm',
+ SHOW_BOOKMARKS = 'shbm',
+
+ ZOOM_FACTOR_INCREASE = 'zfin',
+ ZOOM_FACTOR_DECREASE = 'zfdc',
+ ZOOM_FACTOR_RESET = 'zfrs',
+ ZOOM_TEXT_ONLY = 'zfto',
+
+ TOGGLE_FULLSCREEN = 'tgfs',
+ TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN = 'tgah',
+ CHECK_AUTO_HIDE_INTERFACE = 'cahi',
+
+ SHOW_PAGE_SOURCE = 'spgs',
+
+ EDIT_SHOW_FIND_GROUP = 'sfnd',
+ EDIT_HIDE_FIND_GROUP = 'hfnd',
+ EDIT_FIND_NEXT = 'fndn',
+ EDIT_FIND_PREVIOUS = 'fndp',
+ FIND_TEXT_CHANGED = 'ftxt',
+
+ SELECT_TAB = 'sltb',
+};
+
+
+static BLayoutItem*
+layoutItemFor(BView* view)
+{
+ BLayout* layout = view->Parent()->GetLayout();
+ int32 index = layout->IndexOfView(view);
+ return layout->ItemAt(index);
+}
+
+
+class BookmarkMenu : public BNavMenu {
+public:
+ BookmarkMenu(const char* title, BHandler* target, const entry_ref* navDir)
+ :
+ BNavMenu(title, B_REFS_RECEIVED, target)
+ {
+ // Add these items here already, so the shortcuts work even when
+ // the menu has never been opened yet.
+ _AddStaticItems();
+
+ SetNavDir(navDir);
+ }
+
+ virtual void AttachedToWindow()
+ {
+ RemoveItems(0, CountItems(), true);
+ ForceRebuild();
+ BNavMenu::AttachedToWindow();
+ if (CountItems() > 0)
+ AddItem(new BSeparatorItem(), 0);
+ _AddStaticItems();
+ DoLayout();
+ }
+
+private:
+ void _AddStaticItems()
+ {
+ AddItem(new BMenuItem("Manage bookmarks",
+ new BMessage(SHOW_BOOKMARKS), 'M'), 0);
+ AddItem(new BMenuItem("Bookmark this page",
+ new BMessage(CREATE_BOOKMARK), 'B'), 0);
+ }
+};
+
+
+class PageUserData : public BWebView::UserData {
+public:
+ PageUserData(BView* focusedView)
+ :
+ fFocusedView(focusedView),
+ fPageIcon(NULL),
+ fURLInputSelectionStart(-1),
+ fURLInputSelectionEnd(-1)
+ {
+ }
+
+ ~PageUserData()
+ {
+ delete fPageIcon;
+ }
+
+ void SetFocusedView(BView* focusedView)
+ {
+ fFocusedView = focusedView;
+ }
+
+ BView* FocusedView() const
+ {
+ return fFocusedView;
+ }
+
+ void SetPageIcon(const BBitmap* icon)
+ {
+ delete fPageIcon;
+ if (icon)
+ fPageIcon = new BBitmap(icon);
+ else
+ fPageIcon = NULL;
+ }
+
+ const BBitmap* PageIcon() const
+ {
+ return fPageIcon;
+ }
+
+ void SetURLInputContents(const char* text)
+ {
+ fURLInputContents = text;
+ }
+
+ const BString& URLInputContents() const
+ {
+ return fURLInputContents;
+ }
+
+ void SetURLInputSelection(int32 selectionStart, int32 selectionEnd)
+ {
+ fURLInputSelectionStart = selectionStart;
+ fURLInputSelectionEnd = selectionEnd;
+ }
+
+ int32 URLInputSelectionStart() const
+ {
+ return fURLInputSelectionStart;
+ }
+
+ int32 URLInputSelectionEnd() const
+ {
+ return fURLInputSelectionEnd;
+ }
+
+private:
+ BView* fFocusedView;
+ BBitmap* fPageIcon;
+ BString fURLInputContents;
+ int32 fURLInputSelectionStart;
+ int32 fURLInputSelectionEnd;
+};
+
+
+// #pragma mark - BrowserWindow
+
+
+BrowserWindow::BrowserWindow(BRect frame, SettingsMessage* appSettings,
+ const BString& url, uint32 interfaceElements, BWebView* webView)
+ :
+ BWebWindow(frame, kApplicationName,
+ B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
+ B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS),
+ fIsFullscreen(false),
+ fInterfaceVisible(false),
+ fPulseRunner(NULL),
+ fVisibleInterfaceElements(interfaceElements),
+ fAppSettings(appSettings),
+ fZoomTextOnly(true),
+ fShowTabsIfSinglePageOpen(true),
+ fAutoHideInterfaceInFullscreenMode(false),
+ fAutoHidePointer(false)
+{
+ // Begin listening to settings changes and read some current values.
+ fAppSettings->AddListener(BMessenger(this));
+// fZoomTextOnly = fAppSettings->GetValue("zoom text only", fZoomTextOnly);
+ fShowTabsIfSinglePageOpen = fAppSettings->GetValue(
+ kSettingsKeyShowTabsIfSinglePageOpen, fShowTabsIfSinglePageOpen);
+
+ fAutoHidePointer = fAppSettings->GetValue(kSettingsKeyAutoHidePointer,
+ fAutoHidePointer);
+
+ fNewWindowPolicy = fAppSettings->GetValue(kSettingsKeyNewWindowPolicy,
+ (uint32)OpenStartPage);
+ fNewTabPolicy = fAppSettings->GetValue(kSettingsKeyNewTabPolicy,
+ (uint32)OpenBlankPage);
+ fStartPageURL = fAppSettings->GetValue(kSettingsKeyStartPageURL,
+ kDefaultStartPageURL);
+ fSearchPageURL = fAppSettings->GetValue(kSettingsKeySearchPageURL,
+ kDefaultSearchPageURL);
+
+ // Create the interface elements
+ BMessage* newTabMessage = new BMessage(NEW_TAB);
+ newTabMessage->AddString("url", "");
+ newTabMessage->AddPointer("window", this);
+ newTabMessage->AddBool("select", true);
+ fTabManager = new TabManager(BMessenger(this), newTabMessage);
+
+ // Menu
+#if INTEGRATE_MENU_INTO_TAB_BAR
+ BMenu* mainMenu = fTabManager->Menu();
+#else
+ BMenu* mainMenu = new BMenuBar("Main menu");
+#endif
+ BMenu* menu = new BMenu("Window");
+ BMessage* newWindowMessage = new BMessage(NEW_WINDOW);
+ newWindowMessage->AddString("url", "");
+ BMenuItem* newItem = new BMenuItem("New window", newWindowMessage, 'N');
+ menu->AddItem(newItem);
+ newItem->SetTarget(be_app);
+ newItem = new BMenuItem("New tab", new BMessage(*newTabMessage), 'T');
+ menu->AddItem(newItem);
+ newItem->SetTarget(be_app);
+ menu->AddItem(new BMenuItem("Open location", new BMessage(OPEN_LOCATION),
+ 'L'));
+ menu->AddSeparatorItem();
+ menu->AddItem(new BMenuItem("Close window", new BMessage(B_QUIT_REQUESTED),
+ 'W', B_SHIFT_KEY));
+ menu->AddItem(new BMenuItem("Close tab", new BMessage(CLOSE_TAB), 'W'));
+ menu->AddSeparatorItem();
+ menu->AddItem(new BMenuItem("Downloads",
+ new BMessage(SHOW_DOWNLOAD_WINDOW), 'D'));
+ menu->AddItem(new BMenuItem("Settings",
+ new BMessage(SHOW_SETTINGS_WINDOW)));
+ BMenuItem* aboutItem = new BMenuItem("About",
+ new BMessage(B_ABOUT_REQUESTED));
+ menu->AddItem(aboutItem);
+ aboutItem->SetTarget(be_app);
+ menu->AddSeparatorItem();
+ BMenuItem* quitItem = new BMenuItem("Quit",
+ new BMessage(B_QUIT_REQUESTED), 'Q');
+ menu->AddItem(quitItem);
+ quitItem->SetTarget(be_app);
+ mainMenu->AddItem(menu);
+
+ menu = new BMenu("Edit");
+ menu->AddItem(fCutMenuItem = new BMenuItem("Cut", new BMessage(B_CUT),
+ 'X'));
+ menu->AddItem(fCopyMenuItem = new BMenuItem("Copy", new BMessage(B_COPY),
+ 'C'));
+ menu->AddItem(fPasteMenuItem = new BMenuItem("Paste", new BMessage(B_PASTE),
+ 'V'));
+ menu->AddSeparatorItem();
+ menu->AddItem(new BMenuItem("Find", new BMessage(EDIT_SHOW_FIND_GROUP),
+ 'F'));
+ menu->AddItem(fFindPreviousMenuItem = new BMenuItem("Find previous",
+ new BMessage(EDIT_FIND_PREVIOUS), 'G', B_SHIFT_KEY));
+ menu->AddItem(fFindNextMenuItem = new BMenuItem("Find next",
+ new BMessage(EDIT_FIND_NEXT), 'G'));
+ mainMenu->AddItem(menu);
+ fFindPreviousMenuItem->SetEnabled(false);
+ fFindNextMenuItem->SetEnabled(false);
+
+ menu = new BMenu("View");
+ menu->AddItem(new BMenuItem("Reload", new BMessage(RELOAD), 'R'));
+ menu->AddSeparatorItem();
+ menu->AddItem(new BMenuItem("Increase size",
+ new BMessage(ZOOM_FACTOR_INCREASE), '+'));
+ menu->AddItem(new BMenuItem("Decrease size",
+ new BMessage(ZOOM_FACTOR_DECREASE), '-'));
+ menu->AddItem(new BMenuItem("Reset size",
+ new BMessage(ZOOM_FACTOR_RESET), '0'));
+ fZoomTextOnlyMenuItem = new BMenuItem("Zoom text only",
+ new BMessage(ZOOM_TEXT_ONLY));
+ fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly);
+ menu->AddItem(fZoomTextOnlyMenuItem);
+
+ menu->AddSeparatorItem();
+ fFullscreenItem = new BMenuItem("Fullscreen",
+ new BMessage(TOGGLE_FULLSCREEN), B_RETURN);
+ menu->AddItem(fFullscreenItem);
+ menu->AddItem(new BMenuItem("Page source",
+ new BMessage(SHOW_PAGE_SOURCE), 'U'));
+ mainMenu->AddItem(menu);
+
+ fHistoryMenu = new BMenu("History");
+ fHistoryMenu->AddItem(fBackMenuItem = new BMenuItem("Back",
+ new BMessage(GO_BACK), B_LEFT_ARROW));
+ fHistoryMenu->AddItem(fForwardMenuItem = new BMenuItem("Forward",
+ new BMessage(GO_FORWARD), B_RIGHT_ARROW));
+ fHistoryMenu->AddSeparatorItem();
+ fHistoryMenuFixedItemCount = fHistoryMenu->CountItems();
+ mainMenu->AddItem(fHistoryMenu);
+
+ BPath bookmarkPath;
+ entry_ref bookmarkRef;
+ if (_BookmarkPath(bookmarkPath) == B_OK
+ && get_ref_for_path(bookmarkPath.Path(), &bookmarkRef) == B_OK) {
+ BMenu* bookmarkMenu
+ = new BookmarkMenu("Bookmarks", this, &bookmarkRef);
+ mainMenu->AddItem(bookmarkMenu);
+ }
+
+ // Back, Forward, Stop & Home buttons
+ fBackButton = new IconButton("Back", 0, NULL, new BMessage(GO_BACK));
+ fBackButton->SetIcon(201);
+ fBackButton->TrimIcon();
+
+ fForwardButton = new IconButton("Forward", 0, NULL, new BMessage(GO_FORWARD));
+ fForwardButton->SetIcon(202);
+ fForwardButton->TrimIcon();
+
+ fStopButton = new IconButton("Stop", 0, NULL, new BMessage(STOP));
+ fStopButton->SetIcon(204);
+ fStopButton->TrimIcon();
+
+ fHomeButton = new IconButton("Home", 0, NULL, new BMessage(HOME));
+ fHomeButton->SetIcon(206);
+ fHomeButton->TrimIcon();
+ if (!fAppSettings->GetValue(kSettingsKeyShowHomeButton, true))
+ fHomeButton->Hide();
+
+ // URL input group
+ fURLInputGroup = new URLInputGroup(new BMessage(GOTO_URL));
+
+ // Status Bar
+ fStatusText = new BStringView("status", "");
+ fStatusText->SetAlignment(B_ALIGN_LEFT);
+ fStatusText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
+ fStatusText->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));
+ fStatusText->SetFont(&font, B_FONT_SIZE);
+
+ // Loading progress bar
+ fLoadingProgressBar = new BStatusBar("progress");
+ fLoadingProgressBar->SetMaxValue(100);
+ fLoadingProgressBar->Hide();
+ fLoadingProgressBar->SetBarHeight(12);
+
+ const float kInsetSpacing = 3;
+ const float kElementSpacing = 5;
+
+ // Find group
+ fFindTextControl = new BTextControl("find", "Find:", "",
+ new BMessage(EDIT_FIND_NEXT));
+ fFindTextControl->SetModificationMessage(new BMessage(FIND_TEXT_CHANGED));
+ fFindPreviousButton = new BButton("Previous",
+ new BMessage(EDIT_FIND_PREVIOUS));
+ fFindNextButton = new BButton("Next", new BMessage(EDIT_FIND_NEXT));
+ fFindCloseButton = new BButton("Close",
+ new BMessage(EDIT_HIDE_FIND_GROUP));
+ fFindCaseSensitiveCheckBox = new BCheckBox("Match case");
+ BGroupLayout* findGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
+ .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
+ .Add(BGroupLayoutBuilder(B_HORIZONTAL, kElementSpacing)
+ .Add(fFindTextControl)
+ .Add(fFindPreviousButton)
+ .Add(fFindNextButton)
+ .Add(fFindCaseSensitiveCheckBox)
+ .Add(BSpaceLayoutItem::CreateGlue())
+ .Add(fFindCloseButton)
+ .SetInsets(kInsetSpacing, kInsetSpacing,
+ kInsetSpacing, kInsetSpacing)
+ )
+ ;
+
+ // Navigation group
+ BGroupLayout* navigationGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
+ .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing)
+ .Add(fBackButton)
+ .Add(fForwardButton)
+ .Add(fStopButton)
+ .Add(fHomeButton)
+ .Add(fURLInputGroup)
+ .SetInsets(kInsetSpacing, kInsetSpacing, kInsetSpacing,
+ kInsetSpacing)
+ )
+ .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
+ ;
+
+ // Status bar group
+ BGroupLayout* statusGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
+ .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
+ .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing)
+ .Add(fStatusText)
+ .Add(fLoadingProgressBar, 0.2)
+ .AddStrut(12 - kElementSpacing)
+ .SetInsets(kInsetSpacing, 0, kInsetSpacing, 0)
+ )
+ ;
+
+ BitmapButton* toggleFullscreenButton = new BitmapButton(kWindowIconBits,
+ kWindowIconWidth, kWindowIconHeight, kWindowIconFormat,
+ new BMessage(TOGGLE_FULLSCREEN));
+ toggleFullscreenButton->SetBackgroundMode(BitmapButton::MENUBAR_BACKGROUND);
+
+ BGroupLayout* menuBarGroup = BLayoutBuilder::Group<>(B_HORIZONTAL, 0.0)
+ .Add(mainMenu)
+ .Add(toggleFullscreenButton, 0.0f)
+ ;
+
+ // Layout
+ AddChild(BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
+#if !INTEGRATE_MENU_INTO_TAB_BAR
+ .Add(menuBarGroup)
+#endif
+ .Add(fTabManager->TabGroup())
+ .Add(navigationGroup)
+ .Add(fTabManager->ContainerView())
+ .Add(findGroup)
+ .Add(statusGroup)
+ );
+
+ // TODO: Small hack for fixing some invalidation problems with BMenuBar...
+ mainMenu->SetFlags(mainMenu->Flags() | B_FULL_UPDATE_ON_RESIZE);
+ mainMenu->SetViewColor(B_TRANSPARENT_COLOR);
+
+ fURLInputGroup->MakeFocus(true);
+
+ fMenuGroup = menuBarGroup;
+ fTabGroup = fTabManager->TabGroup()->GetLayout();
+ fNavigationGroup = navigationGroup;
+ fFindGroup = findGroup;
+ fStatusGroup = statusGroup;
+ fToggleFullscreenButton = layoutItemFor(toggleFullscreenButton);
+
+ fFindGroup->SetVisible(false);
+ fToggleFullscreenButton->SetVisible(false);
+
+ CreateNewTab(url, true, webView);
+ _ShowInterface(true);
+ _SetAutoHideInterfaceInFullscreen(fAppSettings->GetValue(
+ kSettingsKeyAutoHideInterfaceInFullscreenMode,
+ fAutoHideInterfaceInFullscreenMode));
+
+ AddShortcut('F', B_COMMAND_KEY | B_SHIFT_KEY,
+ new BMessage(EDIT_HIDE_FIND_GROUP));
+ // TODO: Should be a different shortcut, H is usually for Find selection.
+ AddShortcut('H', B_COMMAND_KEY, new BMessage(HOME));
+
+ // Add shortcuts to select a particular tab
+ for (int32 i = 1; i <= 9; i++) {
+ BMessage *selectTab = new BMessage(SELECT_TAB);
+ selectTab->AddInt32("tab index", i - 1);
+ char numStr[2];
+ snprintf(numStr, sizeof(numStr), "%d", (int) i);
+ AddShortcut(numStr[0], B_COMMAND_KEY, selectTab);
+ }
+
+ be_app->PostMessage(WINDOW_OPENED);
+}
+
+
+BrowserWindow::~BrowserWindow()
+{
+ fAppSettings->RemoveListener(BMessenger(this));
+ delete fTabManager;
+ delete fPulseRunner;
+}
+
+
+void
+BrowserWindow::DispatchMessage(BMessage* message, BHandler* target)
+{
+ const char* bytes;
+ uint32 modifierKeys;
+ if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN)
+ && message->FindString("bytes", &bytes) == B_OK
+ && message->FindInt32("modifiers", (int32*)&modifierKeys) == B_OK) {
+
+ modifierKeys = modifierKeys & 0x000000ff;
+ if (bytes[0] == B_LEFT_ARROW && modifierKeys == B_COMMAND_KEY) {
+ PostMessage(GO_BACK);
+ return;
+ } else if (bytes[0] == B_RIGHT_ARROW && modifierKeys == B_COMMAND_KEY) {
+ PostMessage(GO_FORWARD);
+ return;
+ } else if (bytes[0] == B_FUNCTION_KEY) {
+ // Some function key Firefox compatibility
+ int32 key;
+ if (message->FindInt32("key", &key) == B_OK) {
+ switch (key) {
+ case B_F5_KEY:
+ PostMessage(RELOAD);
+ break;
+ case B_F11_KEY:
+ PostMessage(TOGGLE_FULLSCREEN);
+ break;
+ default:
+ break;
+ }
+ }
+ } else if (target == fURLInputGroup->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.
+ if (bytes[0] == B_RETURN) {
+ // Do it in such a way that the user sees the Go-button go down.
+ _InvokeButtonVisibly(fURLInputGroup->GoButton());
+ return;
+ }
+ } else if (target == fFindTextControl->TextView()) {
+ // Handle B_RETURN when the find text control has focus.
+ if (bytes[0] == B_RETURN) {
+ if ((modifierKeys & B_SHIFT_KEY) != 0)
+ _InvokeButtonVisibly(fFindPreviousButton);
+ else
+ _InvokeButtonVisibly(fFindNextButton);
+ return;
+ } else if (bytes[0] == B_ESCAPE) {
+ _InvokeButtonVisibly(fFindCloseButton);
+ return;
+ }
+ } else if (bytes[0] == B_ESCAPE) {
+ // Default escape key behavior:
+ PostMessage(STOP);
+ return;
+ }
+ }
+ if (message->what == B_MOUSE_MOVED || message->what == B_MOUSE_DOWN
+ || message->what == B_MOUSE_UP) {
+ message->FindPoint("where", &fLastMousePos);
+ if (message->FindInt64("when", &fLastMouseMovedTime) != B_OK)
+ fLastMouseMovedTime = system_time();
+ _CheckAutoHideInterface();
+ }
+ if (message->what == B_MOUSE_WHEEL_CHANGED) {
+ BPoint where;
+ uint32 buttons;
+ CurrentWebView()->GetMouse(&where, &buttons, false);
+ // Only do this when the mouse is over the web view
+ if (CurrentWebView()->Bounds().Contains(where)) {
+ // Zoom and unzoom text on Command + mouse wheel.
+ // This could of course (and maybe should be) implemented in the WebView, but there
+ // would need to be a way for the WebView to know the setting of the
+ // fZoomTextOnly member here. Plus other clients of the API may not want
+ // this feature.
+ if ((modifiers() & B_COMMAND_KEY) != 0) {
+ float dy;
+ if (message->FindFloat("be:wheel_delta_y", &dy) == B_OK) {
+ if (dy < 0)
+ CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly);
+ else
+ CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly);
+ return;
+ }
+ }
+ } else // Also don't scroll up and down if the mouse is not over the web view
+ return;
+ }
+ BWebWindow::DispatchMessage(message, target);
+}
+
+
+void
+BrowserWindow::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ case OPEN_LOCATION:
+ _ShowInterface(true);
+ if (fURLInputGroup->TextView()->IsFocus())
+ fURLInputGroup->TextView()->SelectAll();
+ else
+ fURLInputGroup->MakeFocus(true);
+ break;
+ case RELOAD:
+ CurrentWebView()->Reload();
+ break;
+ case GOTO_URL:
+ {
+ BString url;
+ if (message->FindString("url", &url) != B_OK)
+ url = fURLInputGroup->Text();
+ _SetPageIcon(CurrentWebView(), NULL);
+ BString newUrl = _SmartURLHandler(url);
+ if (newUrl != url)
+ fURLInputGroup->TextView()->SetText(newUrl);
+ CurrentWebView()->LoadURL(newUrl.String());
+ break;
+ }
+ case GO_BACK:
+ CurrentWebView()->GoBack();
+ break;
+ case GO_FORWARD:
+ CurrentWebView()->GoForward();
+ break;
+ case STOP:
+ CurrentWebView()->StopLoading();
+ break;
+ case HOME:
+ CurrentWebView()->LoadURL(fStartPageURL);
+ break;
+
+ case CLEAR_HISTORY: {
+ BrowsingHistory* history = BrowsingHistory::DefaultInstance();
+ if (history->CountItems() == 0)
+ break;
+ BAlert* alert = new BAlert("Confirmation", "Do you really want to "
+ "clear the browsing history?", "Clear", "Cancel");
+ if (alert->Go() == 0)
+ history->Clear();
+ break;
+ }
+
+ case CREATE_BOOKMARK:
+ _CreateBookmark();
+ break;
+ case SHOW_BOOKMARKS:
+ _ShowBookmarks();
+ break;
+
+ case B_REFS_RECEIVED:
+ {
+ // Currently the only source of these messages is the bookmarks menu.
+ // Filter refs into URLs, this also gets rid of refs for folders.
+ // For clicks on sub-folders in the bookmarks menu, we have Tracker
+ // open the corresponding folder.
+ entry_ref ref;
+ uint32 addedCount = 0;
+ for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
+ BEntry entry(&ref);
+ uint32 addedSubCount = 0;
+ if (entry.IsDirectory()) {
+ BDirectory directory(&entry);
+ _AddBookmarkURLsRecursively(directory, message,
+ addedSubCount);
+ } else {
+ BFile file(&ref, B_READ_ONLY);
+ BString url;
+ if (_ReadURLAttr(file, url)) {
+ message->AddString("url", url.String());
+ addedSubCount++;
+ }
+ }
+ if (addedSubCount == 0) {
+ // Don't know what to do with this entry, just pass it
+ // on to the system to handle. Note that this may result
+ // in us opening other supported files via the application
+ // mechanism.
+ be_roster->Launch(&ref);
+ }
+ addedCount += addedSubCount;
+ }
+ message->RemoveName("refs");
+ if (addedCount > 10) {
+ BString string;
+ string << "Do you want to open " << addedCount;
+ string << " bookmarks all at once?";
+ BAlert* alert = new BAlert("Open bookmarks confirmation",
+ string.String(), "Cancel", "Open all");
+ if (alert->Go() == 0)
+ break;
+ }
+ be_app->PostMessage(message);
+ 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 ZOOM_FACTOR_INCREASE:
+ CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly);
+ break;
+ case ZOOM_FACTOR_DECREASE:
+ CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly);
+ break;
+ case ZOOM_FACTOR_RESET:
+ CurrentWebView()->ResetZoomFactor();
+ break;
+ case ZOOM_TEXT_ONLY:
+ fZoomTextOnly = !fZoomTextOnly;
+ fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly);
+ // TODO: Would be nice to have an instant update if the page is
+ // already zoomed.
+ break;
+
+ case TOGGLE_FULLSCREEN:
+ ToggleFullscreen();
+ break;
+
+ case TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN:
+ _SetAutoHideInterfaceInFullscreen(
+ !fAutoHideInterfaceInFullscreenMode);
+ break;
+
+ case CHECK_AUTO_HIDE_INTERFACE:
+ _CheckAutoHideInterface();
+ break;
+
+ case SHOW_PAGE_SOURCE:
+ CurrentWebView()->WebPage()->SendPageSource();
+ break;
+ case B_PAGE_SOURCE_RESULT:
+ _HandlePageSourceResult(message);
+ break;
+
+ case EDIT_FIND_NEXT:
+ CurrentWebView()->FindString(fFindTextControl->Text(), true,
+ fFindCaseSensitiveCheckBox->Value());
+ break;
+ case FIND_TEXT_CHANGED:
+ {
+ bool findTextAvailable = strlen(fFindTextControl->Text()) > 0;
+ fFindPreviousMenuItem->SetEnabled(findTextAvailable);
+ fFindNextMenuItem->SetEnabled(findTextAvailable);
+ break;
+ }
+ case EDIT_FIND_PREVIOUS:
+ CurrentWebView()->FindString(fFindTextControl->Text(), false,
+ fFindCaseSensitiveCheckBox->Value());
+ break;
+ case EDIT_SHOW_FIND_GROUP:
+ if (!fFindGroup->IsVisible())
+ fFindGroup->SetVisible(true);
+ fFindTextControl->MakeFocus(true);
+ break;
+ case EDIT_HIDE_FIND_GROUP:
+ if (fFindGroup->IsVisible()) {
+ fFindGroup->SetVisible(false);
+ if (CurrentWebView() != NULL)
+ CurrentWebView()->MakeFocus(true);
+ }
+ break;
+
+ case B_CUT:
+ case B_COPY:
+ case B_PASTE:
+ {
+ BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus());
+ if (textView != NULL)
+ textView->MessageReceived(message);
+ else if (CurrentWebView() != NULL)
+ CurrentWebView()->MessageReceived(message);
+ break;
+ }
+
+ case B_EDITING_CAPABILITIES_RESULT:
+ {
+ BWebView* webView;
+ if (message->FindPointer("view",
+ reinterpret_cast<void**>(&webView)) != B_OK
+ || webView != CurrentWebView()) {
+ break;
+ }
+ bool canCut;
+ bool canCopy;
+ bool canPaste;
+ if (message->FindBool("can cut", &canCut) != B_OK)
+ canCut = false;
+ if (message->FindBool("can copy", &canCopy) != B_OK)
+ canCopy = false;
+ if (message->FindBool("can paste", &canPaste) != B_OK)
+ canPaste = false;
+ fCutMenuItem->SetEnabled(canCut);
+ fCopyMenuItem->SetEnabled(canCopy);
+ fPasteMenuItem->SetEnabled(canPaste);
+ break;
+ }
+
+ case SHOW_DOWNLOAD_WINDOW:
+ case SHOW_SETTINGS_WINDOW:
+ message->AddUInt32("workspaces", Workspaces());
+ be_app->PostMessage(message);
+ break;
+
+ case CLOSE_TAB:
+ if (fTabManager->CountTabs() > 1) {
+ int32 index;
+ if (message->FindInt32("tab index", &index) != B_OK)
+ index = fTabManager->SelectedTabIndex();
+ _ShutdownTab(index);
+ _UpdateTabGroupVisibility();
+ } else
+ PostMessage(B_QUIT_REQUESTED);
+ break;
+
+ case SELECT_TAB:
+ {
+ int32 index;
+ if (message->FindInt32("tab index", &index) == B_OK
+ && fTabManager->SelectedTabIndex() != index
+ && fTabManager->CountTabs() > index) {
+ fTabManager->SelectTab(index);
+ }
+
+ break;
+ }
+
+ case TAB_CHANGED:
+ {
+ // This message may be received also when the last tab closed,
+ // i.e. with index == -1.
+ int32 index;
+ if (message->FindInt32("tab index", &index) != B_OK)
+ index = -1;
+ _TabChanged(index);
+ break;
+ }
+
+ case SETTINGS_VALUE_CHANGED:
+ {
+ BString name;
+ if (message->FindString("name", &name) != B_OK)
+ break;
+ bool flag;
+ BString string;
+ uint32 value;
+ if (name == kSettingsKeyShowTabsIfSinglePageOpen
+ && message->FindBool("value", &flag) == B_OK) {
+ if (fShowTabsIfSinglePageOpen != flag) {
+ fShowTabsIfSinglePageOpen = flag;
+ _UpdateTabGroupVisibility();
+ }
+ } else if (name == kSettingsKeyAutoHidePointer
+ && message->FindBool("value", &flag) == B_OK) {
+ fAutoHidePointer = flag;
+ if (CurrentWebView())
+ CurrentWebView()->SetAutoHidePointer(fAutoHidePointer);
+ } else if (name == kSettingsKeyStartPageURL
+ && message->FindString("value", &string) == B_OK) {
+ fStartPageURL = string;
+ } else if (name == kSettingsKeySearchPageURL
+ && message->FindString("value", &string) == B_OK) {
+ fSearchPageURL = string;
+ } else if (name == kSettingsKeyNewWindowPolicy
+ && message->FindUInt32("value", &value) == B_OK) {
+ fNewWindowPolicy = value;
+ } else if (name == kSettingsKeyNewTabPolicy
+ && message->FindUInt32("value", &value) == B_OK) {
+ fNewTabPolicy = value;
+ } else if (name == kSettingsKeyAutoHideInterfaceInFullscreenMode
+ && message->FindBool("value", &flag) == B_OK) {
+ _SetAutoHideInterfaceInFullscreen(flag);
+ } else if (name == kSettingsKeyShowHomeButton
+ && message->FindBool("value", &flag) == B_OK) {
+ if (flag)
+ fHomeButton->Show();
+ else
+ fHomeButton->Hide();
+ }
+ break;
+ }
+
+ default:
+ BWebWindow::MessageReceived(message);
+ break;
+ }
+}
+
+
+bool
+BrowserWindow::QuitRequested()
+{
+ // TODO: Check for modified form data and ask user for confirmation, etc.
+
+ // Iterate over all tabs to delete all BWebViews.
+ // Do this here, so WebKit tear down happens earlier.
+ SetCurrentWebView(NULL);
+ while (fTabManager->CountTabs() > 0)
+ _ShutdownTab(0);
+
+ BMessage message(WINDOW_CLOSED);
+ message.AddRect("window frame", WindowFrame());
+ be_app->PostMessage(&message);
+ return true;
+}
+
+
+void
+BrowserWindow::MenusBeginning()
+{
+ _UpdateHistoryMenu();
+ _UpdateClipboardItems();
+ _ShowInterface(true);
+}
+
+
+void
+BrowserWindow::Zoom(BPoint origin, float width, float height)
+{
+ ToggleFullscreen();
+}
+
+
+void
+BrowserWindow::ScreenChanged(BRect screenSize, color_space format)
+{
+ if (fIsFullscreen)
+ _ResizeToScreen();
+}
+
+
+void
+BrowserWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
+{
+ if (fIsFullscreen)
+ _ResizeToScreen();
+}
+
+
+static bool
+viewIsChild(const BView* parent, const BView* view)
+{
+ if (parent == view)
+ return true;
+
+ int32 count = parent->CountChildren();
+ for (int32 i = 0; i < count; i++) {
+ BView* child = parent->ChildAt(i);
+ if (viewIsChild(child, view))
+ return true;
+ }
+ return false;
+}
+
+
+void
+BrowserWindow::SetCurrentWebView(BWebView* webView)
+{
+ if (webView == CurrentWebView())
+ return;
+
+ if (CurrentWebView() != NULL) {
+ // Remember the currently focused view before switching tabs,
+ // so that we can revert the focus when switching back to this tab
+ // later.
+ PageUserData* userData = static_cast<PageUserData*>(
+ CurrentWebView()->GetUserData());
+ if (userData == NULL) {
+ userData = new PageUserData(CurrentFocus());
+ CurrentWebView()->SetUserData(userData);
+ }
+ userData->SetFocusedView(CurrentFocus());
+ userData->SetURLInputContents(fURLInputGroup->Text());
+ int32 selectionStart;
+ int32 selectionEnd;
+ fURLInputGroup->TextView()->GetSelection(&selectionStart,
+ &selectionEnd);
+ userData->SetURLInputSelection(selectionStart, selectionEnd);
+ }
+
+ BWebWindow::SetCurrentWebView(webView);
+
+ if (webView != NULL) {
+ webView->SetAutoHidePointer(fAutoHidePointer);
+
+ _UpdateTitle(webView->MainFrameTitle());
+
+ // Restore the previous focus or focus the web view.
+ PageUserData* userData = static_cast<PageUserData*>(
+ webView->GetUserData());
+ BView* focusedView = NULL;
+ if (userData != NULL)
+ focusedView = userData->FocusedView();
+
+ if (focusedView != NULL
+ && viewIsChild(GetLayout()->View(), focusedView)) {
+ focusedView->MakeFocus(true);
+ } else
+ webView->MakeFocus(true);
+
+ if (userData != NULL) {
+ fURLInputGroup->SetPageIcon(userData->PageIcon());
+ if (userData->URLInputContents().Length())
+ fURLInputGroup->SetText(userData->URLInputContents());
+ else
+ fURLInputGroup->SetText(webView->MainFrameURL());
+ if (userData->URLInputSelectionStart() >= 0) {
+ fURLInputGroup->TextView()->Select(
+ userData->URLInputSelectionStart(),
+ userData->URLInputSelectionEnd());
+ }
+ } else {
+ fURLInputGroup->SetPageIcon(NULL);
+ fURLInputGroup->SetText(webView->MainFrameURL());
+ }
+
+ // Trigger update of the interface to the new page, by requesting
+ // to resend all notifications.
+ webView->WebPage()->ResendNotifications();
+ } else
+ _UpdateTitle("");
+}
+
+
+bool
+BrowserWindow::IsBlankTab() const
+{
+ if (CurrentWebView() == NULL)
+ return false;
+ BString requestedURL = CurrentWebView()->MainFrameRequestedURL();
+ return requestedURL.Length() == 0
+ || requestedURL == _NewTabURL(fTabManager->CountTabs() == 1);
+}
+
+
+void
+BrowserWindow::CreateNewTab(const BString& _url, bool select, BWebView* webView)
+{
+ bool applyNewPagePolicy = webView == NULL;
+ // Executed in app thread (new BWebPage needs to be created in app thread).
+ if (webView == NULL)
+ webView = new BWebView("web view");
+
+ bool isNewWindow = fTabManager->CountTabs() == 0;
+
+ fTabManager->AddTab(webView, "New tab");
+
+ BString url(_url);
+ if (applyNewPagePolicy && url.Length() == 0)
+ url = _NewTabURL(isNewWindow);
+
+ if (url.Length() > 0)
+ webView->LoadURL(url.String());
+
+ if (select) {
+ fTabManager->SelectTab(fTabManager->CountTabs() - 1);
+ SetCurrentWebView(webView);
+ webView->WebPage()->ResendNotifications();
+ fURLInputGroup->SetPageIcon(NULL);
+ fURLInputGroup->SetText(url.String());
+ fURLInputGroup->MakeFocus(true);
+ }
+
+ _ShowInterface(true);
+ _UpdateTabGroupVisibility();
+}
+
+
+BRect
+BrowserWindow::WindowFrame() const
+{
+ if (fIsFullscreen)
+ return fNonFullscreenWindowFrame;
+ else
+ return Frame();
+}
+
+
+void
+BrowserWindow::ToggleFullscreen()
+{
+ if (fIsFullscreen) {
+ MoveTo(fNonFullscreenWindowFrame.LeftTop());
+ ResizeTo(fNonFullscreenWindowFrame.Width(),
+ fNonFullscreenWindowFrame.Height());
+
+ SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE));
+ SetLook(B_DOCUMENT_WINDOW_LOOK);
+
+ _ShowInterface(true);
+ } else {
+ fNonFullscreenWindowFrame = Frame();
+ _ResizeToScreen();
+
+ SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_MOVABLE));
+ SetLook(B_TITLED_WINDOW_LOOK);
+ }
+ fIsFullscreen = !fIsFullscreen;
+ fFullscreenItem->SetMarked(fIsFullscreen);
+ fToggleFullscreenButton->SetVisible(fIsFullscreen);
+}
+
+
+// #pragma mark - Notification API
+
+
+void
+BrowserWindow::NavigationRequested(const BString& url, BWebView* view)
+{
+}
+
+
+void
+BrowserWindow::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_TAB);
+ message.AddPointer("window", this);
+ message.AddString("url", url);
+ message.AddBool("select", primaryAction);
+ be_app->PostMessage(&message);
+}
+
+
+void
+BrowserWindow::NewPageCreated(BWebView* view, BRect windowFrame,
+ bool modalDialog, bool resizable, bool activate)
+{
+ if (windowFrame.IsValid()) {
+ BrowserWindow* window = new BrowserWindow(windowFrame, fAppSettings,
+ BString(), INTERFACE_ELEMENT_STATUS, view);
+ window->Show();
+ } else
+ CreateNewTab(BString(), activate, view);
+}
+
+
+void
+BrowserWindow::CloseWindowRequested(BWebView* view)
+{
+ int32 index = fTabManager->TabForView(view);
+ if (index < 0) {
+ // Tab is already gone.
+ return;
+ }
+ BMessage message(CLOSE_TAB);
+ message.AddInt32("tab index", index);
+ PostMessage(&message, this);
+}
+
+
+void
+BrowserWindow::LoadNegotiating(const BString& url, BWebView* view)
+{
+ if (view != CurrentWebView())
+ return;
+
+ fURLInputGroup->SetText(url.String());
+
+ BString status("Requesting: ");
+ status << url;
+ view->WebPage()->SetStatusMessage(status);
+}
+
+
+void
+BrowserWindow::LoadCommitted(const BString& url, BWebView* view)
+{
+ if (view != CurrentWebView())
+ return;
+
+ // This hook is invoked when the load is commited.
+ fURLInputGroup->SetText(url.String());
+
+ BString status("Loading: ");
+ status << url;
+ view->WebPage()->SetStatusMessage(status);
+}
+
+
+void
+BrowserWindow::LoadProgress(float progress, BWebView* view)
+{
+ if (view != CurrentWebView())
+ return;
+
+ if (progress < 100 && fLoadingProgressBar->IsHidden())
+ _ShowProgressBar(true);
+ else if (progress == 100 && !fLoadingProgressBar->IsHidden())
+ _ShowProgressBar(false);
+ fLoadingProgressBar->SetTo(progress);
+}
+
+
+void
+BrowserWindow::LoadFailed(const BString& url, BWebView* view)
+{
+ if (view != CurrentWebView())
+ return;
+
+ BString status(url);
+ status << " failed.";
+ view->WebPage()->SetStatusMessage(status);
+ if (!fLoadingProgressBar->IsHidden())
+ fLoadingProgressBar->Hide();
+}
+
+
+void
+BrowserWindow::LoadFinished(const BString& url, BWebView* view)
+{
+ if (view != CurrentWebView())
+ return;
+
+ BString status(url);
+ status << " finished.";
+ view->WebPage()->SetStatusMessage(status);
+ if (!fLoadingProgressBar->IsHidden())
+ fLoadingProgressBar->Hide();
+
+ NavigationCapabilitiesChanged(fBackButton->IsEnabled(),
+ fForwardButton->IsEnabled(), false, view);
+
+ int32 tabIndex = fTabManager->TabForView(view);
+ if (tabIndex > 0 && strcmp("New tab", fTabManager->TabLabel(tabIndex)) == 0)
+ fTabManager->SetTabLabel(tabIndex, url);
+}
+
+
+void
+BrowserWindow::MainDocumentError(const BString& failingURL,
+ const BString& localizedDescription, BWebView* view)
+{
+ // Make sure we show the page that contains the view.
+ if (!_ShowPage(view))
+ return;
+
+ BWebWindow::MainDocumentError(failingURL, localizedDescription, view);
+
+ // TODO: Remove the failing URL from the BrowsingHistory!
+}
+
+
+void
+BrowserWindow::TitleChanged(const BString& title, BWebView* view)
+{
+ int32 tabIndex = fTabManager->TabForView(view);
+ if (tabIndex < 0)
+ return;
+
+ fTabManager->SetTabLabel(tabIndex, title);
+
+ if (view != CurrentWebView())
+ return;
+
+ _UpdateTitle(title);
+}
+
+
+void
+BrowserWindow::IconReceived(const BBitmap* icon, BWebView* view)
+{
+ // The view may already be gone, since this notification arrives
+ // asynchronously.
+ if (!fTabManager->HasView(view))
+ return;
+
+ _SetPageIcon(view, icon);
+}
+
+
+void
+BrowserWindow::ResizeRequested(float width, float height, BWebView* view)
+{
+ if (view != CurrentWebView())
+ return;
+
+ // Ignore request when there is more than one BWebView embedded.
+ if (fTabManager->CountTabs() > 1)
+ return;
+
+ // Make sure the new frame is not larger than the screen frame minus
+ // window decorator border.
+ BScreen screen(this);
+ BRect screenFrame = screen.Frame();
+ BRect decoratorFrame = DecoratorFrame();
+ BRect frame = Frame();
+
+ screenFrame.left += decoratorFrame.left - frame.left;
+ screenFrame.right += decoratorFrame.right - frame.right;
+ screenFrame.top += decoratorFrame.top - frame.top;
+ screenFrame.bottom += decoratorFrame.bottom - frame.bottom;
+
+ width = min_c(width, screen.Frame().Width());
+ height = min_c(height, screen.Frame().Height());
+
+ frame.right = frame.left + width;
+ frame.bottom = frame.top + height;
+
+ // frame is now not larger than screenFrame, but may still be partly outside
+ if (!screenFrame.Contains(frame)) {
+ if (frame.left < screenFrame.left)
+ frame.OffsetBy(screenFrame.left - frame.left, 0);
+ else if (frame.right > screenFrame.right)
+ frame.OffsetBy(screenFrame.right - frame.right, 0);
+ if (frame.top < screenFrame.top)
+ frame.OffsetBy(screenFrame.top - frame.top, 0);
+ else if (frame.bottom > screenFrame.bottom)
+ frame.OffsetBy(screenFrame.bottom - frame.bottom, 0);
+ }
+
+ MoveTo(frame.left, frame.top);
+ ResizeTo(width, height);
+}
+
+
+void
+BrowserWindow::SetToolBarsVisible(bool flag, BWebView* view)
+{
+ // TODO
+ // TODO: Ignore request when there is more than one BWebView embedded!
+}
+
+
+void
+BrowserWindow::SetStatusBarVisible(bool flag, BWebView* view)
+{
+ // TODO
+ // TODO: Ignore request when there is more than one BWebView embedded!
+}
+
+
+void
+BrowserWindow::SetMenuBarVisible(bool flag, BWebView* view)
+{
+ // TODO
+ // TODO: Ignore request when there is more than one BWebView embedded!
+}
+
+
+void
+BrowserWindow::SetResizable(bool flag, BWebView* view)
+{
+ // TODO: Ignore request when there is more than one BWebView embedded!
+
+ if (flag)
+ SetFlags(Flags() & ~B_NOT_RESIZABLE);
+ else
+ SetFlags(Flags() | B_NOT_RESIZABLE);
+}
+
+
+void
+BrowserWindow::StatusChanged(const BString& statusText, BWebView* view)
+{
+ if (view != CurrentWebView())
+ return;
+
+ if (fStatusText)
+ fStatusText->SetText(statusText.String());
+}
+
+
+void
+BrowserWindow::NavigationCapabilitiesChanged(bool canGoBackward,
+ bool canGoForward, bool canStop, BWebView* view)
+{
+ if (view != CurrentWebView())
+ return;
+
+ fBackButton->SetEnabled(canGoBackward);
+ fForwardButton->SetEnabled(canGoForward);
+ fStopButton->SetEnabled(canStop);
+
+ fBackMenuItem->SetEnabled(canGoBackward);
+ fForwardMenuItem->SetEnabled(canGoForward);
+}
+
+
+void
+BrowserWindow::UpdateGlobalHistory(const BString& url)
+{
+ BrowsingHistory::DefaultInstance()->AddItem(BrowsingHistoryItem(url));
+}
+
+
+bool
+BrowserWindow::AuthenticationChallenge(BString message, BString& inOutUser,
+ BString& inOutPassword, bool& inOutRememberCredentials, uint32 failureCount,
+ BWebView* view)
+{
+ CredentialsStorage* persistentStorage
+ = CredentialsStorage::PersistentInstance();
+ CredentialsStorage* sessionStorage
+ = CredentialsStorage::SessionInstance();
+
+ // TODO: Using the message as key here is not so smart.
+ HashKeyString key(message);
+
+ if (failureCount == 0) {
+ if (persistentStorage->Contains(key)) {
+ Credentials credentials = persistentStorage->GetCredentials(key);
+ inOutUser = credentials.Username();
+ inOutPassword = credentials.Password();
+ return true;
+ } else if (sessionStorage->Contains(key)) {
+ Credentials credentials = sessionStorage->GetCredentials(key);
+ inOutUser = credentials.Username();
+ inOutPassword = credentials.Password();
+ return true;
+ }
+ }
+ // Switch to the page for which this authentication is required.
+ if (!_ShowPage(view))
+ return false;
+
+ AuthenticationPanel* panel = new AuthenticationPanel(Frame());
+ // Panel auto-destructs.
+ bool success = panel->getAuthentication(message, inOutUser, inOutPassword,
+ inOutRememberCredentials, failureCount > 0, inOutUser, inOutPassword,
+ &inOutRememberCredentials);
+ if (success) {
+ Credentials credentials(inOutUser, inOutPassword);
+ if (inOutRememberCredentials)
+ persistentStorage->PutCredentials(key, credentials);
+ else
+ sessionStorage->PutCredentials(key, credentials);
+ }
+ return success;
+}
+
+
+// #pragma mark - private
+
+
+void
+BrowserWindow::_UpdateTitle(const BString& title)
+{
+ BString windowTitle = title;
+ if (windowTitle.Length() > 0)
+ windowTitle << " - ";
+ windowTitle << kApplicationName;
+ SetTitle(windowTitle.String());
+}
+
+
+void
+BrowserWindow::_UpdateTabGroupVisibility()
+{
+ if (Lock()) {
+ if (fInterfaceVisible)
+ fTabGroup->SetVisible(_TabGroupShouldBeVisible());
+ fTabManager->SetCloseButtonsAvailable(fTabManager->CountTabs() > 1);
+ Unlock();
+ }
+}
+
+
+bool
+BrowserWindow::_TabGroupShouldBeVisible() const
+{
+ return (fShowTabsIfSinglePageOpen || fTabManager->CountTabs() > 1)
+ && (fVisibleInterfaceElements & INTERFACE_ELEMENT_TABS) != 0;
+}
+
+
+void
+BrowserWindow::_ShutdownTab(int32 index)
+{
+ BView* view = fTabManager->RemoveTab(index);
+ BWebView* webView = dynamic_cast<BWebView*>(view);
+ if (webView == CurrentWebView())
+ SetCurrentWebView(NULL);
+ if (webView != NULL)
+ webView->Shutdown();
+ else
+ delete view;
+}
+
+
+void
+BrowserWindow::_TabChanged(int32 index)
+{
+ SetCurrentWebView(dynamic_cast<BWebView*>(fTabManager->ViewForTab(index)));
+}
+
+
+status_t
+BrowserWindow::_BookmarkPath(BPath& path) const
+{
+ status_t ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
+ if (ret != B_OK)
+ return ret;
+
+ ret = path.Append(kApplicationName);
+ if (ret != B_OK)
+ return ret;
+
+ ret = path.Append("Bookmarks");
+ if (ret != B_OK)
+ return ret;
+
+ return create_directory(path.Path(), 0777);
+}
+
+
+void
+BrowserWindow::_CreateBookmark()
+{
+ BPath path;
+ status_t status = _BookmarkPath(path);
+ if (status != B_OK) {
+ BString message("There was an error retrieving the bookmark "
+ "folder.\n\n");
+ message << "Error: " << strerror(status);
+ BAlert* alert = new BAlert("Bookmark error", message.String(), "OK",
+ NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
+ alert->Go();
+ return;
+ }
+ BWebView* webView = CurrentWebView();
+ BString url(webView->MainFrameURL());
+ // Create a bookmark file
+ BFile bookmarkFile;
+ BString bookmarkName(webView->MainFrameTitle());
+ if (bookmarkName.Length() == 0) {
+ bookmarkName = url;
+ int32 leafPos = bookmarkName.FindLast('/');
+ if (leafPos >= 0)
+ bookmarkName.Remove(0, leafPos + 1);
+ }
+ // Make sure the bookmark title does not contain chars that are not
+ // allowed in file names.
+ bookmarkName.ReplaceAll('/', '-');
+
+ // Check that the bookmark exists nowhere in the bookmark hierarchy,
+ // though the intended file name must match, we don't search the stored
+ // URLs, only for matching file names.
+ BDirectory directory(path.Path());
+ if (status == B_OK && _CheckBookmarkExists(directory, bookmarkName, url)) {
+ BString message("A bookmark for this page (");
+ message << bookmarkName;
+ message << ") already exists.";
+ BAlert* alert = new BAlert("Bookmark info", message.String(), "OK");
+ alert->Go();
+ return;
+ }
+
+ BPath entryPath(path);
+ status = entryPath.Append(bookmarkName);
+ BEntry entry;
+ if (status == B_OK)
+ status = entry.SetTo(entryPath.Path(), true);
+ if (status == B_OK) {
+ int32 tries = 1;
+ while (entry.Exists()) {
+ // Find a unique name for the bookmark, there may still be a
+ // file in the way that stores a different URL.
+ bookmarkName = webView->MainFrameTitle();
+ bookmarkName << " " << tries++;
+ entryPath = path;
+ status = entryPath.Append(bookmarkName);
+ if (status == B_OK)
+ status = entry.SetTo(entryPath.Path(), true);
+ if (status != B_OK)
+ break;
+ }
+ }
+ if (status == B_OK) {
+ status = bookmarkFile.SetTo(&entry,
+ B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
+ }
+
+ // Write bookmark meta data
+ if (status == B_OK)
+ status = bookmarkFile.WriteAttrString("META:url", &url);
+ if (status == B_OK) {
+ BString title = webView->MainFrameTitle();
+ bookmarkFile.WriteAttrString("META:title", &title);
+ }
+
+ BNodeInfo nodeInfo(&bookmarkFile);
+ if (status == B_OK) {
+ status = nodeInfo.SetType("application/x-vnd.Be-bookmark");
+ if (status == B_OK) {
+ PageUserData* userData = static_cast<PageUserData*>(
+ webView->GetUserData());
+ if (userData != NULL && userData->PageIcon() != NULL) {
+ BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK,
+ B_CMAP8);
+ status_t ret = miniIcon.ImportBits(userData->PageIcon());
+ if (ret == B_OK)
+ ret = nodeInfo.SetIcon(&miniIcon, B_MINI_ICON);
+ if (ret != B_OK) {
+ fprintf(stderr, "Failed to store mini icon for bookmark: "
+ "%s\n", strerror(ret));
+ }
+ BBitmap largeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK,
+ B_CMAP8);
+ // TODO: Store 32x32 favicon which is often provided by sites.
+ const uint8* src = (const uint8*)miniIcon.Bits();
+ uint32 srcBPR = miniIcon.BytesPerRow();
+ uint8* dst = (uint8*)largeIcon.Bits();
+ uint32 dstBPR = largeIcon.BytesPerRow();
+ for (uint32 y = 0; y < 16; y++) {
+ const uint8* s = src;
+ uint8* d = dst;
+ for (uint32 x = 0; x < 16; x++) {
+ *d++ = *s;
+ *d++ = *s++;
+ }
+ dst += dstBPR;
+ s = src;
+ for (uint32 x = 0; x < 16; x++) {
+ *d++ = *s;
+ *d++ = *s++;
+ }
+ dst += dstBPR;
+ src += srcBPR;
+ }
+ if (ret == B_OK)
+ ret = nodeInfo.SetIcon(&largeIcon, B_LARGE_ICON);
+ if (ret != B_OK) {
+ fprintf(stderr, "Failed to store large icon for bookmark: "
+ "%s\n", strerror(ret));
+ }
+ }
+ }
+ }
+
+ if (status != B_OK) {
+ BString message("There was an error creating the bookmark "
+ "file.\n\n");
+ message << "Error: " << strerror(status);
+ BAlert* alert = new BAlert("Bookmark error", message.String(), "OK",
+ NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
+ alert->Go();
+ return;
+ }
+}
+
+
+void
+BrowserWindow::_ShowBookmarks()
+{
+ BPath path;
+ entry_ref ref;
+ status_t status = _BookmarkPath(path);
+ if (status == B_OK)
+ status = get_ref_for_path(path.Path(), &ref);
+ if (status == B_OK)
+ status = be_roster->Launch(&ref);
+
+ if (status != B_OK && status != B_ALREADY_RUNNING) {
+ BString message("There was an error trying to show the Bookmarks "
+ "folder.\n\n");
+ message << "Error: " << strerror(status);
+ BAlert* alert = new BAlert("Bookmark error", message.String(), "OK",
+ NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
+ alert->Go();
+ return;
+ }
+}
+
+
+bool BrowserWindow::_CheckBookmarkExists(BDirectory& directory,
+ const BString& bookmarkName, const BString& url) const
+{
+ BEntry entry;
+ while (directory.GetNextEntry(&entry) == B_OK) {
+ if (entry.IsDirectory()) {
+ BDirectory subBirectory(&entry);
+ // At least preserve the entry file handle when recursing into
+ // sub-folders... eventually we will run out, though, with very
+ // deep hierarchy.
+ entry.Unset();
+ if (_CheckBookmarkExists(subBirectory, bookmarkName, url))
+ return true;
+ } else {
+ char entryName[B_FILE_NAME_LENGTH];
+ if (entry.GetName(entryName) != B_OK || bookmarkName != entryName)
+ continue;
+ BString storedURL;
+ BFile file(&entry, B_READ_ONLY);
+ if (_ReadURLAttr(file, storedURL)) {
+ // Just bail if the bookmark already exists
+ if (storedURL == url)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+bool
+BrowserWindow::_ReadURLAttr(BFile& bookmarkFile, BString& url) const
+{
+ return bookmarkFile.InitCheck() == B_OK
+ && bookmarkFile.ReadAttrString("META:url", &url) == B_OK;
+}
+
+
+void
+BrowserWindow::_AddBookmarkURLsRecursively(BDirectory& directory,
+ BMessage* message, uint32& addedCount) const
+{
+ BEntry entry;
+ while (directory.GetNextEntry(&entry) == B_OK) {
+ if (entry.IsDirectory()) {
+ BDirectory subBirectory(&entry);
+ // At least preserve the entry file handle when recursing into
+ // sub-folders... eventually we will run out, though, with very
+ // deep hierarchy.
+ entry.Unset();
+ _AddBookmarkURLsRecursively(subBirectory, message, addedCount);
+ } else {
+ BString storedURL;
+ BFile file(&entry, B_READ_ONLY);
+ if (_ReadURLAttr(file, storedURL)) {
+ message->AddString("url", storedURL.String());
+ addedCount++;
+ }
+ }
+ }
+}
+
+
+void
+BrowserWindow::_SetPageIcon(BWebView* view, const BBitmap* icon)
+{
+ PageUserData* userData = static_cast<PageUserData*>(view->GetUserData());
+ if (userData == NULL) {
+ userData = new(std::nothrow) PageUserData(NULL);
+ if (userData == NULL)
+ return;
+ view->SetUserData(userData);
+ }
+ // The PageUserData makes a copy of the icon, which we pass on to
+ // the TabManager for display in the respective tab.
+ userData->SetPageIcon(icon);
+ fTabManager->SetTabIcon(view, userData->PageIcon());
+ if (view == CurrentWebView())
+ fURLInputGroup->SetPageIcon(icon);
+}
+
+
+static void
+addItemToMenuOrSubmenu(BMenu* menu, BMenuItem* newItem)
+{
+ BString baseURLLabel = baseURL(BString(newItem->Label()));
+ for (int32 i = menu->CountItems() - 1; i >= 0; i--) {
+ BMenuItem* item = menu->ItemAt(i);
+ BString label = item->Label();
+ if (label.FindFirst(baseURLLabel) >= 0) {
+ if (item->Submenu()) {
+ // Submenu was already added in previous iteration.
+ item->Submenu()->AddItem(newItem);
+ return;
+ } else {
+ menu->RemoveItem(item);
+ BMenu* subMenu = new BMenu(baseURLLabel.String());
+ subMenu->AddItem(item);
+ subMenu->AddItem(newItem);
+ // Add common submenu for this base URL, clickable.
+ BMessage* message = new BMessage(GOTO_URL);
+ message->AddString("url", baseURLLabel.String());
+ menu->AddItem(new BMenuItem(subMenu, message), i);
+ return;
+ }
+ }
+ }
+ menu->AddItem(newItem);
+}
+
+
+static void
+addOrDeleteMenu(BMenu* menu, BMenu* toMenu)
+{
+ if (menu->CountItems() > 0)
+ toMenu->AddItem(menu);
+ else
+ delete menu;
+}
+
+
+void
+BrowserWindow::_UpdateHistoryMenu()
+{
+ BMenuItem* menuItem;
+ while ((menuItem = fHistoryMenu->RemoveItem(fHistoryMenuFixedItemCount)))
+ delete menuItem;
+
+ BrowsingHistory* history = BrowsingHistory::DefaultInstance();
+ if (!history->Lock())
+ return;
+
+ int32 count = history->CountItems();
+ BMenuItem* clearHistoryItem = new BMenuItem("Clear history",
+ new BMessage(CLEAR_HISTORY));
+ clearHistoryItem->SetEnabled(count > 0);
+ fHistoryMenu->AddItem(clearHistoryItem);
+ if (count == 0) {
+ history->Unlock();
+ return;
+ }
+ fHistoryMenu->AddSeparatorItem();
+
+ BDateTime todayStart = BDateTime::CurrentDateTime(B_LOCAL_TIME);
+ todayStart.SetTime(BTime(0, 0, 0));
+
+ BDateTime oneDayAgoStart = todayStart;
+ oneDayAgoStart.Date().AddDays(-1);
+
+ BDateTime twoDaysAgoStart = oneDayAgoStart;
+ twoDaysAgoStart.Date().AddDays(-1);
+
+ BDateTime threeDaysAgoStart = twoDaysAgoStart;
+ threeDaysAgoStart.Date().AddDays(-1);
+
+ BDateTime fourDaysAgoStart = threeDaysAgoStart;
+ fourDaysAgoStart.Date().AddDays(-1);
+
+ BDateTime fiveDaysAgoStart = fourDaysAgoStart;
+ fiveDaysAgoStart.Date().AddDays(-1);
+
+ BMenu* todayMenu = new BMenu("Today");
+ BMenu* yesterdayMenu = new BMenu("Yesterday");
+ BMenu* twoDaysAgoMenu = new BMenu(
+ twoDaysAgoStart.Date().LongDayName().String());
+ BMenu* threeDaysAgoMenu = new BMenu(
+ threeDaysAgoStart.Date().LongDayName().String());
+ BMenu* fourDaysAgoMenu = new BMenu(
+ fourDaysAgoStart.Date().LongDayName().String());
+ BMenu* fiveDaysAgoMenu = new BMenu(
+ fiveDaysAgoStart.Date().LongDayName().String());
+ BMenu* earlierMenu = new BMenu("Earlier");
+
+ for (int32 i = 0; i < count; i++) {
+ BrowsingHistoryItem historyItem = history->HistoryItemAt(i);
+ BMessage* message = new BMessage(GOTO_URL);
+ message->AddString("url", historyItem.URL().String());
+
+ BString truncatedUrl(historyItem.URL());
+ be_plain_font->TruncateString(&truncatedUrl, B_TRUNCATE_END, 480);
+ menuItem = new BMenuItem(truncatedUrl, message);
+
+ if (historyItem.DateTime() < fiveDaysAgoStart)
+ addItemToMenuOrSubmenu(earlierMenu, menuItem);
+ else if (historyItem.DateTime() < fourDaysAgoStart)
+ addItemToMenuOrSubmenu(fiveDaysAgoMenu, menuItem);
+ else if (historyItem.DateTime() < threeDaysAgoStart)
+ addItemToMenuOrSubmenu(fourDaysAgoMenu, menuItem);
+ else if (historyItem.DateTime() < twoDaysAgoStart)
+ addItemToMenuOrSubmenu(threeDaysAgoMenu, menuItem);
+ else if (historyItem.DateTime() < oneDayAgoStart)
+ addItemToMenuOrSubmenu(twoDaysAgoMenu, menuItem);
+ else if (historyItem.DateTime() < todayStart)
+ addItemToMenuOrSubmenu(yesterdayMenu, menuItem);
+ else
+ addItemToMenuOrSubmenu(todayMenu, menuItem);
+ }
+ history->Unlock();
+
+ addOrDeleteMenu(todayMenu, fHistoryMenu);
+ addOrDeleteMenu(yesterdayMenu, fHistoryMenu);
+ addOrDeleteMenu(twoDaysAgoMenu, fHistoryMenu);
+ addOrDeleteMenu(fourDaysAgoMenu, fHistoryMenu);
+ addOrDeleteMenu(fiveDaysAgoMenu, fHistoryMenu);
+ addOrDeleteMenu(earlierMenu, fHistoryMenu);
+}
+
+
+void
+BrowserWindow::_UpdateClipboardItems()
+{
+ BTextView* focusTextView = dynamic_cast<BTextView*>(CurrentFocus());
+ if (focusTextView != NULL) {
+ int32 selectionStart;
+ int32 selectionEnd;
+ focusTextView->GetSelection(&selectionStart, &selectionEnd);
+ bool hasSelection = selectionStart < selectionEnd;
+ bool canPaste = false;
+ // A BTextView has the focus.
+ if (be_clipboard->Lock()) {
+ BMessage* data = be_clipboard->Data();
+ if (data != NULL)
+ canPaste = data->HasData("text/plain", B_MIME_TYPE);
+ be_clipboard->Unlock();
+ }
+ fCutMenuItem->SetEnabled(hasSelection);
+ fCopyMenuItem->SetEnabled(hasSelection);
+ fPasteMenuItem->SetEnabled(canPaste);
+ } else if (CurrentWebView() != NULL) {
+ // Trigger update of the clipboard items, even if the
+ // BWebView doesn't have focus, we'll dispatch these message
+ // there anyway. This works so fast that the user can never see
+ // the wrong enabled state when the menu opens until the result
+ // message arrives. The initial state needs to be enabled, since
+ // standard shortcut handling is always wrapped inside MenusBeginning()
+ // and MenusEnded(), and since we update items asynchronously, we need
+ // to have them enabled to begin with.
+ fCutMenuItem->SetEnabled(true);
+ fCopyMenuItem->SetEnabled(true);
+ fPasteMenuItem->SetEnabled(true);
+
+ CurrentWebView()->WebPage()->SendEditingCapabilities();
+ }
+}
+
+
+bool
+BrowserWindow::_ShowPage(BWebView* view)
+{
+ if (view != CurrentWebView()) {
+ int32 tabIndex = fTabManager->TabForView(view);
+ if (tabIndex < 0) {
+ // Page seems to be gone already?
+ return false;
+ }
+ fTabManager->SelectTab(tabIndex);
+ _TabChanged(tabIndex);
+ UpdateIfNeeded();
+ }
+ return true;
+}
+
+
+void
+BrowserWindow::_ResizeToScreen()
+{
+ BScreen screen(this);
+ MoveTo(0, 0);
+ ResizeTo(screen.Frame().Width(), screen.Frame().Height());
+}
+
+
+void
+BrowserWindow::_SetAutoHideInterfaceInFullscreen(bool doIt)
+{
+ if (fAutoHideInterfaceInFullscreenMode == doIt)
+ return;
+
+ fAutoHideInterfaceInFullscreenMode = doIt;
+ if (fAppSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode,
+ doIt) != doIt) {
+ fAppSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode,
+ doIt);
+ }
+
+ if (fAutoHideInterfaceInFullscreenMode) {
+ BMessage message(CHECK_AUTO_HIDE_INTERFACE);
+ fPulseRunner = new BMessageRunner(BMessenger(this), &message, 300000);
+ } else {
+ delete fPulseRunner;
+ fPulseRunner = NULL;
+ _ShowInterface(true);
+ }
+}
+
+
+void
+BrowserWindow::_CheckAutoHideInterface()
+{
+ if (!fIsFullscreen || !fAutoHideInterfaceInFullscreenMode
+ || (CurrentWebView() != NULL && !CurrentWebView()->IsFocus())) {
+ return;
+ }
+
+ if (fLastMousePos.y == 0)
+ _ShowInterface(true);
+ else if (fNavigationGroup->IsVisible()
+ && fLastMousePos.y > fNavigationGroup->Frame().bottom
+ && system_time() - fLastMouseMovedTime > 1000000) {
+ // NOTE: Do not re-use navigationGroupBottom in the above
+ // check, since we only want to hide the interface when it is visible.
+ _ShowInterface(false);
+ }
+}
+
+
+void
+BrowserWindow::_ShowInterface(bool show)
+{
+ if (fInterfaceVisible == show)
+ return;
+
+ fInterfaceVisible = show;
+
+ if (show) {
+#if !INTEGRATE_MENU_INTO_TAB_BAR
+ fMenuGroup->SetVisible(
+ (fVisibleInterfaceElements & INTERFACE_ELEMENT_MENU) != 0);
+#endif
+ fTabGroup->SetVisible(_TabGroupShouldBeVisible());
+ fNavigationGroup->SetVisible(
+ (fVisibleInterfaceElements & INTERFACE_ELEMENT_NAVIGATION) != 0);
+ fStatusGroup->SetVisible(
+ (fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0);
+ } else {
+ fMenuGroup->SetVisible(false);
+ fTabGroup->SetVisible(false);
+ fNavigationGroup->SetVisible(false);
+ fStatusGroup->SetVisible(false);
+ }
+ // TODO: Setting the group visible seems to unhide the status bar.
+ // Fix in Haiku?
+ while (!fLoadingProgressBar->IsHidden())
+ fLoadingProgressBar->Hide();
+}
+
+
+void
+BrowserWindow::_ShowProgressBar(bool show)
+{
+ if (show) {
+ if (!fStatusGroup->IsVisible() && (fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0)
+ fStatusGroup->SetVisible(true);
+ fLoadingProgressBar->Show();
+ } else {
+ if (!fInterfaceVisible)
+ fStatusGroup->SetVisible(false);
+ // TODO: This is also used in _ShowInterface. Without it the status bar
+ // doesn't always hide again. It may be an Interface Kit bug.
+ while (!fLoadingProgressBar->IsHidden())
+ fLoadingProgressBar->Hide();
+ }
+}
+
+
+void
+BrowserWindow::_InvokeButtonVisibly(BButton* button)
+{
+ button->SetValue(B_CONTROL_ON);
+ UpdateIfNeeded();
+ button->Invoke();
+ snooze(1000);
+ button->SetValue(B_CONTROL_OFF);
+}
+
+
+BString
+BrowserWindow::_NewTabURL(bool isNewWindow) const
+{
+ BString url;
+ uint32 policy = isNewWindow ? fNewWindowPolicy : fNewTabPolicy;
+ // Implement new page policy
+ switch (policy) {
+ case OpenStartPage:
+ url = fStartPageURL;
+ break;
+ case OpenSearchPage:
+ url = fSearchPageURL;
+ break;
+ case CloneCurrentPage:
+ if (CurrentWebView() != NULL)
+ url = CurrentWebView()->MainFrameURL();
+ break;
+ case OpenBlankPage:
+ default:
+ break;
+ }
+ return url;
+}
+
+
+BString
+BrowserWindow::_SmartURLHandler(const BString& url) const
+{
+ BString result = url;
+
+ // Only process if this doesn't look like a full URL (http:// or
+ // file://, etc.)
+ if (url.FindFirst("://") == B_ERROR) {
+ if (url.FindFirst(".") == B_ERROR || url.FindFirst(" ") != B_ERROR)
+ result.Prepend("http://www.google.com/search?q=");
+ }
+ return result;
+}
+
+
+void
+BrowserWindow::_HandlePageSourceResult(const BMessage* message)
+{
+ // TODO: This should be done in an extra thread perhaps. Doing it in
+ // the application thread is not much better, since it actually draws
+ // the pages...
+
+ BPath pathToPageSource;
+
+ BString url;
+ status_t ret = message->FindString("url", &url);
+ if (ret == B_OK && url.FindFirst("file://") == 0) {
+ // Local file
+ url.Remove(0, strlen("file://"));
+ pathToPageSource.SetTo(url.String());
+ } else {
+ // Something else, store it.
+ // TODO: What if it isn't HTML, but for example SVG?
+ BString source;
+ ret = message->FindString("source", &source);
+
+ if (ret == B_OK)
+ ret = find_directory(B_COMMON_TEMP_DIRECTORY, &pathToPageSource);
+
+ BString tmpFileName("PageSource_");
+ tmpFileName << system_time() << ".html";
+ if (ret == B_OK)
+ ret = pathToPageSource.Append(tmpFileName.String());
+
+ BFile pageSourceFile(pathToPageSource.Path(),
+ B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
+ if (ret == B_OK)
+ ret = pageSourceFile.InitCheck();
+
+ if (ret == B_OK) {
+ ssize_t written = pageSourceFile.Write(source.String(),
+ source.Length());
+ if (written != source.Length())
+ ret = (status_t)written;
+ }
+
+ if (ret == B_OK) {
+ const char* type = "text/html";
+ size_t size = strlen(type);
+ pageSourceFile.WriteAttr("BEOS:TYPE", B_STRING_TYPE, 0, type, size);
+ // If it fails we don't care.
+ }
+ }
+
+ entry_ref ref;
+ if (ret == B_OK)
+ ret = get_ref_for_path(pathToPageSource.Path(), &ref);
+
+ if (ret == B_OK) {
+ BMessage refsMessage(B_REFS_RECEIVED);
+ ret = refsMessage.AddRef("refs", &ref);
+ if (ret == B_OK) {
+ ret = be_roster->Launch("text/x-source-code", &refsMessage);
+ if (ret == B_ALREADY_RUNNING)
+ ret = B_OK;
+ }
+ }
+
+ if (ret != B_OK) {
+ char buffer[1024];
+ snprintf(buffer, sizeof(buffer), "Failed to show the "
+ "page source: %s\n", strerror(ret));
+ BAlert* alert = new BAlert("Page source error", buffer, "OK");
+ alert->Go(NULL);
+ }
+}
+
+
diff --git a/src/apps/webpositive/BrowserWindow.h b/src/apps/webpositive/BrowserWindow.h
new file mode 100644
index 0000000000..fcae9d5f7b
--- /dev/null
+++ b/src/apps/webpositive/BrowserWindow.h
@@ -0,0 +1,257 @@
+/*
+ * 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>
+ *
+ * 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.
+ */
+#ifndef BROWSER_WINDOW_H
+#define BROWSER_WINDOW_H
+
+
+#include "WebWindow.h"
+#include <Messenger.h>
+#include <String.h>
+
+class BButton;
+class BCheckBox;
+class BDirectory;
+class BFile;
+class BLayoutItem;
+class BMenu;
+class BMenuItem;
+class BMessageRunner;
+class BPath;
+class BStatusBar;
+class BStringView;
+class BTextControl;
+class BWebView;
+class IconButton;
+class SettingsMessage;
+class TabManager;
+class URLInputGroup;
+
+enum {
+ INTERFACE_ELEMENT_MENU = 1 << 0,
+ INTERFACE_ELEMENT_TABS = 1 << 1,
+ INTERFACE_ELEMENT_NAVIGATION = 1 << 2,
+ INTERFACE_ELEMENT_STATUS = 1 << 3,
+
+ INTERFACE_ELEMENT_ALL = 0xffff
+};
+
+enum NewPagePolicy {
+ OpenBlankPage = 0,
+ OpenStartPage = 1,
+ OpenSearchPage = 2,
+ CloneCurrentPage = 3
+};
+
+enum {
+ NEW_WINDOW = 'nwnd',
+ NEW_TAB = 'ntab',
+ WINDOW_OPENED = 'wndo',
+ WINDOW_CLOSED = 'wndc',
+ SHOW_DOWNLOAD_WINDOW = 'sdwd',
+ SHOW_SETTINGS_WINDOW = 'sswd'
+};
+
+#define INTEGRATE_MENU_INTO_TAB_BAR 0
+
+
+class BrowserWindow : public BWebWindow {
+public:
+ BrowserWindow(BRect frame,
+ SettingsMessage* appSettings,
+ const BString& url,
+ uint32 interfaceElements
+ = INTERFACE_ELEMENT_ALL,
+ BWebView* webView = NULL);
+ virtual ~BrowserWindow();
+
+ virtual void DispatchMessage(BMessage* message,
+ BHandler* target);
+ virtual void MessageReceived(BMessage* message);
+ virtual bool QuitRequested();
+ virtual void MenusBeginning();
+
+ virtual void Zoom(BPoint origin, float width, float height);
+ virtual void ScreenChanged(BRect screenSize,
+ color_space format);
+ virtual void WorkspacesChanged(uint32 oldWorkspaces,
+ uint32 newWorkspaces);
+
+ virtual void SetCurrentWebView(BWebView* view);
+
+ bool IsBlankTab() const;
+ void CreateNewTab(const BString& url, bool select,
+ BWebView* webView = 0);
+
+ BRect WindowFrame() const;
+
+ void ToggleFullscreen();
+
+private:
+ // WebPage notification API implementations
+ virtual void NavigationRequested(const BString& url,
+ BWebView* view);
+ virtual void NewWindowRequested(const BString& url,
+ bool primaryAction);
+ virtual void CloseWindowRequested(BWebView* view);
+ virtual void NewPageCreated(BWebView* view,
+ BRect windowFrame, bool modalDialog,
+ bool resizable, bool activate);
+ virtual void LoadNegotiating(const BString& url,
+ BWebView* view);
+ virtual void LoadCommitted(const BString& url,
+ BWebView* view);
+ virtual void LoadProgress(float progress, BWebView* view);
+ virtual void LoadFailed(const BString& url, BWebView* view);
+ virtual void LoadFinished(const BString& url,
+ BWebView* view);
+ virtual void MainDocumentError(const BString& failingURL,
+ const BString& localizedDescription,
+ BWebView* view);
+ virtual void TitleChanged(const BString& title,
+ BWebView* view);
+ virtual void IconReceived(const BBitmap* icon,
+ BWebView* view);
+ virtual void ResizeRequested(float width, float height,
+ BWebView* view);
+ virtual void SetToolBarsVisible(bool flag, BWebView* view);
+ virtual void SetStatusBarVisible(bool flag, BWebView* view);
+ virtual void SetMenuBarVisible(bool flag, BWebView* view);
+ virtual void SetResizable(bool flag, BWebView* view);
+ virtual void StatusChanged(const BString& status,
+ BWebView* view);
+ virtual void NavigationCapabilitiesChanged(
+ bool canGoBackward, bool canGoForward,
+ bool canStop, BWebView* view);
+ virtual void UpdateGlobalHistory(const BString& url);
+ virtual bool AuthenticationChallenge(BString message,
+ BString& inOutUser, BString& inOutPassword,
+ bool& inOutRememberCredentials,
+ uint32 failureCount, BWebView* view);
+
+private:
+ void _UpdateTitle(const BString &title);
+ void _UpdateTabGroupVisibility();
+ bool _TabGroupShouldBeVisible() const;
+ void _ShutdownTab(int32 index);
+ void _TabChanged(int32 index);
+
+ status_t _BookmarkPath(BPath& path) const;
+ void _CreateBookmark();
+ void _ShowBookmarks();
+ bool _CheckBookmarkExists(BDirectory& directory,
+ const BString& fileName,
+ const BString& url) const;
+ bool _ReadURLAttr(BFile& bookmarkFile,
+ BString& url) const;
+ void _AddBookmarkURLsRecursively(
+ BDirectory& directory,
+ BMessage* message,
+ uint32& addedCount) const;
+
+ void _SetPageIcon(BWebView* view,
+ const BBitmap* icon);
+
+ void _UpdateHistoryMenu();
+ void _UpdateClipboardItems();
+
+ bool _ShowPage(BWebView* view);
+
+ void _ToggleFullscreen();
+ void _ResizeToScreen();
+ void _SetAutoHideInterfaceInFullscreen(bool doIt);
+ void _CheckAutoHideInterface();
+ void _ShowInterface(bool show);
+ void _ShowProgressBar(bool);
+ void _InvokeButtonVisibly(BButton* button);
+
+ BString _NewTabURL(bool isNewWindow) const;
+ BString _SmartURLHandler(const BString& url) const;
+
+ void _HandlePageSourceResult(
+ const BMessage* message);
+
+private:
+ BMenu* fHistoryMenu;
+ int32 fHistoryMenuFixedItemCount;
+
+ BMenuItem* fCutMenuItem;
+ BMenuItem* fCopyMenuItem;
+ BMenuItem* fPasteMenuItem;
+ BMenuItem* fFindPreviousMenuItem;
+ BMenuItem* fFindNextMenuItem;
+ BMenuItem* fZoomTextOnlyMenuItem;
+ BMenuItem* fFullscreenItem;
+ BMenuItem* fBackMenuItem;
+ BMenuItem* fForwardMenuItem;
+
+ IconButton* fBackButton;
+ IconButton* fForwardButton;
+ IconButton* fStopButton;
+ IconButton* fHomeButton;
+ URLInputGroup* fURLInputGroup;
+ BStringView* fStatusText;
+ BStatusBar* fLoadingProgressBar;
+
+ BLayoutItem* fMenuGroup;
+ BLayoutItem* fTabGroup;
+ BLayoutItem* fNavigationGroup;
+ BLayoutItem* fFindGroup;
+ BLayoutItem* fStatusGroup;
+ BLayoutItem* fToggleFullscreenButton;
+
+ BTextControl* fFindTextControl;
+ BButton* fFindPreviousButton;
+ BButton* fFindNextButton;
+ BButton* fFindCloseButton;
+ BCheckBox* fFindCaseSensitiveCheckBox;
+ TabManager* fTabManager;
+
+ bool fIsFullscreen;
+ bool fInterfaceVisible;
+ BRect fNonFullscreenWindowFrame;
+ BMessageRunner* fPulseRunner;
+ uint32 fVisibleInterfaceElements;
+ bigtime_t fLastMouseMovedTime;
+ BPoint fLastMousePos;
+
+ // cached settings
+ SettingsMessage* fAppSettings;
+ bool fZoomTextOnly;
+ bool fShowTabsIfSinglePageOpen;
+ bool fAutoHideInterfaceInFullscreenMode;
+ bool fAutoHidePointer;
+ uint32 fNewWindowPolicy;
+ uint32 fNewTabPolicy;
+ BString fStartPageURL;
+ BString fSearchPageURL;
+};
+
+
+#endif // BROWSER_WINDOW_H
+
diff --git a/src/apps/webpositive/BrowsingHistory.cpp b/src/apps/webpositive/BrowsingHistory.cpp
new file mode 100644
index 0000000000..42a0442d0f
--- /dev/null
+++ b/src/apps/webpositive/BrowsingHistory.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "BrowsingHistory.h"
+
+#include <new>
+#include <stdio.h>
+
+#include <Autolock.h>
+#include <Entry.h>
+#include <File.h>
+#include <FindDirectory.h>
+#include <Message.h>
+#include <Path.h>
+
+#include "BrowserApp.h"
+
+
+BrowsingHistoryItem::BrowsingHistoryItem(const BString& url)
+ :
+ fURL(url),
+ fDateTime(BDateTime::CurrentDateTime(B_LOCAL_TIME)),
+ fInvokationCount(0)
+{
+}
+
+
+BrowsingHistoryItem::BrowsingHistoryItem(const BrowsingHistoryItem& other)
+{
+ *this = other;
+}
+
+
+BrowsingHistoryItem::BrowsingHistoryItem(const BMessage* archive)
+{
+ if (!archive)
+ return;
+ BMessage dateTimeArchive;
+ if (archive->FindMessage("date time", &dateTimeArchive) == B_OK)
+ fDateTime = BDateTime(&dateTimeArchive);
+ archive->FindString("url", &fURL);
+ archive->FindUInt32("invokations", &fInvokationCount);
+}
+
+
+BrowsingHistoryItem::~BrowsingHistoryItem()
+{
+}
+
+
+status_t
+BrowsingHistoryItem::Archive(BMessage* archive) const
+{
+ if (!archive)
+ return B_BAD_VALUE;
+ BMessage dateTimeArchive;
+ status_t status = fDateTime.Archive(&dateTimeArchive);
+ if (status == B_OK)
+ status = archive->AddMessage("date time", &dateTimeArchive);
+ if (status == B_OK)
+ status = archive->AddString("url", fURL.String());
+ if (status == B_OK)
+ status = archive->AddUInt32("invokations", fInvokationCount);
+ return status;
+}
+
+
+BrowsingHistoryItem&
+BrowsingHistoryItem::operator=(const BrowsingHistoryItem& other)
+{
+ if (this == &other)
+ return *this;
+
+ fURL = other.fURL;
+ fDateTime = other.fDateTime;
+ fInvokationCount = other.fInvokationCount;
+
+ return *this;
+}
+
+
+bool
+BrowsingHistoryItem::operator==(const BrowsingHistoryItem& other) const
+{
+ if (this == &other)
+ return true;
+
+ return fURL == other.fURL && fDateTime == other.fDateTime
+ && fInvokationCount == other.fInvokationCount;
+}
+
+
+bool
+BrowsingHistoryItem::operator!=(const BrowsingHistoryItem& other) const
+{
+ return !(*this == other);
+}
+
+
+bool
+BrowsingHistoryItem::operator<(const BrowsingHistoryItem& other) const
+{
+ if (this == &other)
+ return false;
+
+ return fDateTime < other.fDateTime || fURL < other.fURL;
+}
+
+
+bool
+BrowsingHistoryItem::operator<=(const BrowsingHistoryItem& other) const
+{
+ return (*this == other) || (*this < other);
+}
+
+
+bool
+BrowsingHistoryItem::operator>(const BrowsingHistoryItem& other) const
+{
+ if (this == &other)
+ return false;
+
+ return fDateTime > other.fDateTime || fURL > other.fURL;
+}
+
+
+bool
+BrowsingHistoryItem::operator>=(const BrowsingHistoryItem& other) const
+{
+ return (*this == other) || (*this > other);
+}
+
+
+void
+BrowsingHistoryItem::Invoked()
+{
+ // Eventually, we may overflow...
+ uint32 count = fInvokationCount + 1;
+ if (count > fInvokationCount)
+ fInvokationCount = count;
+ fDateTime = BDateTime::CurrentDateTime(B_LOCAL_TIME);
+}
+
+
+// #pragma mark - BrowsingHistory
+
+
+BrowsingHistory
+BrowsingHistory::sDefaultInstance;
+
+
+BrowsingHistory::BrowsingHistory()
+ :
+ BLocker("browsing history"),
+ fHistoryItems(64),
+ fMaxHistoryItemAge(7),
+ fSettingsLoaded(false)
+{
+}
+
+
+BrowsingHistory::~BrowsingHistory()
+{
+ _SaveSettings();
+ _Clear();
+}
+
+
+/*static*/ BrowsingHistory*
+BrowsingHistory::DefaultInstance()
+{
+ if (sDefaultInstance.Lock()) {
+ sDefaultInstance._LoadSettings();
+ sDefaultInstance.Unlock();
+ }
+ return &sDefaultInstance;
+}
+
+
+bool
+BrowsingHistory::AddItem(const BrowsingHistoryItem& item)
+{
+ BAutolock _(this);
+
+ return _AddItem(item, false);
+}
+
+
+int32
+BrowsingHistory::BrowsingHistory::CountItems() const
+{
+ BAutolock _(const_cast<BrowsingHistory*>(this));
+
+ return fHistoryItems.CountItems();
+}
+
+
+BrowsingHistoryItem
+BrowsingHistory::HistoryItemAt(int32 index) const
+{
+ BAutolock _(const_cast<BrowsingHistory*>(this));
+
+ BrowsingHistoryItem* existingItem = reinterpret_cast<BrowsingHistoryItem*>(
+ fHistoryItems.ItemAt(index));
+ if (!existingItem)
+ return BrowsingHistoryItem(BString());
+
+ return BrowsingHistoryItem(*existingItem);
+}
+
+
+void
+BrowsingHistory::Clear()
+{
+ BAutolock _(this);
+ _Clear();
+ _SaveSettings();
+}
+
+
+void
+BrowsingHistory::SetMaxHistoryItemAge(int32 days)
+{
+ BAutolock _(this);
+ if (fMaxHistoryItemAge != days) {
+ fMaxHistoryItemAge = days;
+ _SaveSettings();
+ }
+}
+
+
+int32
+BrowsingHistory::MaxHistoryItemAge() const
+{
+ return fMaxHistoryItemAge;
+}
+
+
+// #pragma mark - private
+
+
+void
+BrowsingHistory::_Clear()
+{
+ int32 count = CountItems();
+ for (int32 i = 0; i < count; i++) {
+ BrowsingHistoryItem* item = reinterpret_cast<BrowsingHistoryItem*>(
+ fHistoryItems.ItemAtFast(i));
+ delete item;
+ }
+ fHistoryItems.MakeEmpty();
+}
+
+
+bool
+BrowsingHistory::_AddItem(const BrowsingHistoryItem& item, bool internal)
+{
+ int32 count = CountItems();
+ int32 insertionIndex = count;
+ for (int32 i = 0; i < count; i++) {
+ BrowsingHistoryItem* existingItem
+ = reinterpret_cast<BrowsingHistoryItem*>(
+ fHistoryItems.ItemAtFast(i));
+ if (item.URL() == existingItem->URL()) {
+ if (!internal) {
+ existingItem->Invoked();
+ _SaveSettings();
+ }
+ return true;
+ }
+ if (item < *existingItem)
+ insertionIndex = i;
+ }
+ BrowsingHistoryItem* newItem = new(std::nothrow) BrowsingHistoryItem(item);
+ if (!newItem || !fHistoryItems.AddItem(newItem, insertionIndex)) {
+ delete newItem;
+ return false;
+ }
+
+ if (!internal) {
+ newItem->Invoked();
+ _SaveSettings();
+ }
+
+ return true;
+}
+
+
+void
+BrowsingHistory::_LoadSettings()
+{
+ if (fSettingsLoaded)
+ return;
+
+ fSettingsLoaded = true;
+
+ BFile settingsFile;
+ if (_OpenSettingsFile(settingsFile, B_READ_ONLY)) {
+ BMessage settingsArchive;
+ settingsArchive.Unflatten(&settingsFile);
+ if (settingsArchive.FindInt32("max history item age",
+ &fMaxHistoryItemAge) != B_OK) {
+ fMaxHistoryItemAge = 7;
+ }
+ BDateTime oldestAllowedDateTime
+ = BDateTime::CurrentDateTime(B_LOCAL_TIME);
+ oldestAllowedDateTime.Date().AddDays(-fMaxHistoryItemAge);
+
+ BMessage historyItemArchive;
+ for (int32 i = 0; settingsArchive.FindMessage("history item", i,
+ &historyItemArchive) == B_OK; i++) {
+ BrowsingHistoryItem item(&historyItemArchive);
+ if (oldestAllowedDateTime < item.DateTime())
+ _AddItem(item, true);
+ historyItemArchive.MakeEmpty();
+ }
+ }
+}
+
+
+void
+BrowsingHistory::_SaveSettings()
+{
+ BFile settingsFile;
+ if (_OpenSettingsFile(settingsFile,
+ B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY)) {
+ BMessage settingsArchive;
+ settingsArchive.AddInt32("max history item age", fMaxHistoryItemAge);
+ BMessage historyItemArchive;
+ int32 count = CountItems();
+ for (int32 i = 0; i < count; i++) {
+ BrowsingHistoryItem item = HistoryItemAt(i);
+ if (item.Archive(&historyItemArchive) != B_OK)
+ break;
+ if (settingsArchive.AddMessage("history item",
+ &historyItemArchive) != B_OK) {
+ break;
+ }
+ historyItemArchive.MakeEmpty();
+ }
+ settingsArchive.Flatten(&settingsFile);
+ }
+}
+
+
+bool
+BrowsingHistory::_OpenSettingsFile(BFile& file, uint32 mode)
+{
+ BPath path;
+ if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
+ || path.Append(kApplicationName) != B_OK
+ || path.Append("BrowsingHistory") != B_OK) {
+ return false;
+ }
+ return file.SetTo(path.Path(), mode) == B_OK;
+}
+
diff --git a/src/apps/webpositive/BrowsingHistory.h b/src/apps/webpositive/BrowsingHistory.h
new file mode 100644
index 0000000000..bf8f7f15b8
--- /dev/null
+++ b/src/apps/webpositive/BrowsingHistory.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+#ifndef BROWSING_HISTORY_H
+#define BROWSING_HISTORY_H
+
+#include "DateTime.h"
+#include <List.h>
+#include <Locker.h>
+
+class BFile;
+class BString;
+
+
+class BrowsingHistoryItem {
+public:
+ BrowsingHistoryItem(const BString& url);
+ BrowsingHistoryItem(
+ const BrowsingHistoryItem& other);
+ BrowsingHistoryItem(const BMessage* archive);
+ ~BrowsingHistoryItem();
+
+ status_t Archive(BMessage* archive) const;
+
+ BrowsingHistoryItem& operator=(const BrowsingHistoryItem& other);
+
+ bool operator==(
+ const BrowsingHistoryItem& other) const;
+ bool operator!=(
+ const BrowsingHistoryItem& other) const;
+ bool operator<(
+ const BrowsingHistoryItem& other) const;
+ bool operator<=(
+ const BrowsingHistoryItem& other) const;
+ bool operator>(
+ const BrowsingHistoryItem& other) const;
+ bool operator>=(
+ const BrowsingHistoryItem& other) const;
+
+ const BString& URL() const { return fURL; }
+ const BDateTime& DateTime() const { return fDateTime; }
+ uint32 InvokationCount() const {
+ return fInvokationCount; }
+ void Invoked();
+
+private:
+ BString fURL;
+ BDateTime fDateTime;
+ uint32 fInvokationCount;
+};
+
+
+class BrowsingHistory : public BLocker {
+public:
+ static BrowsingHistory* DefaultInstance();
+
+ bool AddItem(const BrowsingHistoryItem& item);
+
+ // Should Lock() the object when using these in some loop or so:
+ int32 CountItems() const;
+ BrowsingHistoryItem HistoryItemAt(int32 index) const;
+ void Clear();
+
+ void SetMaxHistoryItemAge(int32 days);
+ int32 MaxHistoryItemAge() const;
+
+private:
+ BrowsingHistory();
+ virtual ~BrowsingHistory();
+
+ void _Clear();
+ bool _AddItem(const BrowsingHistoryItem& item,
+ bool invoke);
+
+ void _LoadSettings();
+ void _SaveSettings();
+ bool _OpenSettingsFile(BFile& file, uint32 mode);
+
+private:
+ BList fHistoryItems;
+ int32 fMaxHistoryItemAge;
+
+ static BrowsingHistory sDefaultInstance;
+ bool fSettingsLoaded;
+};
+
+
+#endif // BROWSING_HISTORY_H
+
diff --git a/src/apps/webpositive/CredentialsStorage.cpp b/src/apps/webpositive/CredentialsStorage.cpp
new file mode 100644
index 0000000000..ab5f8eae8d
--- /dev/null
+++ b/src/apps/webpositive/CredentialsStorage.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "CredentialsStorage.h"
+
+#include <new>
+#include <stdio.h>
+
+#include <Autolock.h>
+#include <Entry.h>
+#include <File.h>
+#include <FindDirectory.h>
+#include <Message.h>
+#include <Path.h>
+
+#include "BrowserApp.h"
+
+
+Credentials::Credentials()
+ :
+ fUsername(),
+ fPassword()
+{
+}
+
+
+Credentials::Credentials(const BString& username, const BString& password)
+ :
+ fUsername(username),
+ fPassword(password)
+{
+}
+
+
+Credentials::Credentials(const Credentials& other)
+{
+ *this = other;
+}
+
+
+Credentials::Credentials(const BMessage* archive)
+{
+ if (archive == NULL)
+ return;
+ archive->FindString("username", &fUsername);
+ archive->FindString("password", &fPassword);
+}
+
+
+Credentials::~Credentials()
+{
+}
+
+
+status_t
+Credentials::Archive(BMessage* archive) const
+{
+ if (archive == NULL)
+ return B_BAD_VALUE;
+ status_t status = archive->AddString("username", fUsername);
+ if (status == B_OK)
+ status = archive->AddString("password", fPassword);
+ return status;
+}
+
+
+Credentials&
+Credentials::operator=(const Credentials& other)
+{
+ if (this == &other)
+ return *this;
+
+ fUsername = other.fUsername;
+ fPassword = other.fPassword;
+
+ return *this;
+}
+
+
+bool
+Credentials::operator==(const Credentials& other) const
+{
+ if (this == &other)
+ return true;
+
+ return fUsername == other.fUsername && fPassword == other.fPassword;
+}
+
+
+bool
+Credentials::operator!=(const Credentials& other) const
+{
+ return !(*this == other);
+}
+
+
+const BString&
+Credentials::Username() const
+{
+ return fUsername;
+}
+
+
+const BString&
+Credentials::Password() const
+{
+ return fPassword;
+}
+
+
+// #pragma mark - CredentialsStorage
+
+
+CredentialsStorage
+CredentialsStorage::sPersistentInstance(true);
+
+
+CredentialsStorage
+CredentialsStorage::sSessionInstance(false);
+
+
+CredentialsStorage::CredentialsStorage(bool persistent)
+ :
+ BLocker(persistent ? "persistent credential storage"
+ : "credential storage"),
+ fCredentialMap(),
+ fSettingsLoaded(false),
+ fPersistent(persistent)
+{
+}
+
+
+CredentialsStorage::~CredentialsStorage()
+{
+ _SaveSettings();
+}
+
+
+/*static*/ CredentialsStorage*
+CredentialsStorage::SessionInstance()
+{
+ return &sSessionInstance;
+}
+
+
+/*static*/ CredentialsStorage*
+CredentialsStorage::PersistentInstance()
+{
+ if (sPersistentInstance.Lock()) {
+ sPersistentInstance._LoadSettings();
+ sPersistentInstance.Unlock();
+ }
+ return &sPersistentInstance;
+}
+
+
+bool
+CredentialsStorage::Contains(const HashKeyString& key)
+{
+ BAutolock _(this);
+
+ return fCredentialMap.ContainsKey(key);
+}
+
+
+status_t
+CredentialsStorage::PutCredentials(const HashKeyString& key,
+ const Credentials& credentials)
+{
+ BAutolock _(this);
+
+ return fCredentialMap.Put(key, credentials);
+}
+
+
+Credentials
+CredentialsStorage::GetCredentials(const HashKeyString& key)
+{
+ BAutolock _(this);
+
+ return fCredentialMap.Get(key);
+}
+
+
+// #pragma mark - private
+
+
+void
+CredentialsStorage::_LoadSettings()
+{
+ if (!fPersistent || fSettingsLoaded)
+ return;
+
+ fSettingsLoaded = true;
+
+ BFile settingsFile;
+ if (_OpenSettingsFile(settingsFile, B_READ_ONLY)) {
+ BMessage settingsArchive;
+ settingsArchive.Unflatten(&settingsFile);
+ BMessage credentialsArchive;
+ for (int32 i = 0; settingsArchive.FindMessage("credentials", i,
+ &credentialsArchive) == B_OK; i++) {
+ BString key;
+ if (credentialsArchive.FindString("key", &key) == B_OK) {
+ Credentials credentials(&credentialsArchive);
+ fCredentialMap.Put(key, credentials);
+ }
+ }
+ }
+}
+
+
+void
+CredentialsStorage::_SaveSettings() const
+{
+ BFile settingsFile;
+ if (_OpenSettingsFile(settingsFile,
+ B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY)) {
+ BMessage settingsArchive;
+ BMessage credentialsArchive;
+ CredentialMap::Iterator iterator = fCredentialMap.GetIterator();
+ while (iterator.HasNext()) {
+ const CredentialMap::Entry& entry = iterator.Next();
+ if (entry.value.Archive(&credentialsArchive) != B_OK
+ || credentialsArchive.AddString("key",
+ entry.key.value) != B_OK) {
+ break;
+ }
+ if (settingsArchive.AddMessage("credentials",
+ &credentialsArchive) != B_OK) {
+ break;
+ }
+ credentialsArchive.MakeEmpty();
+ }
+ settingsArchive.Flatten(&settingsFile);
+ }
+}
+
+
+bool
+CredentialsStorage::_OpenSettingsFile(BFile& file, uint32 mode) const
+{
+ BPath path;
+ if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
+ || path.Append(kApplicationName) != B_OK
+ || path.Append("CredentialsStorage") != B_OK) {
+ return false;
+ }
+ return file.SetTo(path.Path(), mode) == B_OK;
+}
+
diff --git a/src/apps/webpositive/CredentialsStorage.h b/src/apps/webpositive/CredentialsStorage.h
new file mode 100644
index 0000000000..214c36b371
--- /dev/null
+++ b/src/apps/webpositive/CredentialsStorage.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+#ifndef CREDENTIAL_STORAGE_H
+#define CREDENTIAL_STORAGE_H
+
+#include "HashKeys.h"
+#include "HashMap.h"
+#include <Locker.h>
+#include <String.h>
+
+
+class BFile;
+class BMessage;
+class BString;
+
+
+class Credentials {
+public:
+ Credentials();
+ Credentials(const BString& username,
+ const BString& password);
+ Credentials(
+ const Credentials& other);
+ Credentials(const BMessage* archive);
+ ~Credentials();
+
+ status_t Archive(BMessage* archive) const;
+
+ Credentials& operator=(const Credentials& other);
+
+ bool operator==(const Credentials& other) const;
+ bool operator!=(const Credentials& other) const;
+
+ const BString& Username() const;
+ const BString& Password() const;
+
+private:
+ BString fUsername;
+ BString fPassword;
+};
+
+
+class CredentialsStorage : public BLocker {
+public:
+ static CredentialsStorage* SessionInstance();
+ static CredentialsStorage* PersistentInstance();
+
+ bool Contains(const HashKeyString& key);
+ status_t PutCredentials(const HashKeyString& key,
+ const Credentials& credentials);
+ Credentials GetCredentials(const HashKeyString& key);
+
+private:
+ CredentialsStorage(bool persistent);
+ virtual ~CredentialsStorage();
+
+ void _LoadSettings();
+ void _SaveSettings() const;
+ bool _OpenSettingsFile(BFile& file,
+ uint32 mode) const;
+
+private:
+ typedef HashMap<HashKeyString, Credentials> CredentialMap;
+ CredentialMap fCredentialMap;
+
+ static CredentialsStorage sPersistentInstance;
+ static CredentialsStorage sSessionInstance;
+ bool fSettingsLoaded;
+ bool fPersistent;
+};
+
+
+#endif // CREDENTIAL_STORAGE_H
+
diff --git a/src/apps/webpositive/DownloadProgressView.cpp b/src/apps/webpositive/DownloadProgressView.cpp
new file mode 100644
index 0000000000..7031e429c3
--- /dev/null
+++ b/src/apps/webpositive/DownloadProgressView.cpp
@@ -0,0 +1,838 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "DownloadProgressView.h"
+
+#include <stdio.h>
+
+#include <Alert.h>
+#include <Bitmap.h>
+#include <Button.h>
+#include <Clipboard.h>
+#include <Directory.h>
+#include <Entry.h>
+#include <FindDirectory.h>
+#include <GroupLayoutBuilder.h>
+#include <MenuItem.h>
+#include <NodeInfo.h>
+#include <NodeMonitor.h>
+#include <PopUpMenu.h>
+#include <Roster.h>
+#include <SpaceLayoutItem.h>
+#include <StatusBar.h>
+#include <StringView.h>
+
+#include "WebDownload.h"
+#include "WebPage.h"
+#include "StringForSize.h"
+
+
+enum {
+ OPEN_DOWNLOAD = 'opdn',
+ RESTART_DOWNLOAD = 'rsdn',
+ CANCEL_DOWNLOAD = 'cndn',
+ REMOVE_DOWNLOAD = 'rmdn',
+ COPY_URL_TO_CLIPBOARD = 'curl',
+ OPEN_CONTAINING_FOLDER = 'opfd',
+};
+
+const bigtime_t kMaxUpdateInterval = 100000LL;
+const bigtime_t kSpeedReferenceInterval = 500000LL;
+const bigtime_t kShowSpeedInterval = 8000000LL;
+const bigtime_t kShowEstimatedFinishInterval = 4000000LL;
+
+bigtime_t DownloadProgressView::sLastEstimatedFinishSpeedToggleTime = -1;
+bool DownloadProgressView::sShowSpeed = true;
+
+
+class IconView : public BView {
+public:
+ IconView(const BEntry& entry)
+ :
+ BView("Download icon", B_WILL_DRAW),
+ fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32),
+ fDimmedIcon(false)
+ {
+ SetDrawingMode(B_OP_OVER);
+ SetTo(entry);
+ }
+
+ IconView()
+ :
+ BView("Download icon", B_WILL_DRAW),
+ fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32),
+ fDimmedIcon(false)
+ {
+ SetDrawingMode(B_OP_OVER);
+ memset(fIconBitmap.Bits(), 0, fIconBitmap.BitsLength());
+ }
+
+ IconView(BMessage* archive)
+ :
+ BView("Download icon", B_WILL_DRAW),
+ fIconBitmap(archive),
+ fDimmedIcon(true)
+ {
+ SetDrawingMode(B_OP_OVER);
+ }
+
+ void SetTo(const BEntry& entry)
+ {
+ BNode node(&entry);
+ BNodeInfo info(&node);
+ info.GetTrackerIcon(&fIconBitmap, B_LARGE_ICON);
+ Invalidate();
+ }
+
+ void SetIconDimmed(bool iconDimmed)
+ {
+ if (fDimmedIcon != iconDimmed) {
+ fDimmedIcon = iconDimmed;
+ Invalidate();
+ }
+ }
+
+ bool IsIconDimmed() const
+ {
+ return fDimmedIcon;
+ }
+
+ status_t SaveSettings(BMessage* archive)
+ {
+ return fIconBitmap.Archive(archive);
+ }
+
+ virtual void AttachedToWindow()
+ {
+ SetViewColor(Parent()->ViewColor());
+ }
+
+ virtual void Draw(BRect updateRect)
+ {
+ if (fDimmedIcon) {
+ SetDrawingMode(B_OP_ALPHA);
+ SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
+ SetHighColor(0, 0, 0, 100);
+ }
+ DrawBitmapAsync(&fIconBitmap);
+ }
+
+ virtual BSize MinSize()
+ {
+ return BSize(fIconBitmap.Bounds().Width(), fIconBitmap.Bounds().Height());
+ }
+
+ virtual BSize PreferredSize()
+ {
+ return MinSize();
+ }
+
+ virtual BSize MaxSize()
+ {
+ return MinSize();
+ }
+
+private:
+ BBitmap fIconBitmap;
+ bool fDimmedIcon;
+};
+
+
+class SmallButton : public BButton {
+public:
+ SmallButton(const char* label, BMessage* message = NULL)
+ :
+ BButton(label, message)
+ {
+ BFont font;
+ GetFont(&font);
+ float size = ceilf(font.Size() * 0.8);
+ font.SetSize(max_c(8, size));
+ SetFont(&font, B_FONT_SIZE);
+ }
+};
+
+
+// #pragma mark - DownloadProgressView
+
+
+DownloadProgressView::DownloadProgressView(BWebDownload* download)
+ :
+ BGroupView(B_HORIZONTAL, 8),
+ fDownload(download),
+ fURL(download->URL()),
+ fPath(download->Path())
+{
+}
+
+
+DownloadProgressView::DownloadProgressView(const BMessage* archive)
+ :
+ BGroupView(B_HORIZONTAL, 8),
+ fDownload(NULL),
+ fURL(),
+ fPath()
+{
+ const char* string;
+ if (archive->FindString("path", &string) == B_OK)
+ fPath.SetTo(string);
+ if (archive->FindString("url", &string) == B_OK)
+ fURL = string;
+}
+
+
+bool
+DownloadProgressView::Init(BMessage* archive)
+{
+ fCurrentSize = 0;
+ fExpectedSize = 0;
+ fLastUpdateTime = 0;
+ fBytesPerSecond = 0.0;
+ for (size_t i = 0; i < kBytesPerSecondSlots; i++)
+ fBytesPerSecondSlot[i] = 0.0;
+ fCurrentBytesPerSecondSlot = 0;
+ fLastSpeedReferenceSize = 0;
+ fEstimatedFinishReferenceSize = 0;
+
+ fProcessStartTime = fLastSpeedReferenceTime = fEstimatedFinishReferenceTime
+ = system_time();
+
+ SetViewColor(245, 245, 245);
+ SetFlags(Flags() | B_FULL_UPDATE_ON_RESIZE | B_WILL_DRAW);
+
+ if (archive) {
+ fStatusBar = new BStatusBar("download progress", fPath.Leaf());
+ float value;
+ if (archive->FindFloat("value", &value) == B_OK)
+ fStatusBar->SetTo(value);
+ } else
+ fStatusBar = new BStatusBar("download progress", "Download");
+ fStatusBar->SetMaxValue(100);
+ fStatusBar->SetBarHeight(12);
+
+ // fPath is only valid when constructed from archive (fDownload == NULL)
+ BEntry entry(fPath.Path());
+
+ if (archive) {
+ if (!entry.Exists())
+ fIconView = new IconView(archive);
+ else
+ fIconView = new IconView(entry);
+ } else
+ fIconView = new IconView();
+
+ if (!fDownload && (fStatusBar->CurrentValue() < 100 || !entry.Exists()))
+ fTopButton = new SmallButton("Restart", new BMessage(RESTART_DOWNLOAD));
+ else {
+ fTopButton = new SmallButton("Open", new BMessage(OPEN_DOWNLOAD));
+ fTopButton->SetEnabled(fDownload == NULL);
+ }
+ if (fDownload)
+ fBottomButton = new SmallButton("Cancel", new BMessage(CANCEL_DOWNLOAD));
+ else {
+ fBottomButton = new SmallButton("Remove", new BMessage(REMOVE_DOWNLOAD));
+ fBottomButton->SetEnabled(fDownload == NULL);
+ }
+
+ fInfoView = new BStringView("info view", "");
+
+ BGroupLayout* layout = GroupLayout();
+ layout->SetInsets(8, 5, 5, 6);
+ layout->AddView(fIconView);
+ BView* verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3)
+ .Add(fStatusBar)
+ .Add(fInfoView)
+ .TopView()
+ ;
+ verticalGroup->SetViewColor(ViewColor());
+ layout->AddView(verticalGroup);
+ verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3)
+ .Add(fTopButton)
+ .Add(fBottomButton)
+ .TopView()
+ ;
+ verticalGroup->SetViewColor(ViewColor());
+ layout->AddView(verticalGroup);
+
+ BFont font;
+ fInfoView->GetFont(&font);
+ float fontSize = font.Size() * 0.8f;
+ font.SetSize(max_c(8.0f, fontSize));
+ fInfoView->SetFont(&font, B_FONT_SIZE);
+ fInfoView->SetHighColor(tint_color(fInfoView->LowColor(),
+ B_DARKEN_4_TINT));
+ fInfoView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
+
+ return true;
+}
+
+
+status_t
+DownloadProgressView::SaveSettings(BMessage* archive)
+{
+ if (!archive)
+ return B_BAD_VALUE;
+ status_t ret = archive->AddString("path", fPath.Path());
+ if (ret == B_OK)
+ ret = archive->AddString("url", fURL.String());
+ if (ret == B_OK)
+ ret = archive->AddFloat("value", fStatusBar->CurrentValue());
+ if (ret == B_OK)
+ ret = fIconView->SaveSettings(archive);
+ return ret;
+}
+
+
+void
+DownloadProgressView::AttachedToWindow()
+{
+ if (fDownload) {
+ fDownload->SetProgressListener(BMessenger(this));
+ // Will start node monitor upon receiving the B_DOWNLOAD_STARTED
+ // message.
+ } else {
+ BEntry entry(fPath.Path());
+ if (entry.Exists())
+ _StartNodeMonitor(entry);
+ }
+
+ fTopButton->SetTarget(this);
+ fBottomButton->SetTarget(this);
+}
+
+
+void
+DownloadProgressView::DetachedFromWindow()
+{
+ _StopNodeMonitor();
+}
+
+
+void
+DownloadProgressView::AllAttached()
+{
+ SetViewColor(B_TRANSPARENT_COLOR);
+ SetLowColor(245, 245, 245);
+ SetHighColor(tint_color(LowColor(), B_DARKEN_1_TINT));
+}
+
+
+void
+DownloadProgressView::Draw(BRect updateRect)
+{
+ BRect bounds(Bounds());
+ bounds.bottom--;
+ FillRect(bounds, B_SOLID_LOW);
+ bounds.bottom++;
+ StrokeLine(bounds.LeftBottom(), bounds.RightBottom());
+}
+
+
+void
+DownloadProgressView::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ case B_DOWNLOAD_STARTED:
+ {
+ BString path;
+ if (message->FindString("path", &path) != B_OK)
+ break;
+ fPath.SetTo(path);
+ BEntry entry(fPath.Path());
+ fIconView->SetTo(entry);
+ fStatusBar->Reset(fPath.Leaf());
+ _StartNodeMonitor(entry);
+
+ // Immediately switch to speed display whenever a new download
+ // starts.
+ sShowSpeed = true;
+ sLastEstimatedFinishSpeedToggleTime
+ = fProcessStartTime = fLastSpeedReferenceTime
+ = fEstimatedFinishReferenceTime = system_time();
+ break;
+ }
+ case B_DOWNLOAD_PROGRESS:
+ {
+ int64 currentSize;
+ int64 expectedSize;
+ if (message->FindInt64("current size", &currentSize) == B_OK
+ && message->FindInt64("expected size", &expectedSize) == B_OK) {
+ _UpdateStatus(currentSize, expectedSize);
+ }
+ break;
+ }
+ case B_DOWNLOAD_REMOVED:
+ // TODO: This is a bit asymetric. The removed notification
+ // arrives here, but it would be nicer if it arrived
+ // at the window...
+ Window()->PostMessage(message);
+ break;
+ case OPEN_DOWNLOAD:
+ {
+ // TODO: In case of executable files, ask the user first!
+ entry_ref ref;
+ status_t status = get_ref_for_path(fPath.Path(), &ref);
+ if (status == B_OK)
+ status = be_roster->Launch(&ref);
+ if (status != B_OK && status != B_ALREADY_RUNNING) {
+ BAlert* alert = new BAlert("Open download error",
+ "The download could not be opened.", "OK");
+ alert->Go(NULL);
+ }
+ break;
+ }
+ case RESTART_DOWNLOAD:
+ BWebPage::RequestDownload(fURL);
+ break;
+
+ case CANCEL_DOWNLOAD:
+ fDownload->Cancel();
+ DownloadCanceled();
+ break;
+
+ case REMOVE_DOWNLOAD:
+ {
+ Window()->PostMessage(SAVE_SETTINGS);
+ RemoveSelf();
+ delete this;
+ // TOAST!
+ return;
+ }
+ case B_NODE_MONITOR:
+ {
+ int32 opCode;
+ if (message->FindInt32("opcode", &opCode) != B_OK)
+ break;
+ switch (opCode) {
+ case B_ENTRY_REMOVED:
+ fIconView->SetIconDimmed(true);
+ DownloadCanceled();
+ break;
+ case B_ENTRY_MOVED:
+ {
+ // Follow the entry to the new location
+ dev_t device;
+ ino_t directory;
+ const char* name;
+ if (message->FindInt32("device",
+ reinterpret_cast<int32*>(&device)) != B_OK
+ || message->FindInt64("to directory",
+ reinterpret_cast<int64*>(&directory)) != B_OK
+ || message->FindString("name", &name) != B_OK
+ || strlen(name) == 0) {
+ break;
+ }
+ // Construct the BEntry and update fPath
+ entry_ref ref(device, directory, name);
+ BEntry entry(&ref);
+ if (entry.GetPath(&fPath) != B_OK)
+ break;
+
+ // Find out if the directory is the Trash for this
+ // volume
+ char trashPath[B_PATH_NAME_LENGTH];
+ if (find_directory(B_TRASH_DIRECTORY, device, false,
+ trashPath, B_PATH_NAME_LENGTH) == B_OK) {
+ BPath trashDirectory(trashPath);
+ BPath parentDirectory;
+ fPath.GetParent(&parentDirectory);
+ if (parentDirectory == trashDirectory) {
+ // The entry was moved into the Trash.
+ // If the download is still in progress,
+ // cancel it.
+ if (fDownload)
+ fDownload->Cancel();
+ fIconView->SetIconDimmed(true);
+ DownloadCanceled();
+ break;
+ } else if (fIconView->IsIconDimmed()) {
+ // Maybe it was moved out of the trash.
+ fIconView->SetIconDimmed(false);
+ }
+ }
+
+ // Inform download of the new path
+ if (fDownload)
+ fDownload->HasMovedTo(fPath);
+
+ float value = fStatusBar->CurrentValue();
+ fStatusBar->Reset(name);
+ fStatusBar->SetTo(value);
+ Window()->PostMessage(SAVE_SETTINGS);
+ break;
+ }
+ case B_ATTR_CHANGED:
+ {
+ BEntry entry(fPath.Path());
+ fIconView->SetIconDimmed(false);
+ fIconView->SetTo(entry);
+ break;
+ }
+ }
+ break;
+ }
+
+ // Context menu messages
+ case COPY_URL_TO_CLIPBOARD:
+ if (be_clipboard->Lock()) {
+ BMessage* data = be_clipboard->Data();
+ if (data != NULL) {
+ be_clipboard->Clear();
+ data->AddData("text/plain", B_MIME_TYPE, fURL.String(),
+ fURL.Length());
+ }
+ be_clipboard->Commit();
+ be_clipboard->Unlock();
+ }
+ break;
+ case OPEN_CONTAINING_FOLDER:
+ if (fPath.InitCheck() == B_OK) {
+ BPath containingFolder;
+ if (fPath.GetParent(&containingFolder) != B_OK)
+ break;
+ BEntry entry(containingFolder.Path());
+ if (!entry.Exists())
+ break;
+ entry_ref ref;
+ if (entry.GetRef(&ref) != B_OK)
+ break;
+ be_roster->Launch(&ref);
+
+ // Use Tracker scripting and select the download pose
+ // in the window.
+ // TODO: We should somehow get the window that just openend.
+ // Using the name like this is broken when there are multiple
+ // windows open with this name. Also Tracker does not scroll
+ // to this entry.
+ BString windowName = ref.name;
+ BString fullWindowName = containingFolder.Path();
+
+ BMessenger trackerMessenger("application/x-vnd.Be-TRAK");
+ if (trackerMessenger.IsValid()
+ && get_ref_for_path(fPath.Path(), &ref) == B_OK) {
+ // We need to wait a bit until the folder is open.
+ // TODO: This is also too fragile... we should be able
+ // to wait for the roster message.
+ snooze(250000);
+ int32 tries = 2;
+ while (tries > 0) {
+ BMessage selectionCommand(B_SET_PROPERTY);
+ selectionCommand.AddSpecifier("Selection");
+ selectionCommand.AddSpecifier("Poses");
+ selectionCommand.AddSpecifier("Window",
+ windowName.String());
+ selectionCommand.AddRef("data", &ref);
+ BMessage reply;
+ trackerMessenger.SendMessage(&selectionCommand, &reply);
+ int32 error;
+ if (reply.FindInt32("error", &error) != B_OK
+ || error == B_OK) {
+ break;
+ }
+ windowName = fullWindowName;
+ tries--;
+ }
+ }
+ }
+ break;
+
+ default:
+ BGroupView::MessageReceived(message);
+ }
+}
+
+
+void
+DownloadProgressView::ShowContextMenu(BPoint screenWhere)
+{
+ screenWhere += BPoint(2, 2);
+
+ BPopUpMenu* contextMenu = new BPopUpMenu("download context");
+ BMenuItem* copyURL = new BMenuItem("Copy URL to clipboard",
+ new BMessage(COPY_URL_TO_CLIPBOARD));
+ copyURL->SetEnabled(fURL.Length() > 0);
+ contextMenu->AddItem(copyURL);
+ BMenuItem* openFolder = new BMenuItem("Open containing folder",
+ new BMessage(OPEN_CONTAINING_FOLDER));
+ contextMenu->AddItem(openFolder);
+
+ contextMenu->SetTargetForItems(this);
+ contextMenu->Go(screenWhere, true, true, true);
+}
+
+
+BWebDownload*
+DownloadProgressView::Download() const
+{
+ return fDownload;
+}
+
+
+const BString&
+DownloadProgressView::URL() const
+{
+ return fURL;
+}
+
+
+bool
+DownloadProgressView::IsMissing() const
+{
+ return fIconView->IsIconDimmed();
+}
+
+
+bool
+DownloadProgressView::IsFinished() const
+{
+ return !fDownload && fStatusBar->CurrentValue() == 100;
+}
+
+
+void
+DownloadProgressView::DownloadFinished()
+{
+ fDownload = NULL;
+ if (fExpectedSize == -1) {
+ fStatusBar->SetTo(100.0);
+ fExpectedSize = fCurrentSize;
+ }
+ fTopButton->SetEnabled(true);
+ fBottomButton->SetLabel("Remove");
+ fBottomButton->SetMessage(new BMessage(REMOVE_DOWNLOAD));
+ fBottomButton->SetEnabled(true);
+ fInfoView->SetText("");
+}
+
+
+void
+DownloadProgressView::DownloadCanceled()
+{
+ fDownload = NULL;
+ fTopButton->SetLabel("Restart");
+ fTopButton->SetMessage(new BMessage(RESTART_DOWNLOAD));
+ fTopButton->SetEnabled(true);
+ fBottomButton->SetLabel("Remove");
+ fBottomButton->SetMessage(new BMessage(REMOVE_DOWNLOAD));
+ fBottomButton->SetEnabled(true);
+ fInfoView->SetText("");
+ fPath.Unset();
+}
+
+
+/*static*/ void
+DownloadProgressView::SpeedVersusEstimatedFinishTogglePulse()
+{
+ bigtime_t now = system_time();
+ if (sShowSpeed
+ && sLastEstimatedFinishSpeedToggleTime + kShowSpeedInterval
+ <= now) {
+ sShowSpeed = false;
+ sLastEstimatedFinishSpeedToggleTime = now;
+ } else if (!sShowSpeed
+ && sLastEstimatedFinishSpeedToggleTime
+ + kShowEstimatedFinishInterval <= now) {
+ sShowSpeed = true;
+ sLastEstimatedFinishSpeedToggleTime = now;
+ }
+}
+
+
+// #pragma mark - private
+
+
+void
+DownloadProgressView::_UpdateStatus(off_t currentSize, off_t expectedSize)
+{
+ fCurrentSize = currentSize;
+ fExpectedSize = expectedSize;
+
+ fStatusBar->SetTo(100.0 * currentSize / expectedSize);
+
+ bigtime_t currentTime = system_time();
+ if ((currentTime - fLastUpdateTime) > kMaxUpdateInterval) {
+ fLastUpdateTime = currentTime;
+
+ if (currentTime >= fLastSpeedReferenceTime + kSpeedReferenceInterval) {
+ // update current speed every kSpeedReferenceInterval
+ fCurrentBytesPerSecondSlot
+ = (fCurrentBytesPerSecondSlot + 1) % kBytesPerSecondSlots;
+ fBytesPerSecondSlot[fCurrentBytesPerSecondSlot]
+ = (double)(currentSize - fLastSpeedReferenceSize)
+ * 1000000LL / (currentTime - fLastSpeedReferenceTime);
+ fLastSpeedReferenceSize = currentSize;
+ fLastSpeedReferenceTime = currentTime;
+ fBytesPerSecond = 0.0;
+ size_t count = 0;
+ for (size_t i = 0; i < kBytesPerSecondSlots; i++) {
+ if (fBytesPerSecondSlot[i] != 0.0) {
+ fBytesPerSecond += fBytesPerSecondSlot[i];
+ count++;
+ }
+ }
+ if (count > 0)
+ fBytesPerSecond /= count;
+ }
+ _UpdateStatusText();
+ }
+}
+
+
+void
+DownloadProgressView::_UpdateStatusText()
+{
+ fInfoView->SetText("");
+ BString buffer;
+ if (sShowSpeed && fBytesPerSecond != 0.0) {
+ // Draw speed info
+ char sizeBuffer[128];
+ buffer = "(";
+ // Get strings for current and expected size and remove the unit
+ // from the current size string if it's the same as the expected
+ // size unit.
+ BString currentSize = string_for_size((double)fCurrentSize, sizeBuffer,
+ sizeof(sizeBuffer));
+ BString expectedSize = string_for_size((double)fExpectedSize, sizeBuffer,
+ sizeof(sizeBuffer));
+ int currentSizeUnitPos = currentSize.FindLast(' ');
+ int expectedSizeUnitPos = expectedSize.FindLast(' ');
+ if (currentSizeUnitPos >= 0 && expectedSizeUnitPos >= 0
+ && strcmp(currentSize.String() + currentSizeUnitPos,
+ expectedSize.String() + expectedSizeUnitPos) == 0) {
+ currentSize.Truncate(currentSizeUnitPos);
+ }
+ buffer << currentSize;
+ buffer << " of ";
+ buffer << expectedSize;
+ buffer << ", ";
+ buffer << string_for_size(fBytesPerSecond, sizeBuffer,
+ sizeof(sizeBuffer));
+ buffer << "/s)";
+ float stringWidth = fInfoView->StringWidth(buffer.String());
+ if (stringWidth < fInfoView->Bounds().Width())
+ fInfoView->SetText(buffer.String());
+ else {
+ // complete string too wide, try with shorter version
+ buffer << string_for_size(fBytesPerSecond, sizeBuffer,
+ sizeof(sizeBuffer));
+ buffer << "/s";
+ stringWidth = fInfoView->StringWidth(buffer.String());
+ if (stringWidth < fInfoView->Bounds().Width())
+ fInfoView->SetText(buffer.String());
+ }
+ } else if (!sShowSpeed && fCurrentSize < fExpectedSize) {
+ double totalBytesPerSecond = (double)(fCurrentSize
+ - fEstimatedFinishReferenceSize)
+ * 1000000LL / (system_time() - fEstimatedFinishReferenceTime);
+ double secondsRemaining = (fExpectedSize - fCurrentSize)
+ / totalBytesPerSecond;
+ time_t now = (time_t)real_time_clock();
+ time_t finishTime = (time_t)(now + secondsRemaining);
+
+ tm _time;
+ tm* time = localtime_r(&finishTime, &_time);
+ int32 year = time->tm_year + 1900;
+
+ char timeText[32];
+ time_t secondsPerDay = 24 * 60 * 60;
+ // TODO: Localization of time string...
+ if (now < finishTime - secondsPerDay) {
+ // process is going to take more than a day!
+ sprintf(timeText, "%0*d:%0*d %0*d/%0*d/%ld",
+ 2, time->tm_hour, 2, time->tm_min,
+ 2, time->tm_mon + 1, 2, time->tm_mday, year);
+ } else {
+ sprintf(timeText, "%0*d:%0*d",
+ 2, time->tm_hour, 2, time->tm_min);
+ }
+
+ BString buffer1("Finish: ");
+ buffer1 << timeText;
+ finishTime -= now;
+ time = gmtime(&finishTime);
+
+ BString buffer2;
+ if (finishTime > secondsPerDay) {
+ int64 days = finishTime / secondsPerDay;
+ if (days == 1)
+ buffer2 << "Over 1 day";
+ else
+ buffer2 << "Over " << days << " days";
+ } else if (finishTime > 60 * 60) {
+ int64 hours = finishTime / (60 * 60);
+ if (hours == 1)
+ buffer2 << "Over 1 hour";
+ else
+ buffer2 << "Over " << hours << " hours";
+ } else if (finishTime > 60) {
+ int64 minutes = finishTime / 60;
+ if (minutes == 1)
+ buffer2 << "Over 1 minute";
+ else
+ buffer2 << minutes << " minutes";
+ } else {
+ if (finishTime == 1)
+ buffer2 << "1 second";
+ else
+ buffer2 << finishTime << " seconds";
+ }
+
+ buffer2 << " left";
+
+ buffer = "(";
+ buffer << buffer1 << " - " << buffer2 << ")";
+
+ float stringWidth = fInfoView->StringWidth(buffer.String());
+ if (stringWidth < fInfoView->Bounds().Width())
+ fInfoView->SetText(buffer.String());
+ else {
+ // complete string too wide, try with shorter version
+ buffer = "(";
+ buffer << buffer1 << ")";
+ stringWidth = fInfoView->StringWidth(buffer.String());
+ if (stringWidth < fInfoView->Bounds().Width())
+ fInfoView->SetText(buffer.String());
+ }
+ }
+}
+
+
+void
+DownloadProgressView::_StartNodeMonitor(const BEntry& entry)
+{
+ node_ref nref;
+ if (entry.GetNodeRef(&nref) == B_OK)
+ watch_node(&nref, B_WATCH_ALL, this);
+}
+
+
+void
+DownloadProgressView::_StopNodeMonitor()
+{
+ stop_watching(this);
+}
+
diff --git a/src/apps/webpositive/DownloadProgressView.h b/src/apps/webpositive/DownloadProgressView.h
new file mode 100644
index 0000000000..32a021b169
--- /dev/null
+++ b/src/apps/webpositive/DownloadProgressView.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+#ifndef DOWNLOAD_PROGRESS_VIEW_H
+#define DOWNLOAD_PROGRESS_VIEW_H
+
+
+#include <GroupView.h>
+#include <Path.h>
+#include <String.h>
+
+class BEntry;
+class BStatusBar;
+class BStringView;
+class BWebDownload;
+class IconView;
+class SmallButton;
+
+
+enum {
+ SAVE_SETTINGS = 'svst'
+};
+
+
+class DownloadProgressView : public BGroupView {
+public:
+ DownloadProgressView(BWebDownload* download);
+ DownloadProgressView(const BMessage* archive);
+
+ bool Init(BMessage* archive = NULL);
+
+ status_t SaveSettings(BMessage* archive);
+ virtual void AttachedToWindow();
+ virtual void DetachedFromWindow();
+ virtual void AllAttached();
+
+ virtual void Draw(BRect updateRect);
+
+ virtual void MessageReceived(BMessage* message);
+
+ void ShowContextMenu(BPoint screenWhere);
+
+ BWebDownload* Download() const;
+ const BString& URL() const;
+ bool IsMissing() const;
+ bool IsFinished() const;
+
+ void DownloadFinished();
+ void DownloadCanceled();
+
+ static void SpeedVersusEstimatedFinishTogglePulse();
+
+private:
+ void _UpdateStatus(off_t currentSize,
+ off_t expectedSize);
+ void _UpdateStatusText();
+ void _StartNodeMonitor(const BEntry& entry);
+ void _StopNodeMonitor();
+
+private:
+ IconView* fIconView;
+ BStatusBar* fStatusBar;
+ BStringView* fInfoView;
+ SmallButton* fTopButton;
+ SmallButton* fBottomButton;
+ BWebDownload* fDownload;
+ BString fURL;
+ BPath fPath;
+
+ off_t fCurrentSize;
+ off_t fExpectedSize;
+ off_t fLastSpeedReferenceSize;
+ off_t fEstimatedFinishReferenceSize;
+ bigtime_t fLastUpdateTime;
+ bigtime_t fLastSpeedReferenceTime;
+ bigtime_t fProcessStartTime;
+ bigtime_t fLastSpeedUpdateTime;
+ bigtime_t fEstimatedFinishReferenceTime;
+ static const size_t kBytesPerSecondSlots = 10;
+ size_t fCurrentBytesPerSecondSlot;
+ double fBytesPerSecondSlot[kBytesPerSecondSlots];
+ double fBytesPerSecond;
+
+ static bigtime_t sLastEstimatedFinishSpeedToggleTime;
+ static bool sShowSpeed;
+};
+
+#endif // DOWNLOAD_PROGRESS_VIEW_H
diff --git a/src/apps/webpositive/DownloadWindow.cpp b/src/apps/webpositive/DownloadWindow.cpp
new file mode 100644
index 0000000000..859722df47
--- /dev/null
+++ b/src/apps/webpositive/DownloadWindow.cpp
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "DownloadWindow.h"
+
+#include <stdio.h>
+
+#include <Alert.h>
+#include <Button.h>
+#include <ControlLook.h>
+#include <Entry.h>
+#include <File.h>
+#include <FindDirectory.h>
+#include <GroupLayout.h>
+#include <GroupLayoutBuilder.h>
+#include <MenuBar.h>
+#include <MenuItem.h>
+#include <Path.h>
+#include <Roster.h>
+#include <ScrollView.h>
+#include <SeparatorView.h>
+#include <SpaceLayoutItem.h>
+
+#include "BrowserApp.h"
+#include "BrowserWindow.h"
+#include "DownloadProgressView.h"
+#include "SettingsKeys.h"
+#include "SettingsMessage.h"
+#include "WebDownload.h"
+#include "WebPage.h"
+
+
+enum {
+ INIT = 'init',
+ OPEN_DOWNLOADS_FOLDER = 'odnf',
+ REMOVE_FINISHED_DOWNLOADS = 'rmfd',
+ REMOVE_MISSING_DOWNLOADS = 'rmmd'
+};
+
+
+class DownloadsContainerView : public BGroupView {
+public:
+ DownloadsContainerView()
+ :
+ BGroupView(B_VERTICAL, 0.0)
+ {
+ SetFlags(Flags() | B_PULSE_NEEDED);
+ SetViewColor(245, 245, 245);
+ AddChild(BSpaceLayoutItem::CreateGlue());
+ }
+
+ virtual BSize MinSize()
+ {
+ BSize minSize = BGroupView::MinSize();
+ return BSize(minSize.width, 80);
+ }
+
+ virtual void Pulse()
+ {
+ DownloadProgressView::SpeedVersusEstimatedFinishTogglePulse();
+ }
+
+protected:
+ virtual void DoLayout()
+ {
+ BGroupView::DoLayout();
+ if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) {
+ BSize minSize = BGroupView::MinSize();
+ float height = Bounds().Height();
+ float max = minSize.height - height;
+ scrollBar->SetRange(0, max);
+ if (minSize.height > 0)
+ scrollBar->SetProportion(height / minSize.height);
+ else
+ scrollBar->SetProportion(1);
+ }
+ }
+};
+
+
+class DownloadContainerScrollView : public BScrollView {
+public:
+ DownloadContainerScrollView(BView* target)
+ :
+ BScrollView("Downloads scroll view", target, 0, false, true,
+ B_NO_BORDER)
+ {
+ }
+
+protected:
+ virtual void DoLayout()
+ {
+ BScrollView::DoLayout();
+ // Tweak scroll bar layout to hide part of the frame for better looks.
+ BScrollBar* scrollBar = ScrollBar(B_VERTICAL);
+ scrollBar->MoveBy(1, -1);
+ scrollBar->ResizeBy(0, 2);
+ Target()->ResizeBy(1, 0);
+ // Set the scroll steps
+ if (BView* item = Target()->ChildAt(0)) {
+ scrollBar->SetSteps(item->MinSize().height + 1,
+ item->MinSize().height + 1);
+ }
+ }
+};
+
+
+// #pragma mark -
+
+
+DownloadWindow::DownloadWindow(BRect frame, bool visible,
+ SettingsMessage* settings)
+ : BWindow(frame, "Downloads",
+ B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
+ B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE),
+ fMinimizeOnClose(false)
+{
+ SetPulseRate(1000000);
+
+ settings->AddListener(BMessenger(this));
+ BPath downloadPath;
+ if (find_directory(B_DESKTOP_DIRECTORY, &downloadPath) != B_OK)
+ downloadPath.SetTo("/boot/home/Desktop");
+ fDownloadPath = settings->GetValue(kSettingsKeyDownloadPath,
+ downloadPath.Path());
+ settings->SetValue(kSettingsKeyDownloadPath, fDownloadPath);
+
+ SetLayout(new BGroupLayout(B_VERTICAL, 0.0));
+
+ DownloadsContainerView* downloadsGroupView = new DownloadsContainerView();
+ fDownloadViewsLayout = downloadsGroupView->GroupLayout();
+
+ BMenuBar* menuBar = new BMenuBar("Menu bar");
+ BMenu* menu = new BMenu("Downloads");
+ menu->AddItem(new BMenuItem("Open downloads folder",
+ new BMessage(OPEN_DOWNLOADS_FOLDER)));
+ BMessage* newWindowMessage = new BMessage(NEW_WINDOW);
+ newWindowMessage->AddString("url", "");
+ BMenuItem* newWindowItem = new BMenuItem("New browser window",
+ newWindowMessage, 'N');
+ menu->AddItem(newWindowItem);
+ newWindowItem->SetTarget(be_app);
+ menu->AddSeparatorItem();
+ menu->AddItem(new BMenuItem("Hide", new BMessage(B_QUIT_REQUESTED), 'D'));
+ menuBar->AddItem(menu);
+
+ fDownloadsScrollView = new DownloadContainerScrollView(downloadsGroupView);
+
+ fRemoveFinishedButton = new BButton("Remove finished",
+ new BMessage(REMOVE_FINISHED_DOWNLOADS));
+ fRemoveFinishedButton->SetEnabled(false);
+
+ fRemoveMissingButton = new BButton("Remove missing",
+ new BMessage(REMOVE_MISSING_DOWNLOADS));
+ fRemoveMissingButton->SetEnabled(false);
+
+ const float spacing = be_control_look->DefaultItemSpacing();
+
+ AddChild(BGroupLayoutBuilder(B_VERTICAL, 0.0)
+ .Add(menuBar)
+ .Add(fDownloadsScrollView)
+ .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
+ .Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing)
+ .AddGlue()
+ .Add(fRemoveMissingButton)
+ .Add(fRemoveFinishedButton)
+ .SetInsets(12, 5, 12, 5)
+ )
+ );
+
+ PostMessage(INIT);
+
+ if (!visible)
+ Hide();
+ Show();
+}
+
+
+DownloadWindow::~DownloadWindow()
+{
+ // Only necessary to save the current progress of unfinished downloads:
+ _SaveSettings();
+}
+
+
+void
+DownloadWindow::DispatchMessage(BMessage* message, BHandler* target)
+{
+ // We need to intercept mouse down events inside the area of download
+ // progress views (regardless of whether they have children at the click),
+ // so that they may display a context menu.
+ BPoint where;
+ int32 buttons;
+ if (message->what == B_MOUSE_DOWN
+ && message->FindPoint("screen_where", &where) == B_OK
+ && message->FindInt32("buttons", &buttons) == B_OK
+ && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
+ for (int32 i = fDownloadViewsLayout->CountItems() - 1;
+ BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
+ DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
+ item->View());
+ if (!view)
+ continue;
+ BPoint viewWhere(where);
+ view->ConvertFromScreen(&viewWhere);
+ if (view->Bounds().Contains(viewWhere)) {
+ view->ShowContextMenu(where);
+ return;
+ }
+ }
+ }
+ BWindow::DispatchMessage(message, target);
+}
+
+
+void
+DownloadWindow::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ case INIT:
+ {
+ _LoadSettings();
+ // Small trick to get the correct enabled status of the Remove
+ // finished button
+ _DownloadFinished(NULL);
+ break;
+ }
+ case B_DOWNLOAD_ADDED:
+ {
+ BWebDownload* download;
+ if (message->FindPointer("download", reinterpret_cast<void**>(
+ &download)) == B_OK) {
+ _DownloadStarted(download);
+ }
+ break;
+ }
+ case B_DOWNLOAD_REMOVED:
+ {
+ BWebDownload* download;
+ if (message->FindPointer("download", reinterpret_cast<void**>(
+ &download)) == B_OK) {
+ _DownloadFinished(download);
+ }
+ break;
+ }
+ case OPEN_DOWNLOADS_FOLDER:
+ {
+ entry_ref ref;
+ status_t status = get_ref_for_path(fDownloadPath.String(), &ref);
+ if (status == B_OK)
+ status = be_roster->Launch(&ref);
+ if (status != B_OK && status != B_ALREADY_RUNNING) {
+ BString errorString("The downloads folder could not be "
+ "opened.\n\n");
+ errorString << "Error: " << strerror(status);
+ BAlert* alert = new BAlert("Error opening downloads folder",
+ errorString.String(), "OK");
+ alert->Go(NULL);
+ }
+ break;
+ }
+ case REMOVE_FINISHED_DOWNLOADS:
+ _RemoveFinishedDownloads();
+ break;
+ case REMOVE_MISSING_DOWNLOADS:
+ _RemoveMissingDownloads();
+ break;
+ case SAVE_SETTINGS:
+ _ValidateButtonStatus();
+ _SaveSettings();
+ break;
+
+ case SETTINGS_VALUE_CHANGED:
+ {
+ BString string;
+ if (message->FindString("name", &string) == B_OK
+ && string == kSettingsKeyDownloadPath
+ && message->FindString("value", &string) == B_OK) {
+ fDownloadPath = string;
+ }
+ break;
+ }
+ default:
+ BWindow::MessageReceived(message);
+ break;
+ }
+}
+
+
+bool
+DownloadWindow::QuitRequested()
+{
+ if (fMinimizeOnClose) {
+ if (!IsMinimized())
+ Minimize(true);
+ } else {
+ if (!IsHidden())
+ Hide();
+ }
+ return false;
+}
+
+
+bool
+DownloadWindow::DownloadsInProgress()
+{
+ bool downloadsInProgress = false;
+ if (!Lock())
+ return downloadsInProgress;
+
+ for (int32 i = fDownloadViewsLayout->CountItems() - 1;
+ BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
+ DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
+ item->View());
+ if (!view)
+ continue;
+ if (view->Download() != NULL) {
+ downloadsInProgress = true;
+ break;
+ }
+ }
+
+ Unlock();
+
+ return downloadsInProgress;
+}
+
+
+void
+DownloadWindow::SetMinimizeOnClose(bool minimize)
+{
+ if (Lock()) {
+ fMinimizeOnClose = minimize;
+ Unlock();
+ }
+}
+
+
+// #pragma mark - private
+
+
+void
+DownloadWindow::_DownloadStarted(BWebDownload* download)
+{
+ download->Start(BPath(fDownloadPath.String()));
+
+ int32 finishedCount = 0;
+ int32 missingCount = 0;
+ int32 index = 0;
+ for (int32 i = fDownloadViewsLayout->CountItems() - 1;
+ BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
+ DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
+ item->View());
+ if (!view)
+ continue;
+ if (view->URL() == download->URL()) {
+ index = i;
+ view->RemoveSelf();
+ delete view;
+ continue;
+ }
+ if (view->IsFinished())
+ finishedCount++;
+ if (view->IsMissing())
+ missingCount++;
+ }
+ fRemoveFinishedButton->SetEnabled(finishedCount > 0);
+ fRemoveMissingButton->SetEnabled(missingCount > 0);
+ DownloadProgressView* view = new DownloadProgressView(download);
+ if (!view->Init()) {
+ delete view;
+ return;
+ }
+ fDownloadViewsLayout->AddView(index, view);
+
+ // Scroll new download into view
+ if (BScrollBar* scrollBar = fDownloadsScrollView->ScrollBar(B_VERTICAL)) {
+ float min;
+ float max;
+ scrollBar->GetRange(&min, &max);
+ float viewHeight = view->MinSize().height + 1;
+ float scrollOffset = min + index * viewHeight;
+ float scrollBarHeight = scrollBar->Bounds().Height() - 1;
+ float value = scrollBar->Value();
+ if (scrollOffset < value)
+ scrollBar->SetValue(scrollOffset);
+ else if (scrollOffset + viewHeight > value + scrollBarHeight) {
+ float diff = scrollOffset + viewHeight - (value + scrollBarHeight);
+ scrollBar->SetValue(value + diff);
+ }
+ }
+
+ _SaveSettings();
+
+ SetWorkspaces(B_CURRENT_WORKSPACE);
+ if (IsHidden())
+ Show();
+}
+
+
+void
+DownloadWindow::_DownloadFinished(BWebDownload* download)
+{
+ int32 finishedCount = 0;
+ int32 missingCount = 0;
+ for (int32 i = 0;
+ BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i++) {
+ DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
+ item->View());
+ if (!view)
+ continue;
+ if (download && view->Download() == download) {
+ view->DownloadFinished();
+ finishedCount++;
+ continue;
+ }
+ if (view->IsFinished())
+ finishedCount++;
+ if (view->IsMissing())
+ missingCount++;
+ }
+ fRemoveFinishedButton->SetEnabled(finishedCount > 0);
+ fRemoveMissingButton->SetEnabled(missingCount > 0);
+ if (download)
+ _SaveSettings();
+}
+
+
+void
+DownloadWindow::_RemoveFinishedDownloads()
+{
+ int32 missingCount = 0;
+ for (int32 i = fDownloadViewsLayout->CountItems() - 1;
+ BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
+ DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
+ item->View());
+ if (!view)
+ continue;
+ if (view->IsFinished()) {
+ view->RemoveSelf();
+ delete view;
+ } else if (view->IsMissing())
+ missingCount++;
+ }
+ fRemoveFinishedButton->SetEnabled(false);
+ fRemoveMissingButton->SetEnabled(missingCount > 0);
+ _SaveSettings();
+}
+
+
+void
+DownloadWindow::_RemoveMissingDownloads()
+{
+ int32 finishedCount = 0;
+ for (int32 i = fDownloadViewsLayout->CountItems() - 1;
+ BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
+ DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
+ item->View());
+ if (!view)
+ continue;
+ if (view->IsMissing()) {
+ view->RemoveSelf();
+ delete view;
+ } else if (view->IsFinished())
+ finishedCount++;
+ }
+ fRemoveMissingButton->SetEnabled(false);
+ fRemoveFinishedButton->SetEnabled(finishedCount > 0);
+ _SaveSettings();
+}
+
+
+void
+DownloadWindow::_ValidateButtonStatus()
+{
+ int32 finishedCount = 0;
+ int32 missingCount = 0;
+ for (int32 i = fDownloadViewsLayout->CountItems() - 1;
+ BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
+ DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
+ item->View());
+ if (!view)
+ continue;
+ if (view->IsFinished())
+ finishedCount++;
+ if (view->IsMissing())
+ missingCount++;
+ }
+ fRemoveFinishedButton->SetEnabled(finishedCount > 0);
+ fRemoveMissingButton->SetEnabled(missingCount > 0);
+}
+
+
+void
+DownloadWindow::_SaveSettings()
+{
+ BFile file;
+ if (!_OpenSettingsFile(file, B_ERASE_FILE | B_CREATE_FILE | B_WRITE_ONLY))
+ return;
+ BMessage message;
+ for (int32 i = fDownloadViewsLayout->CountItems() - 1;
+ BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
+ DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
+ item->View());
+ if (!view)
+ continue;
+ BMessage downloadArchive;
+ if (view->SaveSettings(&downloadArchive) == B_OK)
+ message.AddMessage("download", &downloadArchive);
+ }
+ message.Flatten(&file);
+}
+
+
+void
+DownloadWindow::_LoadSettings()
+{
+ BFile file;
+ if (!_OpenSettingsFile(file, B_READ_ONLY))
+ return;
+ BMessage message;
+ if (message.Unflatten(&file) != B_OK)
+ return;
+ BMessage downloadArchive;
+ for (int32 i = 0;
+ message.FindMessage("download", i, &downloadArchive) == B_OK;
+ i++) {
+ DownloadProgressView* view = new DownloadProgressView(
+ &downloadArchive);
+ if (!view->Init(&downloadArchive))
+ continue;
+ fDownloadViewsLayout->AddView(0, view);
+ }
+}
+
+
+bool
+DownloadWindow::_OpenSettingsFile(BFile& file, uint32 mode)
+{
+ BPath path;
+ if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
+ || path.Append(kApplicationName) != B_OK
+ || path.Append("Downloads") != B_OK) {
+ return false;
+ }
+ return file.SetTo(path.Path(), mode) == B_OK;
+}
+
+
diff --git a/src/apps/webpositive/DownloadWindow.h b/src/apps/webpositive/DownloadWindow.h
new file mode 100644
index 0000000000..d76661ba4e
--- /dev/null
+++ b/src/apps/webpositive/DownloadWindow.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+#ifndef DOWNLOAD_WINDOW_H
+#define DOWNLOAD_WINDOW_H
+
+
+#include <String.h>
+#include <Window.h>
+
+class BButton;
+class BFile;
+class BGroupLayout;
+class BScrollView;
+class BWebDownload;
+class SettingsMessage;
+
+
+class DownloadWindow : public BWindow {
+public:
+ DownloadWindow(BRect frame, bool visible,
+ SettingsMessage* settings);
+ virtual ~DownloadWindow();
+
+ virtual void DispatchMessage(BMessage* message,
+ BHandler* target);
+ virtual void MessageReceived(BMessage* message);
+ virtual bool QuitRequested();
+
+ bool DownloadsInProgress();
+ void SetMinimizeOnClose(bool minimize);
+
+private:
+ void _DownloadStarted(BWebDownload* download);
+ void _DownloadFinished(BWebDownload* download);
+ void _RemoveFinishedDownloads();
+ void _RemoveMissingDownloads();
+ void _ValidateButtonStatus();
+ void _SaveSettings();
+ void _LoadSettings();
+ bool _OpenSettingsFile(BFile& file, uint32 mode);
+
+private:
+ BScrollView* fDownloadsScrollView;
+ BGroupLayout* fDownloadViewsLayout;
+ BButton* fRemoveFinishedButton;
+ BButton* fRemoveMissingButton;
+ BString fDownloadPath;
+ bool fMinimizeOnClose;
+};
+
+#endif // DOWNLOAD_WINDOW_H
diff --git a/src/apps/webpositive/SettingsKeys.cpp b/src/apps/webpositive/SettingsKeys.cpp
new file mode 100644
index 0000000000..b72166cd3a
--- /dev/null
+++ b/src/apps/webpositive/SettingsKeys.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "SettingsKeys.h"
+
+const char* kSettingsKeyDownloadPath = "download path";
+const char* kSettingsKeyShowTabsIfSinglePageOpen
+ = "show tabs if single page open";
+const char* kSettingsKeyAutoHideInterfaceInFullscreenMode
+ = "auto hide interface in full screen mode";
+const char* kSettingsKeyAutoHidePointer = "auto hide pointer";
+const char* kSettingsKeyShowHomeButton = "show home button";
+
+const char* kSettingsKeyNewWindowPolicy = "new window policy";
+const char* kSettingsKeyNewTabPolicy = "new tab policy";
+const char* kSettingsKeyStartPageURL = "start page url";
+const char* kSettingsKeySearchPageURL = "search page url";
+
+
+const char* kDefaultDownloadPath = "/boot/home/Desktop/";
+const char* kDefaultStartPageURL
+ = "file:///boot/home/config/settings/WebPositive/LoaderPages/Welcome";
+const char* kDefaultSearchPageURL = "http://www.google.com";
+
+const char* kSettingsKeyUseProxy = "use http proxy";
+const char* kSettingsKeyProxyAddress = "http proxy address";
+const char* kSettingsKeyProxyPort = "http proxy port";
diff --git a/src/apps/webpositive/SettingsKeys.h b/src/apps/webpositive/SettingsKeys.h
new file mode 100644
index 0000000000..1a95bbfe89
--- /dev/null
+++ b/src/apps/webpositive/SettingsKeys.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+#ifndef SETTINGS_KEYS_H
+#define SETTINGS_KEYS_H
+
+#include <SupportDefs.h>
+
+
+extern const char* kSettingsKeyDownloadPath;
+extern const char* kSettingsKeyShowTabsIfSinglePageOpen;
+extern const char* kSettingsKeyAutoHideInterfaceInFullscreenMode;
+extern const char* kSettingsKeyAutoHidePointer;
+extern const char* kSettingsKeyShowHomeButton;
+
+extern const char* kSettingsKeyNewWindowPolicy;
+extern const char* kSettingsKeyNewTabPolicy;
+extern const char* kSettingsKeyStartPageURL;
+extern const char* kSettingsKeySearchPageURL;
+
+extern const char* kDefaultDownloadPath;
+extern const char* kDefaultStartPageURL;
+extern const char* kDefaultSearchPageURL;
+
+extern const char* kSettingsKeyUseProxy;
+extern const char* kSettingsKeyProxyAddress;
+extern const char* kSettingsKeyProxyPort;
+
+#endif // SETTINGS_KEYS_H
diff --git a/src/apps/webpositive/SettingsWindow.cpp b/src/apps/webpositive/SettingsWindow.cpp
new file mode 100644
index 0000000000..5a5ee41873
--- /dev/null
+++ b/src/apps/webpositive/SettingsWindow.cpp
@@ -0,0 +1,884 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "SettingsWindow.h"
+
+#include <Button.h>
+#include <CheckBox.h>
+#include <ControlLook.h>
+#include <GridLayoutBuilder.h>
+#include <GroupLayout.h>
+#include <GroupLayoutBuilder.h>
+#include <Locale.h>
+#include <MenuItem.h>
+#include <MenuField.h>
+#include <Message.h>
+#include <PopUpMenu.h>
+#include <ScrollView.h>
+#include <SeparatorView.h>
+#include <SpaceLayoutItem.h>
+#include <TabView.h>
+#include <TextControl.h>
+#include <debugger.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "BrowserApp.h"
+#include "BrowsingHistory.h"
+#include "BrowserWindow.h"
+#include "FontSelectionView.h"
+#include "SettingsKeys.h"
+#include "SettingsMessage.h"
+#include "WebSettings.h"
+
+
+#undef B_TRANSLATION_CONTEXT
+#define B_TRANSLATION_CONTEXT "Settings Window"
+
+enum {
+ MSG_APPLY = 'aply',
+ MSG_CANCEL = 'cncl',
+ MSG_REVERT = 'rvrt',
+
+ MSG_START_PAGE_CHANGED = 'hpch',
+ MSG_SEARCH_PAGE_CHANGED = 'spch',
+ MSG_DOWNLOAD_FOLDER_CHANGED = 'dnfc',
+ MSG_NEW_WINDOWS_BEHAVIOR_CHANGED = 'nwbc',
+ MSG_NEW_TABS_BEHAVIOR_CHANGED = 'ntbc',
+ MSG_HISTORY_MENU_DAYS_CHANGED = 'digm',
+ MSG_TAB_DISPLAY_BEHAVIOR_CHANGED = 'tdbc',
+ MSG_AUTO_HIDE_INTERFACE_BEHAVIOR_CHANGED = 'ahic',
+ MSG_AUTO_HIDE_POINTER_BEHAVIOR_CHANGED = 'ahpc',
+ MSG_SHOW_HOME_BUTTON_CHANGED = 'shbc',
+
+ MSG_STANDARD_FONT_CHANGED = 'stfc',
+ MSG_SERIF_FONT_CHANGED = 'sefc',
+ MSG_SANS_SERIF_FONT_CHANGED = 'ssfc',
+ MSG_FIXED_FONT_CHANGED = 'ffch',
+
+ MSG_STANDARD_FONT_SIZE_SELECTED = 'sfss',
+ MSG_FIXED_FONT_SIZE_SELECTED = 'ffss',
+
+ MSG_USE_PROXY_CHANGED = 'upsc',
+ MSG_PROXY_ADDRESS_CHANGED = 'psac',
+ MSG_PROXY_PORT_CHANGED = 'pspc',
+};
+
+static const int32 kDefaultFontSize = 14;
+
+
+SettingsWindow::SettingsWindow(BRect frame, SettingsMessage* settings)
+ :
+ BWindow(frame, B_TRANSLATE("Settings"), B_TITLED_WINDOW_LOOK,
+ B_NORMAL_WINDOW_FEEL, B_AUTO_UPDATE_SIZE_LIMITS
+ | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE),
+ fSettings(settings)
+{
+ SetLayout(new BGroupLayout(B_VERTICAL));
+
+ fApplyButton = new BButton(B_TRANSLATE("Apply"), new BMessage(MSG_APPLY));
+ fCancelButton = new BButton(B_TRANSLATE("Cancel"),
+ new BMessage(MSG_CANCEL));
+ fRevertButton = new BButton(B_TRANSLATE("Revert"),
+ new BMessage(MSG_REVERT));
+
+ float spacing = be_control_look->DefaultItemSpacing();
+
+ BTabView* tabView = new BTabView("settings pages", B_WIDTH_FROM_LABEL);
+
+ AddChild(BGroupLayoutBuilder(B_VERTICAL, spacing)
+ .Add(tabView)
+ .Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing)
+ .Add(fRevertButton)
+ .AddGlue()
+ .Add(fCancelButton)
+ .Add(fApplyButton)
+ )
+ .SetInsets(spacing, spacing, spacing, spacing)
+ );
+
+ tabView->AddTab(_CreateGeneralPage(spacing));
+ tabView->AddTab(_CreateFontsPage(spacing));
+ tabView->AddTab(_CreateProxyPage(spacing));
+
+ _SetupFontSelectionView(fStandardFontView,
+ new BMessage(MSG_STANDARD_FONT_CHANGED));
+ _SetupFontSelectionView(fSerifFontView,
+ new BMessage(MSG_SERIF_FONT_CHANGED));
+ _SetupFontSelectionView(fSansSerifFontView,
+ new BMessage(MSG_SANS_SERIF_FONT_CHANGED));
+ _SetupFontSelectionView(fFixedFontView,
+ new BMessage(MSG_FIXED_FONT_CHANGED));
+
+ fApplyButton->MakeDefault(true);
+
+ if (!frame.IsValid())
+ CenterOnScreen();
+
+ // load settings from disk
+ _RevertSettings();
+ // apply to WebKit
+ _ApplySettings();
+
+ // Start hidden
+ Hide();
+ Show();
+}
+
+
+SettingsWindow::~SettingsWindow()
+{
+ RemoveHandler(fStandardFontView);
+ delete fStandardFontView;
+ RemoveHandler(fSerifFontView);
+ delete fSerifFontView;
+ RemoveHandler(fSansSerifFontView);
+ delete fSansSerifFontView;
+ RemoveHandler(fFixedFontView);
+ delete fFixedFontView;
+}
+
+
+void
+SettingsWindow::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ case MSG_APPLY:
+ _ApplySettings();
+ break;
+ case MSG_CANCEL:
+ _RevertSettings();
+ PostMessage(B_QUIT_REQUESTED);
+ break;
+ case MSG_REVERT:
+ _RevertSettings();
+ break;
+
+ case MSG_STANDARD_FONT_SIZE_SELECTED:
+ {
+ int32 size = _SizesMenuValue(fStandardSizesMenu->Menu());
+ fStandardFontView->SetSize(size);
+ fSerifFontView->SetSize(size);
+ fSansSerifFontView->SetSize(size);
+ _ValidateControlsEnabledStatus();
+ break;
+ }
+ case MSG_FIXED_FONT_SIZE_SELECTED:
+ {
+ int32 size = _SizesMenuValue(fFixedSizesMenu->Menu());
+ fFixedFontView->SetSize(size);
+ _ValidateControlsEnabledStatus();
+ break;
+ }
+
+ case MSG_START_PAGE_CHANGED:
+ case MSG_SEARCH_PAGE_CHANGED:
+ case MSG_DOWNLOAD_FOLDER_CHANGED:
+ case MSG_NEW_WINDOWS_BEHAVIOR_CHANGED:
+ case MSG_NEW_TABS_BEHAVIOR_CHANGED:
+ case MSG_HISTORY_MENU_DAYS_CHANGED:
+ case MSG_TAB_DISPLAY_BEHAVIOR_CHANGED:
+ case MSG_AUTO_HIDE_INTERFACE_BEHAVIOR_CHANGED:
+ case MSG_AUTO_HIDE_POINTER_BEHAVIOR_CHANGED:
+ case MSG_SHOW_HOME_BUTTON_CHANGED:
+ case MSG_STANDARD_FONT_CHANGED:
+ case MSG_SERIF_FONT_CHANGED:
+ case MSG_SANS_SERIF_FONT_CHANGED:
+ case MSG_FIXED_FONT_CHANGED:
+ case MSG_USE_PROXY_CHANGED:
+ case MSG_PROXY_ADDRESS_CHANGED:
+ case MSG_PROXY_PORT_CHANGED:
+ // TODO: Some settings could change live, some others not?
+ _ValidateControlsEnabledStatus();
+ break;
+
+ default:
+ BWindow::MessageReceived(message);
+ break;
+ }
+}
+
+
+bool
+SettingsWindow::QuitRequested()
+{
+ if (!IsHidden())
+ Hide();
+ return false;
+}
+
+
+void
+SettingsWindow::Show()
+{
+ // When showing the window, the this is always the
+ // point to which we can revert the settings.
+ _RevertSettings();
+ BWindow::Show();
+}
+
+
+// #pragma mark - private
+
+
+BView*
+SettingsWindow::_CreateGeneralPage(float spacing)
+{
+ fStartPageControl = new BTextControl("start page",
+ B_TRANSLATE("Start page:"), "", new BMessage(MSG_START_PAGE_CHANGED));
+ fStartPageControl->SetModificationMessage(
+ new BMessage(MSG_START_PAGE_CHANGED));
+ fStartPageControl->SetText(
+ fSettings->GetValue(kSettingsKeyStartPageURL, kDefaultStartPageURL));
+
+ fSearchPageControl = new BTextControl("search page",
+ B_TRANSLATE("Search page:"), "",
+ new BMessage(MSG_SEARCH_PAGE_CHANGED));
+ fSearchPageControl->SetModificationMessage(
+ new BMessage(MSG_SEARCH_PAGE_CHANGED));
+ fSearchPageControl->SetText(
+ fSettings->GetValue(kSettingsKeySearchPageURL, kDefaultSearchPageURL));
+
+ fDownloadFolderControl = new BTextControl("download folder",
+ B_TRANSLATE("Download folder:"), "",
+ new BMessage(MSG_DOWNLOAD_FOLDER_CHANGED));
+ fDownloadFolderControl->SetModificationMessage(
+ new BMessage(MSG_DOWNLOAD_FOLDER_CHANGED));
+ fDownloadFolderControl->SetText(
+ fSettings->GetValue(kSettingsKeyDownloadPath, kDefaultDownloadPath));
+
+ fNewWindowBehaviorOpenHomeItem = new BMenuItem(
+ B_TRANSLATE("Open start page"),
+ new BMessage(MSG_NEW_WINDOWS_BEHAVIOR_CHANGED));
+ fNewWindowBehaviorOpenSearchItem = new BMenuItem(
+ B_TRANSLATE("Open search page"),
+ new BMessage(MSG_NEW_WINDOWS_BEHAVIOR_CHANGED));
+ fNewWindowBehaviorOpenBlankItem = new BMenuItem(
+ B_TRANSLATE("Open blank page"),
+ new BMessage(MSG_NEW_WINDOWS_BEHAVIOR_CHANGED));
+
+ fNewTabBehaviorCloneCurrentItem = new BMenuItem(
+ B_TRANSLATE("Clone current page"),
+ new BMessage(MSG_NEW_TABS_BEHAVIOR_CHANGED));
+ fNewTabBehaviorOpenHomeItem = new BMenuItem(
+ B_TRANSLATE("Open start page"),
+ new BMessage(MSG_NEW_TABS_BEHAVIOR_CHANGED));
+ fNewTabBehaviorOpenSearchItem = new BMenuItem(
+ B_TRANSLATE("Open search page"),
+ new BMessage(MSG_NEW_TABS_BEHAVIOR_CHANGED));
+ fNewTabBehaviorOpenBlankItem = new BMenuItem(
+ B_TRANSLATE("Open blank page"),
+ new BMessage(MSG_NEW_TABS_BEHAVIOR_CHANGED));
+
+ fNewWindowBehaviorOpenHomeItem->SetMarked(true);
+ fNewTabBehaviorOpenBlankItem->SetMarked(true);
+
+ BPopUpMenu* newWindowBehaviorMenu = new BPopUpMenu("New windows");
+ newWindowBehaviorMenu->AddItem(fNewWindowBehaviorOpenHomeItem);
+ newWindowBehaviorMenu->AddItem(fNewWindowBehaviorOpenSearchItem);
+ newWindowBehaviorMenu->AddItem(fNewWindowBehaviorOpenBlankItem);
+ fNewWindowBehaviorMenu = new BMenuField("new window behavior",
+ B_TRANSLATE("New windows:"), newWindowBehaviorMenu);
+
+ BPopUpMenu* newTabBehaviorMenu = new BPopUpMenu("New tabs");
+ newTabBehaviorMenu->AddItem(fNewTabBehaviorOpenBlankItem);
+ newTabBehaviorMenu->AddItem(fNewTabBehaviorOpenHomeItem);
+ newTabBehaviorMenu->AddItem(fNewTabBehaviorOpenSearchItem);
+ newTabBehaviorMenu->AddItem(fNewTabBehaviorCloneCurrentItem);
+ fNewTabBehaviorMenu = new BMenuField("new tab behavior",
+ B_TRANSLATE("New tabs:"), newTabBehaviorMenu);
+
+ fDaysInHistoryMenuControl = new BTextControl("days in history",
+ B_TRANSLATE("Number of days to keep links in History menu:"), "",
+ new BMessage(MSG_HISTORY_MENU_DAYS_CHANGED));
+ fDaysInHistoryMenuControl->SetModificationMessage(
+ new BMessage(MSG_HISTORY_MENU_DAYS_CHANGED));
+ BString maxHistoryAge;
+ maxHistoryAge << BrowsingHistory::DefaultInstance()->MaxHistoryItemAge();
+ fDaysInHistoryMenuControl->SetText(maxHistoryAge.String());
+ for (uchar i = 0; i < '0'; i++)
+ fDaysInHistoryMenuControl->TextView()->DisallowChar(i);
+ for (uchar i = '9' + 1; i <= 128; i++)
+ fDaysInHistoryMenuControl->TextView()->DisallowChar(i);
+
+ fShowTabsIfOnlyOnePage = new BCheckBox("show tabs if only one page",
+ B_TRANSLATE("Show tabs if only one page is open."),
+ new BMessage(MSG_TAB_DISPLAY_BEHAVIOR_CHANGED));
+ fShowTabsIfOnlyOnePage->SetValue(B_CONTROL_ON);
+
+ fAutoHideInterfaceInFullscreenMode = new BCheckBox("auto-hide interface",
+ B_TRANSLATE("Auto-hide interface in fullscreen mode."),
+ new BMessage(MSG_AUTO_HIDE_INTERFACE_BEHAVIOR_CHANGED));
+ fAutoHideInterfaceInFullscreenMode->SetValue(B_CONTROL_OFF);
+
+ fAutoHidePointer = new BCheckBox("auto-hide pointer",
+ B_TRANSLATE("Auto-hide mouse pointer."),
+ new BMessage(MSG_AUTO_HIDE_POINTER_BEHAVIOR_CHANGED));
+ fAutoHidePointer->SetValue(B_CONTROL_OFF);
+
+ fShowHomeButton = new BCheckBox("show home button",
+ B_TRANSLATE("Show Home Button"),
+ new BMessage(MSG_SHOW_HOME_BUTTON_CHANGED));
+ fShowHomeButton->SetValue(B_CONTROL_ON);
+
+ BView* view = BGroupLayoutBuilder(B_VERTICAL, spacing / 2)
+ .Add(BGridLayoutBuilder(spacing / 2, spacing / 2)
+ .Add(fStartPageControl->CreateLabelLayoutItem(), 0, 0)
+ .Add(fStartPageControl->CreateTextViewLayoutItem(), 1, 0)
+
+ .Add(fSearchPageControl->CreateLabelLayoutItem(), 0, 1)
+ .Add(fSearchPageControl->CreateTextViewLayoutItem(), 1, 1)
+
+ .Add(fDownloadFolderControl->CreateLabelLayoutItem(), 0, 2)
+ .Add(fDownloadFolderControl->CreateTextViewLayoutItem(), 1, 2)
+
+ .Add(fNewWindowBehaviorMenu->CreateLabelLayoutItem(), 0, 3)
+ .Add(fNewWindowBehaviorMenu->CreateMenuBarLayoutItem(), 1, 3)
+
+ .Add(fNewTabBehaviorMenu->CreateLabelLayoutItem(), 0, 4)
+ .Add(fNewTabBehaviorMenu->CreateMenuBarLayoutItem(), 1, 4)
+ )
+ .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing))
+ .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
+ .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing))
+ .Add(fShowTabsIfOnlyOnePage)
+ .Add(fAutoHideInterfaceInFullscreenMode)
+ .Add(fAutoHidePointer)
+ .Add(fShowHomeButton)
+ .Add(fDaysInHistoryMenuControl)
+ .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing))
+
+ .SetInsets(spacing, spacing, spacing, spacing)
+
+ .TopView()
+ ;
+ view->SetName(B_TRANSLATE("General"));
+ return view;
+}
+
+
+BView*
+SettingsWindow::_CreateFontsPage(float spacing)
+{
+ fStandardFontView = new FontSelectionView("standard",
+ B_TRANSLATE("Standard font:"), true, be_plain_font);
+ BFont defaultSerifFont = _FindDefaultSerifFont();
+ fSerifFontView = new FontSelectionView("serif",
+ B_TRANSLATE("Serif font:"), true, &defaultSerifFont);
+ fSansSerifFontView = new FontSelectionView("sans serif",
+ B_TRANSLATE("Sans serif font:"), true, be_plain_font);
+ fFixedFontView = new FontSelectionView("fixed",
+ B_TRANSLATE("Fixed font:"), true, be_fixed_font);
+
+ fStandardSizesMenu = new BMenuField("standard font size",
+ B_TRANSLATE("Default standard font size:"), new BPopUpMenu("sizes"),
+ NULL);
+ _BuildSizesMenu(fStandardSizesMenu->Menu(),
+ MSG_STANDARD_FONT_SIZE_SELECTED);
+
+ fFixedSizesMenu = new BMenuField("fixed font size",
+ B_TRANSLATE("Default fixed font size:"), new BPopUpMenu("sizes"),
+ NULL);
+ _BuildSizesMenu(fFixedSizesMenu->Menu(), MSG_FIXED_FONT_SIZE_SELECTED);
+
+ BView* view = BGridLayoutBuilder(spacing / 2, spacing / 2)
+ .Add(fStandardFontView->CreateFontsLabelLayoutItem(), 0, 0)
+ .Add(fStandardFontView->CreateFontsMenuBarLayoutItem(), 1, 0)
+ .Add(fStandardFontView->PreviewBox(), 0, 1, 2)
+ .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing), 0, 2, 2)
+
+ .Add(fSerifFontView->CreateFontsLabelLayoutItem(), 0, 3)
+ .Add(fSerifFontView->CreateFontsMenuBarLayoutItem(), 1, 3)
+ .Add(fSerifFontView->PreviewBox(), 0, 4, 2)
+ .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing), 0, 5, 2)
+
+ .Add(fSansSerifFontView->CreateFontsLabelLayoutItem(), 0, 6)
+ .Add(fSansSerifFontView->CreateFontsMenuBarLayoutItem(), 1, 6)
+ .Add(fSansSerifFontView->PreviewBox(), 0, 7, 2)
+ .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing), 0, 8, 2)
+
+ .Add(fFixedFontView->CreateFontsLabelLayoutItem(), 0, 9)
+ .Add(fFixedFontView->CreateFontsMenuBarLayoutItem(), 1, 9)
+ .Add(fFixedFontView->PreviewBox(), 0, 10, 2)
+ .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing), 0, 11, 2)
+
+ .Add(fStandardSizesMenu->CreateLabelLayoutItem(), 0, 12)
+ .Add(fStandardSizesMenu->CreateMenuBarLayoutItem(), 1, 12)
+ .Add(fFixedSizesMenu->CreateLabelLayoutItem(), 0, 13)
+ .Add(fFixedSizesMenu->CreateMenuBarLayoutItem(), 1, 13)
+
+ .SetInsets(spacing, spacing, spacing, spacing)
+
+ .View()
+ ;
+ view->SetName(B_TRANSLATE("Fonts"));
+ return view;
+}
+
+
+BView*
+SettingsWindow::_CreateProxyPage(float spacing)
+{
+ fUseProxyCheckBox = new BCheckBox("use proxy",
+ B_TRANSLATE("Use proxy server to connect to the internet."),
+ new BMessage(MSG_USE_PROXY_CHANGED));
+ fUseProxyCheckBox->SetValue(B_CONTROL_ON);
+
+ fProxyAddressControl = new BTextControl("proxy address",
+ B_TRANSLATE("Proxy server address:"), "",
+ new BMessage(MSG_PROXY_ADDRESS_CHANGED));
+ fProxyAddressControl->SetModificationMessage(
+ new BMessage(MSG_PROXY_ADDRESS_CHANGED));
+ fProxyAddressControl->SetText(
+ fSettings->GetValue(kSettingsKeyProxyAddress, ""));
+
+ fProxyPortControl = new BTextControl("proxy port",
+ B_TRANSLATE("Proxy server port:"), "",
+ new BMessage(MSG_PROXY_PORT_CHANGED));
+ fProxyPortControl->SetModificationMessage(
+ new BMessage(MSG_PROXY_PORT_CHANGED));
+ fProxyPortControl->SetText(
+ fSettings->GetValue(kSettingsKeyProxyAddress, ""));
+
+ BView* view = BGroupLayoutBuilder(B_VERTICAL, spacing / 2)
+ .Add(fUseProxyCheckBox)
+ .Add(BGridLayoutBuilder(spacing / 2, spacing / 2)
+ .Add(fProxyAddressControl->CreateLabelLayoutItem(), 0, 0)
+ .Add(fProxyAddressControl->CreateTextViewLayoutItem(), 1, 0)
+
+ .Add(fProxyPortControl->CreateLabelLayoutItem(), 0, 1)
+ .Add(fProxyPortControl->CreateTextViewLayoutItem(), 1, 1)
+ )
+ .Add(BSpaceLayoutItem::CreateGlue())
+
+ .SetInsets(spacing, spacing, spacing, spacing)
+
+ .TopView()
+ ;
+ view->SetName(B_TRANSLATE("Proxy server"));
+ return view;
+}
+
+
+void
+SettingsWindow::_BuildSizesMenu(BMenu* menu, uint32 messageWhat)
+{
+ const float kMinSize = 8.0;
+ const float kMaxSize = 18.0;
+
+ const int32 kSizes[] = {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 0};
+
+ for (int32 i = 0; kSizes[i]; i++) {
+ int32 size = kSizes[i];
+ if (size < kMinSize || size > kMaxSize)
+ continue;
+
+ char label[32];
+ snprintf(label, sizeof(label), "%ld", size);
+
+ BMessage* message = new BMessage(messageWhat);
+ message->AddInt32("size", size);
+
+ BMenuItem* item = new BMenuItem(label, message);
+
+ menu->AddItem(item);
+ item->SetTarget(this);
+ }
+}
+
+
+void
+SettingsWindow::_SetupFontSelectionView(FontSelectionView* view,
+ BMessage* message)
+{
+ AddHandler(view);
+ view->AttachedToLooper();
+ view->SetMessage(message);
+ view->SetTarget(this);
+}
+
+
+// #pragma mark -
+
+
+bool
+SettingsWindow::_CanApplySettings() const
+{
+ bool canApply = false;
+
+ // General settings
+ canApply = canApply || (strcmp(fStartPageControl->Text(),
+ fSettings->GetValue(kSettingsKeyStartPageURL,
+ kDefaultStartPageURL)) != 0);
+
+ canApply = canApply || (strcmp(fSearchPageControl->Text(),
+ fSettings->GetValue(kSettingsKeySearchPageURL,
+ kDefaultSearchPageURL)) != 0);
+
+ canApply = canApply || (strcmp(fDownloadFolderControl->Text(),
+ fSettings->GetValue(kSettingsKeyDownloadPath,
+ kDefaultDownloadPath)) != 0);
+
+ canApply = canApply || ((fShowTabsIfOnlyOnePage->Value() == B_CONTROL_ON)
+ != fSettings->GetValue(kSettingsKeyShowTabsIfSinglePageOpen, true));
+
+ canApply = canApply || (
+ (fAutoHideInterfaceInFullscreenMode->Value() == B_CONTROL_ON)
+ != fSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode,
+ false));
+
+ canApply = canApply || (
+ (fAutoHidePointer->Value() == B_CONTROL_ON)
+ != fSettings->GetValue(kSettingsKeyAutoHidePointer, false));
+
+ canApply = canApply || ((fShowHomeButton->Value() == B_CONTROL_ON)
+ != fSettings->GetValue(kSettingsKeyShowHomeButton, true));
+
+ canApply = canApply || (_MaxHistoryAge()
+ != BrowsingHistory::DefaultInstance()->MaxHistoryItemAge());
+
+ // New window policy
+ canApply = canApply || (_NewWindowPolicy()
+ != fSettings->GetValue(kSettingsKeyNewWindowPolicy,
+ (uint32)OpenStartPage));
+
+ // New tab policy
+ canApply = canApply || (_NewTabPolicy()
+ != fSettings->GetValue(kSettingsKeyNewTabPolicy,
+ (uint32)OpenBlankPage));
+
+ // Font settings
+ canApply = canApply || (fStandardFontView->Font()
+ != fSettings->GetValue("standard font", *be_plain_font));
+
+ canApply = canApply || (fSerifFontView->Font()
+ != fSettings->GetValue("serif font", _FindDefaultSerifFont()));
+
+ canApply = canApply || (fSansSerifFontView->Font()
+ != fSettings->GetValue("sans serif font", *be_plain_font));
+
+ canApply = canApply || (fFixedFontView->Font()
+ != fSettings->GetValue("fixed font", *be_fixed_font));
+
+ canApply = canApply || (_SizesMenuValue(fStandardSizesMenu->Menu())
+ != fSettings->GetValue("standard font size", kDefaultFontSize));
+
+ canApply = canApply || (_SizesMenuValue(fFixedSizesMenu->Menu())
+ != fSettings->GetValue("fixed font size", kDefaultFontSize));
+
+ // Proxy settings
+ canApply = canApply || ((fUseProxyCheckBox->Value() == B_CONTROL_ON)
+ != fSettings->GetValue(kSettingsKeyUseProxy, false));
+
+ canApply = canApply || (strcmp(fProxyAddressControl->Text(),
+ fSettings->GetValue(kSettingsKeyProxyAddress, "")) != 0);
+
+ canApply = canApply || (_ProxyPort()
+ != fSettings->GetValue(kSettingsKeyProxyPort, (uint32)0));
+
+ return canApply;
+}
+
+
+void
+SettingsWindow::_ApplySettings()
+{
+ // Store general settings
+ int32 maxHistoryAge = _MaxHistoryAge();
+ BString text;
+ text << maxHistoryAge;
+ fDaysInHistoryMenuControl->SetText(text.String());
+ BrowsingHistory::DefaultInstance()->SetMaxHistoryItemAge(maxHistoryAge);
+
+ fSettings->SetValue(kSettingsKeyStartPageURL, fStartPageControl->Text());
+ fSettings->SetValue(kSettingsKeySearchPageURL, fSearchPageControl->Text());
+ fSettings->SetValue(kSettingsKeyDownloadPath, fDownloadFolderControl->Text());
+ fSettings->SetValue(kSettingsKeyShowTabsIfSinglePageOpen,
+ fShowTabsIfOnlyOnePage->Value() == B_CONTROL_ON);
+ fSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode,
+ fAutoHideInterfaceInFullscreenMode->Value() == B_CONTROL_ON);
+ fSettings->SetValue(kSettingsKeyAutoHidePointer,
+ fAutoHidePointer->Value() == B_CONTROL_ON);
+ fSettings->SetValue(kSettingsKeyShowHomeButton,
+ fShowHomeButton->Value() == B_CONTROL_ON);
+
+ // New page policies
+ fSettings->SetValue(kSettingsKeyNewWindowPolicy, _NewWindowPolicy());
+ fSettings->SetValue(kSettingsKeyNewTabPolicy, _NewTabPolicy());
+
+ // Store fond settings
+ fSettings->SetValue("standard font", fStandardFontView->Font());
+ fSettings->SetValue("serif font", fSerifFontView->Font());
+ fSettings->SetValue("sans serif font", fSansSerifFontView->Font());
+ fSettings->SetValue("fixed font", fFixedFontView->Font());
+ int32 standardFontSize = _SizesMenuValue(fStandardSizesMenu->Menu());
+ int32 fixedFontSize = _SizesMenuValue(fFixedSizesMenu->Menu());
+ fSettings->SetValue("standard font size", standardFontSize);
+ fSettings->SetValue("fixed font size", fixedFontSize);
+
+ // Store proxy settings
+
+ fSettings->SetValue(kSettingsKeyUseProxy,
+ fUseProxyCheckBox->Value() == B_CONTROL_ON);
+ fSettings->SetValue(kSettingsKeyProxyAddress,
+ fProxyAddressControl->Text());
+ uint32 proxyPort = _ProxyPort();
+ fSettings->SetValue(kSettingsKeyProxyPort, proxyPort);
+
+ fSettings->Save();
+
+ // Apply settings to default web page settings.
+ BWebSettings::Default()->SetStandardFont(fStandardFontView->Font());
+ BWebSettings::Default()->SetSerifFont(fSerifFontView->Font());
+ BWebSettings::Default()->SetSansSerifFont(fSansSerifFontView->Font());
+ BWebSettings::Default()->SetFixedFont(fFixedFontView->Font());
+ BWebSettings::Default()->SetDefaultStandardFontSize(standardFontSize);
+ BWebSettings::Default()->SetDefaultFixedFontSize(fixedFontSize);
+
+ if (fUseProxyCheckBox->Value() == B_CONTROL_ON) {
+ BWebSettings::Default()->SetProxyInfo(fProxyAddressControl->Text(),
+ proxyPort, B_PROXY_TYPE_HTTP, "", "");
+ } else
+ BWebSettings::Default()->SetProxyInfo();
+
+ // This will find all currently instantiated page settings and apply
+ // the default values, unless the page settings have local overrides.
+ BWebSettings::Default()->Apply();
+
+
+ _ValidateControlsEnabledStatus();
+}
+
+
+void
+SettingsWindow::_RevertSettings()
+{
+ fStartPageControl->SetText(
+ fSettings->GetValue(kSettingsKeyStartPageURL, kDefaultStartPageURL));
+
+ fSearchPageControl->SetText(
+ fSettings->GetValue(kSettingsKeySearchPageURL, kDefaultSearchPageURL));
+
+ fDownloadFolderControl->SetText(
+ fSettings->GetValue(kSettingsKeyDownloadPath, kDefaultDownloadPath));
+ fShowTabsIfOnlyOnePage->SetValue(
+ fSettings->GetValue(kSettingsKeyShowTabsIfSinglePageOpen, true));
+ fAutoHideInterfaceInFullscreenMode->SetValue(
+ fSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode,
+ false));
+ fAutoHidePointer->SetValue(
+ fSettings->GetValue(kSettingsKeyAutoHidePointer, false));
+ fShowHomeButton->SetValue(
+ fSettings->GetValue(kSettingsKeyShowHomeButton, true));
+
+ BString text;
+ text << BrowsingHistory::DefaultInstance()->MaxHistoryItemAge();
+ fDaysInHistoryMenuControl->SetText(text.String());
+
+ // New window policy
+ uint32 newWindowPolicy = fSettings->GetValue(kSettingsKeyNewWindowPolicy,
+ (uint32)OpenStartPage);
+ switch (newWindowPolicy) {
+ default:
+ case OpenStartPage:
+ fNewWindowBehaviorOpenHomeItem->SetMarked(true);
+ break;
+ case OpenSearchPage:
+ fNewWindowBehaviorOpenSearchItem->SetMarked(true);
+ break;
+ case OpenBlankPage:
+ fNewWindowBehaviorOpenBlankItem->SetMarked(true);
+ break;
+ }
+
+ // New tab policy
+ uint32 newTabPolicy = fSettings->GetValue(kSettingsKeyNewTabPolicy,
+ (uint32)OpenBlankPage);
+ switch (newTabPolicy) {
+ default:
+ case OpenBlankPage:
+ fNewTabBehaviorOpenBlankItem->SetMarked(true);
+ break;
+ case OpenStartPage:
+ fNewTabBehaviorOpenHomeItem->SetMarked(true);
+ break;
+ case OpenSearchPage:
+ fNewTabBehaviorOpenSearchItem->SetMarked(true);
+ break;
+ case CloneCurrentPage:
+ fNewTabBehaviorCloneCurrentItem->SetMarked(true);
+ break;
+ }
+
+ // Font settings
+ int32 defaultFontSize = fSettings->GetValue("standard font size",
+ kDefaultFontSize);
+ int32 defaultFixedFontSize = fSettings->GetValue("fixed font size",
+ kDefaultFontSize);
+
+ _SetSizesMenuValue(fStandardSizesMenu->Menu(), defaultFontSize);
+ _SetSizesMenuValue(fFixedSizesMenu->Menu(), defaultFixedFontSize);
+
+ fStandardFontView->SetFont(fSettings->GetValue("standard font",
+ *be_plain_font), defaultFontSize);
+ fSerifFontView->SetFont(fSettings->GetValue("serif font",
+ _FindDefaultSerifFont()), defaultFontSize);
+ fSansSerifFontView->SetFont(fSettings->GetValue("sans serif font",
+ *be_plain_font), defaultFontSize);
+ fFixedFontView->SetFont(fSettings->GetValue("fixed font",
+ *be_fixed_font), defaultFixedFontSize);
+
+ // Proxy settings
+ fUseProxyCheckBox->SetValue(fSettings->GetValue(kSettingsKeyUseProxy,
+ false));
+ fProxyAddressControl->SetText(fSettings->GetValue(kSettingsKeyProxyAddress,
+ ""));
+ text = "";
+ text << fSettings->GetValue(kSettingsKeyProxyPort, (uint32)0);
+ fProxyPortControl->SetText(text.String());
+
+ _ValidateControlsEnabledStatus();
+}
+
+
+void
+SettingsWindow::_ValidateControlsEnabledStatus()
+{
+ bool canApply = _CanApplySettings();
+ fApplyButton->SetEnabled(canApply);
+ fRevertButton->SetEnabled(canApply);
+ // Let the Cancel button be enabled always, as another way to close the
+ // window...
+ fCancelButton->SetEnabled(true);
+
+ bool useProxy = fUseProxyCheckBox->Value() == B_CONTROL_ON;
+ fProxyAddressControl->SetEnabled(useProxy);
+ fProxyPortControl->SetEnabled(useProxy);
+}
+
+
+// #pragma mark -
+
+
+uint32
+SettingsWindow::_NewWindowPolicy() const
+{
+ uint32 newWindowPolicy = OpenStartPage;
+ BMenuItem* markedItem = fNewWindowBehaviorMenu->Menu()->FindMarked();
+ if (markedItem == fNewWindowBehaviorOpenSearchItem)
+ newWindowPolicy = OpenSearchPage;
+ else if (markedItem == fNewWindowBehaviorOpenBlankItem)
+ newWindowPolicy = OpenBlankPage;
+ return newWindowPolicy;
+}
+
+
+uint32
+SettingsWindow::_NewTabPolicy() const
+{
+ uint32 newTabPolicy = OpenBlankPage;
+ BMenuItem* markedItem = fNewTabBehaviorMenu->Menu()->FindMarked();
+ if (markedItem == fNewTabBehaviorCloneCurrentItem)
+ newTabPolicy = CloneCurrentPage;
+ else if (markedItem == fNewTabBehaviorOpenHomeItem)
+ newTabPolicy = OpenStartPage;
+ else if (markedItem == fNewTabBehaviorOpenSearchItem)
+ newTabPolicy = OpenSearchPage;
+ return newTabPolicy;
+}
+
+
+int32
+SettingsWindow::_MaxHistoryAge() const
+{
+ int32 maxHistoryAge = atoi(fDaysInHistoryMenuControl->Text());
+ if (maxHistoryAge <= 0)
+ maxHistoryAge = 1;
+ if (maxHistoryAge >= 35)
+ maxHistoryAge = 35;
+ return maxHistoryAge;
+}
+
+
+void
+SettingsWindow::_SetSizesMenuValue(BMenu* menu, int32 value)
+{
+ for (int32 i = 0; BMenuItem* item = menu->ItemAt(i); i++) {
+ bool marked = false;
+ if (BMessage* message = item->Message()) {
+ int32 size;
+ if (message->FindInt32("size", &size) == B_OK && size == value)
+ marked = true;
+ }
+ item->SetMarked(marked);
+ }
+}
+
+
+int32
+SettingsWindow::_SizesMenuValue(BMenu* menu) const
+{
+ if (BMenuItem* item = menu->FindMarked()) {
+ if (BMessage* message = item->Message()) {
+ int32 size;
+ if (message->FindInt32("size", &size) == B_OK)
+ return size;
+ }
+ }
+ return kDefaultFontSize;
+}
+
+
+BFont
+SettingsWindow::_FindDefaultSerifFont() const
+{
+ // Default to the first "serif" font we find.
+ BFont serifFont(*be_plain_font);
+ font_family family;
+ int32 familyCount = count_font_families();
+ for (int32 i = 0; i < familyCount; i++) {
+ if (get_font_family(i, &family) == B_OK) {
+ BString familyString(family);
+ if (familyString.IFindFirst("sans") >= 0)
+ continue;
+ if (familyString.IFindFirst("serif") >= 0) {
+ serifFont.SetFamilyAndFace(family, B_REGULAR_FACE);
+ break;
+ }
+ }
+ }
+ return serifFont;
+}
+
+
+uint32
+SettingsWindow::_ProxyPort() const
+{
+ return atoul(fProxyPortControl->Text());
+}
+
+
diff --git a/src/apps/webpositive/SettingsWindow.h b/src/apps/webpositive/SettingsWindow.h
new file mode 100644
index 0000000000..251d84405a
--- /dev/null
+++ b/src/apps/webpositive/SettingsWindow.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+#ifndef SETTINGS_WINDOW_H
+#define SETTINGS_WINDOW_H
+
+#include <Window.h>
+
+class BButton;
+class BCheckBox;
+class BMenu;
+class BMenuField;
+class BMenuItem;
+class BTextControl;
+class FontSelectionView;
+class SettingsMessage;
+
+
+class SettingsWindow : public BWindow {
+public:
+ SettingsWindow(BRect frame,
+ SettingsMessage* settings);
+ virtual ~SettingsWindow();
+
+ virtual void MessageReceived(BMessage* message);
+ virtual bool QuitRequested();
+
+ virtual void Show();
+
+private:
+ BView* _CreateGeneralPage(float spacing);
+ BView* _CreateFontsPage(float spacing);
+ BView* _CreateProxyPage(float spacing);
+ void _BuildSizesMenu(BMenu* menu,
+ uint32 messageWhat);
+ void _SetupFontSelectionView(
+ FontSelectionView* view,
+ BMessage* message);
+
+ bool _CanApplySettings() const;
+ void _ApplySettings();
+ void _RevertSettings();
+ void _ValidateControlsEnabledStatus();
+
+ uint32 _NewWindowPolicy() const;
+ uint32 _NewTabPolicy() const;
+ int32 _MaxHistoryAge() const;
+
+ void _SetSizesMenuValue(BMenu* menu, int32 value);
+ int32 _SizesMenuValue(BMenu* menu) const;
+
+ BFont _FindDefaultSerifFont() const;
+
+ uint32 _ProxyPort() const;
+
+private:
+ SettingsMessage* fSettings;
+
+ BTextControl* fStartPageControl;
+ BTextControl* fSearchPageControl;
+ BTextControl* fDownloadFolderControl;
+
+ BMenuField* fNewWindowBehaviorMenu;
+ BMenuItem* fNewWindowBehaviorOpenHomeItem;
+ BMenuItem* fNewWindowBehaviorOpenSearchItem;
+ BMenuItem* fNewWindowBehaviorOpenBlankItem;
+
+ BMenuField* fNewTabBehaviorMenu;
+ BMenuItem* fNewTabBehaviorCloneCurrentItem;
+ BMenuItem* fNewTabBehaviorOpenHomeItem;
+ BMenuItem* fNewTabBehaviorOpenSearchItem;
+ BMenuItem* fNewTabBehaviorOpenBlankItem;
+
+ BTextControl* fDaysInHistoryMenuControl;
+ BCheckBox* fShowTabsIfOnlyOnePage;
+ BCheckBox* fAutoHideInterfaceInFullscreenMode;
+ BCheckBox* fAutoHidePointer;
+ BCheckBox* fShowHomeButton;
+
+ FontSelectionView* fStandardFontView;
+ FontSelectionView* fSerifFontView;
+ FontSelectionView* fSansSerifFontView;
+ FontSelectionView* fFixedFontView;
+
+ BCheckBox* fUseProxyCheckBox;
+ BTextControl* fProxyAddressControl;
+ BTextControl* fProxyPortControl;
+
+ BButton* fApplyButton;
+ BButton* fCancelButton;
+ BButton* fRevertButton;
+
+ BMenuField* fStandardSizesMenu;
+ BMenuField* fFixedSizesMenu;
+};
+
+
+#endif // SETTINGS_WINDOW_H
+
diff --git a/src/apps/webpositive/URLInputGroup.cpp b/src/apps/webpositive/URLInputGroup.cpp
new file mode 100644
index 0000000000..f84f0e12bf
--- /dev/null
+++ b/src/apps/webpositive/URLInputGroup.cpp
@@ -0,0 +1,654 @@
+/*
+ * Copyright 2010 Stephan Aßmus <superstippi@gmx.de>
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+
+#include "URLInputGroup.h"
+
+#include <Bitmap.h>
+#include <Button.h>
+#include <ControlLook.h>
+#include <Clipboard.h>
+#include <GroupLayout.h>
+#include <GroupLayoutBuilder.h>
+#include <LayoutUtils.h>
+#include <MenuItem.h>
+#include <PopUpMenu.h>
+#include <SeparatorView.h>
+#include <TextView.h>
+#include <Window.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "BaseURL.h"
+#include "BitmapButton.h"
+#include "BrowsingHistory.h"
+#include "IconButton.h"
+#include "TextViewCompleter.h"
+
+
+class URLChoice : public BAutoCompleter::Choice {
+public:
+ URLChoice(const BString& choiceText, const BString& displayText,
+ int32 matchPos, int32 matchLen, int32 priority)
+ :
+ BAutoCompleter::Choice(choiceText, displayText, matchPos, matchLen),
+ fPriority(priority)
+ {
+ }
+
+ bool operator<(const URLChoice& other) const
+ {
+ if (fPriority > other.fPriority)
+ return true;
+ return DisplayText() < other.DisplayText();
+ }
+
+ bool operator==(const URLChoice& other) const
+ {
+ return fPriority == other.fPriority
+ && DisplayText() < other.DisplayText();
+ }
+
+private:
+ int32 fPriority;
+};
+
+
+class BrowsingHistoryChoiceModel : public BAutoCompleter::ChoiceModel {
+ virtual void FetchChoicesFor(const BString& pattern)
+ {
+ int32 count = CountChoices();
+ for (int32 i = 0; i < count; i++) {
+ delete reinterpret_cast<BAutoCompleter::Choice*>(
+ fChoices.ItemAtFast(i));
+ }
+ fChoices.MakeEmpty();
+
+ // Search through BrowsingHistory for any matches.
+ BrowsingHistory* history = BrowsingHistory::DefaultInstance();
+ if (!history->Lock())
+ return;
+
+ BString lastBaseURL;
+ int32 priority = INT_MAX;
+
+ count = history->CountItems();
+ for (int32 i = 0; i < count; i++) {
+ BrowsingHistoryItem item = history->HistoryItemAt(i);
+ const BString& choiceText = item.URL();
+ int32 matchPos = choiceText.IFindFirst(pattern);
+ if (matchPos < 0)
+ continue;
+ if (lastBaseURL.Length() > 0
+ && choiceText.FindFirst(lastBaseURL) >= 0) {
+ priority--;
+ } else
+ priority = INT_MAX;
+ lastBaseURL = baseURL(choiceText);
+ fChoices.AddItem(new URLChoice(choiceText,
+ choiceText, matchPos, pattern.Length(), priority));
+ }
+
+ history->Unlock();
+
+ fChoices.SortItems(_CompareChoices);
+ }
+
+ virtual int32 CountChoices() const
+ {
+ return fChoices.CountItems();
+ }
+
+ virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const
+ {
+ return reinterpret_cast<BAutoCompleter::Choice*>(
+ fChoices.ItemAt(index));
+ }
+
+ static int _CompareChoices(const void* a, const void* b)
+ {
+ const URLChoice* aChoice
+ = *reinterpret_cast<const URLChoice* const *>(a);
+ const URLChoice* bChoice
+ = *reinterpret_cast<const URLChoice* const *>(b);
+ if (*aChoice < *bChoice)
+ return -1;
+ else if (*aChoice == *bChoice)
+ return 0;
+ return 1;
+ }
+
+private:
+ BList fChoices;
+};
+
+
+// #pragma mark - URLTextView
+
+
+static const float kHorizontalTextRectInset = 4.0;
+
+
+class URLInputGroup::URLTextView : public BTextView {
+private:
+ static const uint32 MSG_CLEAR = 'cler';
+
+public:
+ URLTextView(URLInputGroup* parent);
+ virtual ~URLTextView();
+
+ virtual void MessageReceived(BMessage* message);
+ virtual void FrameResized(float width, float height);
+ virtual void MouseDown(BPoint where);
+ virtual void KeyDown(const char* bytes, int32 numBytes);
+ virtual void MakeFocus(bool focused = true);
+
+ virtual BSize MinSize();
+ virtual BSize MaxSize();
+
+ void SetUpdateAutoCompleterChoices(bool update);
+
+protected:
+ virtual void InsertText(const char* inText, int32 inLength,
+ int32 inOffset,
+ const text_run_array* inRuns);
+ virtual void DeleteText(int32 fromOffset, int32 toOffset);
+
+private:
+ void _AlignTextRect();
+
+private:
+ URLInputGroup* fURLInputGroup;
+ TextViewCompleter* fURLAutoCompleter;
+ BString fPreviousText;
+ bool fUpdateAutoCompleterChoices;
+};
+
+
+URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent)
+ :
+ BTextView("url"),
+ fURLInputGroup(parent),
+ fURLAutoCompleter(new TextViewCompleter(this,
+ new BrowsingHistoryChoiceModel())),
+ fPreviousText(""),
+ fUpdateAutoCompleterChoices(true)
+{
+ MakeResizable(true);
+ SetStylable(true);
+ fURLAutoCompleter->SetModificationsReported(true);
+}
+
+
+URLInputGroup::URLTextView::~URLTextView()
+{
+ delete fURLAutoCompleter;
+}
+
+
+void
+URLInputGroup::URLTextView::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ case MSG_CLEAR:
+ SetText("");
+ break;
+ default:
+ BTextView::MessageReceived(message);
+ break;
+ }
+}
+
+
+void
+URLInputGroup::URLTextView::FrameResized(float width, float height)
+{
+ BTextView::FrameResized(width, height);
+ _AlignTextRect();
+}
+
+
+void
+URLInputGroup::URLTextView::MouseDown(BPoint where)
+{
+ bool wasFocus = IsFocus();
+ if (!wasFocus)
+ MakeFocus(true);
+
+ int32 buttons;
+ if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
+ buttons = B_PRIMARY_MOUSE_BUTTON;
+
+ if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
+ // Display context menu
+ int32 selectionStart;
+ int32 selectionEnd;
+ GetSelection(&selectionStart, &selectionEnd);
+ bool canCutOrCopy = selectionEnd > selectionStart;
+
+ bool canPaste = false;
+ if (be_clipboard->Lock()) {
+ if (BMessage* data = be_clipboard->Data())
+ canPaste = data->HasData("text/plain", B_MIME_TYPE);
+ be_clipboard->Unlock();
+ }
+
+ BMenuItem* cutItem = new BMenuItem("Cut", new BMessage(B_CUT));
+ BMenuItem* copyItem = new BMenuItem("Copy", new BMessage(B_COPY));
+ BMenuItem* pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE));
+ BMenuItem* clearItem = new BMenuItem("Clear", new BMessage(MSG_CLEAR));
+ cutItem->SetEnabled(canCutOrCopy);
+ copyItem->SetEnabled(canCutOrCopy);
+ pasteItem->SetEnabled(canPaste);
+ clearItem->SetEnabled(strlen(Text()) > 0);
+
+ BPopUpMenu* menu = new BPopUpMenu("url context");
+ menu->AddItem(cutItem);
+ menu->AddItem(copyItem);
+ menu->AddItem(pasteItem);
+ menu->AddItem(clearItem);
+
+ menu->SetTargetForItems(this);
+ menu->Go(ConvertToScreen(where), true, true, true);
+ return;
+ }
+
+ // Only pass through to base class if we already have focus.
+ if (!wasFocus)
+ return;
+
+ BTextView::MouseDown(where);
+}
+
+
+void
+URLInputGroup::URLTextView::KeyDown(const char* bytes, int32 numBytes)
+{
+ switch (bytes[0]) {
+ case B_TAB:
+ BView::KeyDown(bytes, numBytes);
+ break;
+
+ case B_ESCAPE:
+ // Revert to text as it was when we received keyboard focus.
+ SetText(fPreviousText.String());
+ SelectAll();
+ break;
+
+ case B_RETURN:
+ // Don't let this through to the text view.
+ break;
+
+ default:
+ BTextView::KeyDown(bytes, numBytes);
+ break;
+ }
+}
+
+void
+URLInputGroup::URLTextView::MakeFocus(bool focus)
+{
+ if (focus == IsFocus())
+ return;
+
+ BTextView::MakeFocus(focus);
+
+ if (focus) {
+ fPreviousText = Text();
+ SelectAll();
+ }
+
+ fURLInputGroup->Invalidate();
+}
+
+
+BSize
+URLInputGroup::URLTextView::MinSize()
+{
+ BSize min;
+ min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset);
+ // we always add at least one pixel vertical inset top/bottom for
+ // the text rect.
+ min.width = min.height * 3;
+ return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
+}
+
+
+BSize
+URLInputGroup::URLTextView::MaxSize()
+{
+ BSize max(MinSize());
+ max.width = B_SIZE_UNLIMITED;
+ return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
+}
+
+
+void
+URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update)
+{
+ fUpdateAutoCompleterChoices = update;
+}
+
+
+void
+URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength,
+ int32 inOffset, const text_run_array* inRuns)
+{
+ // Filter all line breaks, note that inText is not terminated.
+ if (inLength == 1) {
+ if (*inText == '\n' || *inText == '\r')
+ BTextView::InsertText(" ", 1, inOffset, inRuns);
+ else
+ BTextView::InsertText(inText, 1, inOffset, inRuns);
+ } else {
+ BString filteredText(inText, inLength);
+ filteredText.ReplaceAll('\n', ' ');
+ filteredText.ReplaceAll('\r', ' ');
+ BTextView::InsertText(filteredText.String(), inLength, inOffset,
+ inRuns);
+ }
+
+ // Make the base URL part bold.
+ BString text(Text(), TextLength());
+ int32 baseUrlStart = text.FindFirst("://");
+ if (baseUrlStart >= 0)
+ baseUrlStart += 3;
+ else
+ baseUrlStart = 0;
+ int32 baseUrlEnd = text.FindFirst("/", baseUrlStart);
+ if (baseUrlEnd < 0)
+ baseUrlEnd = TextLength();
+ BFont font;
+ GetFont(&font);
+ const rgb_color black = (rgb_color){ 0, 0, 0, 255 };
+ const rgb_color gray = (rgb_color){ 60, 60, 60, 255 };
+ if (baseUrlStart > 0)
+ SetFontAndColor(0, baseUrlStart - 1, &font, B_FONT_ALL, &gray);
+ if (baseUrlEnd > baseUrlStart) {
+ font.SetFace(B_BOLD_FACE);
+ SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black);
+ }
+ if (baseUrlEnd < TextLength()) {
+ font.SetFace(B_REGULAR_FACE);
+ SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray);
+ }
+
+ fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
+}
+
+
+void
+URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
+{
+ BTextView::DeleteText(fromOffset, toOffset);
+
+ fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
+}
+
+
+void
+URLInputGroup::URLTextView::_AlignTextRect()
+{
+ // Layout the text rect to be in the middle, normally this means there
+ // is one pixel spacing on each side.
+ BRect textRect(Bounds());
+ textRect.left = 0.0;
+ float vInset = max_c(1,
+ floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5));
+ float hInset = kHorizontalTextRectInset;
+
+ if (be_control_look)
+ hInset = be_control_look->DefaultLabelSpacing();
+
+ textRect.InsetBy(hInset, vInset);
+ SetTextRect(textRect);
+}
+
+
+const uint32 kGoBitmapWidth = 14;
+const uint32 kGoBitmapHeight = 14;
+const color_space kGoBitmapFormat = B_RGBA32;
+
+const unsigned char kGoBitmapBits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0,
+ 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
+ 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
+ 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
+ 0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
+ 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
+ 0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
+ 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+// #pragma mark - PageIconView
+
+
+class URLInputGroup::PageIconView : public BView {
+public:
+ PageIconView()
+ :
+ BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
+ fIcon(NULL)
+ {
+ SetDrawingMode(B_OP_ALPHA);
+ SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
+ }
+
+ ~PageIconView()
+ {
+ delete fIcon;
+ }
+
+ virtual void Draw(BRect updateRect)
+ {
+ if (fIcon == NULL)
+ return;
+
+ BRect bounds(Bounds());
+ BRect iconBounds(0, 0, 15, 15);
+ iconBounds.OffsetTo(
+ floorf((bounds.left + bounds.right
+ - (iconBounds.left + iconBounds.right)) / 2 + 0.5f),
+ floorf((bounds.top + bounds.bottom
+ - (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f));
+ DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
+ B_FILTER_BITMAP_BILINEAR);
+ }
+
+ virtual BSize MinSize()
+ {
+ if (fIcon != NULL)
+ return BSize(18, 18);
+ return BSize(0, 0);
+ }
+
+ virtual BSize MaxSize()
+ {
+ return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
+ }
+
+ virtual BSize PreferredSize()
+ {
+ return MinSize();
+ }
+
+ void SetIcon(const BBitmap* icon)
+ {
+ if (icon == NULL && fIcon == NULL)
+ return;
+ if (!(fIcon != NULL && icon != NULL))
+ InvalidateLayout();
+ delete fIcon;
+ if (icon)
+ fIcon = new BBitmap(icon);
+ else
+ fIcon = NULL;
+ Invalidate();
+ }
+
+private:
+ BBitmap* fIcon;
+};
+
+
+// #pragma mark - URLInputGroup
+
+
+URLInputGroup::URLInputGroup(BMessage* goMessage)
+ :
+ BGroupView(B_HORIZONTAL, 0.0),
+ fWindowActive(false)
+{
+ GroupLayout()->SetInsets(2, 2, 2, 2);
+
+ fIconView = new PageIconView();
+ GroupLayout()->AddView(fIconView, 0.0f);
+
+ fTextView = new URLTextView(this);
+ AddChild(fTextView);
+
+ AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER));
+
+// TODO: Fix in Haiku, no in-built support for archived BBitmaps from
+// resources?
+// fGoButton = new BitmapButton("kActionGo", NULL);
+ fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth,
+ kGoBitmapHeight, kGoBitmapFormat, goMessage);
+ GroupLayout()->AddView(fGoButton, 0.0f);
+
+ SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
+ SetLowColor(ViewColor());
+ SetViewColor(B_TRANSPARENT_COLOR);
+
+ SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
+ B_ALIGN_VERTICAL_CENTER));
+}
+
+
+URLInputGroup::~URLInputGroup()
+{
+}
+
+
+void
+URLInputGroup::AttachedToWindow()
+{
+ BGroupView::AttachedToWindow();
+ fWindowActive = Window()->IsActive();
+}
+
+
+void
+URLInputGroup::WindowActivated(bool active)
+{
+ BGroupView::WindowActivated(active);
+ if (fWindowActive != active) {
+ fWindowActive = active;
+ Invalidate();
+ }
+}
+
+
+void
+URLInputGroup::Draw(BRect updateRect)
+{
+ BRect bounds(Bounds());
+ rgb_color base(LowColor());
+ uint32 flags = 0;
+ if (fWindowActive && fTextView->IsFocus())
+ flags |= BControlLook::B_FOCUSED;
+ be_control_look->DrawTextControlBorder(this, bounds, updateRect, base,
+ flags);
+}
+
+
+void
+URLInputGroup::MakeFocus(bool focus)
+{
+ // Forward this to the text view, we never accept focus ourselves.
+ fTextView->MakeFocus(focus);
+}
+
+
+BTextView*
+URLInputGroup::TextView() const
+{
+ return fTextView;
+}
+
+
+void
+URLInputGroup::SetText(const char* text)
+{
+ if (!text || !Text() || strcmp(Text(), text) != 0) {
+ fTextView->SetUpdateAutoCompleterChoices(false);
+ fTextView->SetText(text);
+ fTextView->SetUpdateAutoCompleterChoices(true);
+ }
+}
+
+
+const char*
+URLInputGroup::Text() const
+{
+ return fTextView->Text();
+}
+
+
+BButton*
+URLInputGroup::GoButton() const
+{
+ return fGoButton;
+}
+
+
+void
+URLInputGroup::SetPageIcon(const BBitmap* icon)
+{
+ fIconView->SetIcon(icon);
+}
+
diff --git a/src/apps/webpositive/URLInputGroup.h b/src/apps/webpositive/URLInputGroup.h
new file mode 100644
index 0000000000..d71f713ca7
--- /dev/null
+++ b/src/apps/webpositive/URLInputGroup.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010 Stephan Aßmus <superstippi@gmx.de>
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+#ifndef URL_INPUT_GROUP_H
+#define URL_INPUT_GROUP_H
+
+#include <GroupView.h>
+
+class BButton;
+class BTextView;
+
+
+class URLInputGroup : public BGroupView {
+public:
+ URLInputGroup(BMessage* goMessage);
+ virtual ~URLInputGroup();
+
+ virtual void AttachedToWindow();
+ virtual void WindowActivated(bool active);
+ virtual void Draw(BRect updateRect);
+ virtual void MakeFocus(bool focus = true);
+
+ BTextView* TextView() const;
+ void SetText(const char* text);
+ const char* Text() const;
+
+ BButton* GoButton() const;
+
+ void SetPageIcon(const BBitmap* icon);
+
+private:
+ class PageIconView;
+ class URLTextView;
+
+ PageIconView* fIconView;
+ URLTextView* fTextView;
+ BButton* fGoButton;
+ bool fWindowActive;
+};
+
+#endif // URL_INPUT_GROUP_H
+
diff --git a/src/apps/webpositive/WebPositive.rdef b/src/apps/webpositive/WebPositive.rdef
new file mode 100644
index 0000000000..9b475b34d1
--- /dev/null
+++ b/src/apps/webpositive/WebPositive.rdef
@@ -0,0 +1,350 @@
+resource app_signature "application/x-vnd.Haiku-WebPositive";
+
+resource app_version {
+ major = 0,
+ middle = 0,
+ minor = 1,
+ variety = B_APPV_ALPHA,
+ internal = 0,
+ short_info = "WebPositive",
+ long_info = "WebPositive ©2007-2010 The WebKit Haiku Project"
+};
+
+resource app_flags B_SINGLE_LAUNCH;
+
+resource file_types message {
+ "types" = "text/html",
+ "types" = "image/svg+xml",
+ "types" = "application/x-vnd.Be.URL.file",
+ "types" = "application/x-vnd.Be.URL.ftp",
+ "types" = "application/x-vnd.Be.URL.http",
+ "types" = "application/x-vnd.Be.URL.https"
+};
+
+resource vector_icon {
+ $"6E636966450500020106033D0AFE000000000000BD0AFE48F10748A0783133FF"
+ $"FFC80066FFFF0034CC020106033D0AFE000000000000BD0AFE48F10748A07854"
+ $"09B4B6D8033784FF051D61020106043D950B0000000000003D950B494FBC479B"
+ $"EC00FFFFFF4C00FF008F00B200FF008000050002030605B812A5BE03E13DE784"
+ $"B8021049F79F49EDD800F1F1F136D9DDF48A9996B9B4B8BEDBFFF4F4F404EBD0"
+ $"020006023C92C0388F5FB854503C576348D8DF48895B004137A9FFB9B9B90401"
+ $"7E03FF360003FFFF0003FF9C0003FF000002010202BB36AEBA1760B80FFF392B"
+ $"574ACCFB48652300000001FFFF0000013205400500020004024AFFFC00FFFFBA"
+ $"360500020106033D0AFE000000000000BD0AFE48F10748A0783133FFFFC80066"
+ $"FFFF0034CC020106023D0AFE000000000000BD0AFE48F10748A0780033FFFFFF"
+ $"0033CC020106023D0AFE000000000000BD0AFE48F10748A0780033FFFFFF0066"
+ $"FF020004027AFFFC00DDFFBA360201040289FF0000A9C000000200040200FFE5"
+ $"CED0ECB989020004020DEFBC8CFFD4AA820200040200FFCB9CE9ECB989020004"
+ $"0200FFCB9CE9ECB98902000402B3FFCB9CBBD4AA820500020106033D0AFE0000"
+ $"00000000BD0AFE48F10748A0783133FFFFC80066FFFF0034CC020106033D0AFE"
+ $"000000000000BD0AFE48F10748A0785409B4B6D8033784FF051D61020106043D"
+ $"950B0000000000003D950B494FBC479BEC00FFFFFF4C00FF008F00B200FF0080"
+ $"00050002030605B812A5BE03E13DE784B8021049F79F49EDD800F1F1F136D9DD"
+ $"F48A9996B9B4B8BEDBFFF4F4F404EBD0020006023C92C0388F5FB854503C5763"
+ $"48D8DF48895B004137A9FFB9B9B904017E03FF360003FFFF000200040200FFBC"
+ $"34FFFF9C0003FF000002010202BB36AEBA1760B80FFF392B574ACCFB48652300"
+ $"000001FFFF00000132054003FFFFC9020014020026FF1C0500050002030605B8"
+ $"12A5BE03E13DE784B8021049F79F49EDD800F1F1F136D9DDF48A9996B9B4B8BE"
+ $"DBFFF4F4F404EBD0020006023C92C0388F5FB854503C576348D8DF48895B0041"
+ $"37A9FFB9B9B904017E03FF360003FFFF0003FF9C0003FF000002010202BB36AE"
+ $"BA1760B80FFF392B574ACCFB48652300000001FFFF0000013205400500030052"
+ $"7502001002F8006BFF003E04006C050003900000020310020000006E00650400"
+ $"6B05000359463503C3A414039D816035000000000204BFC1B55A36B55AC3BEB5"
+ $"5AC6FABC92C6FAB895C6FAC08FBFC1C3CBC3BEC3CB36C3CBB889BC92B889C08F"
+ $"B889B895066EFFFFF30FFFFEFFFFBFBFFEFFFEFFFEFFFFFFFFBFFFEFDFFBFBFF"
+ $"FF0FC5DBB8B5C713BA9FC4CAB706C10FB579C307B5D4C0A226BFC1B55AC031B5"
+ $"5ABDBCB55ABA69B7BA36B63AB8F9B94EB89CBD9BB84EBB7EB8C7BE9AB924BF76"
+ $"B8E0BEE7B9DEC11FBCDFC332BB37C278BEC4C405C2D7C31AC0FAC3FCC2C5C2C0"
+ $"C22EC2B7C279C2BDC22EC2B7C226C240C2BDC240C2BDC206C2AFC199C2DAC1D0"
+ $"C2CB44C2EEC0D4C2DCC114C2E5C051C2CEBF42C312BFB0C2B4BF3DC312BF35BF"
+ $"1FBDECC312BE23C312BDB8C2EDBD57C2DEBD86C2E2BD53C2DBBD3EC2C2BD3EC2"
+ $"C2BD31C2B4BD12C2A4BD26C2AABD12C2A4BCA4C27EBC31C254BC31C254BC27C2"
+ $"51BC11C24FBC1DC250BC10C24BBC0FC241BC10C245BC0FC241BBF5C219BBF5C2"
+ $"19BBD2C1DDBB68C194BBADC1A4BB70C16EBB58C128BB62C14EBB4BC101BB4BC0"
+ $"ACBB47C0D7BB4BC0ACBB4EC087BB4EC087BB51C054BB4DBFEEBB54C021BB9DBF"
+ $"92BBA9BEBFBBAFBF35BBA9BEBFBBA7BE92BBA7BE92BBA7BE52BBBBBE2FBBA8BE"
+ $"4BBBBBBE2FBBC6BE20BBC6BE20BC00BDD4BBE2BCFBBC5BBD3DBBE2BCFBBAB7BC"
+ $"58BA8ABC43BA90BC45BA8ABC43BA8ABC42BA8ABC42BA88BBEABA22BB78BA6ABB"
+ $"ACBA22BB78BA12BB6AB9F8BB48B9ADBAEFB9ADBAEFB97DBAC3B927BAB0B94FBA"
+ $"B5B924BAA9B91ABA9AB920BAA0B984BA6DB9DEB996B9B5B9F9B9F6B95DBA36B8"
+ $"EFBA0AB91FBA5EB8C4BA78B85EBA83B89EBACCB83CBB60B7E7BB18B813BB60B7"
+ $"E7BB6AB7E1BBFEB787BBFEB787BC65B749BCB6B683BC93B6F5BCF5B689BD58B6"
+ $"64BD2CB67BBDA8B695BE6CB6503C29BE6FB64FBE88B64FBE7CB64FBE62B6A0BE"
+ $"E1B72FBE88B70EBEE4B733BEE8B73DBEE5B73ABED8B743BEC5B749BE74B7A5BE"
+ $"74B7A5BE63B7DCBE8FB848BE74B816BE90B84CBE91B852BE91B852BE93B85BBE"
+ $"9CB87ABE97B86BBE93B883BE8CB88DBE8CB88DBE39B8EFBD91B98FBDB6B906BD"
+ $"91B98FBD8DB9A0BD8DB9A0BD64B9D7BD7EBA54BD59BA19BD7ABA5CBD76BA62BD"
+ $"76BA62BD56BA9FBD4DBB2BBD38BAE6BD5FBB6FBDCCBBCEBD98BBA1BDFFBBF9BE"
+ $"73BC3FBE33BC27BEBEBC5CBF5DBC42BF11BC55BF5DBC42BFAB37BFAB37BFD0BC"
+ $"1DC01CBC0FBFF4BC0BC045BC13C083BC4EC066BC35C04DBCD9C0F3BDDAC0B6BD"
+ $"63C0F3BDDAC107BDF8C102BDF2C0BABE83C126BF90C0B1BF1CC126BF90C133BF"
+ $"9CC133BF9CC130C00EC1C4C0F8C135C0D6C258C11CC347C059C2EEC0C8C347C0"
+ $"59C36BC02EC388C018C388C018C3A7C003C3DABFC4C3C9BFE8C3EEBF9FC408BF"
+ $"51C3EFBF73C408BF51C408BF52C408BF52C40CBF99C49540C450BFCEC4EBBFB3"
+ $"C541BF0FC51FBF56C541BF0FC551BEF24EBEEA4EBEEAC57FBEA3C568BDFCC59E"
+ $"BE46C53ABDBAC4A2BDDEC4E0BDA9C4A2BDCABDB5C4A4BD31C4A4BD31C4A7BD1D"
+ $"C4E8BCDEC4D2BCF5C4E8BCDEC4FEBCC8C516BCB3C516BCB3C544BC8BC582BC1F"
+ $"C570BC5DC58BBC04C587BBBBC586BBD9C587BBBBC587BBA8C587BB9DC587BB9D"
+ $"C58CBB604EBAF9C584BB23C589BADAC5C1BA5DC5B0BAA1C5C3BA61C5CBBA6FC5"
+ $"CBBA6FC5D8BA8EC60EBAD3C5EDBAB3C609BB1EC648BBAAC626BB67C669BBEAC6"
+ $"6EBC7AC66FBC33C66EBC7AC671BCC3C671BCC3C673BCE9C66DBD36C677BD10C6"
+ $"64BD5AC649BD9BC652BD78C633BDEBC62BBE91C62DBE3F50BED5C67FBF2BC648"
+ $"BF16C750BD0F0006BC11B6E3BC11B6E3BC32B6AFBC54B637BC46B674BB81B6AA"
+ $"BA2BB801BAC4B747BA87B7E2BB2FB77DBADDB7AFBB59B764BBAAB733BB82B74D"
+ $"BBCBB71CBC11B6E3BBFBB707BC11B6E3001DBACDBFD1BACDBFD1BAEBBFA9BB29"
+ $"BF5BBB12BF89BB43BF24BB34BE92BB34BECFBB34BE4DBB5FBDE9BB34BE25BB80"
+ $"BDBEBBAABD5FBBA0BD98BB47BD29BA81BCBEBAE5BCF3BA67BCB1BA1FBC8332BC"
+ $"9FBA0FBC5BBA0DBC02BA1FBC28B9FDBBE5B9B9BBB6B9D0BBD0B99BBB91B95FBB"
+ $"44B983BB66B93DBB23B8EFBB2CB91BBB15B8C0BB47B8BEBADEB8C2BAFFB8B1BB"
+ $"13B89FBB7EB8A7BB48B8C8BB7EB8C5BBB2B8C2BB89B8C6BBDBB8C8BC2DB8C7BC"
+ $"04B8CFBC40B8BABC48B8CBBC48B8B3BC4BB8A9BC4EB8AEBC4DB8A0BC57B893BC"
+ $"4BB898BC55B881BC4AB88EBD0FB88DBD00B893BD49B8E0BDE7B88DBE02B90DBE"
+ $"03B922BE74B906BE4BB946BEA7B985BF00B972BEC1B996BF3EB9A240B997BF80"
+ $"B9AFC010BA16C05EB9DBC02DBA24C0B3BA75C126BA3CC0E7BA8FC143BAC0C17F"
+ $"BAA9C15FBAD7C19FBAFBC17BBAE3C19DBADDC13ABAD6C0A5BAD2C0EFBADBC060"
+ $"BACDBFD1BAEAC012BACDBFD10009BD5DB5D6BD5DB5D6BD81B5EDBDB1B60DBD83"
+ $"B605BDCFB613BE19B600BE01B619BE55B5C3BEF5B5C3BEB0B5EFBF2CB5A1BFA3"
+ $"B55D3FB55DBFCEB55DC00CB58CBFE9B577C03CB5A6C08FB566C063B574BF39B5"
+ $"40BCA7B60DBDDCB57ABCEBB623BD5DB5D6BD29B600BD5DB5D60005BF34B6E9BF"
+ $"34B6E9BF55B6EBBF99B6F5BF7BB702BFCEB6E0BF87B67CBF9BB693BF77B66DBF"
+ $"3CB663BF2FB623BF44B68FBF34B6E9BF6AB6CBBF34B6E90004BA06B87FBA06B8"
+ $"7FBA0BB867B9EEB84FB9FDB85FB982B8E4B8EFBA35B92CB987B97D31BA06B87F"
+ $"B974B8EFBA06B87F0066C6F6BC24C6F6BC24C6DBBB09C6002FC68AB9F1C5B9B8"
+ $"7BC4FFB79BC563B805C4CEB768C463B708C49AB736C452B6FBC427B6EFC43FB6"
+ $"E2C411B6FAC407B6C1C401B6D0C315B60FC0D0B56EC1F8B59AC0FAB58AC15EB5"
+ $"9CC130B589C170B5A1C178B5C9C19FB5BDC12CB5E2C08AB5C9C0D7B5C3C059B5"
+ $"CDC03FB60DC042B5DDC03DB629C03FB676C036B65CC04AB694C0B6B65EC0A7B6"
+ $"6FC0CDB643C0F1B619C0B0B607C108B64AC0D0B6B0C13AB6D5C0B5B6A7C066B6"
+ $"89C083B67CC063B689C05CB6AFC05EB6AAC053B6C0C030B6CFC043B6CABFE5B6"
+ $"E4BF94B741BFB0B6EFBF87B768BF5CB790BF88B781BF3BB79CBEF6B7B2BF17B7"
+ $"A3BECCB7C7BEF9B81BBEE9B7FFBF05B82FBF19B86CBF02B860BF59B891BFBEB7"
+ $"E0BFA3B801BFE0B7B3C038B7ADC008B7BCC04FB7A6C084B776C06CB76EC0D3B7"
+ $"95C0F7B83EC0EBB7F4C10FB83FC135B80FC100B801C10CB7E5C0ECB76F43B7AA"
+ $"C125B795C15EB7FCC14DB7BCC170B83CC1C1B820C18DB860C1CFB813C1FCB7D4"
+ $"C1E1B7BCC213B7E9C1F1B838C2142DC206B84BC23AB83CC222B842C259B83447"
+ $"B841C26CB835C2AEB84FC2F9B856C2D5B854C320B859C346B898C335B87AC376"
+ $"B8E6C249B8C5C258B8C6C227B8C2C1E1B8CFC202B8C1C1C8B8DAC199B8EFC1B6"
+ $"2FC178B8E5C13EB8B5C15AB8C7C117B89BC0C4B872C0EFB885C07EB853BFE2B8"
+ $"4EC02EB83FBFBFB855BF7FB87BBF9BB866BF5BB896BF10B8A4BF34B87FBEDBB8"
+ $"DBBE6DB941BEB5B91FBE50B94FBE19B976BE2DB95BBE03B990BDF5B9D1BE03B9"
+ $"B4BDE8B9EDBDE2BA1DBDCAB9FBBDF7BA3BBDEFBA76BDFEBA52BDD9BAA9BDC9BB"
+ $"27BD9EBAEEBDF235BE71BBBBBE39BB93BEAFBBE9BF40BBD1BEFBBBE5BF82BBC1"
+ $"C009BB9CBFC4BBA0C05EBB95C0DCBC03C0A1BBCDC104BC26C0F4BC67C113BC34"
+ $"C0D3BC9EC103BD00C0E8BCC7C11FBD3644BDA3C13CBD6CC166BDB9C182BDE8C1"
+ $"76BDCFC18DBE03C16DBE2CC17ABE15C152BE5DC13EBECEC13EBE97C13EBF0DC1"
+ $"85BF4CC15CBF24C1A9BF71C1A4BFE2C1A7BFB3C1A0C025C1DAC087C1B3C053C2"
+ $"15C08AC289C06CC253C086C2CBC04DC319BFDDC2E8C00EC346BFB0C386BF66C3"
+ $"77BFAAC396BF12C3FEBEBFC3C1BEF2C435BE8FC430BE0CC42DBE4DC432BDC0C4"
+ $"33BD21C428BD6CC43BBCDDC4AABC77C47EBCA7C4CDBC55C511BC03C505BC34C5"
+ $"19BBE7C512BBA8C51236C512BB92C51035C51ABB70C4F7BB29C47EBB2BC489BB"
+ $"5DC475BB0EC48ABB01C476BB13C4A1BAECC477BACDC48ABAD7C441BAACC3F0BA"
+ $"66C418BA9AC3CCBA36C390B9CAC3ADBA00C389B9BDC36CB93AC341B92DC39AB9"
+ $"4AC3E1B9A2C3C3B981C419B9DFC46DBA6AC44ABA1FC46EBA73C496BA89C48EBA"
+ $"85C4AFBA9CC4EABAA8C4CABAABC533BAA1C559BA1F4EBA60C56231C4E8B9AEC5"
+ $"1DB9DAC4CAB996C49EB958C4B6B975C48EB946C473B90DC466B92AC492B8F9C4"
+ $"BCB91CC49FB90BC4DFB932C52BB951C505B9444EB95EC5B4B96CC591B952C5C9"
+ $"B97BC5E7B9B8C5D8B9A1C5FEB9DCC626BA26C614BA00C644BA60C694BA9EC64D"
+ $"BA88C667BAE8C6B6BB83C693BB3FC6DEBBD3C6E3BC7BC6E4BC22C6E2BCCEC6D0"
+ $"BD78C6F4BD2BC6A6BDCCC69FBE9AC69FBE3EC6A0BE95C6A4BE88C6A2BE8DC6A1"
+ $"3DC6A0BEC0C696BEBEC6B2BEC3C6D5BE01C6D2BE15C6F5BD65C6F6BC24C700BC"
+ $"C3C6F6BC240008C301B7D8C301B7D8C2E6B7DBC2D4B7A3C2E8B7AEC2B1B792C2"
+ $"69B7B7C288B7A7C204B7EEC236B742C221B77BC239B73AC285B743C279B746C2"
+ $"A6B740C2F2B74EC2D7B730C30BB76AC33CB7A1C31FB787C35FB7C1C301B7D8C3"
+ $"1CB7D4C301B7D80004BB52C203BB52C203BB44C20CBB26C221BB37C21DBB54C2"
+ $"48BBB9C290BB86C26EBB91C265BB52C203BB84C226BB52C2030005C4F3BE2DC4"
+ $"F3BE2DC4D3BE57C48BBEA4C4A5BE75C46EBEDAC47DBF52C47DBF18C4B6BF37C4"
+ $"EDBEB8C4D0BEEBC502BE92C4F3BE2DC531BE48C4F3BE2D0009C212C326C212C3"
+ $"26C17FC38FBFF6C344C09DC332BFD0C348BF8DC36ABFABC350BF66C38BBF1FC3"
+ $"86BF56C386BEAFC386BDCDC386BE3EC386BEC1C3CBC0BFC3BABFC3C3DCC137C3"
+ $"A9C221C367C1AEC38DC229C364C261C32AC280C337C248C322C212C326C229C3"
+ $"31C212C32602024E284E2C542E4C324C30482E020250304C3252344C364E344C"
+ $"34020258B81858305C32543654324E300202583456365836563C5A3854380204"
+ $"BCD5C1F1BACDC36FBEA5C09BC4A6BFA6C360BE3CC67FC1B0C064C5ACC23AC444"
+ $"BE90C715B827C85CB961C9B8B682C68B0204C3CFC072C309BF45C521C2733DC6"
+ $"5EC0DDC4E4BC10C7F6B880C76BB8DAC831B841C6E2BC6FC2E2B880C5CDBE42C1"
+ $"890622AEAAE6BAEAAAAF9A0EB9E4C6DEB9CB50B985C672BA12C5CDBAABC593BA"
+ $"C54DBAB8C4BBBAEBC3C8BAABC37CBA85C0CBBC88BE0E36B8E6B993B8DA2EB874"
+ $"B92DB970B7AFBCD5BA52BCEEB87ABE3AB7AEBD61B7AEBF12B7AEBF852EBF92BA"
+ $"78C0D2BB91C49BBB6AC51ABC10C51ABBB6C51ABC5AC49BBCB6C21DBCC3BF9FBD"
+ $"CEBFCBC18BC1FCC323BF00C13E46C33BC196C43B46C3DBC12AC484C02BC493C0"
+ $"11C3EFC091C36FBE20C2BDBF40BC50C14ABBF74EBB8451BBE8C66EBA9EC6B906"
+ $"11AAAAA8BA02BBEFB9ECBB49BAB8BCC8BB9DBCE1BE4EBB70C018BC7BC0A5BE07"
+ $"BF73BE6044BF9FBF6CBDDBC06CBD0245BCAEC1C9BC2FBF40BAABBE0EBB04BF0D"
+ $"BB04BD82BB04BD14BAB8BCAEBA38060BBAAA2A31C653BA0BC6C4BB84C645BB2B"
+ $"C69FBBB6C614BBAFC5A0BBDDC309BC1DC163BCAEC0CABB70BFE5BAD7C0CABADE"
+ $"C36FBAE3C5C5060AAAAB0ABE46C125BF99C130BF80454448C1FCC394C21DC361"
+ $"C1C3C3F0C11EC447C052C493C02BC3EFC105C389BE67470605EA02BB37BAC5BB"
+ $"D6BA12B9ACB887B8E0B8ADB90BB869B887B939B920B96C0A05C16BBB91C473BB"
+ $"91C4D9BC36C4A6BCC3C16BBCE906078A2EBD4F2EBDB5B814BEA7BF402EBF4DBA"
+ $"45BE0EBB04BEA7BB2BBD8ABAE2BD29BA520607AA3A39B8EDBE012F3CB99F3EB9"
+ $"93BEDAB920BF99B946BE34B77BBFF5B7C239B74804032EBCE932BDCEBB2BBD34"
+ $"BB12BECDBB51BF4DBA380208CA68BA5FCB40BD02CA39B9D0CA0EB6BCCAF3B7AE"
+ $"C947B5E955B58A55B58A55B58AC843B841C8FFB76C53B953C718B814C6E62FC7"
+ $"4BB728C5A02FC601B84EC2703DC645BCB6C5ACBC1DC711BD82C89DBEE7C79E40"
+ $"C89DBEE7060ABFFB0FC6F1C097C625BFCBC6F1BFCB5A345C3758315A2E5A305A"
+ $"2C582A562E582C54305230C691B854C691B920C691B7DAC53ABAAB4FB9ACC501"
+ $"BB1EC46EBBF7C4ADBB9DC3FABCB6C56DBE0EC38EBE4EC64DBDF00207C86FBE74"
+ $"C86FBE74C7A3BDA8C78ABB44C80ABB28C71835C633BBB7C6D4BBEFC5DABB98C5"
+ $"01BB44C509BB79C4F1BAEDC3F4BC43C43EBBD1C4B4BC83C6FF38C63336C7CB3A"
+ $"C83CBE9AC5B3BF8DC908BD020202C6392F4DB9F9C66CBAC54DBBE24FBB16C46E"
+ $"BAEB0202C969B7CEC967B915CAFFB9E1543654324E300202583456365836C876"
+ $"BD8FC969BB84C7AABBF70604EAC2BD4448C110C32EC07EC361C17DC33CC0E4C3"
+ $"7BC1E30205C5ACB861C65E31C52BB75D50204E224E20C3E1BA5FC37BB94FC455"
+ $"BB91C606BD02C493BCB6C722BE0DCB1BBDA8CB97BDE4C79EBBF702044EB44BC4"
+ $"D4284EB5174BB9B9C408B8BAC43DBAD755BD4F4BBBD057BD4FC744BB77C818BC"
+ $"3B51BAC502034E28C4A1B847C53AB61650BA52C592B8A0C690BBAEC4E0BAAB59"
+ $"BD8FC4E0B7A104032EB77BC612B63CC969B50AC7AAB777CB35BCA9C99C04032E"
+ $"B965B906B953B7D4B8D3B86D31B748BAB82C0406BE0BB689BE67B5FDC04BB607"
+ $"BF7FB5F0C11CB689C1D7B65BC1A2B73BC2A3B85448B77BC33CB801C31EB70849"
+ $"B609C32304032EBDA8B887BEB4B76EBE10B775BFEC2BC059B8AD04032E28BE01"
+ $"B3FFC0D8B4253FB3D2C288B5BDC2FB0406FE0BC79EBB2BC8F5BBAAC840BB47C9"
+ $"EBBC2ECB26BDDBCB05BD14CB40BE74CA4FC07ECA99BFCACA0EC117C9CFC27CCA"
+ $"0EC1E3C998C30058C3890A04C6FFBB11C856BC50C8BCBCC3C889BD4F040CAAFA"
+ $"BABC6FC74BC091C699C303C5CDC3DAC54DC440C4CEC44EC382C1EAC303C237C3"
+ $"4DC1D5C2EEC05EC402C149C3DFBF754BBD87C460BD47C4F9BE46C513BE2D4DBE"
+ $"98C58BBDBAC5870608BAAABFECC1FC4448C1FCC394C21DC361C1C3C3F0C11EC4"
+ $"47C052C493C02BC3EFC105C389BED3C2BD0A04C369BC83C6D738C666BD1CC35B"
+ $"BD350A03B46542B4BE41B4BE420605AF03B6AFC969B63CC9F1B72FC8D0BE4155"
+ $"B821CA8DC11CC6FAC42DC5C5C55FC53ABE67C91DC1E8C7EAB86DCB260604EEBF"
+ $"CBBB51C1FCBBD0C13EBC43C29FBB6EC18BBAAB43BB1EC145BAF2C09ABB4B0A04"
+ $"30303050505050302C0A0001021240BAB100000000000040B755C6CCDBBE9979"
+ $"01178300040A0101020240BAB100000000000040B755C6CCDBBE99790A020103"
+ $"0240BAB100000000000040B755C6CCDBBE99790A030A05060708090A0C0D040B"
+ $"0240BAB100000000000040B755C6CCDBBE99790A110113124084473AE65BBB05"
+ $"39406A8F40BD7AC6ED3001178400040A00012712401CCA0000000000004031D6"
+ $"BEA7B4C5651401178100040A150112024046733A7FB6BABB05401508437714C3"
+ $"BB840A3B012E0A401CC92E6813AE6813401CC9C0B293C4BE831DFF0A11011412"
+ $"402E52374883B74883402E5243A84DC5C35901178200040A16011502402E5237"
+ $"4883B74883402E5243A84DC5C3590A17011602402E52374883B74883402E5243"
+ $"A84DC5C3590A18011702402E52374883B74883402E5243A84DC5C3590A170118"
+ $"02402E52374883B74883402E5243A84DC5C3590A1801190240268F36C1F1B73C"
+ $"D33FA9DD442903C369B00A1B011A02402E52374883B74883402E5243A84DC5C3"
+ $"590A2C011B02402E52374883B74883402E5243A84DC5C3590A11011C12402E52"
+ $"374883B74883402E5243A84DC5C35901178100040A20011D124018A13C2DD3BC"
+ $"6F843F776243040C3B35B701178400040A28011D024018A13C481BBC6F843FA1"
+ $"3143AD0CC180CE0A27011E024018A13C3669BC6F843F850B43AD0CBDA6830A26"
+ $"03212022023F94B53C057BBC14913F373546DE4E423FFC0A2B012202401CCA00"
+ $"0000000000401CCAC64FB846E7CF0A2B01230241774E3E9634BD97FB40685AC6"
+ $"A141CA33220A0001291240075E0000000000004029CCC0DC88C59D5C01178100"
+ $"040A2E0124123E3A7739EBE1BA96D53D1E7BC874E948577D01178400040A3601"
+ $"24023DE4F539818BBA43313CC9E6C7EFC848861D0A350125023DE4F539818BBA"
+ $"43313CC9E6C7EFC848861D0A340126023DE4F539D28CBA43313D0A55C7EFC848"
+ $"5E9A0A000128123FEA4E3675EFB6F2733F44E3440366C2D26601178200040A00"
+ $"012A12402E52374883B74883402E5241C554C5CE0401178100040A00012B1240"
+ $"1CCA000000000000401CCAC0623FC4B46E01178100040A00012C124031960000"
+ $"00000000401CCAC3AEA8C4B46E01178100040A3A012D02401CCA000000000000"
+ $"401CCAC0E971C4B46E0A3C012F0A401CCA000000000000401CCAC0E971C4B46E"
+ $"1DFF0A3B01300A3FEC520000000000003C6A0B41051D473FEE1DFF0A3E013102"
+ $"40FA05000000000000417C4D3E5061C9E26E0A3F013212401CCA000000000000"
+ $"401CCAC0E971C42D3C01178200040A4001330A401CCA000000000000401CCAC0"
+ $"E971C4B46E1DFF0A420134023600000000000000003600004890004AE8000A42"
+ $"0134023600000000000000003600004870004AE8000A43013402360000000000"
+ $"0000003600004850004AF8000A43013402360000000000000000360000487000"
+ $"4AF8000A430134023600000000000000003600004890004AF8000A4401340236"
+ $"0000000000000000360000485000466000"
+};
+
+resource(201, "kActionBack") #'VICN' array {
+ $"6E636966070500020006023B2FA63AC896BCCBD33D335A4A6980490D0B00ACE2"
+ $"FFFF6ECDFF0200060236D841374D6EBD38553CC6154AA9DF4607770026A8EBFF"
+ $"0694DE020006023729EA388C8FBC29073AAE614A51AF4A729A00035E8CFF0585"
+ $"C70200060238E8A83647FBBA8E2D3D42294AF6F7496CE200047BB8FF0694DE02"
+ $"0006023957BB3923F0BC39AC3C5E604AA873475AB1000592DAFD67CBFF04FF49"
+ $"080A092E3C3A53424C484F5047503A4435442B3A300A072E3C3A533A48484F48"
+ $"403A3A3A300A043A303A3A4435442B0A033A483A53424C0A044840484F504750"
+ $"3A0A043A3A4435503A48400803C125B7D43AB99FB8D3BE0E08023A3A4840070A"
+ $"0001001001178400040A010101000A020102000A030103000A040104000A0501"
+ $"05000A06020607100117812004"
+};
+
+resource(202, "kActionForward") #'VICN' array {
+ $"6E636966050500020006023B2FA63AC896BCCBD33D335A4A6980490D0B00ACE2"
+ $"FFFF6ECDFF0200060239AF2C39E19BBC89D83C68DC4AFA1247CFA20006A5F7FF"
+ $"069EEC02000602B8EEBEB986C33C7FB4BC13FB46FA0C49FBDD000592DAFD3EBD"
+ $"FF04FF49050A0A2E423A483A524847503F432A3A2F3A3136302E350A0748473A"
+ $"2F3A392E352E423A483A520A043A2F4847503F432A0A042E3536303A313A3908"
+ $"0536B9932FBB773B3A3BB939C0E5B755050A0001001001178400040A01010100"
+ $"0A020102000A030103000A040104100117812004"
+};
+
+resource(203, "kActionForward2") #'VICN' array {
+ $"6E63696607050002000602396DF23A056B3B94FCBB09F547CD484AAB5F0090D9"
+ $"FFFF6ECDFF02000602B5E5793878A83E023B3B2AC34893F147CC490060C8FFFF"
+ $"44BEFD02000602B6F90538542D3C2FCA3ACA9249A8634A929A00034F77FF0468"
+ $"9C020006023AC62E392AB63B6BA0BCFF0B49DAAA4AAE4500046FA7FF2BA5E502"
+ $"0006023A1CB834EBA6383E33BD3E8D499DE44A647800ACE2FFFD6DCCFF04FF49"
+ $"080A09523A48533F503C532E4C2E3E3A363A2B482E0A07523A485348473C533C"
+ $"43483A482E0A04482E483A3A363A2B0A03484748533F500A043C433C532E4C2E"
+ $"3E0A04483A3A362E3E3C430802BD8FB795C2BDB8E008023C43B8C7BF0D070A00"
+ $"01001001178400040A040101000A020102000A030103000A010104000A050105"
+ $"000A06020607100117812004"
+};
+
+resource(204, "kActionStop") #'VICN' array {
+ $"6E63696606050102000602393D323BB602BEA28F3C4CC64B601449439F00FF3E"
+ $"3EFFF70606020006033B27153C10C7BDF9893D088A4B2F634882C500C805057D"
+ $"9D0404FFB4040403750303020006023B06EB3B5469BC08EB3BB31F4AB15D4478"
+ $"7700FF9090FFFF757504FF440A0A0C2E4C364E3C4742524A5440434A3842353C"
+ $"BE1B36312E2E38400A0636313C3C40434A54504E402E0A03364E3E483C470A04"
+ $"483FC712BADE4A3840430A042E2E3631402E382C0A0442354A38C712BADE4AB9"
+ $"EC0A0E2E4C364EBEB6C37042524A54504E483FC712BADE4AB9EC4335402E382C"
+ $"2E2E384008022FB8A0BCC3B80708033C3C42364ABA120802BCA9BFF3B8D3C47B"
+ $"060A0001061001178400040A020101000A03020302000A010100000A04020504"
+ $"000A0503070809100117810004"
+};
+
+resource(206, "kActionGoStart") #'VICN' array {
+ $"6E63696605050002001602B53D77BAF2653D77A7B7CFFC460F874A2D7700F0FF"
+ $"D50200160236FB2637191CBC57873C40E14B0E7149D82700A2FFBB0200060239"
+ $"FB483A6DC2BDAC713D02DB4B384746086300EA0606FFD005050200060238B0C7"
+ $"397C47BD2A963C71B74A82C348B2B700FF4444FFF106060A0A06304942525044"
+ $"50383E32303D0A09303B3049324A32C0723643364C4252424036320A04503650"
+ $"44425242400A082D3E3535434645455439462C35312B3C0A062B3C2D3E353543"
+ $"46454535310A0445455439462C35310A0432C072324A364C36430A043A453A4A"
+ $"3E4C3EC2630A044645464A484848430A044C3F4C444E424E3D070A0001001001"
+ $"178400040A010101000A020102000A000406070809000A000103100117840004"
+ $"0A030105000A04010400"
+};
+
+resource(205, "kActionGo") archive BBitmap {
+ "_frame" = rect { 0.0, 0.0, 15.0, 15.0 },
+ "_cspace" = 8200,
+ "_bmflags" = 0,
+ "_rowbytes" = 64,
+ "_data" = array {
+ $"0000000000000000000000000000000000000000000000000000000000000000"
+ $"0000000000000000000000000000000000000000000000000000000000000000"
+ $"0000000000000000000000C1000000DE00000061000000070000000100000000"
+ $"0000000000000000000000000000000000000000000000000000000000000000"
+ $"0000000000000000000000FF9BD4A5FF48634EFF000000C00000003500000004"
+ $"0000000000000000000000000000000000000000000000000000000000000000"
+ $"0000000000000000000000FFB6F9C2FFB3F9C0FF84B68EFF263426FD0000009B"
+ $"0000001600000002000000000000000000000000000000000000000000000000"
+ $"0000000000000000000000FFB6F9C2FF7EF995FFA3F9B3FFAAE9B6FF69906FFF"
+ $"000000E200000064000000070000000100000000000000000000000000000000"
+ $"0000000000000000000000FFB6F9C2FF6AFB85FF6AFB85FF83FA98FFA4F5B2FF"
+ $"7CC88AFF325C3BFF000000C00000003500000004000000000000000000000000"
+ $"0000000000000000000000FFB6F9C2FF5DFC7BFF5DFC7BFF5DFC7BFF5DFB7CFF"
+ $"60ED7AFF54D76DFF389A48FF002A14FD0000009B000000160000000200000000"
+ $"0000000000000000000000FFB5F8C1FF59FC77FF59FC77FF59FC77FF59FC77FF"
+ $"59FC77FF55F674FF40DB5CFF2BBC48FF1C752BFF000000D00000002000000003"
+ $"0000000000000000000000FFA8F4B5FF2EFA54FF2EFA54FF2EFA54FF2EFA54FF"
+ $"2EFA54FF2BF350FF22D542FF1CB638FF157027FF000000D00000003D00000009"
+ $"0000000000000000000000FF92ECA2FF15F940FF15F940FF15F940FF15F740FF"
+ $"15DE35FF00BE2EFF008922FF002600FD0000009B000000430000002000000003"
+ $"0000000000000000000000FF7EE591FF15F83DFF15F83DFF1CEA3DFF00C732FF"
+ $"009E22FF004815FF000000BF000000560000002D0000000F0000000200000000"
+ $"0000000000000000000000FF6DDF81FF1CF340FF2BDA4AFF1CB638FF006D1CFF"
+ $"000000E200000074000000360000001900000004000000000000000000000000"
+ $"0000000000000000000000FF5DDA75FF44D25FFF229338FF002600FD0000009B"
+ $"0000004300000023000000070000000100000000000000000000000000000000"
+ $"0000000000000000000000FF46B65CFF1C5222FF000000BF000000560000002D"
+ $"0000000F00000002000000000000000000000000000000000000000000000000"
+ $"0000000000000000000000C2000000DE00000074000000360000001900000004"
+ $"0000000000000000000000000000000000000000000000000000000000000000"
+ $"0000000000000000000000030000001F00000020000000080000000100000000"
+ $"0000000000000000000000000000000000000000000000000000000000000000"
+ }
+};
+
+
diff --git a/src/apps/webpositive/autocompletion/AutoCompleter.cpp b/src/apps/webpositive/autocompletion/AutoCompleter.cpp
new file mode 100644
index 0000000000..09060512f6
--- /dev/null
+++ b/src/apps/webpositive/autocompletion/AutoCompleter.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam).
+ * All rights reserved. Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Oliver Tappe <beam@hirschkaefer.de>
+ */
+
+#include "AutoCompleter.h"
+
+#include <Looper.h>
+#include <Message.h>
+
+#include "AutoCompleterDefaultImpl.h"
+
+
+// #pragma mark - DefaultPatternSelector
+
+
+class DefaultPatternSelector : public BAutoCompleter::PatternSelector {
+public:
+ virtual void SelectPatternBounds(const BString& text, int32 caretPos,
+ int32* start, int32* length);
+};
+
+
+void
+DefaultPatternSelector::SelectPatternBounds(const BString& text,
+ int32 caretPos, int32* start, int32* length)
+{
+ if (!start || !length)
+ return;
+ *start = 0;
+ *length = text.Length();
+}
+
+
+// #pragma mark - CompletionStyle
+
+
+BAutoCompleter::CompletionStyle::CompletionStyle(EditView* editView,
+ ChoiceModel* choiceModel, ChoiceView* choiceView,
+ PatternSelector* patternSelector)
+ :
+ fEditView(editView),
+ fPatternSelector(patternSelector ? patternSelector
+ : new DefaultPatternSelector()),
+ fChoiceModel(choiceModel),
+ fChoiceView(choiceView)
+{
+}
+
+
+BAutoCompleter::CompletionStyle::~CompletionStyle()
+{
+ delete fEditView;
+ delete fChoiceModel;
+ delete fChoiceView;
+ delete fPatternSelector;
+}
+
+
+void
+BAutoCompleter::CompletionStyle::SetEditView(EditView* view)
+{
+ delete fEditView;
+ fEditView = view;
+}
+
+
+void
+BAutoCompleter::CompletionStyle::SetPatternSelector(
+ PatternSelector* selector)
+{
+ delete fPatternSelector;
+ fPatternSelector = selector;
+}
+
+
+void
+BAutoCompleter::CompletionStyle::SetChoiceModel(ChoiceModel* model)
+{
+ delete fChoiceModel;
+ fChoiceModel = model;
+}
+
+
+void
+BAutoCompleter::CompletionStyle::SetChoiceView(ChoiceView* view)
+{
+ delete fChoiceView;
+ fChoiceView = view;
+}
+
+
+// #pragma mark - BAutoCompleter
+
+
+BAutoCompleter::BAutoCompleter(CompletionStyle* completionStyle)
+ :
+ fCompletionStyle(completionStyle)
+{
+}
+
+
+BAutoCompleter::BAutoCompleter(EditView* editView, ChoiceModel* choiceModel,
+ ChoiceView* choiceView, PatternSelector* patternSelector)
+ :
+ fCompletionStyle(new BDefaultCompletionStyle(editView, choiceModel,
+ choiceView, patternSelector))
+{
+}
+
+
+BAutoCompleter::~BAutoCompleter()
+{
+ delete fCompletionStyle;
+}
+
+
+bool
+BAutoCompleter::Select(int32 index)
+{
+ if (fCompletionStyle)
+ return fCompletionStyle->Select(index);
+ else
+ return false;
+}
+
+
+bool
+BAutoCompleter::SelectNext(bool wrap)
+{
+ if (fCompletionStyle)
+ return fCompletionStyle->SelectNext(wrap);
+ else
+ return false;
+}
+
+
+bool
+BAutoCompleter::SelectPrevious(bool wrap)
+{
+ if (fCompletionStyle)
+ return fCompletionStyle->SelectPrevious(wrap);
+ else
+ return false;
+}
+
+
+bool
+BAutoCompleter::IsChoiceSelected() const
+{
+ if (fCompletionStyle)
+ return fCompletionStyle->IsChoiceSelected();
+ else
+ return false;
+}
+
+
+int32
+BAutoCompleter::CountChoices() const
+{
+ if (fCompletionStyle && fCompletionStyle->GetChoiceModel())
+ return fCompletionStyle->GetChoiceModel()->CountChoices();
+ else
+ return 0;
+}
+
+
+int32
+BAutoCompleter::CountVisibleChoices() const
+{
+ if (fCompletionStyle && fCompletionStyle->GetChoiceView())
+ return fCompletionStyle->GetChoiceView()->CountVisibleChoices();
+ else
+ return 0;
+}
+
+
+int32
+BAutoCompleter::SelectedChoiceIndex() const
+{
+ if (fCompletionStyle)
+ return fCompletionStyle->SelectedChoiceIndex();
+ else
+ return -1;
+}
+
+
+void
+BAutoCompleter::ApplyChoice(bool hideChoices)
+{
+ if (fCompletionStyle)
+ fCompletionStyle->ApplyChoice(hideChoices);
+}
+
+
+void
+BAutoCompleter::CancelChoice()
+{
+ if (fCompletionStyle)
+ fCompletionStyle->CancelChoice();
+}
+
+
+void
+BAutoCompleter::EditViewStateChanged(bool updateChoices)
+{
+ if (fCompletionStyle)
+ fCompletionStyle->EditViewStateChanged(updateChoices);
+}
+
+
+void
+BAutoCompleter::SetEditView(EditView* view)
+{
+ if (fCompletionStyle)
+ fCompletionStyle->SetEditView(view);
+}
+
+
+void
+BAutoCompleter::SetPatternSelector(PatternSelector* selector)
+{
+ if (fCompletionStyle)
+ fCompletionStyle->SetPatternSelector(selector);
+}
+
+
+void
+BAutoCompleter::SetChoiceModel(ChoiceModel* model)
+{
+ if (fCompletionStyle)
+ fCompletionStyle->SetChoiceModel(model);
+}
+
+
+void
+BAutoCompleter::SetChoiceView(ChoiceView* view)
+{
+ if (fCompletionStyle)
+ fCompletionStyle->SetChoiceView(view);
+}
+
+
+void
+BAutoCompleter::SetCompletionStyle(CompletionStyle* style)
+{
+ delete fCompletionStyle;
+ fCompletionStyle = style;
+}
+
diff --git a/src/apps/webpositive/autocompletion/AutoCompleter.h b/src/apps/webpositive/autocompletion/AutoCompleter.h
new file mode 100644
index 0000000000..dab8268db2
--- /dev/null
+++ b/src/apps/webpositive/autocompletion/AutoCompleter.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam).
+ * All rights reserved. Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Oliver Tappe <beam@hirschkaefer.de>
+ */
+#ifndef _AUTO_COMPLETER_H
+#define _AUTO_COMPLETER_H
+
+#include <MessageFilter.h>
+
+#include <Rect.h>
+#include <String.h>
+
+class BAutoCompleter {
+public:
+ class Choice {
+ public:
+ Choice(const BString& choiceText,
+ const BString& displayText, int32 matchPos,
+ int32 matchLen)
+ :
+ fText(choiceText),
+ fDisplayText(displayText),
+ fMatchPos(matchPos),
+ fMatchLen(matchLen)
+ {
+ }
+ virtual ~Choice() {}
+ const BString& Text() const { return fText; }
+ const BString& DisplayText() const { return fDisplayText; }
+ int32 MatchPos() const { return fMatchPos; }
+ int32 MatchLen() const { return fMatchLen; }
+
+ private:
+ BString fText;
+ BString fDisplayText;
+ int32 fMatchPos;
+ int32 fMatchLen;
+ };
+
+ class EditView {
+ public:
+ virtual ~EditView() {}
+
+ virtual BRect GetAdjustmentFrame() = 0;
+ virtual void GetEditViewState(BString& text,
+ int32* caretPos) = 0;
+ virtual void SetEditViewState(const BString& text,
+ int32 caretPos,
+ int32 selectionLength = 0) = 0;
+ };
+
+ class PatternSelector {
+ public:
+ virtual ~PatternSelector() {}
+
+ virtual void SelectPatternBounds(const BString& text,
+ int32 caretPos, int32* start,
+ int32* length) = 0;
+ };
+
+ class ChoiceModel {
+ public:
+
+ virtual ~ChoiceModel() {}
+
+ virtual void FetchChoicesFor(const BString& pattern) = 0;
+
+ virtual int32 CountChoices() const = 0;
+ virtual const Choice* ChoiceAt(int32 index) const = 0;
+ };
+
+ class CompletionStyle;
+ class ChoiceView {
+ public:
+ virtual ~ChoiceView() {}
+
+ virtual void SelectChoiceAt(int32 index) = 0;
+ virtual void ShowChoices(
+ BAutoCompleter::CompletionStyle* completer)
+ = 0;
+ virtual void HideChoices() = 0;
+ virtual bool ChoicesAreShown() = 0;
+ virtual int32 CountVisibleChoices() const = 0;
+ };
+
+ class CompletionStyle {
+ public:
+ CompletionStyle(EditView* editView,
+ ChoiceModel* choiceModel,
+ ChoiceView* choiceView,
+ PatternSelector* patternSelector);
+ virtual ~CompletionStyle();
+
+ virtual bool Select(int32 index) = 0;
+ virtual bool SelectNext(bool wrap = false) = 0;
+ virtual bool SelectPrevious(bool wrap = false) = 0;
+ virtual bool IsChoiceSelected() const = 0;
+ virtual int32 SelectedChoiceIndex() const = 0;
+
+ virtual void ApplyChoice(bool hideChoices = true) = 0;
+ virtual void CancelChoice() = 0;
+
+ virtual void EditViewStateChanged(bool updateChoices) = 0;
+
+ void SetEditView(EditView* view);
+ void SetPatternSelector(PatternSelector* selector);
+ void SetChoiceModel(ChoiceModel* model);
+ void SetChoiceView(ChoiceView* view);
+
+ EditView* GetEditView() { return fEditView; }
+ PatternSelector* GetPatternSelector()
+ { return fPatternSelector; }
+ ChoiceModel* GetChoiceModel() { return fChoiceModel; }
+ ChoiceView* GetChoiceView() { return fChoiceView; }
+
+ protected:
+ EditView* fEditView;
+ PatternSelector* fPatternSelector;
+ ChoiceModel* fChoiceModel;
+ ChoiceView* fChoiceView;
+ };
+
+protected:
+ BAutoCompleter(
+ CompletionStyle* completionStyle = NULL);
+ BAutoCompleter(EditView* editView,
+ ChoiceModel* choiceModel,
+ ChoiceView* choiceView,
+ PatternSelector* patternSelector);
+ virtual ~BAutoCompleter();
+
+ void EditViewStateChanged(
+ bool updateChoices = true);
+
+ bool Select(int32 index);
+ bool SelectNext(bool wrap = false);
+ bool SelectPrevious(bool wrap = false);
+ bool IsChoiceSelected() const;
+ int32 CountChoices() const;
+ int32 CountVisibleChoices() const;
+ int32 SelectedChoiceIndex() const;
+
+ void ApplyChoice(bool hideChoices = true);
+ void CancelChoice();
+
+ void SetEditView(EditView* view);
+ void SetPatternSelector(PatternSelector* selector);
+ void SetChoiceModel(ChoiceModel* model);
+ void SetChoiceView(ChoiceView* view);
+
+ void SetCompletionStyle(CompletionStyle* style);
+
+private:
+ CompletionStyle* fCompletionStyle;
+};
+
+
+#endif // _AUTO_COMPLETER_H
diff --git a/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.cpp b/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.cpp
new file mode 100644
index 0000000000..0808239ef3
--- /dev/null
+++ b/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam).
+ * All rights reserved. Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Oliver Tappe <beam@hirschkaefer.de>
+ */
+
+#include "AutoCompleterDefaultImpl.h"
+
+#include <ListView.h>
+#include <Screen.h>
+#include <ScrollView.h>
+#include <Window.h>
+
+
+// #pragma mark - BDefaultPatternSelector
+
+
+void
+BDefaultPatternSelector::SelectPatternBounds(const BString& text,
+ int32 caretPos, int32* start, int32* length)
+{
+ if (!start || !length)
+ return;
+ *start = 0;
+ *length = text.Length();
+}
+
+
+// #pragma mark - BDefaultCompletionStyle
+
+
+BDefaultCompletionStyle::BDefaultCompletionStyle(
+ BAutoCompleter::EditView* editView,
+ BAutoCompleter::ChoiceModel* choiceModel,
+ BAutoCompleter::ChoiceView* choiceView,
+ BAutoCompleter::PatternSelector* patternSelector)
+ :
+ CompletionStyle(editView, choiceModel, choiceView, patternSelector),
+ fSelectedIndex(-1),
+ fPatternStartPos(0),
+ fPatternLength(0),
+ fIgnoreEditViewStateChanges(false)
+{
+}
+
+
+BDefaultCompletionStyle::~BDefaultCompletionStyle()
+{
+}
+
+
+bool
+BDefaultCompletionStyle::Select(int32 index)
+{
+ if (!fChoiceView || !fChoiceModel || index == fSelectedIndex
+ || index < -1 || index >= fChoiceModel->CountChoices()) {
+ return false;
+ }
+
+ fSelectedIndex = index;
+ fChoiceView->SelectChoiceAt(index);
+ return true;
+}
+
+
+bool
+BDefaultCompletionStyle::SelectNext(bool wrap)
+{
+ if (!fChoiceModel || fChoiceModel->CountChoices() == 0)
+ return false;
+
+ int32 newIndex = fSelectedIndex + 1;
+ if (newIndex >= fChoiceModel->CountChoices()) {
+ if (wrap)
+ newIndex = 0;
+ else
+ newIndex = fSelectedIndex;
+ }
+ return Select(newIndex);
+}
+
+
+bool
+BDefaultCompletionStyle::SelectPrevious(bool wrap)
+{
+ if (!fChoiceModel || fChoiceModel->CountChoices() == 0)
+ return false;
+
+ int32 newIndex = fSelectedIndex - 1;
+ if (newIndex < 0) {
+ if (wrap)
+ newIndex = fChoiceModel->CountChoices() - 1;
+ else
+ newIndex = 0;
+ }
+ return Select(newIndex);
+}
+
+
+bool
+BDefaultCompletionStyle::IsChoiceSelected() const
+{
+ return fSelectedIndex >= 0;
+}
+
+
+int32
+BDefaultCompletionStyle::SelectedChoiceIndex() const
+{
+ return fSelectedIndex;
+}
+
+
+void
+BDefaultCompletionStyle::ApplyChoice(bool hideChoices)
+{
+ if (!fChoiceModel || !fChoiceView || !fEditView || fSelectedIndex < 0)
+ return;
+
+ BString completedText(fFullEnteredText);
+ completedText.Remove(fPatternStartPos, fPatternLength);
+ const BString& choiceStr = fChoiceModel->ChoiceAt(fSelectedIndex)->Text();
+ completedText.Insert(choiceStr, fPatternStartPos);
+
+ fIgnoreEditViewStateChanges = true;
+
+ fFullEnteredText = completedText;
+ fPatternLength = choiceStr.Length();
+ fEditView->SetEditViewState(completedText,
+ fPatternStartPos + choiceStr.Length());
+
+ if (hideChoices) {
+ fChoiceView->HideChoices();
+ Select(-1);
+ }
+
+ fIgnoreEditViewStateChanges = false;
+}
+
+
+void
+BDefaultCompletionStyle::CancelChoice()
+{
+ if (!fChoiceView || !fEditView)
+ return;
+ if (fChoiceView->ChoicesAreShown()) {
+ fIgnoreEditViewStateChanges = true;
+
+ fEditView->SetEditViewState(fFullEnteredText,
+ fPatternStartPos + fPatternLength);
+ fChoiceView->HideChoices();
+ Select(-1);
+
+ fIgnoreEditViewStateChanges = false;
+ }
+}
+
+void
+BDefaultCompletionStyle::EditViewStateChanged(bool updateChoices)
+{
+ if (fIgnoreEditViewStateChanges || !fChoiceModel || !fChoiceView
+ || !fEditView) {
+ return;
+ }
+
+ BString text;
+ int32 caretPos;
+ fEditView->GetEditViewState(text, &caretPos);
+ if (fFullEnteredText == text)
+ return;
+
+ fFullEnteredText = text;
+
+ if (!updateChoices)
+ return;
+
+ fPatternSelector->SelectPatternBounds(text, caretPos, &fPatternStartPos,
+ &fPatternLength);
+ BString pattern(text.String() + fPatternStartPos, fPatternLength);
+ fChoiceModel->FetchChoicesFor(pattern);
+
+ Select(-1);
+ // show a single choice only if it doesn't match the pattern exactly:
+ if (fChoiceModel->CountChoices() > 1 || (fChoiceModel->CountChoices() == 1
+ && pattern.ICompare(fChoiceModel->ChoiceAt(0)->Text())) != 0) {
+ fChoiceView->ShowChoices(this);
+ fChoiceView->SelectChoiceAt(fSelectedIndex);
+ } else
+ fChoiceView->HideChoices();
+}
+
+
+// #pragma mark - BDefaultChoiceView::ListView
+
+
+static const int32 MSG_INVOKED = 'invk';
+
+
+BDefaultChoiceView::ListView::ListView(
+ BAutoCompleter::CompletionStyle* completer)
+ :
+ BListView(BRect(0, 0, 100, 100), "ChoiceViewList"),
+ fCompleter(completer)
+{
+ // we need to check if user clicks outside of window-bounds:
+ SetEventMask(B_POINTER_EVENTS);
+}
+
+
+void
+BDefaultChoiceView::ListView::AttachedToWindow()
+{
+ SetTarget(this);
+ SetInvocationMessage(new BMessage(MSG_INVOKED));
+ BListView::AttachedToWindow();
+}
+
+
+void
+BDefaultChoiceView::ListView::SelectionChanged()
+{
+ fCompleter->Select(CurrentSelection(0));
+}
+
+
+void
+BDefaultChoiceView::ListView::MessageReceived(BMessage* message)
+{
+ switch(message->what) {
+ case MSG_INVOKED:
+ fCompleter->ApplyChoice();
+ break;
+ default:
+ BListView::MessageReceived(message);
+ }
+}
+
+
+void
+BDefaultChoiceView::ListView::MouseDown(BPoint point)
+{
+ if (!Window()->Frame().Contains(ConvertToScreen(point)))
+ // click outside of window, so we close it:
+ Window()->Quit();
+ else
+ BListView::MouseDown(point);
+}
+
+
+// #pragma mark - BDefaultChoiceView::ListItem
+
+
+BDefaultChoiceView::ListItem::ListItem(const BAutoCompleter::Choice* choice)
+ :
+ BListItem()
+{
+ fPreText = choice->DisplayText();
+ if (choice->MatchLen() > 0) {
+ fPreText.MoveInto(fMatchText, choice->MatchPos(), choice->MatchLen());
+ fPreText.MoveInto(fPostText, choice->MatchPos(), fPreText.Length());
+ }
+}
+
+
+void
+BDefaultChoiceView::ListItem::DrawItem(BView* owner, BRect frame,
+ bool complete)
+{
+ rgb_color textColor;
+ rgb_color nonMatchTextColor;
+ rgb_color backColor;
+ rgb_color matchColor;
+ if (IsSelected()) {
+ textColor = ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR);
+ backColor = ui_color(B_MENU_SELECTED_BACKGROUND_COLOR);
+ } else {
+ textColor = ui_color(B_DOCUMENT_TEXT_COLOR);
+ backColor = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
+ }
+ matchColor = tint_color(backColor, (B_NO_TINT + B_DARKEN_1_TINT) / 2);
+ nonMatchTextColor = tint_color(backColor, 1.7);
+
+ BFont font;
+ font_height fontHeight;
+ owner->GetFont(&font);
+ font.GetHeight(&fontHeight);
+ float xPos = frame.left + 1;
+ float yPos = frame.top + fontHeight.ascent;
+ float w;
+ if (fPreText.Length()) {
+ w = owner->StringWidth(fPreText.String());
+ owner->SetLowColor(backColor);
+ owner->FillRect(BRect(xPos, frame.top, xPos + w - 1, frame.bottom),
+ B_SOLID_LOW);
+ owner->SetHighColor(nonMatchTextColor);
+ owner->DrawString(fPreText.String(), BPoint(xPos, yPos));
+ xPos += w;
+ }
+ if (fMatchText.Length()) {
+ w = owner->StringWidth(fMatchText.String());
+ owner->SetLowColor(matchColor);
+ owner->FillRect(BRect(xPos, frame.top, xPos + w - 1, frame.bottom),
+ B_SOLID_LOW);
+ owner->SetHighColor(textColor);
+ owner->DrawString(fMatchText.String(), BPoint(xPos, yPos));
+ xPos += w;
+ }
+ if (fPostText.Length()) {
+ w = owner->StringWidth(fPostText.String());
+ owner->SetLowColor(backColor);
+ owner->FillRect(BRect(xPos, frame.top, xPos + w - 1, frame.bottom),
+ B_SOLID_LOW);
+ owner->SetHighColor(nonMatchTextColor);
+ owner->DrawString(fPostText.String(), BPoint(xPos, yPos));
+ }
+}
+
+
+// #pragma mark - BDefaultChoiceView
+
+
+BDefaultChoiceView::BDefaultChoiceView()
+ :
+ fWindow(NULL),
+ fListView(NULL),
+ fMaxVisibleChoices(8)
+{
+
+}
+
+
+BDefaultChoiceView::~BDefaultChoiceView()
+{
+ HideChoices();
+}
+
+
+void
+BDefaultChoiceView::SelectChoiceAt(int32 index)
+{
+ if (fListView && fListView->LockLooper()) {
+ if (index < 0)
+ fListView->DeselectAll();
+ else {
+ fListView->Select(index);
+ fListView->ScrollToSelection();
+ }
+ fListView->UnlockLooper();
+ }
+}
+
+
+void
+BDefaultChoiceView::ShowChoices(BAutoCompleter::CompletionStyle* completer)
+{
+ if (!completer)
+ return;
+
+ HideChoices();
+
+ BAutoCompleter::ChoiceModel* choiceModel = completer->GetChoiceModel();
+ BAutoCompleter::EditView* editView = completer->GetEditView();
+
+ if (!editView || !choiceModel || choiceModel->CountChoices() == 0)
+ return;
+
+ fListView = new ListView(completer);
+ int32 count = choiceModel->CountChoices();
+ for(int32 i = 0; i<count; ++i) {
+ fListView->AddItem(
+ new ListItem(choiceModel->ChoiceAt(i))
+ );
+ }
+
+ BScrollView *scrollView = NULL;
+ if (count > fMaxVisibleChoices) {
+ scrollView = new BScrollView("", fListView, B_FOLLOW_NONE, 0, false, true, B_NO_BORDER);
+ }
+
+ fWindow = new BWindow(BRect(0, 0, 100, 100), "", B_BORDERED_WINDOW_LOOK,
+ B_NORMAL_WINDOW_FEEL, B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK
+ | B_AVOID_FOCUS | B_ASYNCHRONOUS_CONTROLS);
+ if (scrollView != NULL)
+ fWindow->AddChild(scrollView);
+ else
+ fWindow->AddChild(fListView);
+
+ int32 visibleCount = min_c(count, fMaxVisibleChoices);
+ float listHeight = fListView->ItemFrame(visibleCount - 1).bottom + 1;
+
+ BRect pvRect = editView->GetAdjustmentFrame();
+ BRect listRect = pvRect;
+ listRect.bottom = listRect.top + listHeight - 1;
+ BRect screenRect = BScreen().Frame();
+ if (listRect.bottom + 1 + listHeight <= screenRect.bottom)
+ listRect.OffsetTo(pvRect.left, pvRect.bottom + 1);
+ else
+ listRect.OffsetTo(pvRect.left, pvRect.top - listHeight);
+
+ if (scrollView != NULL) {
+ // Moving here to cut off the scrollbar top
+ scrollView->MoveTo(0, -1);
+ // Adding the 1 and 2 to cut-off the scroll-bar top, right and bottom
+ scrollView->ResizeTo(listRect.Width() + 1, listRect.Height() + 2);
+ // Move here to compensate for the above
+ fListView->MoveTo(0, 1);
+ fListView->ResizeTo(listRect.Width() - B_V_SCROLL_BAR_WIDTH, listRect.Height());
+ } else {
+ fListView->MoveTo(0, 0);
+ fListView->ResizeTo(listRect.Width(), listRect.Height());
+ }
+ fWindow->MoveTo(listRect.left, listRect.top);
+ fWindow->ResizeTo(listRect.Width(), listRect.Height());
+ fWindow->Show();
+}
+
+
+void
+BDefaultChoiceView::HideChoices()
+{
+ if (fWindow && fWindow->Lock()) {
+ fWindow->Quit();
+ fWindow = NULL;
+ fListView = NULL;
+ }
+}
+
+
+bool
+BDefaultChoiceView::ChoicesAreShown()
+{
+ return (fWindow != NULL);
+}
+
+
+int32
+BDefaultChoiceView::CountVisibleChoices() const
+{
+ if (fListView)
+ return min_c(fMaxVisibleChoices, fListView->CountItems());
+ else
+ return 0;
+}
+
+
+void
+BDefaultChoiceView::SetMaxVisibleChoices(int32 choices)
+{
+ if (choices < 1)
+ choices = 1;
+ if (choices == fMaxVisibleChoices)
+ return;
+
+ fMaxVisibleChoices = choices;
+
+ // TODO: Update live?
+}
+
+
+int32
+BDefaultChoiceView::MaxVisibleChoices() const
+{
+ return fMaxVisibleChoices;
+}
+
diff --git a/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.h b/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.h
new file mode 100644
index 0000000000..6c3e50e8f5
--- /dev/null
+++ b/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam).
+ * All rights reserved. Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Oliver Tappe <beam@hirschkaefer.de>
+ */
+#ifndef _AUTO_COMPLETER_DEFAULT_IMPL_H
+#define _AUTO_COMPLETER_DEFAULT_IMPL_H
+
+#include <ListView.h>
+#include <String.h>
+
+#include "AutoCompleter.h"
+
+class BDefaultPatternSelector : public BAutoCompleter::PatternSelector {
+public:
+ virtual void SelectPatternBounds(const BString& text,
+ int32 caretPos, int32* start,
+ int32* length);
+};
+
+
+class BDefaultCompletionStyle : public BAutoCompleter::CompletionStyle {
+public:
+ BDefaultCompletionStyle(
+ BAutoCompleter::EditView* editView,
+ BAutoCompleter::ChoiceModel* choiceModel,
+ BAutoCompleter::ChoiceView* choiceView,
+ BAutoCompleter::PatternSelector*
+ patternSelector);
+ virtual ~BDefaultCompletionStyle();
+
+ virtual bool Select(int32 index);
+ virtual bool SelectNext(bool wrap = false);
+ virtual bool SelectPrevious(bool wrap = false);
+ virtual bool IsChoiceSelected() const;
+ virtual int32 SelectedChoiceIndex() const;
+
+ virtual void ApplyChoice(bool hideChoices = true);
+ virtual void CancelChoice();
+
+ virtual void EditViewStateChanged(bool updateChoices);
+
+private:
+ BString fFullEnteredText;
+ int32 fSelectedIndex;
+ int32 fPatternStartPos;
+ int32 fPatternLength;
+ bool fIgnoreEditViewStateChanges;
+};
+
+
+class BDefaultChoiceView : public BAutoCompleter::ChoiceView {
+protected:
+ class ListView : public BListView {
+ public:
+ ListView(
+ BAutoCompleter::CompletionStyle* completer);
+ virtual void SelectionChanged();
+ virtual void MessageReceived(BMessage* msg);
+ virtual void MouseDown(BPoint point);
+ virtual void AttachedToWindow();
+
+ private:
+ BAutoCompleter::CompletionStyle* fCompleter;
+ };
+
+ class ListItem : public BListItem {
+ public:
+ ListItem(const BAutoCompleter::Choice* choice);
+ virtual void DrawItem(BView* owner, BRect frame,
+ bool complete = false);
+ private:
+ BString fPreText;
+ BString fMatchText;
+ BString fPostText;
+ };
+
+public:
+ BDefaultChoiceView();
+ virtual ~BDefaultChoiceView();
+
+ virtual void SelectChoiceAt(int32 index);
+ virtual void ShowChoices(
+ BAutoCompleter::CompletionStyle* completer);
+ virtual void HideChoices();
+ virtual bool ChoicesAreShown();
+ virtual int32 CountVisibleChoices() const;
+
+ void SetMaxVisibleChoices(int32 choices);
+ int32 MaxVisibleChoices() const;
+
+private:
+ BWindow* fWindow;
+ ListView* fListView;
+ int32 fMaxVisibleChoices;
+};
+
+#endif // _AUTO_COMPLETER_DEFAULT_IMPL_H
diff --git a/src/apps/webpositive/autocompletion/TextViewCompleter.cpp b/src/apps/webpositive/autocompletion/TextViewCompleter.cpp
new file mode 100644
index 0000000000..48f7bcb4da
--- /dev/null
+++ b/src/apps/webpositive/autocompletion/TextViewCompleter.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam).
+ * All rights reserved. Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Oliver Tappe <beam@hirschkaefer.de>
+ */
+
+#include "TextViewCompleter.h"
+
+#include <Looper.h>
+#include <TextControl.h>
+#include <stdio.h>
+
+#include "AutoCompleterDefaultImpl.h"
+
+
+// #pragma mark - TextViewWrapper
+
+
+TextViewCompleter::TextViewWrapper::TextViewWrapper(BTextView* textView)
+ :
+ fTextView(textView)
+{
+}
+
+
+void
+TextViewCompleter::TextViewWrapper::GetEditViewState(BString& text,
+ int32* caretPos)
+{
+ if (fTextView && fTextView->LockLooper()) {
+ text = fTextView->Text();
+ if (caretPos) {
+ int32 end;
+ fTextView->GetSelection(caretPos, &end);
+ }
+ fTextView->UnlockLooper();
+ }
+}
+
+
+void
+TextViewCompleter::TextViewWrapper::SetEditViewState(const BString& text,
+ int32 caretPos, int32 selectionLength)
+{
+ if (fTextView && fTextView->LockLooper()) {
+ fTextView->SetText(text.String(), text.Length());
+ fTextView->Select(caretPos, caretPos + selectionLength);
+ fTextView->ScrollToSelection();
+ fTextView->UnlockLooper();
+ }
+}
+
+
+BRect
+TextViewCompleter::TextViewWrapper::GetAdjustmentFrame()
+{
+ BRect frame = fTextView->Bounds();
+ frame = fTextView->ConvertToScreen(frame);
+ frame.InsetBy(0, -3);
+ return frame;
+}
+
+
+// #pragma mark -
+
+
+TextViewCompleter::TextViewCompleter(BTextView* textView, ChoiceModel* model,
+ PatternSelector* patternSelector)
+ :
+ BAutoCompleter(new TextViewWrapper(textView), model,
+ new BDefaultChoiceView(), patternSelector),
+ BMessageFilter(B_KEY_DOWN),
+ fTextView(textView),
+ fModificationsReported(false)
+{
+ fTextView->AddFilter(this);
+}
+
+
+TextViewCompleter::~TextViewCompleter()
+{
+ fTextView->RemoveFilter(this);
+}
+
+
+void
+TextViewCompleter::SetModificationsReported(bool reported)
+{
+ fModificationsReported = reported;
+}
+
+
+void
+TextViewCompleter::TextModified(bool updateChoices)
+{
+ EditViewStateChanged(updateChoices);
+}
+
+
+filter_result
+TextViewCompleter::Filter(BMessage* message, BHandler** target)
+{
+ const char* bytes;
+ int32 modifiers;
+ if ((!target || message->FindString("bytes", &bytes) != B_OK
+ || message->FindInt32("modifiers", &modifiers) != B_OK)
+ || (modifiers & (B_CONTROL_KEY | B_COMMAND_KEY | B_OPTION_KEY
+ | B_SHIFT_KEY)) != 0) {
+ return B_DISPATCH_MESSAGE;
+ }
+
+ switch (bytes[0]) {
+ case B_UP_ARROW:
+ SelectPrevious();
+ // Insert the current choice into the text view, so the user can
+ // continue typing. This will not trigger another evaluation, since
+ // we don't invoke EditViewStateChanged().
+ ApplyChoice(false);
+ return B_SKIP_MESSAGE;
+ case B_DOWN_ARROW:
+ SelectNext();
+ // See above.
+ ApplyChoice(false);
+ return B_SKIP_MESSAGE;
+ case B_PAGE_UP:
+ {
+ int32 index = SelectedChoiceIndex() - CountVisibleChoices();
+ index = max_c(index, 0);
+ Select(index);
+ ApplyChoice(false);
+ return B_SKIP_MESSAGE;
+ }
+ case B_PAGE_DOWN:
+ {
+ int32 index = SelectedChoiceIndex() + CountVisibleChoices();
+ index = min_c(index, CountChoices() - 1);
+ Select(index);
+ ApplyChoice(false);
+ return B_SKIP_MESSAGE;
+ }
+
+ case B_ESCAPE:
+ CancelChoice();
+ return B_DISPATCH_MESSAGE;
+ case B_RETURN:
+ if (IsChoiceSelected()) {
+ ApplyChoice();
+ EditViewStateChanged();
+ } else
+ CancelChoice();
+ return B_DISPATCH_MESSAGE;
+ case B_TAB: {
+ // make sure that the choices-view is closed when tabbing out:
+ CancelChoice();
+ return B_DISPATCH_MESSAGE;
+ }
+ default:
+ if (!fModificationsReported) {
+ // dispatch message to textview manually...
+ Looper()->DispatchMessage(message, *target);
+ // ...and propagate the new state to the auto-completer:
+ EditViewStateChanged();
+ return B_SKIP_MESSAGE;
+ }
+ return B_DISPATCH_MESSAGE;
+ }
+}
diff --git a/src/apps/webpositive/autocompletion/TextViewCompleter.h b/src/apps/webpositive/autocompletion/TextViewCompleter.h
new file mode 100644
index 0000000000..1a2955f6d7
--- /dev/null
+++ b/src/apps/webpositive/autocompletion/TextViewCompleter.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam).
+ * All rights reserved. Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Oliver Tappe <beam@hirschkaefer.de>
+ */
+#ifndef TEXT_CONTROL_COMPLETER_H
+#define TEXT_CONTROL_COMPLETER_H
+
+#include <MessageFilter.h>
+
+#include "AutoCompleter.h"
+
+
+class BTextView;
+
+class TextViewCompleter : protected BAutoCompleter, public BMessageFilter {
+public:
+ TextViewCompleter(BTextView* textView,
+ ChoiceModel* choiceModel = NULL,
+ PatternSelector* patternSelector = NULL);
+ virtual ~TextViewCompleter();
+
+ void SetModificationsReported(bool reported);
+ void TextModified(bool updateChoices);
+
+private:
+ virtual filter_result Filter(BMessage* message, BHandler** target);
+
+ class TextViewWrapper : public EditView {
+ public:
+ TextViewWrapper(BTextView* textView);
+ virtual BRect GetAdjustmentFrame();
+ virtual void GetEditViewState(BString& text,
+ int32* caretPos);
+ virtual void SetEditViewState(const BString& text,
+ int32 caretPos, int32 selectionLength = 0);
+ private:
+ BTextView* fTextView;
+ };
+
+private:
+ BTextView* fTextView;
+ bool fModificationsReported;
+};
+
+#endif // TEXT_CONTROL_COMPLETER_H
diff --git a/src/apps/webpositive/support/AutoLocker.h b/src/apps/webpositive/support/AutoLocker.h
new file mode 100644
index 0000000000..9baa7aa7a5
--- /dev/null
+++ b/src/apps/webpositive/support/AutoLocker.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2005-2007, Ingo Weinhold, bonefish@users.sf.net.
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+#ifndef _AUTO_LOCKER_H
+#define _AUTO_LOCKER_H
+
+
+#include <stddef.h>
+
+
+namespace BPrivate {
+
+// AutoLockerStandardLocking
+template<typename Lockable>
+class AutoLockerStandardLocking {
+public:
+ inline bool Lock(Lockable* lockable)
+ {
+ return lockable->Lock();
+ }
+
+ inline void Unlock(Lockable* lockable)
+ {
+ lockable->Unlock();
+ }
+};
+
+// AutoLockerReadLocking
+template<typename Lockable>
+class AutoLockerReadLocking {
+public:
+ inline bool Lock(Lockable* lockable)
+ {
+ return lockable->ReadLock();
+ }
+
+ inline void Unlock(Lockable* lockable)
+ {
+ lockable->ReadUnlock();
+ }
+};
+
+// AutoLockerWriteLocking
+template<typename Lockable>
+class AutoLockerWriteLocking {
+public:
+ inline bool Lock(Lockable* lockable)
+ {
+ return lockable->WriteLock();
+ }
+
+ inline void Unlock(Lockable* lockable)
+ {
+ lockable->WriteUnlock();
+ }
+};
+
+// AutoLocker
+template<typename Lockable,
+ typename Locking = AutoLockerStandardLocking<Lockable> >
+class AutoLocker {
+private:
+ typedef AutoLocker<Lockable, Locking> ThisClass;
+public:
+ inline AutoLocker()
+ :
+ fLockable(NULL),
+ fLocked(false)
+ {
+ }
+
+ inline AutoLocker(const Locking& locking)
+ :
+ fLockable(NULL),
+ fLocking(locking),
+ fLocked(false)
+ {
+ }
+
+ inline AutoLocker(Lockable* lockable, bool alreadyLocked = false,
+ bool lockIfNotLocked = true)
+ :
+ fLockable(lockable),
+ fLocked(fLockable && alreadyLocked)
+ {
+ if (!alreadyLocked && lockIfNotLocked)
+ Lock();
+ }
+
+ inline AutoLocker(Lockable& lockable, bool alreadyLocked = false,
+ bool lockIfNotLocked = true)
+ :
+ fLockable(&lockable),
+ fLocked(fLockable && alreadyLocked)
+ {
+ if (!alreadyLocked && lockIfNotLocked)
+ Lock();
+ }
+
+ inline ~AutoLocker()
+ {
+ Unlock();
+ }
+
+ inline void SetTo(Lockable* lockable, bool alreadyLocked,
+ bool lockIfNotLocked = true)
+ {
+ Unlock();
+ fLockable = lockable;
+ fLocked = (lockable && alreadyLocked);
+ if (!alreadyLocked && lockIfNotLocked)
+ Lock();
+ }
+
+ inline void SetTo(Lockable& lockable, bool alreadyLocked,
+ bool lockIfNotLocked = true)
+ {
+ SetTo(&lockable, alreadyLocked, lockIfNotLocked);
+ }
+
+ inline void Unset()
+ {
+ Unlock();
+ Detach();
+ }
+
+ inline bool Lock()
+ {
+ if (fLockable && !fLocked)
+ fLocked = fLocking.Lock(fLockable);
+ return fLocked;
+ }
+
+ inline void Unlock()
+ {
+ if (fLockable && fLocked) {
+ fLocking.Unlock(fLockable);
+ fLocked = false;
+ }
+ }
+
+ inline void Detach()
+ {
+ fLockable = NULL;
+ fLocked = false;
+ }
+
+ inline AutoLocker<Lockable, Locking>& operator=(Lockable* lockable)
+ {
+ SetTo(lockable);
+ return *this;
+ }
+
+ inline AutoLocker<Lockable, Locking>& operator=(Lockable& lockable)
+ {
+ SetTo(&lockable);
+ return *this;
+ }
+
+ inline bool IsLocked() const { return fLocked; }
+
+ inline operator bool() const { return fLocked; }
+
+protected:
+ Lockable* fLockable;
+ Locking fLocking;
+ bool fLocked;
+};
+
+
+} // namespace BPrivate
+
+using BPrivate::AutoLocker;
+using BPrivate::AutoLockerReadLocking;
+using BPrivate::AutoLockerWriteLocking;
+
+#endif // _AUTO_LOCKER_H
diff --git a/src/apps/webpositive/support/BaseURL.cpp b/src/apps/webpositive/support/BaseURL.cpp
new file mode 100644
index 0000000000..4d2f47cd29
--- /dev/null
+++ b/src/apps/webpositive/support/BaseURL.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2010 Stephan Aßmus <superstippi@gmx.de>
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+
+#include "BaseURL.h"
+
+
+BString
+baseURL(const BString string)
+{
+ int32 baseURLStart = string.FindFirst("://") + 3;
+ int32 baseURLEnd = string.FindFirst("/", baseURLStart + 1);
+ BString result;
+ result.SetTo(string.String() + baseURLStart, baseURLEnd - baseURLStart);
+ return result;
+}
+
diff --git a/src/apps/webpositive/support/BaseURL.h b/src/apps/webpositive/support/BaseURL.h
new file mode 100644
index 0000000000..b4589ef58f
--- /dev/null
+++ b/src/apps/webpositive/support/BaseURL.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2010 Stephan Aßmus <superstippi@gmx.de>
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+#ifndef BASE_URL_H
+#define BASE_URL_H
+
+#include <String.h>
+
+
+BString baseURL(const BString string);
+
+
+#endif // BASE_URL_H
diff --git a/src/apps/webpositive/support/BitmapButton.cpp b/src/apps/webpositive/support/BitmapButton.cpp
new file mode 100644
index 0000000000..009079969c
--- /dev/null
+++ b/src/apps/webpositive/support/BitmapButton.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2010 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+
+#include "BitmapButton.h"
+
+#include <string.h>
+
+#include <Bitmap.h>
+#include <ControlLook.h>
+#include <TranslationUtils.h>
+
+
+static const float kFrameInset = 2;
+
+
+BitmapButton::BitmapButton(const char* resourceName, BMessage* message)
+ :
+ BButton("", message),
+ fBitmap(BTranslationUtils::GetBitmap(resourceName)),
+ fBackgroundMode(BUTTON_BACKGROUND)
+{
+}
+
+
+BitmapButton::BitmapButton(const uint8* bits, uint32 width, uint32 height,
+ color_space format, BMessage* message)
+ :
+ BButton("", message),
+ fBitmap(new BBitmap(BRect(0, 0, width - 1, height - 1), 0, format)),
+ fBackgroundMode(BUTTON_BACKGROUND)
+{
+ memcpy(fBitmap->Bits(), bits, fBitmap->BitsLength());
+}
+
+
+BitmapButton::~BitmapButton()
+{
+ delete fBitmap;
+}
+
+
+BSize
+BitmapButton::MinSize()
+{
+ BSize min(0, 0);
+ if (fBitmap) {
+ min.width = fBitmap->Bounds().Width();
+ min.height = fBitmap->Bounds().Height();
+ }
+ min.width += kFrameInset * 2;
+ min.height += kFrameInset * 2;
+ return min;
+}
+
+
+BSize
+BitmapButton::MaxSize()
+{
+ return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
+}
+
+
+BSize
+BitmapButton::PreferredSize()
+{
+ return MinSize();
+}
+
+
+void
+BitmapButton::Draw(BRect updateRect)
+{
+ BRect bounds(Bounds());
+ rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
+ uint32 flags = be_control_look->Flags(this);
+
+ if (fBackgroundMode == BUTTON_BACKGROUND || Value() == B_CONTROL_ON) {
+ be_control_look->DrawButtonBackground(this, bounds, updateRect, base,
+ flags);
+ } else {
+ SetHighColor(tint_color(base, B_DARKEN_2_TINT));
+ StrokeLine(bounds.LeftBottom(), bounds.RightBottom());
+ bounds.bottom--;
+ be_control_look->DrawMenuBarBackground(this, bounds, updateRect, base,
+ flags);
+ }
+
+ if (fBitmap == NULL)
+ return;
+
+ SetDrawingMode(B_OP_ALPHA);
+
+ if (!IsEnabled()) {
+ SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
+ SetHighColor(0, 0, 0, 120);
+ }
+
+ BRect bitmapBounds(fBitmap->Bounds());
+ BPoint bitmapLocation(
+ floorf((bounds.left + bounds.right
+ - (bitmapBounds.left + bitmapBounds.right)) / 2 + 0.5f),
+ floorf((bounds.top + bounds.bottom
+ - (bitmapBounds.top + bitmapBounds.bottom)) / 2 + 0.5f));
+
+ DrawBitmap(fBitmap, bitmapLocation);
+}
+
+
+void
+BitmapButton::SetBackgroundMode(uint32 mode)
+{
+ if (fBackgroundMode != mode) {
+ fBackgroundMode = mode;
+ Invalidate();
+ }
+}
+
diff --git a/src/apps/webpositive/support/BitmapButton.h b/src/apps/webpositive/support/BitmapButton.h
new file mode 100644
index 0000000000..432adcb34e
--- /dev/null
+++ b/src/apps/webpositive/support/BitmapButton.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef BITMAP_BUTTON_H
+#define BITMAP_BUTTON_H
+
+#include <Button.h>
+
+
+class BBitmap;
+
+
+class BitmapButton : public BButton {
+public:
+ enum {
+ BUTTON_BACKGROUND = 0,
+ MENUBAR_BACKGROUND,
+ };
+
+ BitmapButton(const char* resourceName,
+ BMessage* message);
+
+ BitmapButton(const uint8* bits, uint32 width,
+ uint32 height, color_space format,
+ BMessage* message);
+
+ virtual ~BitmapButton();
+
+ virtual BSize MinSize();
+ virtual BSize MaxSize();
+ virtual BSize PreferredSize();
+
+ virtual void Draw(BRect updateRect);
+
+ void SetBackgroundMode(uint32 mode);
+
+private:
+ BBitmap* fBitmap;
+ uint32 fBackgroundMode;
+};
+
+
+#endif // BITMAP_BUTTON_H
diff --git a/src/apps/webpositive/support/DateTime.cpp b/src/apps/webpositive/support/DateTime.cpp
new file mode 100644
index 0000000000..19030db00a
--- /dev/null
+++ b/src/apps/webpositive/support/DateTime.cpp
@@ -0,0 +1,1473 @@
+/*
+ * Copyright 2007-2010, Haiku, Inc. All Rights Reserved.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Julun <host.haiku@gmx.de>
+ * Stephan Aßmus <superstippi@gmx.de>
+ */
+
+#include "DateTime.h"
+
+
+#include <time.h>
+#include <sys/time.h>
+
+#include <Message.h>
+
+
+namespace BPrivate {
+
+
+const int32 kSecondsPerMinute = 60;
+
+const int32 kHoursPerDay = 24;
+const int32 kMinutesPerDay = 1440;
+const int32 kSecondsPerDay = 86400;
+const int32 kMillisecondsPerDay = 86400000;
+
+const bigtime_t kMicrosecondsPerSecond = 1000000LL;
+const bigtime_t kMicrosecondsPerMinute = 60000000LL;
+const bigtime_t kMicrosecondsPerHour = 3600000000LL;
+const bigtime_t kMicrosecondsPerDay = 86400000000LL;
+
+
+/*!
+ Constructs a new BTime object. Asked for its time representation, it will
+ return 0 for Hour(), Minute(), Second() etc. This can represent midnight,
+ but be aware IsValid() will return false.
+*/
+BTime::BTime()
+ :
+ fMicroseconds(-1)
+{
+}
+
+
+/*!
+ Constructs a new BTime object as a copy of \c other.
+*/
+BTime::BTime(const BTime& other)
+ :
+ fMicroseconds(other.fMicroseconds)
+{
+}
+
+
+/*!
+ Constructs a BTime object with \c hour \c minute, \c second, \c microsecond.
+
+ \c hour must be between 0 and 23, \c minute and \c second must be between
+ 0 and 59 and \c microsecond should be in the range of 0 and 999999. If the
+ specified time is invalid, the time is not set and IsValid() returns false.
+*/
+BTime::BTime(int32 hour, int32 minute, int32 second, int32 microsecond)
+ :
+ fMicroseconds(-1)
+{
+ _SetTime(hour, minute, second, microsecond);
+}
+
+
+/*!
+ Constructs a new BTime object from the provided BMessage archive.
+*/
+BTime::BTime(const BMessage* archive)
+ :
+ fMicroseconds(-1)
+{
+ if (archive == NULL)
+ return;
+ archive->FindInt64("mircoseconds", &fMicroseconds);
+}
+
+
+/*!
+ Empty destructor.
+*/
+BTime::~BTime()
+{
+}
+
+
+/*!
+ Archives the BTime object into the provided BMessage object.
+ @returns \c B_OK if all went well.
+ \c B_BAD_VALUE, if the message is \c NULL.
+ \c other error codes, depending on failure to append
+ fields to the message.
+*/
+status_t
+BTime::Archive(BMessage* into) const
+{
+ if (into == NULL)
+ return B_BAD_VALUE;
+ return into->AddInt64("mircoseconds", fMicroseconds);
+}
+
+
+/*!
+ Returns true if the time is valid, otherwise false. A valid time can be
+ BTime(23, 59, 59, 999999) while BTime(24, 00, 01) would be invalid.
+*/
+bool
+BTime::IsValid() const
+{
+ return fMicroseconds > -1 && fMicroseconds < kMicrosecondsPerDay;
+}
+
+
+/*!
+ This is an overloaded member function, provided for convenience.
+*/
+bool
+BTime::IsValid(const BTime& time) const
+{
+ return time.fMicroseconds > -1 && time.fMicroseconds < kMicrosecondsPerDay;
+}
+
+
+/*!
+ This is an overloaded member function, provided for convenience.
+*/
+bool
+BTime::IsValid(int32 hour, int32 minute, int32 second, int32 microsecond) const
+{
+ return BTime(hour, minute, second, microsecond).IsValid();
+}
+
+
+/*!
+ Returns the current time as reported by the system depending on the given
+ time_type \c type.
+*/
+BTime
+BTime::CurrentTime(time_type type)
+{
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) != 0) {
+ // gettimeofday failed?
+ time(&tv.tv_sec);
+ }
+
+ struct tm result;
+ struct tm* timeinfo;
+ if (type == B_GMT_TIME)
+ timeinfo = gmtime_r(&tv.tv_sec, &result);
+ else
+ timeinfo = localtime_r(&tv.tv_sec, &result);
+
+ int32 sec = timeinfo->tm_sec;
+ return BTime(timeinfo->tm_hour, timeinfo->tm_min, (sec > 59) ? 59 : sec,
+ tv.tv_usec);
+}
+
+
+/*!
+ Returns a copy of the current BTime object.
+*/
+BTime
+BTime::Time() const
+{
+ return *this;
+}
+
+
+/*!
+ This is an overloaded member function, provided for convenience. Set the
+ current BTime object to the passed BTime \c time object.
+*/
+bool
+BTime::SetTime(const BTime& time)
+{
+ fMicroseconds = time.fMicroseconds;
+ return IsValid();
+}
+
+
+/*!
+ Set the time to \c hour \c minute, \c second and \c microsecond.
+
+ \c hour must be between 0 and 23, \c minute and \c second must be between
+ 0 and 59 and \c microsecond should be in the range of 0 and 999999. Returns
+ true if the time is valid; otherwise false. If the specified time is
+ invalid, the time is not set and the function returns false.
+*/
+bool
+BTime::SetTime(int32 hour, int32 minute, int32 second, int32 microsecond)
+{
+ return _SetTime(hour, minute, second, microsecond);
+}
+
+
+
+/*!
+ Adds \c hours to the current time. If the passed value is negativ it will
+ become earlier. Note: The time will wrap if it passes midnight.
+*/
+BTime&
+BTime::AddHours(int32 hours)
+{
+ return _AddMicroseconds(bigtime_t(hours % kHoursPerDay)
+ * kMicrosecondsPerHour);
+}
+
+
+/*!
+ Adds \c minutes to the current time. If the passed value is negativ it will
+ become earlier. Note: The time will wrap if it passes midnight.
+*/
+BTime&
+BTime::AddMinutes(int32 minutes)
+{
+ return _AddMicroseconds(bigtime_t(minutes % kMinutesPerDay)
+ * kMicrosecondsPerMinute);
+}
+
+
+/*!
+ Adds \c seconds to the current time. If the passed value is negativ it will
+ become earlier. Note: The time will wrap if it passes midnight.
+*/
+BTime&
+BTime::AddSeconds(int32 seconds)
+{
+ return _AddMicroseconds(bigtime_t(seconds % kSecondsPerDay)
+ * kMicrosecondsPerSecond);
+}
+
+
+/*!
+ Adds \c milliseconds to the current time. If the passed value is negativ it
+ will become earlier. Note: The time will wrap if it passes midnight.
+*/
+BTime&
+BTime::AddMilliseconds(int32 milliseconds)
+{
+ return _AddMicroseconds(bigtime_t(milliseconds % kMillisecondsPerDay)
+ * 1000);
+}
+
+
+/*!
+ Adds \c microseconds to the current time. If the passed value is negativ it
+ will become earlier. Note: The time will wrap if it passes midnight.
+*/
+BTime&
+BTime::AddMicroseconds(int32 microseconds)
+{
+ return _AddMicroseconds(microseconds);
+}
+
+
+/*!
+ Returns the hour fragment of the time.
+*/
+int32
+BTime::Hour() const
+{
+ return int32(_Microseconds() / kMicrosecondsPerHour);
+}
+
+
+/*!
+ Returns the minute fragment of the time.
+*/
+int32
+BTime::Minute() const
+{
+ return int32(((_Microseconds() % kMicrosecondsPerHour)) / kMicrosecondsPerMinute);
+}
+
+
+/*!
+ Returns the second fragment of the time.
+*/
+int32
+BTime::Second() const
+{
+ return int32(_Microseconds() / kMicrosecondsPerSecond) % kSecondsPerMinute;
+}
+
+
+/*!
+ Returns the millisecond fragment of the time.
+*/
+int32
+BTime::Millisecond() const
+{
+
+ return Microsecond() / 1000;
+}
+
+
+/*!
+ Returns the microsecond fragment of the time.
+*/
+int32
+BTime::Microsecond() const
+{
+ return int32(_Microseconds() % 1000000);
+}
+
+
+bigtime_t
+BTime::_Microseconds() const
+{
+ return fMicroseconds == -1 ? 0 : fMicroseconds;
+}
+
+
+/*!
+ Returns the difference between this time and the given BTime \c time based
+ on the passed diff_type \c type. If \c time is earlier the return value will
+ be negativ.
+
+ The return value then can be hours, minutes, seconds, milliseconds or
+ microseconds while its range will always be between -86400000000 and
+ 86400000000 depending on diff_type \c type.
+*/
+bigtime_t
+BTime::Difference(const BTime& time, diff_type type) const
+{
+ bigtime_t diff = time._Microseconds() - _Microseconds();
+ switch (type) {
+ case B_HOURS_DIFF: {
+ diff /= kMicrosecondsPerHour;
+ } break;
+ case B_MINUTES_DIFF: {
+ diff /= kMicrosecondsPerMinute;
+ } break;
+ case B_SECONDS_DIFF: {
+ diff /= kMicrosecondsPerSecond;
+ } break;
+ case B_MILLISECONDS_DIFF: {
+ diff /= 1000;
+ } break;
+ case B_MICROSECONDS_DIFF:
+ default: break;
+ }
+ return diff;
+}
+
+
+/*!
+ Returns true if this time is different from \c time, otherwise false.
+*/
+bool
+BTime::operator!=(const BTime& time) const
+{
+ return fMicroseconds != time.fMicroseconds;
+}
+
+
+/*!
+ Returns true if this time is equal to \c time, otherwise false.
+*/
+bool
+BTime::operator==(const BTime& time) const
+{
+ return fMicroseconds == time.fMicroseconds;
+}
+
+
+/*!
+ Returns true if this time is earlier than \c time, otherwise false.
+*/
+bool
+BTime::operator<(const BTime& time) const
+{
+ return fMicroseconds < time.fMicroseconds;
+}
+
+
+/*!
+ Returns true if this time is earlier than or equal to \c time, otherwise false.
+*/
+bool
+BTime::operator<=(const BTime& time) const
+{
+ return fMicroseconds <= time.fMicroseconds;
+}
+
+
+/*!
+ Returns true if this time is later than \c time, otherwise false.
+*/
+bool
+BTime::operator>(const BTime& time) const
+{
+ return fMicroseconds > time.fMicroseconds;
+}
+
+
+/*!
+ Returns true if this time is later than or equal to \c time, otherwise false.
+*/
+bool
+BTime::operator>=(const BTime& time) const
+{
+ return fMicroseconds >= time.fMicroseconds;
+}
+
+
+BTime&
+BTime::_AddMicroseconds(bigtime_t microseconds)
+{
+ bigtime_t count = 0;
+ if (microseconds < 0) {
+ count = ((kMicrosecondsPerDay - microseconds) / kMicrosecondsPerDay) *
+ kMicrosecondsPerDay;
+ }
+ fMicroseconds = (_Microseconds() + microseconds + count) % kMicrosecondsPerDay;
+ return *this;
+}
+
+
+bool
+BTime::_SetTime(bigtime_t hour, bigtime_t minute, bigtime_t second,
+ bigtime_t microsecond)
+{
+ fMicroseconds = hour * kMicrosecondsPerHour +
+ minute * kMicrosecondsPerMinute +
+ second * kMicrosecondsPerSecond +
+ microsecond;
+
+ bool isValid = IsValid();
+ if (!isValid)
+ fMicroseconds = -1;
+
+ return isValid;
+}
+
+
+// #pragma mark - BDate
+
+
+/*!
+ Constructs a new BDate object. IsValid() will return false.
+*/
+BDate::BDate()
+ :
+ fDay(-1),
+ fYear(0),
+ fMonth(-1)
+{
+}
+
+
+/*!
+ Constructs a new BDate object as a copy of \c other.
+*/
+BDate::BDate(const BDate& other)
+ :
+ fDay(other.fDay),
+ fYear(other.fYear),
+ fMonth(other.fMonth)
+{
+}
+
+
+/*!
+ Constructs a BDate object with \c year \c month and \c day.
+
+ Please note that a date before 1.1.4713 BC, a date with year 0 and a date
+ between 4.10.1582 and 15.10.1582 are considered invalid. If the specified
+ date is invalid, the date is not set and IsValid() returns false. Also note
+ that every passed year will be interpreted as is.
+
+*/
+BDate::BDate(int32 year, int32 month, int32 day)
+{
+ _SetDate(year, month, day);
+}
+
+
+/*!
+ Constructs a new BDate object from the provided archive.
+*/
+BDate::BDate(const BMessage* archive)
+ :
+ fDay(-1),
+ fYear(0),
+ fMonth(-1)
+{
+ if (archive == NULL)
+ return;
+ archive->FindInt32("day", &fDay);
+ archive->FindInt32("year", &fYear);
+ archive->FindInt32("month", &fMonth);
+}
+
+
+/*!
+ Empty destructor.
+*/
+BDate::~BDate()
+{
+}
+
+
+/*!
+ Archives the BDate object into the provided BMessage object.
+ @returns \c B_OK if all went well.
+ \c B_BAD_VALUE, if the message is \c NULL.
+ \c other error codes, depending on failure to append
+ fields to the message.
+*/
+status_t
+BDate::Archive(BMessage* into) const
+{
+ if (into == NULL)
+ return B_BAD_VALUE;
+ status_t ret = into->AddInt32("day", fDay);
+ if (ret == B_OK)
+ ret = into->AddInt32("year", fYear);
+ if (ret == B_OK)
+ ret = into->AddInt32("month", fMonth);
+ return ret;
+}
+
+
+/*!
+ Returns true if the date is valid, otherwise false.
+
+ Please note that a date before 1.1.4713 BC, a date with year 0 and a date
+ between 4.10.1582 and 15.10.1582 are considered invalid.
+*/
+bool
+BDate::IsValid() const
+{
+ return IsValid(fYear, fMonth, fDay);
+}
+
+
+/*!
+ This is an overloaded member function, provided for convenience.
+*/
+bool
+BDate::IsValid(const BDate& date) const
+{
+ return IsValid(date.fYear, date.fMonth, date.fDay);
+}
+
+
+/*!
+ This is an overloaded member function, provided for convenience.
+*/
+bool
+BDate::IsValid(int32 year, int32 month, int32 day) const
+{
+ // no year 0 in Julian and nothing before 1.1.4713 BC
+ if (year == 0 || year < -4713)
+ return false;
+
+ if (month < 1 || month > 12)
+ return false;
+
+ if (day < 1 || day > _DaysInMonth(year, month))
+ return false;
+
+ // 'missing' days between switch julian - gregorian
+ if (year == 1582 && month == 10 && day > 4 && day < 15)
+ return false;
+
+ return true;
+}
+
+
+/*!
+ Returns the current date as reported by the system depending on the given
+ time_type \c type.
+*/
+BDate
+BDate::CurrentDate(time_type type)
+{
+ time_t timer;
+ struct tm result;
+ struct tm* timeinfo;
+
+ time(&timer);
+
+ if (type == B_GMT_TIME)
+ timeinfo = gmtime_r(&timer, &result);
+ else
+ timeinfo = localtime_r(&timer, &result);
+
+ return BDate(timeinfo->tm_year + 1900, timeinfo->tm_mon +1, timeinfo->tm_mday);
+}
+
+
+/*!
+ Returns a copy of the current BTime object.
+*/
+BDate
+BDate::Date() const
+{
+ return *this;
+}
+
+
+/*!
+ This is an overloaded member function, provided for convenience.
+*/
+bool
+BDate::SetDate(const BDate& date)
+{
+ return _SetDate(date.fYear, date.fMonth, date.fDay);
+}
+
+
+/*!
+ Set the date to \c year \c month and \c day.
+
+ Returns true if the date is valid; otherwise false. If the specified date is
+ invalid, the date is not set and the function returns false.
+*/
+bool
+BDate::SetDate(int32 year, int32 month, int32 day)
+{
+ return _SetDate(year, month, day);
+}
+
+
+/*!
+ This function sets the given \c year, \c month and \c day to the
+ representative values of this date. The pointers can be NULL. If the date is
+ invalid, the values will be set to -1 for \c month and \c day, the \c year
+ will be set to 0.
+*/
+void
+BDate::GetDate(int32* year, int32* month, int32* day)
+{
+ if (year)
+ *year = fYear;
+
+ if (month)
+ *month = fMonth;
+
+ if (day)
+ *day = fDay;
+}
+
+
+/*!
+ Adds \c days to the current date. If the passed value is negativ it will
+ become earlier. If the current date is invalid, the \c days are not added.
+*/
+void
+BDate::AddDays(int32 days)
+{
+ if (IsValid())
+ *this = JulianDayToDate(DateToJulianDay() + days);
+}
+
+
+/*!
+ Adds \c years to the current date. If the passed value is negativ it will
+ become earlier. If the current date is invalid, the \c years are not added.
+ The day/ month combination will be adjusted if it does not exist in the
+ resulting year, so this function will then return the latest valid date.
+*/
+void
+BDate::AddYears(int32 years)
+{
+ if (IsValid()) {
+ const int32 tmp = fYear;
+ fYear += years;
+
+ if ((tmp > 0 && fYear <= 0) || (tmp < 0 && fYear >= 0))
+ fYear += (years > 0) ? +1 : -1;
+
+ fDay = min_c(fDay, _DaysInMonth(fYear, fMonth));
+ }
+}
+
+
+/*!
+ Adds \c months to the current date. If the passed value is negativ it will
+ become earlier. If the current date is invalid, the \c months are not added.
+ The day/ month combination will be adjusted if it does not exist in the
+ resulting year, so this function will then return the latest valid date.
+*/
+void
+BDate::AddMonths(int32 months)
+{
+ if (IsValid()) {
+ const int32 tmp = fYear;
+ fYear += months / 12;
+ fMonth += months % 12;
+
+ if (fMonth > 12) {
+ fYear++;
+ fMonth -= 12;
+ } else if (fMonth < 1) {
+ fYear--;
+ fMonth += 12;
+ }
+
+ if ((tmp > 0 && fYear <= 0) || (tmp < 0 && fYear >= 0))
+ fYear += (months > 0) ? +1 : -1;
+
+ // 'missing' days between switch julian - gregorian
+ if (fYear == 1582 && fMonth == 10 && fDay > 4 && fDay < 15)
+ fDay = (months > 0) ? 15 : 4;
+
+ fDay = min_c(fDay, DaysInMonth());
+ }
+}
+
+
+/*!
+ Returns the day fragment of the date. The return value will be in the range
+ of 1 to 31, in case the date is invalid it will be -1.
+*/
+int32
+BDate::Day() const
+{
+ return fDay;
+}
+
+
+/*!
+ Returns the year fragment of the date. If the date is invalid, the function
+ returns 0.
+*/
+int32
+BDate::Year() const
+{
+ return fYear;
+}
+
+
+/*!
+ Returns the month fragment of the date. The return value will be in the
+ range of 1 to 12, in case the date is invalid it will be -1.
+*/
+int32
+BDate::Month() const
+{
+ return fMonth;
+}
+
+
+/*!
+ Returns the difference in days between this date and the given BDate \c date.
+ If \c date is earlier the return value will be negativ. If the calculation
+ is done with an invalid date, the result is undefined.
+*/
+int32
+BDate::Difference(const BDate& date) const
+{
+ return date.DateToJulianDay() - DateToJulianDay();
+}
+
+
+/*!
+ Returns the week number of the date, if the date is invalid it will return
+ B_ERROR. Please note that this function does only work within the Gregorian
+ calendar, thus a date before 15.10.1582 will return B_ERROR.
+*/
+int32
+BDate::WeekNumber() const
+{
+ /*
+ This algorithm is taken from:
+ Frequently Asked Questions about Calendars
+ Version 2.8 Claus Tøndering 15 December 2005
+
+ Note: it will work only within the Gregorian Calendar
+ */
+
+ if (!IsValid() || fYear < 1582
+ || (fYear == 1582 && fMonth < 10)
+ || (fYear == 1582 && fMonth == 10 && fDay < 15))
+ return int32(B_ERROR);
+
+ int32 a;
+ int32 b;
+ int32 s;
+ int32 e;
+ int32 f;
+
+ if (fMonth > 0 && fMonth < 3) {
+ a = fYear - 1;
+ b = (a / 4) - (a / 100) + (a / 400);
+ int32 c = ((a - 1) / 4) - ((a - 1) / 100) + ((a -1) / 400);
+ s = b - c;
+ e = 0;
+ f = fDay - 1 + 31 * (fMonth - 1);
+ } else if (fMonth >= 3 && fMonth <= 12) {
+ a = fYear;
+ b = (a / 4) - (a / 100) + (a / 400);
+ int32 c = ((a - 1) / 4) - ((a - 1) / 100) + ((a -1) / 400);
+ s = b - c;
+ e = s + 1;
+ f = fDay + ((153 * (fMonth - 3) + 2) / 5) + 58 + s;
+ } else
+ return int32(B_ERROR);
+
+ int32 g = (a + b) % 7;
+ int32 d = (f + g - e) % 7;
+ int32 n = f + 3 - d;
+
+ int32 weekNumber;
+ if (n < 0)
+ weekNumber = 53 - (g -s) / 5;
+ else if (n > 364 + s)
+ weekNumber = 1;
+ else
+ weekNumber = n / 7 + 1;
+
+ return weekNumber;
+}
+
+
+/*!
+ Returns the day of the week in the range of 1 to 7, while 1 stands for
+ monday. If the date is invalid, the function will return B_ERROR.
+*/
+int32
+BDate::DayOfWeek() const
+{
+ // http://en.wikipedia.org/wiki/Julian_day#Calculation
+ return IsValid() ? (DateToJulianDay() % 7) + 1 : int32(B_ERROR);
+}
+
+
+/*!
+ Returns the day of the year in the range of 1 to 365 (366 in leap years). If
+ the date is invalid, the function will return B_ERROR.
+*/
+int32
+BDate::DayOfYear() const
+{
+ if (!IsValid())
+ return int32(B_ERROR);
+
+ return DateToJulianDay() - _DateToJulianDay(fYear, 1, 1) + 1;
+}
+
+
+/*!
+ Returns true if the passed \c year is a leap year, otherwise false. If the
+ \c year passed is before 4713 BC, the result is undefined.
+*/
+bool
+BDate::IsLeapYear(int32 year) const
+{
+ if (year < 1582) {
+ if (year < 0)
+ year++;
+ return (year % 4) == 0;
+ }
+ return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
+}
+
+
+/*!
+ Returns the number of days in the year of the current date. If the date is
+ valid it will return 365 or 366, otherwise B_ERROR;
+*/
+int32
+BDate::DaysInYear() const
+{
+ if (!IsValid())
+ return int32(B_ERROR);
+
+ return IsLeapYear(fYear) ? 366 : 365;
+}
+
+
+/*!
+ Returns the number of days in the month of the current date. If the date is
+ valid it will return 28 up to 31, otherwise B_ERROR;
+*/
+int32
+BDate::DaysInMonth() const
+{
+ if (!IsValid())
+ return int32(B_ERROR);
+
+ return _DaysInMonth(fYear, fMonth);
+}
+
+
+/*!
+ Returns the short day name of this object.
+*/
+BString
+BDate::ShortDayName() const
+{
+ return ShortDayName(DayOfWeek());
+}
+
+
+/*!
+ Returns the short day name in case of an valid day, otherwise an empty
+ string. The passed \c day must be in the range of 1 to 7 while 1 stands for
+ monday.
+*/
+/*static*/ BString
+BDate::ShortDayName(int32 day)
+{
+ if (day < 1 || day > 7)
+ return BString();
+
+ tm tm_struct;
+ memset(&tm_struct, 0, sizeof(tm));
+ tm_struct.tm_wday = day == 7 ? 0 : day;
+
+ char buffer[256];
+ strftime(buffer, sizeof(buffer), "%a", &tm_struct);
+
+ return BString(buffer);
+}
+
+
+/*!
+ Returns the short month name of this object.
+*/
+BString
+BDate::ShortMonthName() const
+{
+ return ShortMonthName(Month());
+}
+
+
+/*!
+ Returns the short month name in case of an valid month, otherwise an empty
+ string. The passed \c month must be in the range of 1 to 12.
+*/
+/*static*/ BString
+BDate::ShortMonthName(int32 month)
+{
+ if (month < 1 || month > 12)
+ return BString();
+
+ tm tm_struct;
+ memset(&tm_struct, 0, sizeof(tm));
+ tm_struct.tm_mon = month - 1;
+
+ char buffer[256];
+ strftime(buffer, sizeof(buffer), "%b", &tm_struct);
+
+ return BString(buffer);
+}
+
+
+/*!
+ Returns the long day name of this object's week day.
+*/
+BString
+BDate::LongDayName() const
+{
+ return LongDayName(DayOfWeek());
+}
+
+
+/*!
+ Returns the long day name in case of an valid day, otherwise an empty
+ string. The passed \c day must be in the range of 1 to 7 while 1 stands for
+ monday.
+*/
+/*static*/ BString
+BDate::LongDayName(int32 day)
+{
+ if (day < 1 || day > 7)
+ return BString();
+
+ tm tm_struct;
+ memset(&tm_struct, 0, sizeof(tm));
+ tm_struct.tm_wday = day == 7 ? 0 : day;
+
+ char buffer[256];
+ strftime(buffer, sizeof(buffer), "%A", &tm_struct);
+
+ return BString(buffer);
+}
+
+
+/*!
+ Returns the long month name of this object's month.
+*/
+BString
+BDate::LongMonthName() const
+{
+ return LongMonthName(Month());
+}
+
+
+/*!
+ Returns the long month name in case of an valid month, otherwise an empty
+ string. The passed \c month must be in the range of 1 to 12.
+*/
+/*static*/ BString
+BDate::LongMonthName(int32 month)
+{
+ if (month < 1 || month > 12)
+ return BString();
+
+ tm tm_struct;
+ memset(&tm_struct, 0, sizeof(tm));
+ tm_struct.tm_mon = month - 1;
+
+ char buffer[256];
+ strftime(buffer, sizeof(buffer), "%B", &tm_struct);
+
+ return BString(buffer);
+}
+
+
+/*!
+ Converts the date to Julian day. If your date is invalid, the function will
+ return B_ERROR.
+*/
+int32
+BDate::DateToJulianDay() const
+{
+ return _DateToJulianDay(fYear, fMonth, fDay);
+}
+
+
+/*
+ Converts the passed \c julianDay to an BDate. If the \c julianDay is negativ,
+ the function will return an invalid date. Because of the switch from Julian
+ calendar to Gregorian calendar the 4.10.1582 is followed by the 15.10.1582.
+*/
+BDate
+BDate::JulianDayToDate(int32 julianDay)
+{
+ BDate date;
+ const int32 kGregorianCalendarStart = 2299161;
+ if (julianDay >= kGregorianCalendarStart) {
+ // http://en.wikipedia.org/wiki/Julian_day#Gregorian_calendar_from_Julian_day_number
+ int32 j = julianDay + 32044;
+ int32 dg = j % 146097;
+ int32 c = (dg / 36524 + 1) * 3 / 4;
+ int32 dc = dg - c * 36524;
+ int32 db = dc % 1461;
+ int32 a = (db / 365 + 1) * 3 / 4;
+ int32 da = db - a * 365;
+ int32 m = (da * 5 + 308) / 153 - 2;
+ date.fYear = ((j / 146097) * 400 + c * 100 + (dc / 1461) * 4 + a) - 4800 +
+ (m + 2) / 12;
+ date.fMonth = (m + 2) % 12 + 1;
+ date.fDay = int32((da - (m + 4) * 153 / 5 + 122) + 1.5);
+ } else if (julianDay >= 0) {
+ // http://en.wikipedia.org/wiki/Julian_day#Calculation
+ julianDay += 32082;
+ int32 d = (4 * julianDay + 3) / 1461;
+ int32 e = julianDay - (1461 * d) / 4;
+ int32 m = ((5 * e) + 2) / 153;
+ date.fDay = e - (153 * m + 2) / 5 + 1;
+ date.fMonth = m + 3 - 12 * (m / 10);
+ int32 year = d - 4800 + (m / 10);
+ if (year <= 0)
+ year--;
+ date.fYear = year;
+ }
+ return date;
+}
+
+
+/*!
+ Returns true if this date is different from \c date, otherwise false.
+*/
+bool
+BDate::operator!=(const BDate& date) const
+{
+ return DateToJulianDay() != date.DateToJulianDay();
+}
+
+
+/*!
+ Returns true if this date is equal to \c date, otherwise false.
+*/
+bool
+BDate::operator==(const BDate& date) const
+{
+ return DateToJulianDay() == date.DateToJulianDay();
+}
+
+
+/*!
+ Returns true if this date is earlier than \c date, otherwise false.
+*/
+bool
+BDate::operator<(const BDate& date) const
+{
+ return DateToJulianDay() < date.DateToJulianDay();
+}
+
+
+/*!
+ Returns true if this date is earlier than or equal to \c date, otherwise false.
+*/
+bool
+BDate::operator<=(const BDate& date) const
+{
+ return DateToJulianDay() <= date.DateToJulianDay();
+}
+
+
+/*!
+ Returns true if this date is later than \c date, otherwise false.
+*/
+bool
+BDate::operator>(const BDate& date) const
+{
+ return DateToJulianDay() > date.DateToJulianDay();
+}
+
+
+/*!
+ Returns true if this date is later than or equal to \c date, otherwise false.
+*/
+bool
+BDate::operator>=(const BDate& date) const
+{
+ return DateToJulianDay() >= date.DateToJulianDay();
+}
+
+
+bool
+BDate::_SetDate(int32 year, int32 month, int32 day)
+{
+ fDay = -1;
+ fYear = 0;
+ fMonth = -1;
+
+ bool valid = IsValid(year, month, day);
+ if (valid) {
+ fDay = day;
+ fYear = year;
+ fMonth = month;
+ }
+
+ return valid;
+}
+
+
+int32
+BDate::_DaysInMonth(int32 year, int32 month) const
+{
+ if (month == 2 && IsLeapYear(year))
+ return 29;
+
+ const int32 daysInMonth[12] =
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ return daysInMonth[month -1];
+}
+
+
+int32
+BDate::_DateToJulianDay(int32 _year, int32 month, int32 day) const
+{
+ if (IsValid(_year, month, day)) {
+ int32 year = _year;
+ if (year < 0) year++;
+
+ int32 a = (14 - month) / 12;
+ int32 y = year + 4800 - a;
+ int32 m = month + (12 * a) - 3;
+
+ // http://en.wikipedia.org/wiki/Julian_day#Calculation
+ if (year > 1582
+ || (year == 1582 && month > 10)
+ || (year == 1582 && month == 10 && day >= 15)) {
+ return day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) -
+ (y / 100) + (y / 400) - 32045;
+ } else if (year < 1582
+ || (year == 1582 && month < 10)
+ || (year == 1582 && month == 10 && day <= 4)) {
+ return day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) - 32083;
+ }
+ }
+
+ // http://en.wikipedia.org/wiki/Gregorian_calendar:
+ // The last day of the Julian calendar was Thursday October 4, 1582
+ // and this was followed by the first day of the Gregorian calendar,
+ // Friday October 15, 1582 (the cycle of weekdays was not affected).
+ return int32(B_ERROR);
+}
+
+
+// #pragma mark - BDateTime
+
+
+/*!
+ Constructs a new BDateTime object. IsValid() will return false.
+*/
+BDateTime::BDateTime()
+ : fDate(),
+ fTime()
+{
+}
+
+
+/*!
+ Constructs a BDateTime object with \c date and \c time. The return value
+ of IsValid() depends on the validity of the passed objects.
+*/
+BDateTime::BDateTime(const BDate& date, const BTime& time)
+ : fDate(date),
+ fTime(time)
+{
+}
+
+
+/*!
+ Constructs a new BDateTime object. IsValid() will return false.
+*/
+BDateTime::BDateTime(const BMessage* archive)
+ : fDate(archive),
+ fTime(archive)
+{
+}
+
+
+/*!
+ Empty destructor.
+*/
+BDateTime::~BDateTime()
+{
+}
+
+
+/*!
+ Archives the BDateTime object into the provided BMessage object.
+ @returns \c B_OK if all went well.
+ \c B_BAD_VALUE, if the message is \c NULL.
+ \c other error codes, depending on failure to append
+ fields to the message.
+*/
+status_t
+BDateTime::Archive(BMessage* into) const
+{
+ status_t ret = fDate.Archive(into);
+ if (ret == B_OK)
+ ret = fTime.Archive(into);
+ return ret;
+}
+
+
+/*!
+ Returns true if the date time is valid, otherwise false.
+*/
+bool
+BDateTime::IsValid() const
+{
+ return fDate.IsValid() && fTime.IsValid();
+}
+
+
+/*!
+ Returns the current date and time as reported by the system depending on the
+ given time_type \c type.
+*/
+BDateTime
+BDateTime::CurrentDateTime(time_type type)
+{
+ return BDateTime(BDate::CurrentDate(type), BTime::CurrentTime(type));
+}
+
+
+/*!
+ Sets the current date and time of this object to \c date and \c time.
+*/
+void
+BDateTime::SetDateTime(const BDate& date, const BTime& time)
+{
+ fDate = date;
+ fTime = time;
+}
+
+
+/*!
+ Returns the current date of this object.
+*/
+BDate&
+BDateTime::Date()
+{
+ return fDate;
+}
+
+
+/*!
+ Returns the current date of this object.
+*/
+const BDate&
+BDateTime::Date() const
+{
+ return fDate;
+}
+
+
+/*!
+ Set the current date of this object to \c date.
+*/
+void
+BDateTime::SetDate(const BDate& date)
+{
+ fDate = date;
+}
+
+
+/*!
+ Returns the current time of this object.
+*/
+BTime&
+BDateTime::Time()
+{
+ return fTime;
+}
+
+
+/*!
+ Returns the current time of this object.
+*/
+const BTime&
+BDateTime::Time() const
+{
+ return fTime;
+}
+
+
+/*!
+ Sets the current time of this object to \c time.
+*/
+void
+BDateTime::SetTime(const BTime& time)
+{
+ fTime = time;
+}
+
+
+/*!
+ Returns the current date and time converted to seconds since
+ 1.1.1970 - 00:00:00. If the current date is before 1.1.1970 the function
+ returns -1;
+*/
+int32
+BDateTime::Time_t() const
+{
+ BDate date(1970, 1, 1);
+ if (date.Difference(fDate) < 0)
+ return -1;
+
+ tm tm_struct;
+
+ tm_struct.tm_hour = fTime.Hour();
+ tm_struct.tm_min = fTime.Minute();
+ tm_struct.tm_sec = fTime.Second();
+
+ tm_struct.tm_year = fDate.Year() - 1900;
+ tm_struct.tm_mon = fDate.Month() - 1;
+ tm_struct.tm_mday = fDate.Day();
+
+ // set less 0 as we wan't use it
+ tm_struct.tm_isdst = -1;
+
+ // return secs_since_jan1_1970 or -1 on error
+ return int32(mktime(&tm_struct));
+}
+
+
+/*!
+ Sets the current date and time converted from seconds since
+ 1.1.1970 - 00:00:00.
+*/
+void
+BDateTime::SetTime_t(uint32 seconds)
+{
+ BTime time;
+ time.AddSeconds(seconds % kSecondsPerDay);
+ fTime.SetTime(time);
+
+ BDate date(1970, 1, 1);
+ date.AddDays(seconds / kSecondsPerDay);
+ fDate.SetDate(date);
+}
+
+
+/*!
+ Returns true if this datetime is different from \c dateTime, otherwise false.
+*/
+bool
+BDateTime::operator!=(const BDateTime& dateTime) const
+{
+ return fTime != dateTime.fTime && fDate != dateTime.fDate;
+}
+
+
+/*!
+ Returns true if this datetime is equal to \c dateTime, otherwise false.
+*/
+bool
+BDateTime::operator==(const BDateTime& dateTime) const
+{
+ return fTime == dateTime.fTime && fDate == dateTime.fDate;
+}
+
+
+/*!
+ Returns true if this datetime is earlier than \c dateTime, otherwise false.
+*/
+bool
+BDateTime::operator<(const BDateTime& dateTime) const
+{
+ if (fDate < dateTime.fDate)
+ return true;
+ if (fDate == dateTime.fDate)
+ return fTime < dateTime.fTime;
+ return false;
+}
+
+
+/*!
+ Returns true if this datetime is earlier than or equal to \c dateTime,
+ otherwise false.
+*/
+bool
+BDateTime::operator<=(const BDateTime& dateTime) const
+{
+ if (fDate < dateTime.fDate)
+ return true;
+ if (fDate == dateTime.fDate)
+ return fTime <= dateTime.fTime;
+ return false;
+}
+
+
+/*!
+ Returns true if this datetime is later than \c dateTime, otherwise false.
+*/
+bool
+BDateTime::operator>(const BDateTime& dateTime) const
+{
+ if (fDate > dateTime.fDate)
+ return true;
+ if (fDate == dateTime.fDate)
+ return fTime > dateTime.fTime;
+ return false;
+}
+
+
+/*!
+ Returns true if this datetime is later than or equal to \c dateTime,
+ otherwise false.
+*/
+bool
+BDateTime::operator>=(const BDateTime& dateTime) const
+{
+ if (fDate > dateTime.fDate)
+ return true;
+ if (fDate == dateTime.fDate)
+ return fTime >= dateTime.fTime;
+ return false;
+}
+
+
+} //namespace BPrivate
diff --git a/src/apps/webpositive/support/DateTime.h b/src/apps/webpositive/support/DateTime.h
new file mode 100644
index 0000000000..93f76399d6
--- /dev/null
+++ b/src/apps/webpositive/support/DateTime.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2007-2010, Haiku, Inc. All Rights Reserved.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef _DATE_TIME_H_
+#define _DATE_TIME_H_
+
+
+#include <String.h>
+
+
+class BMessage;
+
+
+namespace BPrivate {
+
+
+enum time_type {
+ B_GMT_TIME,
+ B_LOCAL_TIME
+};
+
+
+enum diff_type {
+ B_HOURS_DIFF,
+ B_MINUTES_DIFF,
+ B_SECONDS_DIFF,
+ B_MILLISECONDS_DIFF,
+ B_MICROSECONDS_DIFF
+};
+
+
+class BTime {
+public:
+ BTime();
+ BTime(const BTime& other);
+ BTime(int32 hour, int32 minute, int32 second,
+ int32 microsecond = 0);
+ BTime(const BMessage* archive);
+ ~BTime();
+
+ status_t Archive(BMessage* into) const;
+
+ bool IsValid() const;
+ bool IsValid(const BTime& time) const;
+ bool IsValid(int32 hour, int32 minute, int32 second,
+ int32 microsecond = 0) const;
+
+ static BTime CurrentTime(time_type type);
+
+ BTime Time() const;
+ bool SetTime(const BTime& time);
+ bool SetTime(int32 hour, int32 minute, int32 second,
+ int32 microsecond = 0);
+
+ BTime& AddHours(int32 hours);
+ BTime& AddMinutes(int32 minutes);
+ BTime& AddSeconds(int32 seconds);
+ BTime& AddMilliseconds(int32 milliseconds);
+ BTime& AddMicroseconds(int32 microseconds);
+
+ int32 Hour() const;
+ int32 Minute() const;
+ int32 Second() const;
+ int32 Millisecond() const;
+ int32 Microsecond() const;
+ bigtime_t Difference(const BTime& time,
+ diff_type type) const;
+
+ bool operator!=(const BTime& time) const;
+ bool operator==(const BTime& time) const;
+
+ bool operator<(const BTime& time) const;
+ bool operator<=(const BTime& time) const;
+
+ bool operator>(const BTime& time) const;
+ bool operator>=(const BTime& time) const;
+
+private:
+ bigtime_t _Microseconds() const;
+ BTime& _AddMicroseconds(bigtime_t microseconds);
+ bool _SetTime(bigtime_t hour, bigtime_t minute,
+ bigtime_t second, bigtime_t microsecond);
+
+private:
+ bigtime_t fMicroseconds;
+};
+
+
+class BDate {
+public:
+ BDate();
+ BDate(const BDate& other);
+ BDate(int32 year, int32 month, int32 day);
+ BDate(const BMessage* archive);
+ ~BDate();
+
+ status_t Archive(BMessage* into) const;
+
+ bool IsValid() const;
+ bool IsValid(const BDate& date) const;
+ bool IsValid(int32 year, int32 month,
+ int32 day) const;
+
+ static BDate CurrentDate(time_type type);
+
+ BDate Date() const;
+ bool SetDate(const BDate& date);
+
+ bool SetDate(int32 year, int32 month, int32 day);
+ void GetDate(int32* year, int32* month, int32* day);
+
+ void AddDays(int32 days);
+ void AddYears(int32 years);
+ void AddMonths(int32 months);
+
+ int32 Day() const;
+ int32 Year() const;
+ int32 Month() const;
+ int32 Difference(const BDate& date) const;
+
+ int32 DayOfWeek() const;
+ int32 DayOfYear() const;
+
+ int32 WeekNumber() const;
+ bool IsLeapYear(int32 year) const;
+
+ int32 DaysInYear() const;
+ int32 DaysInMonth() const;
+
+ BString ShortDayName() const;
+ static BString ShortDayName(int32 day);
+
+ BString ShortMonthName() const;
+ static BString ShortMonthName(int32 month);
+
+ BString LongDayName() const;
+ static BString LongDayName(int32 day);
+
+ BString LongMonthName() const;
+ static BString LongMonthName(int32 month);
+
+ int32 DateToJulianDay() const;
+ static BDate JulianDayToDate(int32 julianDay);
+
+ bool operator!=(const BDate& date) const;
+ bool operator==(const BDate& date) const;
+
+ bool operator<(const BDate& date) const;
+ bool operator<=(const BDate& date) const;
+
+ bool operator>(const BDate& date) const;
+ bool operator>=(const BDate& date) const;
+
+private:
+ int32 _DaysInMonth(int32 year, int32 month) const;
+ bool _SetDate(int32 year, int32 month, int32 day);
+ int32 _DateToJulianDay(int32 year, int32 month,
+ int32 day) const;
+
+private:
+ int32 fDay;
+ int32 fYear;
+ int32 fMonth;
+};
+
+
+class BDateTime {
+public:
+ BDateTime();
+ BDateTime(const BDate &date, const BTime &time);
+ BDateTime(const BMessage* archive);
+ ~BDateTime();
+
+ status_t Archive(BMessage* into) const;
+
+ bool IsValid() const;
+
+ static BDateTime CurrentDateTime(time_type type);
+ void SetDateTime(const BDate &date, const BTime &time);
+
+ BDate& Date();
+ const BDate& Date() const;
+ void SetDate(const BDate &date);
+
+ BTime& Time();
+ const BTime& Time() const;
+ void SetTime(const BTime &time);
+
+ int32 Time_t() const;
+ void SetTime_t(uint32 seconds);
+
+ bool operator!=(const BDateTime& dateTime) const;
+ bool operator==(const BDateTime& dateTime) const;
+
+ bool operator<(const BDateTime& dateTime) const;
+ bool operator<=(const BDateTime& dateTime) const;
+
+ bool operator>(const BDateTime& dateTime) const;
+ bool operator>=(const BDateTime& dateTime) const;
+
+private:
+ BDate fDate;
+ BTime fTime;
+};
+
+
+} // namespace BPrivate
+
+
+using BPrivate::time_type;
+using BPrivate::B_GMT_TIME;
+using BPrivate::B_LOCAL_TIME;
+using BPrivate::diff_type;
+using BPrivate::B_HOURS_DIFF;
+using BPrivate::B_MINUTES_DIFF;
+using BPrivate::B_SECONDS_DIFF;
+using BPrivate::B_MILLISECONDS_DIFF;
+using BPrivate::B_MICROSECONDS_DIFF;
+using BPrivate::BTime;
+using BPrivate::BDate;
+using BPrivate::BDateTime;
+
+
+#endif // _DATE_TIME_H_
diff --git a/src/apps/webpositive/support/FontSelectionView.cpp b/src/apps/webpositive/support/FontSelectionView.cpp
new file mode 100644
index 0000000000..d0df01baa1
--- /dev/null
+++ b/src/apps/webpositive/support/FontSelectionView.cpp
@@ -0,0 +1,527 @@
+/*
+ * Copyright 2001-2010, Haiku.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Mark Hogben
+ * DarkWyrm <bpmagic@columbus.rr.com>
+ * Axel Dörfler, axeld@pinc-software.de
+ * Philippe Saint-Pierre, stpere@gmail.com
+ * Stephan Aßmus <superstippi@gmx.de>
+ */
+
+#include "FontSelectionView.h"
+
+#include <Box.h>
+#include <Catalog.h>
+#include <Locale.h>
+#include <Looper.h>
+#include <MenuField.h>
+#include <MenuItem.h>
+#include <PopUpMenu.h>
+#include <String.h>
+#include <StringView.h>
+#include <LayoutItem.h>
+#include <GroupLayoutBuilder.h>
+
+#include <stdio.h>
+
+#undef B_TRANSLATION_CONTEXT
+#define B_TRANSLATION_CONTEXT "Font Selection view"
+
+
+static const float kMinSize = 8.0;
+static const float kMaxSize = 18.0;
+
+static const int32 kMsgSetFamily = 'fmly';
+static const int32 kMsgSetStyle = 'styl';
+static const int32 kMsgSetSize = 'size';
+
+
+// #pragma mark -
+
+
+FontSelectionView::FontSelectionView(const char* name, const char* label,
+ bool separateStyles, const BFont* currentFont)
+ :
+ BHandler(name),
+ fMessage(NULL),
+ fTarget(NULL)
+{
+ if (currentFont == NULL)
+ fCurrentFont = _DefaultFont();
+ else
+ fCurrentFont = *currentFont;
+
+ fSavedFont = fCurrentFont;
+
+ fSizesMenu = new BPopUpMenu("size menu");
+ fFontsMenu = new BPopUpMenu("font menu");
+
+ // font menu
+ fFontsMenuField = new BMenuField("fonts", label, fFontsMenu, NULL);
+ fFontsMenuField->SetFont(be_bold_font);
+
+ // styles menu, if desired
+ if (separateStyles) {
+ fStylesMenu = new BPopUpMenu("styles menu");
+ fStylesMenuField = new BMenuField("styles", B_TRANSLATE("Style:"),
+ fStylesMenu, NULL);
+ } else {
+ fStylesMenu = NULL;
+ fStylesMenuField = NULL;
+ }
+
+ // size menu
+ fSizesMenuField = new BMenuField("size", B_TRANSLATE("Size:"), fSizesMenu,
+ NULL);
+ fSizesMenuField->SetAlignment(B_ALIGN_RIGHT);
+
+ // preview
+ fPreviewText = new BStringView("preview text",
+ B_TRANSLATE_COMMENT("The quick brown fox jumps over the lazy dog.",
+ "Don't translate this literally ! Use a phrase showing all "
+ "chars from A to Z."));
+
+ fPreviewText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
+ B_SIZE_UNLIMITED));
+ fPreviewText->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
+ 1.65));
+ fPreviewText->SetAlignment(B_ALIGN_RIGHT);
+ _UpdateFontPreview();
+}
+
+
+FontSelectionView::~FontSelectionView()
+{
+ // Some controls may not have been attached...
+ if (!fPreviewText->Window())
+ delete fPreviewText;
+ if (!fSizesMenuField->Window())
+ delete fSizesMenuField;
+ if (fStylesMenuField && !fStylesMenuField->Window())
+ delete fStylesMenuField;
+ if (!fFontsMenuField->Window())
+ delete fFontsMenuField;
+
+ delete fMessage;
+}
+
+
+void
+FontSelectionView::AttachedToLooper()
+{
+ _BuildSizesMenu();
+ UpdateFontsMenu();
+}
+
+
+void
+FontSelectionView::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ case kMsgSetSize:
+ {
+ int32 size;
+ if (message->FindInt32("size", &size) != B_OK
+ || size == fCurrentFont.Size())
+ break;
+
+ fCurrentFont.SetSize(size);
+ _UpdateFontPreview();
+ _Invoke();
+ break;
+ }
+
+ case kMsgSetFamily:
+ {
+ const char* family;
+ if (message->FindString("family", &family) != B_OK)
+ break;
+
+ font_style style;
+ fCurrentFont.GetFamilyAndStyle(NULL, &style);
+
+ BMenuItem* familyItem = fFontsMenu->FindItem(family);
+ if (familyItem != NULL) {
+ _SelectCurrentFont(false);
+
+ BMenuItem* styleItem;
+ if (fStylesMenuField != NULL)
+ styleItem = fStylesMenuField->Menu()->FindMarked();
+ else {
+ styleItem = familyItem->Submenu()->FindItem(style);
+ if (styleItem == NULL)
+ styleItem = familyItem->Submenu()->ItemAt(0);
+ }
+
+ if (styleItem != NULL) {
+ styleItem->SetMarked(true);
+ fCurrentFont.SetFamilyAndStyle(family, styleItem->Label());
+ _UpdateFontPreview();
+ }
+ if (fStylesMenuField != NULL)
+ _AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu());
+ }
+
+ _Invoke();
+ break;
+ }
+
+ case kMsgSetStyle:
+ {
+ const char* family;
+ const char* style;
+ if (message->FindString("family", &family) != B_OK
+ || message->FindString("style", &style) != B_OK)
+ break;
+
+ BMenuItem *familyItem = fFontsMenu->FindItem(family);
+ if (!familyItem)
+ break;
+
+ _SelectCurrentFont(false);
+ familyItem->SetMarked(true);
+
+ fCurrentFont.SetFamilyAndStyle(family, style);
+ _UpdateFontPreview();
+ _Invoke();
+ break;
+ }
+
+ default:
+ BHandler::MessageReceived(message);
+ }
+}
+
+
+void
+FontSelectionView::SetMessage(BMessage* message)
+{
+ delete fMessage;
+ fMessage = message;
+}
+
+
+void
+FontSelectionView::SetTarget(BHandler* target)
+{
+ fTarget = target;
+}
+
+
+// #pragma mark -
+
+
+void
+FontSelectionView::SetFont(const BFont& font, float size)
+{
+ BFont resizedFont(font);
+ resizedFont.SetSize(size);
+ SetFont(resizedFont);
+}
+
+
+void
+FontSelectionView::SetFont(const BFont& font)
+{
+ if (font == fCurrentFont && font == fSavedFont)
+ return;
+
+ _SelectCurrentFont(false);
+ fSavedFont = fCurrentFont = font;
+ _UpdateFontPreview();
+
+ _SelectCurrentFont(true);
+ _SelectCurrentSize(true);
+}
+
+
+void
+FontSelectionView::SetSize(float size)
+{
+ SetFont(fCurrentFont, size);
+}
+
+
+const BFont&
+FontSelectionView::Font() const
+{
+ return fCurrentFont;
+}
+
+
+void
+FontSelectionView::SetDefaults()
+{
+ BFont defaultFont = _DefaultFont();
+ if (defaultFont == fCurrentFont)
+ return;
+
+ _SelectCurrentFont(false);
+
+ fCurrentFont = defaultFont;
+ _UpdateFontPreview();
+
+ _SelectCurrentFont(true);
+ _SelectCurrentSize(true);
+}
+
+
+void
+FontSelectionView::Revert()
+{
+ if (!IsRevertable())
+ return;
+
+ _SelectCurrentFont(false);
+
+ fCurrentFont = fSavedFont;
+ _UpdateFontPreview();
+
+ _SelectCurrentFont(true);
+ _SelectCurrentSize(true);
+}
+
+
+bool
+FontSelectionView::IsDefaultable()
+{
+ return fCurrentFont != _DefaultFont();
+}
+
+
+bool
+FontSelectionView::IsRevertable()
+{
+ return fCurrentFont != fSavedFont;
+}
+
+
+void
+FontSelectionView::UpdateFontsMenu()
+{
+ int32 numFamilies = count_font_families();
+
+ fFontsMenu->RemoveItems(0, fFontsMenu->CountItems(), true);
+
+ BFont font = fCurrentFont;
+
+ font_family currentFamily;
+ font_style currentStyle;
+ font.GetFamilyAndStyle(&currentFamily, &currentStyle);
+
+ for (int32 i = 0; i < numFamilies; i++) {
+ font_family family;
+ uint32 flags;
+ if (get_font_family(i, &family, &flags) != B_OK)
+ continue;
+
+ // if we're setting the fixed font, we only want to show fixed fonts
+ if (!strcmp(Name(), "fixed") && (flags & B_IS_FIXED) == 0)
+ continue;
+
+ font.SetFamilyAndFace(family, B_REGULAR_FACE);
+
+ BMessage* message = new BMessage(kMsgSetFamily);
+ message->AddString("family", family);
+ message->AddString("name", Name());
+
+ BMenuItem* familyItem;
+ if (fStylesMenuField != NULL) {
+ familyItem = new BMenuItem(family, message);
+ } else {
+ // Each family item has a submenu with all styles for that font.
+ BMenu* stylesMenu = new BMenu(family);
+ _AddStylesToMenu(font, stylesMenu);
+ familyItem = new BMenuItem(stylesMenu, message);
+ }
+
+ familyItem->SetMarked(strcmp(family, currentFamily) == 0);
+ fFontsMenu->AddItem(familyItem);
+ familyItem->SetTarget(this);
+ }
+
+ // Separate styles menu for only the current font.
+ if (fStylesMenuField != NULL)
+ _AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu());
+}
+
+
+// #pragma mark - private
+
+
+BLayoutItem*
+FontSelectionView::CreateSizesLabelLayoutItem()
+{
+ return fSizesMenuField->CreateLabelLayoutItem();
+}
+
+
+BLayoutItem*
+FontSelectionView::CreateSizesMenuBarLayoutItem()
+{
+ return fSizesMenuField->CreateMenuBarLayoutItem();
+}
+
+
+BLayoutItem*
+FontSelectionView::CreateFontsLabelLayoutItem()
+{
+ return fFontsMenuField->CreateLabelLayoutItem();
+}
+
+
+BLayoutItem*
+FontSelectionView::CreateFontsMenuBarLayoutItem()
+{
+ return fFontsMenuField->CreateMenuBarLayoutItem();
+}
+
+
+BLayoutItem*
+FontSelectionView::CreateStylesLabelLayoutItem()
+{
+ if (fStylesMenuField)
+ return fStylesMenuField->CreateLabelLayoutItem();
+ return NULL;
+}
+
+
+BLayoutItem*
+FontSelectionView::CreateStylesMenuBarLayoutItem()
+{
+ if (fStylesMenuField)
+ return fStylesMenuField->CreateMenuBarLayoutItem();
+ return NULL;
+}
+
+
+BView*
+FontSelectionView::PreviewBox() const
+{
+ return fPreviewText;
+}
+
+
+// #pragma mark - private
+
+
+void
+FontSelectionView::_Invoke()
+{
+ if (fTarget != NULL && fTarget->Looper() != NULL && fMessage != NULL) {
+ BMessage message(*fMessage);
+ fTarget->Looper()->PostMessage(&message, fTarget);
+ }
+}
+
+
+BFont
+FontSelectionView::_DefaultFont() const
+{
+ if (strcmp(Name(), "bold") == 0)
+ return *be_bold_font;
+ if (strcmp(Name(), "fixed") == 0)
+ return *be_fixed_font;
+ else
+ return *be_plain_font;
+}
+
+
+void
+FontSelectionView::_SelectCurrentFont(bool select)
+{
+ font_family family;
+ font_style style;
+ fCurrentFont.GetFamilyAndStyle(&family, &style);
+
+ BMenuItem *item = fFontsMenu->FindItem(family);
+ if (item != NULL) {
+ item->SetMarked(select);
+
+ if (item->Submenu() != NULL) {
+ item = item->Submenu()->FindItem(style);
+ if (item != NULL)
+ item->SetMarked(select);
+ }
+ }
+}
+
+
+void
+FontSelectionView::_SelectCurrentSize(bool select)
+{
+ char label[16];
+ snprintf(label, sizeof(label), "%ld", (int32)fCurrentFont.Size());
+
+ BMenuItem* item = fSizesMenu->FindItem(label);
+ if (item != NULL)
+ item->SetMarked(select);
+}
+
+
+void
+FontSelectionView::_UpdateFontPreview()
+{
+ fPreviewText->SetFont(&fCurrentFont);
+}
+
+
+void
+FontSelectionView::_BuildSizesMenu()
+{
+ const int32 sizes[] = {7, 8, 9, 10, 11, 12, 13, 14, 18, 21, 24, 0};
+
+ // build size menu
+ for (int32 i = 0; sizes[i]; i++) {
+ int32 size = sizes[i];
+ if (size < kMinSize || size > kMaxSize)
+ continue;
+
+ char label[32];
+ snprintf(label, sizeof(label), "%ld", size);
+
+ BMessage* message = new BMessage(kMsgSetSize);
+ message->AddInt32("size", size);
+ message->AddString("name", Name());
+
+ BMenuItem* item = new BMenuItem(label, message);
+ if (size == fCurrentFont.Size())
+ item->SetMarked(true);
+
+ fSizesMenu->AddItem(item);
+ item->SetTarget(this);
+ }
+}
+
+
+void
+FontSelectionView::_AddStylesToMenu(const BFont& font, BMenu* stylesMenu) const
+{
+ stylesMenu->RemoveItems(0, stylesMenu->CountItems(), true);
+ stylesMenu->SetRadioMode(true);
+
+ font_family family;
+ font_style style;
+ font.GetFamilyAndStyle(&family, &style);
+ BString currentStyle(style);
+
+ int32 numStyles = count_font_styles(family);
+
+ for (int32 j = 0; j < numStyles; j++) {
+ if (get_font_style(family, j, &style) != B_OK)
+ continue;
+
+ BMessage* message = new BMessage(kMsgSetStyle);
+ message->AddString("family", (char*)family);
+ message->AddString("style", (char*)style);
+
+ BMenuItem* item = new BMenuItem(style, message);
+ item->SetMarked(currentStyle == style);
+
+ stylesMenu->AddItem(item);
+ item->SetTarget(this);
+ }
+}
+
diff --git a/src/apps/webpositive/support/FontSelectionView.h b/src/apps/webpositive/support/FontSelectionView.h
new file mode 100644
index 0000000000..8ee630b448
--- /dev/null
+++ b/src/apps/webpositive/support/FontSelectionView.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2001-2010, Haiku.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Mark Hogben
+ * DarkWyrm <bpmagic@columbus.rr.com>
+ * Axel Dörfler, axeld@pinc-software.de
+ * Philippe Saint-Pierre, stpere@gmail.com
+ * Stephan Aßmus <superstippi@gmx.de>
+ */
+#ifndef FONT_SELECTION_VIEW_H
+#define FONT_SELECTION_VIEW_H
+
+
+#include <Font.h>
+#include <Handler.h>
+
+class BLayoutItem;
+class BBox;
+class BMenu;
+class BMenuField;
+class BPopUpMenu;
+class BStringView;
+class BView;
+
+
+class FontSelectionView : public BHandler {
+public:
+ FontSelectionView(const char* name,
+ const char* label, bool separateStyles,
+ const BFont* font = NULL);
+ virtual ~FontSelectionView();
+
+ void AttachedToLooper();
+ virtual void MessageReceived(BMessage* message);
+
+ void SetMessage(BMessage* message);
+ void SetTarget(BHandler* target);
+
+ void SetFont(const BFont& font, float size);
+ void SetFont(const BFont& font);
+ void SetSize(float size);
+ const BFont& Font() const;
+
+ void SetDefaults();
+ void Revert();
+ bool IsDefaultable();
+ bool IsRevertable();
+
+ void UpdateFontsMenu();
+
+ BLayoutItem* CreateSizesLabelLayoutItem();
+ BLayoutItem* CreateSizesMenuBarLayoutItem();
+
+ BLayoutItem* CreateFontsLabelLayoutItem();
+ BLayoutItem* CreateFontsMenuBarLayoutItem();
+
+ BLayoutItem* CreateStylesLabelLayoutItem();
+ BLayoutItem* CreateStylesMenuBarLayoutItem();
+
+ BView* PreviewBox() const;
+
+private:
+ void _Invoke();
+
+ BFont _DefaultFont() const;
+ void _SelectCurrentFont(bool select);
+ void _SelectCurrentSize(bool select);
+ void _UpdateFontPreview();
+ void _BuildSizesMenu();
+ void _AddStylesToMenu(const BFont& font,
+ BMenu* menu) const;
+
+protected:
+ BMenuField* fFontsMenuField;
+ BMenuField* fStylesMenuField;
+ BMenuField* fSizesMenuField;
+ BPopUpMenu* fFontsMenu;
+ BPopUpMenu* fStylesMenu;
+ BPopUpMenu* fSizesMenu;
+ BStringView* fPreviewText;
+
+ BFont fSavedFont;
+ BFont fCurrentFont;
+
+ BMessage* fMessage;
+ BHandler* fTarget;
+};
+
+#endif // FONT_SELECTION_VIEW_H
diff --git a/src/apps/webpositive/support/HashKeys.h b/src/apps/webpositive/support/HashKeys.h
new file mode 100644
index 0000000000..3480b56492
--- /dev/null
+++ b/src/apps/webpositive/support/HashKeys.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2010, Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
+ * Copyright 2004-2007, Ingo Weinhold <ingo_weinhold@gmx.de>. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef HASH_KEYS_H
+#define HASH_KEYS_H
+
+#include <String.h>
+
+
+namespace BPrivate {
+
+#if 0
+// TODO: Move here from HashMap.h and adapt all clients.
+// HashKey32
+template<typename Value>
+struct HashKey32 {
+ HashKey32() {}
+ HashKey32(const Value& value) : value(value) {}
+
+ uint32 GetHashCode() const
+ {
+ return (uint32)value;
+ }
+
+ HashKey32<Value> operator=(const HashKey32<Value>& other)
+ {
+ value = other.value;
+ return *this;
+ }
+
+ bool operator==(const HashKey32<Value>& other) const
+ {
+ return (value == other.value);
+ }
+
+ bool operator!=(const HashKey32<Value>& other) const
+ {
+ return (value != other.value);
+ }
+
+ Value value;
+};
+
+
+// HashKey64
+template<typename Value>
+struct HashKey64 {
+ HashKey64() {}
+ HashKey64(const Value& value) : value(value) {}
+
+ uint32 GetHashCode() const
+ {
+ uint64 v = (uint64)value;
+ return (uint32)(v >> 32) ^ (uint32)v;
+ }
+
+ HashKey64<Value> operator=(const HashKey64<Value>& other)
+ {
+ value = other.value;
+ return *this;
+ }
+
+ bool operator==(const HashKey64<Value>& other) const
+ {
+ return (value == other.value);
+ }
+
+ bool operator!=(const HashKey64<Value>& other) const
+ {
+ return (value != other.value);
+ }
+
+ Value value;
+};
+#endif
+
+
+struct HashKeyString {
+ HashKeyString() {}
+ HashKeyString(const BString& value) : value(value) {}
+ HashKeyString(const char* string) : value(string) {}
+
+ uint32 GetHashCode() const
+ {
+ // from the Dragon Book: a slightly modified hashpjw()
+ uint32 hash = 0;
+ const char* string = value.String();
+ if (string != NULL) {
+ for (; *string; string++) {
+ uint32 g = hash & 0xf0000000;
+ if (g != 0)
+ hash ^= g >> 24;
+ hash = (hash << 4) + *string;
+ }
+ }
+ return hash;
+ }
+
+ HashKeyString operator=(const HashKeyString& other)
+ {
+ value = other.value;
+ return *this;
+ }
+
+ bool operator==(const HashKeyString& other) const
+ {
+ return (value == other.value);
+ }
+
+ bool operator!=(const HashKeyString& other) const
+ {
+ return (value != other.value);
+ }
+
+ BString value;
+};
+
+} // namespace BPrivate
+
+using BPrivate::HashKeyString;
+
+#endif // HASH_KEYS_H
diff --git a/src/apps/webpositive/support/HashMap.h b/src/apps/webpositive/support/HashMap.h
new file mode 100644
index 0000000000..36b321f2b8
--- /dev/null
+++ b/src/apps/webpositive/support/HashMap.h
@@ -0,0 +1,481 @@
+// HashMap.h
+//
+// Copyright (c) 2004-2007, Ingo Weinhold (bonefish@cs.tu-berlin.de)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name of a copyright holder shall
+// not be used in advertising or otherwise to promote the sale, use or other
+// dealings in this Software without prior written authorization of the
+// copyright holder.
+
+#ifndef HASH_MAP_H
+#define HASH_MAP_H
+
+
+#include <Locker.h>
+
+#include "AutoLocker.h"
+#include "OpenHashTable.h"
+
+
+namespace BPrivate {
+
+// HashMapElement
+template<typename Key, typename Value>
+class HashMapElement : public OpenHashElement {
+private:
+ typedef HashMapElement<Key, Value> Element;
+public:
+
+ HashMapElement() : OpenHashElement(), fKey(), fValue()
+ {
+ fNext = -1;
+ }
+
+ inline uint32 Hash() const
+ {
+ return fKey.GetHashCode();
+ }
+
+ inline bool operator==(const OpenHashElement &_element) const
+ {
+ const Element &element = static_cast<const Element&>(_element);
+ return (fKey == element.fKey);
+ }
+
+ inline void Adopt(Element &element)
+ {
+ fKey = element.fKey;
+ fValue = element.fValue;
+ }
+
+ Key fKey;
+ Value fValue;
+};
+
+// HashMap
+template<typename Key, typename Value>
+class HashMap {
+public:
+ class Entry {
+ public:
+ Entry() {}
+ Entry(const Key& key, Value value) : key(key), value(value) {}
+
+ Key key;
+ Value value;
+ };
+
+ class Iterator {
+ private:
+ typedef HashMapElement<Key, Value> Element;
+ public:
+ Iterator(const Iterator& other)
+ :
+ fMap(other.fMap),
+ fIndex(other.fIndex),
+ fElement(other.fElement),
+ fLastElement(other.fElement)
+ {
+ }
+
+ bool HasNext() const
+ {
+ return fElement;
+ }
+
+ Entry Next()
+ {
+ if (!fElement)
+ return Entry();
+ Entry result(fElement->fKey, fElement->fValue);
+ _FindNext();
+ return result;
+ }
+
+ Value* NextValue()
+ {
+ if (fElement == NULL)
+ return NULL;
+
+ Value* value = &fElement->fValue;
+ _FindNext();
+ return value;
+ }
+
+ Entry Remove()
+ {
+ if (!fLastElement)
+ return Entry();
+ Entry result(fLastElement->fKey, fLastElement->fValue);
+ fMap->fTable.Remove(fLastElement, true);
+ fLastElement = NULL;
+ return result;
+ }
+
+ Iterator& operator=(const Iterator& other)
+ {
+ fMap = other.fMap;
+ fIndex = other.fIndex;
+ fElement = other.fElement;
+ fLastElement = other.fLastElement;
+ return *this;
+ }
+
+ private:
+ Iterator(const HashMap<Key, Value>* map)
+ :
+ fMap(const_cast<HashMap<Key, Value>*>(map)),
+ fIndex(0),
+ fElement(NULL),
+ fLastElement(NULL)
+ {
+ // find first
+ _FindNext();
+ }
+
+ void _FindNext()
+ {
+ fLastElement = fElement;
+ if (fElement && fElement->fNext >= 0) {
+ fElement = fMap->fTable.ElementAt(fElement->fNext);
+ return;
+ }
+ fElement = NULL;
+ int32 arraySize = fMap->fTable.ArraySize();
+ for (; !fElement && fIndex < arraySize; fIndex++)
+ fElement = fMap->fTable.FindFirst(fIndex);
+ }
+
+ private:
+ friend class HashMap<Key, Value>;
+
+ HashMap<Key, Value>* fMap;
+ int32 fIndex;
+ Element* fElement;
+ Element* fLastElement;
+ };
+
+ HashMap();
+ ~HashMap();
+
+ status_t InitCheck() const;
+
+ status_t Put(const Key& key, Value value);
+ Value Remove(const Key& key);
+ void Clear();
+ Value Get(const Key& key) const;
+ bool Get(const Key& key, Value*& _value) const;
+
+ bool ContainsKey(const Key& key) const;
+
+ int32 Size() const;
+
+ Iterator GetIterator() const;
+
+protected:
+ typedef HashMapElement<Key, Value> Element;
+ friend class Iterator;
+
+private:
+ Element *_FindElement(const Key& key) const;
+
+protected:
+ OpenHashElementArray<Element> fElementArray;
+ OpenHashTable<Element, OpenHashElementArray<Element> > fTable;
+};
+
+// SynchronizedHashMap
+template<typename Key, typename Value>
+class SynchronizedHashMap : public BLocker {
+public:
+ typedef struct HashMap<Key, Value>::Entry Entry;
+ typedef struct HashMap<Key, Value>::Iterator Iterator;
+
+ SynchronizedHashMap() : BLocker("synchronized hash map") {}
+ ~SynchronizedHashMap() { Lock(); }
+
+ status_t InitCheck() const
+ {
+ return fMap.InitCheck();
+ }
+
+ status_t Put(const Key& key, Value value)
+ {
+ MapLocker locker(this);
+ if (!locker.IsLocked())
+ return B_ERROR;
+ return fMap.Put(key, value);
+ }
+
+ Value Remove(const Key& key)
+ {
+ MapLocker locker(this);
+ if (!locker.IsLocked())
+ return Value();
+ return fMap.Remove(key);
+ }
+
+ void Clear()
+ {
+ MapLocker locker(this);
+ return fMap.Clear();
+ }
+
+ Value Get(const Key& key) const
+ {
+ const BLocker* lock = this;
+ MapLocker locker(const_cast<BLocker*>(lock));
+ if (!locker.IsLocked())
+ return Value();
+ return fMap.Get(key);
+ }
+
+ bool ContainsKey(const Key& key) const
+ {
+ const BLocker* lock = this;
+ MapLocker locker(const_cast<BLocker*>(lock));
+ if (!locker.IsLocked())
+ return false;
+ return fMap.ContainsKey(key);
+ }
+
+ int32 Size() const
+ {
+ const BLocker* lock = this;
+ MapLocker locker(const_cast<BLocker*>(lock));
+ return fMap.Size();
+ }
+
+ Iterator GetIterator()
+ {
+ return fMap.GetIterator();
+ }
+
+ // for debugging only
+ const HashMap<Key, Value>& GetUnsynchronizedMap() const { return fMap; }
+ HashMap<Key, Value>& GetUnsynchronizedMap() { return fMap; }
+
+protected:
+ typedef AutoLocker<BLocker> MapLocker;
+
+ HashMap<Key, Value> fMap;
+};
+
+// HashKey32
+template<typename Value>
+struct HashKey32 {
+ HashKey32() {}
+ HashKey32(const Value& value) : value(value) {}
+
+ uint32 GetHashCode() const
+ {
+ return (uint32)value;
+ }
+
+ HashKey32<Value> operator=(const HashKey32<Value>& other)
+ {
+ value = other.value;
+ return *this;
+ }
+
+ bool operator==(const HashKey32<Value>& other) const
+ {
+ return (value == other.value);
+ }
+
+ bool operator!=(const HashKey32<Value>& other) const
+ {
+ return (value != other.value);
+ }
+
+ Value value;
+};
+
+
+// HashKey64
+template<typename Value>
+struct HashKey64 {
+ HashKey64() {}
+ HashKey64(const Value& value) : value(value) {}
+
+ uint32 GetHashCode() const
+ {
+ uint64 v = (uint64)value;
+ return (uint32)(v >> 32) ^ (uint32)v;
+ }
+
+ HashKey64<Value> operator=(const HashKey64<Value>& other)
+ {
+ value = other.value;
+ return *this;
+ }
+
+ bool operator==(const HashKey64<Value>& other) const
+ {
+ return (value == other.value);
+ }
+
+ bool operator!=(const HashKey64<Value>& other) const
+ {
+ return (value != other.value);
+ }
+
+ Value value;
+};
+
+
+// HashMap
+
+// constructor
+template<typename Key, typename Value>
+HashMap<Key, Value>::HashMap()
+ :
+ fElementArray(1000),
+ fTable(1000, &fElementArray)
+{
+}
+
+// destructor
+template<typename Key, typename Value>
+HashMap<Key, Value>::~HashMap()
+{
+}
+
+// InitCheck
+template<typename Key, typename Value>
+status_t
+HashMap<Key, Value>::InitCheck() const
+{
+ return (fTable.InitCheck() && fElementArray.InitCheck()
+ ? B_OK : B_NO_MEMORY);
+}
+
+// Put
+template<typename Key, typename Value>
+status_t
+HashMap<Key, Value>::Put(const Key& key, Value value)
+{
+ Element* element = _FindElement(key);
+ if (element) {
+ // already contains the key: just set the new value
+ element->fValue = value;
+ return B_OK;
+ }
+ // does not contain the key yet: add an element
+ element = fTable.Add(key.GetHashCode());
+ if (!element)
+ return B_NO_MEMORY;
+ element->fKey = key;
+ element->fValue = value;
+ return B_OK;
+}
+
+// Remove
+template<typename Key, typename Value>
+Value
+HashMap<Key, Value>::Remove(const Key& key)
+{
+ Value value = Value();
+ if (Element* element = _FindElement(key)) {
+ value = element->fValue;
+ fTable.Remove(element);
+ }
+ return value;
+}
+
+// Clear
+template<typename Key, typename Value>
+void
+HashMap<Key, Value>::Clear()
+{
+ fTable.RemoveAll();
+}
+
+// Get
+template<typename Key, typename Value>
+Value
+HashMap<Key, Value>::Get(const Key& key) const
+{
+ if (Element* element = _FindElement(key))
+ return element->fValue;
+ return Value();
+}
+
+// Get
+template<typename Key, typename Value>
+bool
+HashMap<Key, Value>::Get(const Key& key, Value*& _value) const
+{
+ if (Element* element = _FindElement(key)) {
+ _value = &element->fValue;
+ return true;
+ }
+
+ return false;
+}
+
+// ContainsKey
+template<typename Key, typename Value>
+bool
+HashMap<Key, Value>::ContainsKey(const Key& key) const
+{
+ return _FindElement(key);
+}
+
+// Size
+template<typename Key, typename Value>
+int32
+HashMap<Key, Value>::Size() const
+{
+ return fTable.CountElements();
+}
+
+// GetIterator
+template<typename Key, typename Value>
+struct HashMap<Key, Value>::Iterator
+HashMap<Key, Value>::GetIterator() const
+{
+ return Iterator(this);
+}
+
+// _FindElement
+template<typename Key, typename Value>
+struct HashMap<Key, Value>::Element *
+HashMap<Key, Value>::_FindElement(const Key& key) const
+{
+ Element* element = fTable.FindFirst(key.GetHashCode());
+ while (element && element->fKey != key) {
+ if (element->fNext >= 0)
+ element = fTable.ElementAt(element->fNext);
+ else
+ element = NULL;
+ }
+ return element;
+}
+
+} // namespace BPrivate
+
+using BPrivate::HashMap;
+using BPrivate::HashKey32;
+using BPrivate::HashKey64;
+using BPrivate::SynchronizedHashMap;
+
+#endif // HASH_MAP_H
diff --git a/src/apps/webpositive/support/HashSet.h b/src/apps/webpositive/support/HashSet.h
new file mode 100644
index 0000000000..ee7be71288
--- /dev/null
+++ b/src/apps/webpositive/support/HashSet.h
@@ -0,0 +1,342 @@
+// HashSet.h
+//
+// Copyright (c) 2004, Ingo Weinhold (bonefish@cs.tu-berlin.de)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name of a copyright holder shall
+// not be used in advertising or otherwise to promote the sale, use or other
+// dealings in this Software without prior written authorization of the
+// copyright holder.
+
+#ifndef HASH_SET_H
+#define HASH_SET_H
+
+#include <Locker.h>
+
+#include "AutoLocker.h"
+#include "OpenHashTable.h"
+
+
+namespace BPrivate {
+
+// HashSetElement
+template<typename Key>
+class HashSetElement : public OpenHashElement {
+private:
+ typedef HashSetElement<Key> Element;
+public:
+
+ HashSetElement() : OpenHashElement(), fKey()
+ {
+ fNext = -1;
+ }
+
+ inline uint32 Hash() const
+ {
+ return fKey.GetHashCode();
+ }
+
+ inline bool operator==(const OpenHashElement &_element) const
+ {
+ const Element &element = static_cast<const Element&>(_element);
+ return (fKey == element.fKey);
+ }
+
+ inline void Adopt(Element &element)
+ {
+ fKey = element.fKey;
+ }
+
+ Key fKey;
+};
+
+// HashSet
+template<typename Key>
+class HashSet {
+public:
+ class Iterator {
+ private:
+ typedef HashSetElement<Key> Element;
+ public:
+ Iterator(const Iterator& other)
+ : fSet(other.fSet),
+ fIndex(other.fIndex),
+ fElement(other.fElement),
+ fLastElement(other.fElement)
+ {
+ }
+
+ bool HasNext() const
+ {
+ return fElement;
+ }
+
+ Key Next()
+ {
+ if (!fElement)
+ return Key();
+ Key result(fElement->fKey);
+ _FindNext();
+ return result;
+ }
+
+ bool Remove()
+ {
+ if (!fLastElement)
+ return false;
+ fSet->fTable.Remove(fLastElement);
+ fLastElement = NULL;
+ return true;
+ }
+
+ Iterator& operator=(const Iterator& other)
+ {
+ fSet = other.fSet;
+ fIndex = other.fIndex;
+ fElement = other.fElement;
+ fLastElement = other.fLastElement;
+ return *this;
+ }
+
+ private:
+ Iterator(HashSet<Key>* map)
+ : fSet(map),
+ fIndex(0),
+ fElement(NULL),
+ fLastElement(NULL)
+ {
+ // find first
+ _FindNext();
+ }
+
+ void _FindNext()
+ {
+ fLastElement = fElement;
+ if (fElement && fElement->fNext >= 0) {
+ fElement = fSet->fTable.ElementAt(fElement->fNext);
+ return;
+ }
+ fElement = NULL;
+ int32 arraySize = fSet->fTable.ArraySize();
+ for (; !fElement && fIndex < arraySize; fIndex++)
+ fElement = fSet->fTable.FindFirst(fIndex);
+ }
+
+ private:
+ friend class HashSet<Key>;
+
+ HashSet<Key>* fSet;
+ int32 fIndex;
+ Element* fElement;
+ Element* fLastElement;
+ };
+
+ HashSet();
+ ~HashSet();
+
+ status_t InitCheck() const;
+
+ status_t Add(const Key& key);
+ bool Remove(const Key& key);
+ void Clear();
+ bool Contains(const Key& key) const;
+
+ int32 Size() const;
+ bool IsEmpty() const { return Size() == 0; }
+
+ Iterator GetIterator();
+
+protected:
+ typedef HashSetElement<Key> Element;
+ friend class Iterator;
+
+private:
+ Element *_FindElement(const Key& key) const;
+
+protected:
+ OpenHashElementArray<Element> fElementArray;
+ OpenHashTable<Element, OpenHashElementArray<Element> > fTable;
+};
+
+// SynchronizedHashSet
+template<typename Key>
+class SynchronizedHashSet : public BLocker {
+public:
+ typedef struct HashSet<Key>::Iterator Iterator;
+
+ SynchronizedHashSet() : BLocker("synchronized hash set") {}
+ ~SynchronizedHashSet() { Lock(); }
+
+ status_t InitCheck() const
+ {
+ return fSet.InitCheck();
+ }
+
+ status_t Add(const Key& key)
+ {
+ MapLocker locker(this);
+ if (!locker.IsLocked())
+ return B_ERROR;
+ return fSet.Add(key);
+ }
+
+ bool Remove(const Key& key)
+ {
+ MapLocker locker(this);
+ if (!locker.IsLocked())
+ return false;
+ return fSet.Remove(key);
+ }
+
+ bool Contains(const Key& key) const
+ {
+ const BLocker* lock = this;
+ MapLocker locker(const_cast<BLocker*>(lock));
+ if (!locker.IsLocked())
+ return false;
+ return fSet.Contains(key);
+ }
+
+ int32 Size() const
+ {
+ const BLocker* lock = this;
+ MapLocker locker(const_cast<BLocker*>(lock));
+ return fSet.Size();
+ }
+
+ Iterator GetIterator()
+ {
+ return fSet.GetIterator();
+ }
+
+ // for debugging only
+ const HashSet<Key>& GetUnsynchronizedSet() const { return fSet; }
+ HashSet<Key>& GetUnsynchronizedSet() { return fSet; }
+
+protected:
+ typedef AutoLocker<BLocker> MapLocker;
+
+ HashSet<Key> fSet;
+};
+
+// HashSet
+
+// constructor
+template<typename Key>
+HashSet<Key>::HashSet()
+ : fElementArray(1000),
+ fTable(1000, &fElementArray)
+{
+}
+
+// destructor
+template<typename Key>
+HashSet<Key>::~HashSet()
+{
+}
+
+// InitCheck
+template<typename Key>
+status_t
+HashSet<Key>::InitCheck() const
+{
+ return (fTable.InitCheck() && fElementArray.InitCheck()
+ ? B_OK : B_NO_MEMORY);
+}
+
+// Add
+template<typename Key>
+status_t
+HashSet<Key>::Add(const Key& key)
+{
+ if (Contains(key))
+ return B_OK;
+ Element* element = fTable.Add(key.GetHashCode());
+ if (!element)
+ return B_NO_MEMORY;
+ element->fKey = key;
+ return B_OK;
+}
+
+// Remove
+template<typename Key>
+bool
+HashSet<Key>::Remove(const Key& key)
+{
+ if (Element* element = _FindElement(key)) {
+ fTable.Remove(element);
+ return true;
+ }
+ return false;
+}
+
+// Clear
+template<typename Key>
+void
+HashSet<Key>::Clear()
+{
+ fTable.RemoveAll();
+}
+
+// Contains
+template<typename Key>
+bool
+HashSet<Key>::Contains(const Key& key) const
+{
+ return _FindElement(key);
+}
+
+// Size
+template<typename Key>
+int32
+HashSet<Key>::Size() const
+{
+ return fTable.CountElements();
+}
+
+// GetIterator
+template<typename Key>
+struct HashSet<Key>::Iterator
+HashSet<Key>::GetIterator()
+{
+ return Iterator(this);
+}
+
+// _FindElement
+template<typename Key>
+struct HashSet<Key>::Element *
+HashSet<Key>::_FindElement(const Key& key) const
+{
+ Element* element = fTable.FindFirst(key.GetHashCode());
+ while (element && element->fKey != key) {
+ if (element->fNext >= 0)
+ element = fTable.ElementAt(element->fNext);
+ else
+ element = NULL;
+ }
+ return element;
+}
+
+} // namespace BPrivate
+
+using BPrivate::HashSet;
+using BPrivate::SynchronizedHashSet;
+
+#endif // HASH_SET_H
diff --git a/src/apps/webpositive/support/IconButton.cpp b/src/apps/webpositive/support/IconButton.cpp
new file mode 100644
index 0000000000..5cd2ede604
--- /dev/null
+++ b/src/apps/webpositive/support/IconButton.cpp
@@ -0,0 +1,929 @@
+/*
+ * Copyright 2006-2010, Haiku.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Stephan Aßmus <superstippi@gmx.de>
+ */
+
+// NOTE: this file is a duplicate of the version in Icon-O-Matic/generic
+// it should be placed into a common folder for generic useful stuff
+
+#include "IconButton.h"
+
+#include <new>
+#include <stdio.h>
+
+#include <Application.h>
+#include <Bitmap.h>
+#include <Control.h>
+#include <ControlLook.h>
+#include <Entry.h>
+#include <Looper.h>
+#include <Message.h>
+#include <Mime.h>
+#include <Path.h>
+#include <Region.h>
+#include <Resources.h>
+#include <Roster.h>
+#include <TranslationUtils.h>
+#include <Window.h>
+#include "IconUtils.h"
+
+using std::nothrow;
+
+// constructor
+IconButton::IconButton(const char* name, uint32 id, const char* label,
+ BMessage* message, BHandler* target)
+ : BView(name, B_WILL_DRAW),
+ BInvoker(message, target),
+ fButtonState(STATE_ENABLED),
+ fID(id),
+ fNormalBitmap(NULL),
+ fDisabledBitmap(NULL),
+ fClickedBitmap(NULL),
+ fDisabledClickedBitmap(NULL),
+ fLabel(label),
+ fTargetCache(target)
+{
+ SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
+ SetViewColor(B_TRANSPARENT_32_BIT);
+}
+
+// destructor
+IconButton::~IconButton()
+{
+ _DeleteBitmaps();
+}
+
+// MessageReceived
+void
+IconButton::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ default:
+ BView::MessageReceived(message);
+ break;
+ }
+}
+
+// AttachedToWindow
+void
+IconButton::AttachedToWindow()
+{
+ rgb_color background = B_TRANSPARENT_COLOR;
+ if (BView* parent = Parent()) {
+ background = parent->ViewColor();
+ if (background == B_TRANSPARENT_COLOR)
+ background = parent->LowColor();
+ }
+ if (background == B_TRANSPARENT_COLOR)
+ background = ui_color(B_PANEL_BACKGROUND_COLOR);
+ SetLowColor(background);
+
+ SetTarget(fTargetCache);
+ if (!Target())
+ SetTarget(Window());
+}
+
+// Draw
+void
+IconButton::Draw(BRect area)
+{
+ rgb_color background = LowColor();
+
+ BRect r(Bounds());
+
+ if (be_control_look != NULL) {
+ uint32 flags = 0;
+ BBitmap* bitmap = fNormalBitmap;
+ if (!IsEnabled()) {
+ flags |= BControlLook::B_DISABLED;
+ bitmap = fDisabledBitmap;
+ }
+ if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED))
+ flags |= BControlLook::B_ACTIVATED;
+
+ if (DrawBorder()) {
+ be_control_look->DrawButtonFrame(this, r, area, background,
+ background, flags);
+ be_control_look->DrawButtonBackground(this, r, area, background,
+ flags);
+ } else {
+ SetHighColor(background);
+ FillRect(r);
+ }
+
+ if (bitmap && bitmap->IsValid()) {
+ float x = r.left + floorf((r.Width()
+ - bitmap->Bounds().Width()) / 2.0 + 0.5);
+ float y = r.top + floorf((r.Height()
+ - bitmap->Bounds().Height()) / 2.0 + 0.5);
+ BPoint point(x, y);
+ if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED))
+ point += BPoint(1.0, 1.0);
+ if (bitmap->ColorSpace() == B_RGBA32
+ || bitmap->ColorSpace() == B_RGBA32_BIG) {
+ SetDrawingMode(B_OP_ALPHA);
+ SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
+ }
+ DrawBitmap(bitmap, point);
+ }
+ return;
+ }
+
+ rgb_color lightShadow, shadow, darkShadow, light;
+ BBitmap* bitmap = fNormalBitmap;
+ // adjust colors and bitmap according to flags
+ if (IsEnabled()) {
+ lightShadow = tint_color(background, B_DARKEN_1_TINT);
+ shadow = tint_color(background, B_DARKEN_2_TINT);
+ darkShadow = tint_color(background, B_DARKEN_4_TINT);
+ light = tint_color(background, B_LIGHTEN_MAX_TINT);
+ SetHighColor(0, 0, 0, 255);
+ } else {
+ lightShadow = tint_color(background, 1.11);
+ shadow = tint_color(background, B_DARKEN_1_TINT);
+ darkShadow = tint_color(background, B_DARKEN_2_TINT);
+ light = tint_color(background, B_LIGHTEN_2_TINT);
+ bitmap = fDisabledBitmap;
+ SetHighColor(tint_color(background, B_DISABLED_LABEL_TINT));
+ }
+ if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) {
+ if (IsEnabled()) {
+// background = tint_color(background, B_DARKEN_2_TINT);
+// background = tint_color(background, B_LIGHTEN_1_TINT);
+ background = tint_color(background, B_DARKEN_1_TINT);
+ bitmap = fClickedBitmap;
+ } else {
+// background = tint_color(background, B_DARKEN_1_TINT);
+// background = tint_color(background, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0);
+ background = tint_color(background, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
+ bitmap = fDisabledClickedBitmap;
+ }
+ // background
+ SetLowColor(background);
+ r.InsetBy(2.0, 2.0);
+ StrokeLine(r.LeftBottom(), r.LeftTop(), B_SOLID_LOW);
+ StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_LOW);
+ r.InsetBy(-2.0, -2.0);
+ }
+ // draw frame only if tracking
+ if (DrawBorder()) {
+ if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED))
+ DrawPressedBorder(r, background, shadow, darkShadow, lightShadow, light);
+ else
+ DrawNormalBorder(r, background, shadow, darkShadow, lightShadow, light);
+ r.InsetBy(2.0, 2.0);
+ } else
+ _DrawFrame(r, background, background, background, background);
+ float width = Bounds().Width();
+ float height = Bounds().Height();
+ // bitmap
+ BRegion originalClippingRegion;
+ if (bitmap && bitmap->IsValid()) {
+ float x = floorf((width - bitmap->Bounds().Width()) / 2.0 + 0.5);
+ float y = floorf((height - bitmap->Bounds().Height()) / 2.0 + 0.5);
+ BPoint point(x, y);
+ if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED))
+ point += BPoint(1.0, 1.0);
+ if (bitmap->ColorSpace() == B_RGBA32 || bitmap->ColorSpace() == B_RGBA32_BIG) {
+ FillRect(r, B_SOLID_LOW);
+ SetDrawingMode(B_OP_ALPHA);
+ SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
+ }
+ DrawBitmap(bitmap, point);
+ // constrain clipping region
+ BRegion region= originalClippingRegion;
+ GetClippingRegion(&region);
+ region.Exclude(bitmap->Bounds().OffsetByCopy(point));
+ ConstrainClippingRegion(&region);
+ }
+ // background
+ SetDrawingMode(B_OP_COPY);
+ FillRect(r, B_SOLID_LOW);
+ ConstrainClippingRegion(NULL);
+ // label
+ if (fLabel.CountChars() > 0) {
+ SetDrawingMode(B_OP_COPY);
+ font_height fh;
+ GetFontHeight(&fh);
+ float y = Bounds().bottom - 4.0;
+ y -= fh.descent;
+ float x = (width - StringWidth(fLabel.String())) / 2.0;
+ DrawString(fLabel.String(), BPoint(x, y));
+ }
+}
+
+// MouseDown
+void
+IconButton::MouseDown(BPoint where)
+{
+ if (!IsValid())
+ return;
+
+ if (_HasFlags(STATE_ENABLED)/* && !_HasFlags(STATE_FORCE_PRESSED)*/) {
+ if (Bounds().Contains(where)) {
+ SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
+ _AddFlags(STATE_PRESSED | STATE_TRACKING);
+ } else {
+ _ClearFlags(STATE_PRESSED | STATE_TRACKING);
+ }
+ }
+}
+
+// MouseUp
+void
+IconButton::MouseUp(BPoint where)
+{
+ if (!IsValid())
+ return;
+
+// if (!_HasFlags(STATE_FORCE_PRESSED)) {
+ if (_HasFlags(STATE_ENABLED) && _HasFlags(STATE_PRESSED) && Bounds().Contains(where))
+ Invoke();
+ else if (Bounds().Contains(where))
+ _AddFlags(STATE_INSIDE);
+ _ClearFlags(STATE_PRESSED | STATE_TRACKING);
+// }
+}
+
+// MouseMoved
+void
+IconButton::MouseMoved(BPoint where, uint32 transit, const BMessage* message)
+{
+ if (!IsValid())
+ return;
+
+ uint32 buttons = 0;
+ Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
+ // catch a mouse up event that we might have missed
+ if (!buttons && _HasFlags(STATE_PRESSED)) {
+ MouseUp(where);
+ return;
+ }
+ if (buttons && !_HasFlags(STATE_TRACKING))
+ return;
+ if ((transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW)
+ && _HasFlags(STATE_ENABLED))
+ _AddFlags(STATE_INSIDE);
+ else
+ _ClearFlags(STATE_INSIDE);
+ if (_HasFlags(STATE_TRACKING)) {
+ if (Bounds().Contains(where))
+ _AddFlags(STATE_PRESSED);
+ else
+ _ClearFlags(STATE_PRESSED);
+ }
+}
+
+#define MIN_SPACE 15.0
+
+// GetPreferredSize
+void
+IconButton::GetPreferredSize(float* width, float* height)
+{
+ float minWidth = 0.0;
+ float minHeight = 0.0;
+ if (IsValid()) {
+ minWidth += fNormalBitmap->Bounds().IntegerWidth() + 1.0;
+ minHeight += fNormalBitmap->Bounds().IntegerHeight() + 1.0;
+ }
+ if (minWidth < MIN_SPACE)
+ minWidth = MIN_SPACE;
+ if (minHeight < MIN_SPACE)
+ minHeight = MIN_SPACE;
+
+ float hPadding = max_c(6.0, ceilf(minHeight / 4.0));
+ float vPadding = max_c(6.0, ceilf(minWidth / 4.0));
+
+ if (fLabel.CountChars() > 0) {
+ font_height fh;
+ GetFontHeight(&fh);
+ minHeight += ceilf(fh.ascent + fh.descent) + vPadding;
+ minWidth += StringWidth(fLabel.String()) + vPadding;
+ }
+
+ if (width)
+ *width = minWidth + hPadding;
+ if (height)
+ *height = minHeight + vPadding;
+}
+
+// MinSize
+BSize
+IconButton::MinSize()
+{
+ BSize size;
+ GetPreferredSize(&size.width, &size.height);
+ return size;
+}
+
+// MaxSize
+BSize
+IconButton::MaxSize()
+{
+ return MinSize();
+}
+
+// Invoke
+status_t
+IconButton::Invoke(BMessage* message)
+{
+ if (!message)
+ message = Message();
+ if (message) {
+ BMessage clone(*message);
+ clone.AddInt64("be:when", system_time());
+ clone.AddPointer("be:source", (BView*)this);
+ clone.AddInt32("be:value", Value());
+ clone.AddInt32("id", ID());
+ return BInvoker::Invoke(&clone);
+ }
+ return BInvoker::Invoke(message);
+}
+
+// SetPressed
+void
+IconButton::SetPressed(bool pressed)
+{
+ if (pressed)
+ _AddFlags(STATE_FORCE_PRESSED);
+ else
+ _ClearFlags(STATE_FORCE_PRESSED);
+}
+
+// IsPressed
+bool
+IconButton::IsPressed() const
+{
+ return _HasFlags(STATE_FORCE_PRESSED);
+}
+
+status_t
+IconButton::SetIcon(int32 resourceID)
+{
+ app_info info;
+ status_t status = be_app->GetAppInfo(&info);
+ if (status != B_OK)
+ return status;
+
+ BResources resources(&info.ref);
+ status = resources.InitCheck();
+ if (status != B_OK)
+ return status;
+
+ size_t size;
+ const void* data = resources.LoadResource(B_VECTOR_ICON_TYPE, resourceID,
+ &size);
+ if (data != NULL) {
+ BBitmap bitmap(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK, B_RGBA32);
+ status = bitmap.InitCheck();
+ if (status != B_OK)
+ return status;
+ status = BIconUtils::GetVectorIcon(reinterpret_cast<const uint8*>(data),
+ size, &bitmap);
+ if (status != B_OK)
+ return status;
+ return SetIcon(&bitmap);
+ }
+// const void* data = resources.LoadResource(B_BITMAP_TYPE, resourceID, &size);
+ return B_ERROR;
+}
+
+// SetIcon
+status_t
+IconButton::SetIcon(const char* pathToBitmap)
+{
+ if (pathToBitmap == NULL)
+ return B_BAD_VALUE;
+
+ status_t status = B_BAD_VALUE;
+ BBitmap* fileBitmap = NULL;
+ // try to load bitmap from either relative or absolute path
+ BEntry entry(pathToBitmap, true);
+ if (!entry.Exists()) {
+ app_info info;
+ status = be_app->GetAppInfo(&info);
+ if (status == B_OK) {
+ BEntry app_entry(&info.ref, true);
+ BPath path;
+ app_entry.GetPath(&path);
+ status = path.InitCheck();
+ if (status == B_OK) {
+ status = path.GetParent(&path);
+ if (status == B_OK) {
+ status = path.Append(pathToBitmap, true);
+ if (status == B_OK)
+ fileBitmap = BTranslationUtils::GetBitmap(path.Path());
+ else
+ printf("IconButton::SetIcon() - path.Append() failed: %s\n", strerror(status));
+ } else
+ printf("IconButton::SetIcon() - path.GetParent() failed: %s\n", strerror(status));
+ } else
+ printf("IconButton::SetIcon() - path.InitCheck() failed: %s\n", strerror(status));
+ } else
+ printf("IconButton::SetIcon() - be_app->GetAppInfo() failed: %s\n", strerror(status));
+ } else
+ fileBitmap = BTranslationUtils::GetBitmap(pathToBitmap);
+ if (fileBitmap) {
+ status = _MakeBitmaps(fileBitmap);
+ delete fileBitmap;
+ } else
+ status = B_ERROR;
+ return status;
+}
+
+// SetIcon
+status_t
+IconButton::SetIcon(const BBitmap* bitmap)
+{
+ if (bitmap && bitmap->ColorSpace() == B_CMAP8) {
+ status_t status = bitmap->InitCheck();
+ if (status >= B_OK) {
+ if (BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap)) {
+ status = _MakeBitmaps(rgb32Bitmap);
+ delete rgb32Bitmap;
+ } else
+ status = B_NO_MEMORY;
+ }
+ return status;
+ } else
+ return _MakeBitmaps(bitmap);
+}
+
+// SetIcon
+status_t
+IconButton::SetIcon(const BMimeType* fileType, bool small)
+{
+ status_t status = fileType ? fileType->InitCheck() : B_BAD_VALUE;
+ if (status >= B_OK) {
+ BBitmap* mimeBitmap = new(nothrow) BBitmap(BRect(0.0, 0.0, 15.0, 15.0), B_CMAP8);
+ if (mimeBitmap && mimeBitmap->IsValid()) {
+ status = fileType->GetIcon(mimeBitmap, small ? B_MINI_ICON : B_LARGE_ICON);
+ if (status >= B_OK) {
+ if (BBitmap* bitmap = _ConvertToRGB32(mimeBitmap)) {
+ status = _MakeBitmaps(bitmap);
+ delete bitmap;
+ } else
+ printf("IconButton::SetIcon() - B_RGB32 bitmap is not valid\n");
+ } else
+ printf("IconButton::SetIcon() - fileType->GetIcon() failed: %s\n", strerror(status));
+ } else
+ printf("IconButton::SetIcon() - B_CMAP8 bitmap is not valid\n");
+ delete mimeBitmap;
+ } else
+ printf("IconButton::SetIcon() - fileType is not valid: %s\n", strerror(status));
+ return status;
+}
+
+// SetIcon
+status_t
+IconButton::SetIcon(const unsigned char* bitsFromQuickRes,
+ uint32 width, uint32 height, color_space format, bool convertToBW)
+{
+ status_t status = B_BAD_VALUE;
+ if (bitsFromQuickRes && width > 0 && height > 0) {
+ BBitmap* quickResBitmap = new(nothrow) BBitmap(BRect(0.0, 0.0, width - 1.0, height - 1.0), format);
+ status = quickResBitmap ? quickResBitmap->InitCheck() : B_ERROR;
+ if (status >= B_OK) {
+ // It doesn't look right to copy BitsLength() bytes, but bitmaps
+ // exported from QuickRes still contain their padding, so it is alright.
+ memcpy(quickResBitmap->Bits(), bitsFromQuickRes, quickResBitmap->BitsLength());
+ if (format != B_RGB32 && format != B_RGBA32 && format != B_RGB32_BIG && format != B_RGBA32_BIG) {
+ // colorspace needs conversion
+ BBitmap* bitmap = new(nothrow) BBitmap(quickResBitmap->Bounds(), B_RGB32, true);
+ if (bitmap && bitmap->IsValid()) {
+ BView* helper = new BView(bitmap->Bounds(), "helper",
+ B_FOLLOW_NONE, B_WILL_DRAW);
+ if (bitmap->Lock()) {
+ bitmap->AddChild(helper);
+ helper->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
+ helper->FillRect(helper->Bounds());
+ helper->SetDrawingMode(B_OP_OVER);
+ helper->DrawBitmap(quickResBitmap, BPoint(0.0, 0.0));
+ helper->Sync();
+ bitmap->Unlock();
+ }
+ status = _MakeBitmaps(bitmap);
+ } else
+ printf("IconButton::SetIcon() - B_RGB32 bitmap is not valid\n");
+ delete bitmap;
+ } else {
+ // native colorspace (32 bits)
+ if (convertToBW) {
+ // convert to gray scale icon
+ uint8* bits = (uint8*)quickResBitmap->Bits();
+ uint32 bpr = quickResBitmap->BytesPerRow();
+ for (uint32 y = 0; y < height; y++) {
+ uint8* handle = bits;
+ uint8 gray;
+ for (uint32 x = 0; x < width; x++) {
+ gray = uint8((116 * handle[0] + 600 * handle[1] + 308 * handle[2]) / 1024);
+ handle[0] = gray;
+ handle[1] = gray;
+ handle[2] = gray;
+ handle += 4;
+ }
+ bits += bpr;
+ }
+ }
+ status = _MakeBitmaps(quickResBitmap);
+ }
+ } else
+ printf("IconButton::SetIcon() - error allocating bitmap: %s\n", strerror(status));
+ delete quickResBitmap;
+ }
+ return status;
+}
+
+// ClearIcon
+void
+IconButton::ClearIcon()
+{
+ _DeleteBitmaps();
+ _Update();
+}
+
+void
+IconButton::TrimIcon(bool keepAspect)
+{
+ if (fNormalBitmap == NULL)
+ return;
+
+ uint8* bits = (uint8*)fNormalBitmap->Bits();
+ uint32 bpr = fNormalBitmap->BytesPerRow();
+ uint32 width = fNormalBitmap->Bounds().IntegerWidth() + 1;
+ uint32 height = fNormalBitmap->Bounds().IntegerHeight() + 1;
+ BRect trimmed(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN);
+ for (uint32 y = 0; y < height; y++) {
+ uint8* b = bits + 3;
+ bool rowHasAlpha = false;
+ for (uint32 x = 0; x < width; x++) {
+ if (*b) {
+ rowHasAlpha = true;
+ if (x < trimmed.left)
+ trimmed.left = x;
+ if (x > trimmed.right)
+ trimmed.right = x;
+ }
+ b += 4;
+ }
+ if (rowHasAlpha) {
+ if (y < trimmed.top)
+ trimmed.top = y;
+ if (y > trimmed.bottom)
+ trimmed.bottom = y;
+ }
+ bits += bpr;
+ }
+ if (!trimmed.IsValid())
+ return;
+ if (keepAspect) {
+ float minInset = trimmed.left;
+ minInset = min_c(minInset, trimmed.top);
+ minInset = min_c(minInset, fNormalBitmap->Bounds().right - trimmed.right);
+ minInset = min_c(minInset, fNormalBitmap->Bounds().bottom - trimmed.bottom);
+ trimmed = fNormalBitmap->Bounds().InsetByCopy(minInset, minInset);
+ }
+ trimmed = trimmed & fNormalBitmap->Bounds();
+ BBitmap trimmedBitmap(trimmed.OffsetToCopy(B_ORIGIN),
+ B_BITMAP_NO_SERVER_LINK, B_RGBA32);
+ bits = (uint8*)fNormalBitmap->Bits();
+ bits += 4 * (int32)trimmed.left + bpr * (int32)trimmed.top;
+ uint8* dst = (uint8*)trimmedBitmap.Bits();
+ uint32 trimmedWidth = trimmedBitmap.Bounds().IntegerWidth() + 1;
+ uint32 trimmedHeight = trimmedBitmap.Bounds().IntegerHeight() + 1;
+ uint32 trimmedBPR = trimmedBitmap.BytesPerRow();
+ for (uint32 y = 0; y < trimmedHeight; y++) {
+ memcpy(dst, bits, trimmedWidth * 4);
+ dst += trimmedBPR;
+ bits += bpr;
+ }
+ SetIcon(&trimmedBitmap);
+}
+
+// Bitmap
+BBitmap*
+IconButton::Bitmap() const
+{
+ BBitmap* bitmap = NULL;
+ if (fNormalBitmap && fNormalBitmap->IsValid()) {
+ bitmap = new(nothrow) BBitmap(fNormalBitmap);
+ if (bitmap->IsValid()) {
+ // TODO: remove this functionality when we use real transparent bitmaps
+ uint8* bits = (uint8*)bitmap->Bits();
+ uint32 bpr = bitmap->BytesPerRow();
+ uint32 width = bitmap->Bounds().IntegerWidth() + 1;
+ uint32 height = bitmap->Bounds().IntegerHeight() + 1;
+ color_space format = bitmap->ColorSpace();
+ if (format == B_CMAP8) {
+ // replace gray with magic transparent index
+ } else if (format == B_RGB32) {
+ for (uint32 y = 0; y < height; y++) {
+ uint8* bitsHandle = bits;
+ for (uint32 x = 0; x < width; x++) {
+ if (bitsHandle[0] == 216
+ && bitsHandle[1] == 216
+ && bitsHandle[2] == 216) {
+ bitsHandle[3] = 0; // make this pixel completely transparent
+ }
+ bitsHandle += 4;
+ }
+ bits += bpr;
+ }
+ }
+ } else {
+ delete bitmap;
+ bitmap = NULL;
+ }
+ }
+ return bitmap;
+}
+
+// DrawBorder
+bool
+IconButton::DrawBorder() const
+{
+ return ((IsEnabled() && (_HasFlags(STATE_INSIDE)
+ || _HasFlags(STATE_TRACKING))) || _HasFlags(STATE_FORCE_PRESSED));
+}
+
+// DrawNormalBorder
+void
+IconButton::DrawNormalBorder(BRect r, rgb_color background,
+ rgb_color shadow, rgb_color darkShadow,
+ rgb_color lightShadow, rgb_color light)
+{
+ _DrawFrame(r, shadow, darkShadow, light, lightShadow);
+}
+
+// DrawPressedBorder
+void
+IconButton::DrawPressedBorder(BRect r, rgb_color background,
+ rgb_color shadow, rgb_color darkShadow,
+ rgb_color lightShadow, rgb_color light)
+{
+ _DrawFrame(r, shadow, light, darkShadow, background);
+}
+
+// IsValid
+bool
+IconButton::IsValid() const
+{
+ return (fNormalBitmap && fDisabledBitmap && fClickedBitmap
+ && fDisabledClickedBitmap
+ && fNormalBitmap->IsValid()
+ && fDisabledBitmap->IsValid()
+ && fClickedBitmap->IsValid()
+ && fDisabledClickedBitmap->IsValid());
+}
+
+// Value
+int32
+IconButton::Value() const
+{
+ return _HasFlags(STATE_PRESSED) ? B_CONTROL_ON : B_CONTROL_OFF;
+}
+
+// SetValue
+void
+IconButton::SetValue(int32 value)
+{
+ if (value)
+ _AddFlags(STATE_PRESSED);
+ else
+ _ClearFlags(STATE_PRESSED);
+}
+
+// IsEnabled
+bool
+IconButton::IsEnabled() const
+{
+ return _HasFlags(STATE_ENABLED) ? B_CONTROL_ON : B_CONTROL_OFF;
+}
+
+// SetEnabled
+void
+IconButton::SetEnabled(bool enabled)
+{
+ if (enabled)
+ _AddFlags(STATE_ENABLED);
+ else
+ _ClearFlags(STATE_ENABLED | STATE_TRACKING | STATE_INSIDE);
+}
+
+// _ConvertToRGB32
+BBitmap*
+IconButton::_ConvertToRGB32(const BBitmap* bitmap) const
+{
+ BBitmap* convertedBitmap = new(nothrow) BBitmap(bitmap->Bounds(),
+ B_BITMAP_ACCEPTS_VIEWS, B_RGBA32);
+ if (convertedBitmap && convertedBitmap->IsValid()) {
+ memset(convertedBitmap->Bits(), 0, convertedBitmap->BitsLength());
+ BView* helper = new BView(bitmap->Bounds(), "helper",
+ B_FOLLOW_NONE, B_WILL_DRAW);
+ if (convertedBitmap->Lock()) {
+ convertedBitmap->AddChild(helper);
+ helper->SetDrawingMode(B_OP_OVER);
+ helper->DrawBitmap(bitmap, BPoint(0.0, 0.0));
+ helper->Sync();
+ convertedBitmap->Unlock();
+ }
+ } else {
+ delete convertedBitmap;
+ convertedBitmap = NULL;
+ }
+ return convertedBitmap;
+}
+
+// _MakeBitmaps
+status_t
+IconButton::_MakeBitmaps(const BBitmap* bitmap)
+{
+ status_t status = bitmap ? bitmap->InitCheck() : B_BAD_VALUE;
+ if (status >= B_OK) {
+ // make our own versions of the bitmap
+ BRect b(bitmap->Bounds());
+ _DeleteBitmaps();
+ color_space format = bitmap->ColorSpace();
+ fNormalBitmap = new(nothrow) BBitmap(b, format);
+ fDisabledBitmap = new(nothrow) BBitmap(b, format);
+ fClickedBitmap = new(nothrow) BBitmap(b, format);
+ fDisabledClickedBitmap = new(nothrow) BBitmap(b, format);
+ if (IsValid()) {
+ // copy bitmaps from file bitmap
+ uint8* nBits = (uint8*)fNormalBitmap->Bits();
+ uint8* dBits = (uint8*)fDisabledBitmap->Bits();
+ uint8* cBits = (uint8*)fClickedBitmap->Bits();
+ uint8* dcBits = (uint8*)fDisabledClickedBitmap->Bits();
+ uint8* fBits = (uint8*)bitmap->Bits();
+ int32 nbpr = fNormalBitmap->BytesPerRow();
+ int32 fbpr = bitmap->BytesPerRow();
+ int32 pixels = b.IntegerWidth() + 1;
+ int32 lines = b.IntegerHeight() + 1;
+ // nontransparent version:
+ if (format == B_RGB32 || format == B_RGB32_BIG) {
+ // iterate over color components
+ for (int32 y = 0; y < lines; y++) {
+ for (int32 x = 0; x < pixels; x++) {
+ int32 nOffset = 4 * x;
+ int32 fOffset = 4 * x;
+ nBits[nOffset + 0] = fBits[fOffset + 0];
+ nBits[nOffset + 1] = fBits[fOffset + 1];
+ nBits[nOffset + 2] = fBits[fOffset + 2];
+ nBits[nOffset + 3] = 255;
+ // clicked bits are darker (lame method...)
+ cBits[nOffset + 0] = (uint8)((float)nBits[nOffset + 0] * 0.8);
+ cBits[nOffset + 1] = (uint8)((float)nBits[nOffset + 1] * 0.8);
+ cBits[nOffset + 2] = (uint8)((float)nBits[nOffset + 2] * 0.8);
+ cBits[nOffset + 3] = 255;
+ // disabled bits have less contrast (lame method...)
+ uint8 grey = 216;
+ float dist = (nBits[nOffset + 0] - grey) * 0.4;
+ dBits[nOffset + 0] = (uint8)(grey + dist);
+ dist = (nBits[nOffset + 1] - grey) * 0.4;
+ dBits[nOffset + 1] = (uint8)(grey + dist);
+ dist = (nBits[nOffset + 2] - grey) * 0.4;
+ dBits[nOffset + 2] = (uint8)(grey + dist);
+ dBits[nOffset + 3] = 255;
+ // disabled bits have less contrast (lame method...)
+ grey = 188;
+ dist = (nBits[nOffset + 0] - grey) * 0.4;
+ dcBits[nOffset + 0] = (uint8)(grey + dist);
+ dist = (nBits[nOffset + 1] - grey) * 0.4;
+ dcBits[nOffset + 1] = (uint8)(grey + dist);
+ dist = (nBits[nOffset + 2] - grey) * 0.4;
+ dcBits[nOffset + 2] = (uint8)(grey + dist);
+ dcBits[nOffset + 3] = 255;
+ }
+ nBits += nbpr;
+ dBits += nbpr;
+ cBits += nbpr;
+ dcBits += nbpr;
+ fBits += fbpr;
+ }
+ // transparent version:
+ } else if (format == B_RGBA32 || format == B_RGBA32_BIG) {
+ // iterate over color components
+ for (int32 y = 0; y < lines; y++) {
+ for (int32 x = 0; x < pixels; x++) {
+ int32 nOffset = 4 * x;
+ int32 fOffset = 4 * x;
+ nBits[nOffset + 0] = fBits[fOffset + 0];
+ nBits[nOffset + 1] = fBits[fOffset + 1];
+ nBits[nOffset + 2] = fBits[fOffset + 2];
+ nBits[nOffset + 3] = fBits[fOffset + 3];
+ // clicked bits are darker (lame method...)
+ cBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8);
+ cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8);
+ cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8);
+ cBits[nOffset + 3] = fBits[fOffset + 3];
+ // disabled bits have less opacity
+
+ uint8 grey = ((uint16)nBits[nOffset + 0] * 10
+ + nBits[nOffset + 1] * 60
+ + nBits[nOffset + 2] * 30) / 100;
+ float dist = (nBits[nOffset + 0] - grey) * 0.3;
+ dBits[nOffset + 0] = (uint8)(grey + dist);
+ dist = (nBits[nOffset + 1] - grey) * 0.3;
+ dBits[nOffset + 1] = (uint8)(grey + dist);
+ dist = (nBits[nOffset + 2] - grey) * 0.3;
+ dBits[nOffset + 2] = (uint8)(grey + dist);
+ dBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3);
+ // disabled bits have less contrast (lame method...)
+ dcBits[nOffset + 0] = (uint8)(dBits[nOffset + 0] * 0.8);
+ dcBits[nOffset + 1] = (uint8)(dBits[nOffset + 1] * 0.8);
+ dcBits[nOffset + 2] = (uint8)(dBits[nOffset + 2] * 0.8);
+ dcBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3);
+ }
+ nBits += nbpr;
+ dBits += nbpr;
+ cBits += nbpr;
+ dcBits += nbpr;
+ fBits += fbpr;
+ }
+ // unsupported format
+ } else {
+ printf("IconButton::_MakeBitmaps() - bitmap has unsupported colorspace\n");
+ status = B_MISMATCHED_VALUES;
+ _DeleteBitmaps();
+ }
+ } else {
+ printf("IconButton::_MakeBitmaps() - error allocating local bitmaps\n");
+ status = B_NO_MEMORY;
+ _DeleteBitmaps();
+ }
+ } else
+ printf("IconButton::_MakeBitmaps() - bitmap is not valid\n");
+ return status;
+}
+
+// _DeleteBitmaps
+void
+IconButton::_DeleteBitmaps()
+{
+ delete fNormalBitmap;
+ fNormalBitmap = NULL;
+ delete fDisabledBitmap;
+ fDisabledBitmap = NULL;
+ delete fClickedBitmap;
+ fClickedBitmap = NULL;
+ delete fDisabledClickedBitmap;
+ fDisabledClickedBitmap = NULL;
+}
+
+// _Update
+void
+IconButton::_Update()
+{
+ if (LockLooper()) {
+ Invalidate();
+ UnlockLooper();
+ }
+}
+
+// _AddFlags
+void
+IconButton::_AddFlags(uint32 flags)
+{
+ if (!(fButtonState & flags)) {
+ fButtonState |= flags;
+ _Update();
+ }
+}
+
+// _ClearFlags
+void
+IconButton::_ClearFlags(uint32 flags)
+{
+ if (fButtonState & flags) {
+ fButtonState &= ~flags;
+ _Update();
+ }
+}
+
+// _HasFlags
+bool
+IconButton::_HasFlags(uint32 flags) const
+{
+ return (fButtonState & flags);
+}
+
+// _DrawFrame
+void
+IconButton::_DrawFrame(BRect r, rgb_color col1, rgb_color col2,
+ rgb_color col3, rgb_color col4)
+{
+ BeginLineArray(8);
+ AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col1);
+ AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col1);
+ AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col2);
+ AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col2);
+ r.InsetBy(1.0, 1.0);
+ AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col3);
+ AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col3);
+ AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col4);
+ AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col4);
+ EndLineArray();
+}
diff --git a/src/apps/webpositive/support/IconButton.h b/src/apps/webpositive/support/IconButton.h
new file mode 100644
index 0000000000..2876dd8cfd
--- /dev/null
+++ b/src/apps/webpositive/support/IconButton.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2006-2010, Haiku.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Stephan Aßmus <superstippi@gmx.de>
+ */
+
+/** gui class that loads an image from disk and shows it
+ as clickable button */
+
+// TODO: inherit from BControl?
+
+// NOTE: this file is a duplicate of the version in Icon-O-Matic/generic
+// it should be placed into a common folder for generic useful stuff
+
+#ifndef ICON_BUTTON_H
+#define ICON_BUTTON_H
+
+#include <Invoker.h>
+#include <String.h>
+#include <View.h>
+
+class BBitmap;
+class BMimeType;
+
+class IconButton : public BView, public BInvoker {
+public:
+ IconButton(const char* name,
+ uint32 id,
+ const char* label = NULL,
+ BMessage* message = NULL,
+ BHandler* target = NULL);
+ virtual ~IconButton();
+
+ // BView interface
+ virtual void MessageReceived(BMessage* message);
+ virtual void AttachedToWindow();
+ virtual void Draw(BRect updateRect);
+ virtual void MouseDown(BPoint where);
+ virtual void MouseUp(BPoint where);
+ virtual void MouseMoved(BPoint where, uint32 transit,
+ const BMessage* message);
+ virtual void GetPreferredSize(float* width,
+ float* height);
+ virtual BSize MinSize();
+ virtual BSize MaxSize();
+
+
+ // BInvoker interface
+ virtual status_t Invoke(BMessage* message = NULL);
+
+ // IconButton
+ bool IsValid() const;
+
+ virtual int32 Value() const;
+ virtual void SetValue(int32 value);
+
+ bool IsEnabled() const;
+ void SetEnabled(bool enable);
+
+ void SetPressed(bool pressed);
+ bool IsPressed() const;
+ uint32 ID() const
+ { return fID; }
+
+ status_t SetIcon(int32 resourceID);
+ status_t SetIcon(const char* pathToBitmap);
+ status_t SetIcon(const BBitmap* bitmap);
+ status_t SetIcon(const BMimeType* fileType,
+ bool small = true);
+ status_t SetIcon(const unsigned char* bitsFromQuickRes,
+ uint32 width, uint32 height,
+ color_space format,
+ bool convertToBW = false);
+ void ClearIcon();
+ void TrimIcon(bool keepAspect = true);
+
+ BBitmap* Bitmap() const;
+ // caller has to delete the returned bitmap
+
+ virtual bool DrawBorder() const;
+ virtual void DrawNormalBorder(BRect r,
+ rgb_color background,
+ rgb_color shadow,
+ rgb_color darkShadow,
+ rgb_color lightShadow,
+ rgb_color light);
+ virtual void DrawPressedBorder(BRect r,
+ rgb_color background,
+ rgb_color shadow,
+ rgb_color darkShadow,
+ rgb_color lightShadow,
+ rgb_color light);
+
+protected:
+ enum {
+ STATE_NONE = 0x0000,
+ STATE_TRACKING = 0x0001,
+ STATE_PRESSED = 0x0002,
+ STATE_ENABLED = 0x0004,
+ STATE_INSIDE = 0x0008,
+ STATE_FORCE_PRESSED = 0x0010,
+ };
+
+ void _AddFlags(uint32 flags);
+ void _ClearFlags(uint32 flags);
+ bool _HasFlags(uint32 flags) const;
+
+ void _DrawFrame(BRect frame,
+ rgb_color col1,
+ rgb_color col2,
+ rgb_color col3,
+ rgb_color col4);
+
+// private:
+ BBitmap* _ConvertToRGB32(const BBitmap* bitmap) const;
+ status_t _MakeBitmaps(const BBitmap* bitmap);
+ void _DeleteBitmaps();
+ void _SendMessage() const;
+ void _Update();
+
+ uint32 fButtonState;
+ int32 fID;
+ BBitmap* fNormalBitmap;
+ BBitmap* fDisabledBitmap;
+ BBitmap* fClickedBitmap;
+ BBitmap* fDisabledClickedBitmap;
+ BString fLabel;
+
+ BHandler* fTargetCache;
+};
+
+#endif // ICON_BUTTON_H
diff --git a/src/apps/webpositive/support/IconUtils.h b/src/apps/webpositive/support/IconUtils.h
new file mode 100644
index 0000000000..fdcfbd946f
--- /dev/null
+++ b/src/apps/webpositive/support/IconUtils.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2006-2008, Haiku. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef _ICON_UTILS_H
+#define _ICON_UTILS_H
+
+
+#include <Mime.h>
+
+class BBitmap;
+class BNode;
+
+
+// This class is a little different from many other classes.
+// You don't create an instance of it; you just call its various
+// static member functions for utility-like operations.
+class BIconUtils {
+ BIconUtils();
+ ~BIconUtils();
+ BIconUtils(const BIconUtils&);
+ BIconUtils& operator=(const BIconUtils&);
+
+public:
+
+ // Utility function to import an icon from the node that
+ // has either of the provided attribute names. Which icon type
+ // is preferred (vector, small or large B_CMAP8 icon) depends
+ // on the colorspace of the provided bitmap. If the colorspace
+ // is B_CMAP8, B_CMAP8 icons are preferred. In that case, the
+ // bitmap size must also match the provided icon_size "size"!
+ static status_t GetIcon(BNode* node,
+ const char* vectorIconAttrName,
+ const char* smallIconAttrName,
+ const char* largeIconAttrName,
+ icon_size size, BBitmap* result);
+
+ // Utility functions to import a vector icon in "flat icon"
+ // format from a BNode attribute or from a flat buffer in
+ // memory into the preallocated BBitmap "result".
+ // The colorspace of result needs to be B_RGBA32 or at
+ // least B_RGB32 (though that makes less sense). The icon
+ // will be scaled from it's "native" size of 64x64 to the
+ // size of the bitmap, the scale is derived from the bitmap
+ // width, the bitmap should have square dimension, or the
+ // icon will be cut off at the bottom (or have room left).
+ static status_t GetVectorIcon(BNode* node,
+ const char* attrName, BBitmap* result);
+
+ static status_t GetVectorIcon(const uint8* buffer,
+ size_t size, BBitmap* result);
+
+ // Utility function to import an "old" BeOS icon in B_CMAP8
+ // colorspace from either the small icon attribute or the
+ // large icon attribute as given in "smallIconAttrName" and
+ // "largeIconAttrName". Which icon is loaded depends on
+ // the given "size".
+ static status_t GetCMAP8Icon(BNode* node,
+ const char* smallIconAttrName,
+ const char* largeIconAttrName,
+ icon_size size, BBitmap* icon);
+
+ // Utility functions to convert from old icon colorspace
+ // into colorspace of BBitmap "result" (should be B_RGBA32
+ // to make any sense).
+ static status_t ConvertFromCMAP8(BBitmap* source,
+ BBitmap* result);
+ static status_t ConvertToCMAP8(BBitmap* source,
+ BBitmap* result);
+
+ static status_t ConvertFromCMAP8(const uint8* data,
+ uint32 width, uint32 height,
+ uint32 bytesPerRow, BBitmap* result);
+
+ static status_t ConvertToCMAP8(const uint8* data,
+ uint32 width, uint32 height,
+ uint32 bytesPerRow, BBitmap* result);
+};
+
+#endif // _ICON_UTILS_H
diff --git a/src/apps/webpositive/support/NavMenu.h b/src/apps/webpositive/support/NavMenu.h
new file mode 100644
index 0000000000..127d94f255
--- /dev/null
+++ b/src/apps/webpositive/support/NavMenu.h
@@ -0,0 +1,168 @@
+/*
+Open Tracker License
+
+Terms and Conditions
+
+Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice applies to all licensees
+and shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Be Incorporated shall not be
+used in advertising or otherwise to promote the sale, use or other dealings in
+this Software without prior written authorization from Be Incorporated.
+
+Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
+of Be Incorporated in the United States and other countries. Other brand product
+names are registered trademarks or trademarks of their respective holders.
+All rights reserved.
+*/
+
+// NavMenu is a hierarchical menu of volumes, folders, files and queries
+// displays icons, uses the SlowMenu API for full interruptability
+
+#ifndef NAV_MENU_H
+#define NAV_MENU_H
+
+
+#include <Messenger.h>
+#include <StorageDefs.h>
+#include <Entry.h>
+
+#include "SlowMenu.h"
+
+
+template<class T> class BObjectList;
+class BMenuItem;
+
+namespace BPrivate {
+
+class Model;
+class BContainerWindow;
+class ModelMenuItem;
+class EntryListBase;
+
+
+class TrackingHookData {
+ public:
+ TrackingHookData()
+ :
+ fTrackingHook(NULL),
+ fDragMessage(NULL)
+ {
+ }
+
+ bool (*fTrackingHook)(BMenu *, void *);
+ BMessenger fTarget;
+ const BMessage *fDragMessage;
+};
+
+
+class BNavMenu : public BSlowMenu {
+ public:
+ BNavMenu(const char* title, uint32 message, const BHandler *,
+ BWindow *parentWindow = NULL, const BObjectList<BString> *list = NULL);
+ BNavMenu(const char* title, uint32 message, const BMessenger &,
+ BWindow *parentWindow = NULL, const BObjectList<BString> *list = NULL);
+ // parentWindow, if specified, will be closed if nav menu item invoked
+ // with option held down
+
+ virtual ~BNavMenu();
+
+ virtual void AttachedToWindow();
+ virtual void DetachedFromWindow();
+
+ void SetNavDir(const entry_ref *);
+ void ForceRebuild();
+ bool NeedsToRebuild() const;
+ // will cause menu to get rebuilt next time it is shown
+
+ virtual void ResetTargets();
+ void SetTarget(const BMessenger &);
+ BMessenger Target();
+
+ void SetTypesList(const BObjectList<BString> *list);
+ const BObjectList<BString> *TypesList() const;
+
+ void AddNavDir(const Model *model, uint32 what, BHandler *target,
+ bool populateSubmenu);
+
+ void AddNavParentDir(const char *name, const Model *model, uint32 what, BHandler *target);
+ void AddNavParentDir(const Model *model, uint32 what, BHandler *target);
+ void SetShowParent(bool show);
+
+ static int32 GetMaxMenuWidth();
+
+ static int CompareFolderNamesFirstOne(const BMenuItem *, const BMenuItem *);
+ static int CompareOne(const BMenuItem *, const BMenuItem *);
+
+ static ModelMenuItem *NewModelItem(Model *, const BMessage *, const BMessenger &,
+ bool suppressFolderHierarchy=false, BContainerWindow * = NULL,
+ const BObjectList<BString> *typeslist = NULL,
+ TrackingHookData *hook = NULL);
+
+ TrackingHookData *InitTrackingHook(bool (*hookfunction)(BMenu *, void *),
+ const BMessenger *target, const BMessage *dragMessage);
+
+ protected:
+ virtual bool StartBuildingItemList();
+ virtual bool AddNextItem();
+ virtual void DoneBuildingItemList();
+ virtual void ClearMenuBuildingState();
+
+ void BuildVolumeMenu();
+
+ void AddOneItem(Model *);
+ void AddRootItemsIfNeeded();
+ void AddTrashItem();
+ static void SetTrackingHookDeep(BMenu *, bool (*)(BMenu *, void *), void *);
+
+ entry_ref fNavDir;
+ BMessage fMessage;
+ BMessenger fMessenger;
+ BWindow *fParentWindow;
+
+ // menu building state
+ uint8 fFlags;
+ BObjectList<BMenuItem> *fItemList;
+ EntryListBase *fContainer;
+ bool fIteratingDesktop;
+
+ const BObjectList<BString> *fTypesList;
+
+ TrackingHookData fTrackingHook;
+};
+
+// Spring Loaded Folder convenience routines
+// used in both Tracker and Deskbar
+#ifndef _IMPEXP_TRACKER
+# define _IMPEXP_TRACKER
+#endif
+_IMPEXP_TRACKER bool SpringLoadedFolderCompareMessages(const BMessage *incoming,
+ const BMessage *dragmessage);
+_IMPEXP_TRACKER void SpringLoadedFolderSetMenuStates(const BMenu *menu,
+ const BObjectList<BString> *typeslist);
+_IMPEXP_TRACKER void SpringLoadedFolderAddUniqueTypeToList(entry_ref *ref,
+ BObjectList<BString> *typeslist);
+_IMPEXP_TRACKER void SpringLoadedFolderCacheDragData(const BMessage *incoming,
+ BMessage **, BObjectList<BString> **typeslist);
+
+} // namespace BPrivate
+
+using namespace BPrivate;
+
+#endif // NAV_MENU_H
diff --git a/src/apps/webpositive/support/OpenHashTable.h b/src/apps/webpositive/support/OpenHashTable.h
new file mode 100644
index 0000000000..437fbed170
--- /dev/null
+++ b/src/apps/webpositive/support/OpenHashTable.h
@@ -0,0 +1,514 @@
+/*
+Open Tracker License
+
+Terms and Conditions
+
+Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice applies to all licensees
+and shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Be Incorporated shall not be
+used in advertising or otherwise to promote the sale, use or other dealings in
+this Software without prior written authorization from Be Incorporated.
+
+Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
+of Be Incorporated in the United States and other countries. Other brand product
+names are registered trademarks or trademarks of their respective holders.
+All rights reserved.
+*/
+
+// bonefish:
+// * removed need for exceptions
+// * fixed warnings
+// * implemented rehashing
+// * added RemoveAll()
+// TODO:
+// * shrinking of element vectors
+
+// Hash table with open addresssing
+
+#ifndef __OPEN_HASH_TABLE__
+#define __OPEN_HASH_TABLE__
+
+#include <stdlib.h>
+#include <new>
+
+// don't include <Debug.h>
+#ifndef ASSERT
+# define ASSERT(E) (void)0
+#endif
+#ifndef TRESPASS
+# define TRESPASS() (void)0
+#endif
+
+namespace BPrivate {
+
+template <class Element>
+class ElementVector {
+ // element vector for OpenHashTable needs to implement this
+ // interface
+public:
+ Element &At(int32 index);
+ Element *Add();
+ int32 IndexOf(const Element &) const;
+ void Remove(int32 index);
+};
+
+class OpenHashElement {
+public:
+ uint32 Hash() const;
+ bool operator==(const OpenHashElement &) const;
+ void Adopt(OpenHashElement &);
+ // low overhead copy, original element is in undefined state
+ // after call (calls Adopt on BString members, etc.)
+ int32 fNext;
+};
+
+const uint32 kPrimes [] = {
+ 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139,
+ 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859,
+ 134217689, 268435399, 536870909, 1073741789, 2147483647, 0
+};
+
+template <class Element, class ElementVec = ElementVector<Element> >
+class OpenHashTable {
+public:
+ OpenHashTable(int32 minSize, ElementVec *elementVector = 0,
+ float maxLoadFactor = 0.8);
+ // it is up to the subclass of OpenHashTable to supply
+ // elementVector
+ ~OpenHashTable();
+
+ bool InitCheck() const;
+
+ void SetElementVector(ElementVec *elementVector);
+
+ Element *FindFirst(uint32 elementHash) const;
+ Element *Add(uint32 elementHash);
+
+ void Remove(Element *element, bool dontRehash = false);
+ void RemoveAll();
+
+ // when calling Add, any outstanding element pointer may become
+ // invalid; to deal with this, get the element index and restore
+ // it after the add
+ int32 ElementIndex(const Element *) const;
+ Element *ElementAt(int32 index) const;
+
+ int32 ArraySize() const;
+ int32 VectorSize() const;
+ int32 CountElements() const;
+
+protected:
+ static int32 OptimalSize(int32 minSize);
+
+private:
+ bool _RehashIfNeeded();
+ bool _Rehash();
+
+ int32 fArraySize;
+ int32 fInitialSize;
+ int32 fElementCount;
+ int32 *fHashArray;
+ ElementVec *fElementVector;
+ float fMaxLoadFactor;
+};
+
+template <class Element>
+class OpenHashElementArray : public ElementVector<Element> {
+ // this is a straightforward implementation of an element vector
+ // deleting is handled by linking deleted elements into a free list
+ // the vector never shrinks
+public:
+ OpenHashElementArray(int32 initialSize);
+ ~OpenHashElementArray();
+
+ bool InitCheck() const;
+
+ Element &At(int32 index);
+ const Element &At(int32 index) const;
+ Element *Add(const Element &);
+ Element *Add();
+ void Remove(int32 index);
+ int32 IndexOf(const Element &) const;
+ int32 Size() const;
+
+private:
+ Element *fData;
+ int32 fSize;
+ int32 fNextFree;
+ int32 fNextDeleted;
+};
+
+
+//-----------------------------------
+
+template<class Element, class ElementVec>
+OpenHashTable<Element, ElementVec>::OpenHashTable(int32 minSize,
+ ElementVec *elementVector, float maxLoadFactor)
+ : fArraySize(OptimalSize(minSize)),
+ fInitialSize(fArraySize),
+ fElementCount(0),
+ fElementVector(elementVector),
+ fMaxLoadFactor(maxLoadFactor)
+{
+ // sanity check the maximal load factor
+ if (fMaxLoadFactor < 0.5)
+ fMaxLoadFactor = 0.5;
+ // allocate and init the array
+ fHashArray = (int32*)calloc(fArraySize, sizeof(int32));
+ if (fHashArray) {
+ for (int32 index = 0; index < fArraySize; index++)
+ fHashArray[index] = -1;
+ }
+}
+
+template<class Element, class ElementVec>
+OpenHashTable<Element, ElementVec>::~OpenHashTable()
+{
+ RemoveAll();
+ free(fHashArray);
+}
+
+template<class Element, class ElementVec>
+bool
+OpenHashTable<Element, ElementVec>::InitCheck() const
+{
+ return (fHashArray && fElementVector);
+}
+
+template<class Element, class ElementVec>
+int32
+OpenHashTable<Element, ElementVec>::OptimalSize(int32 minSize)
+{
+ for (int32 index = 0; ; index++)
+ if (!kPrimes[index] || kPrimes[index] >= (uint32)minSize)
+ return (int32)kPrimes[index];
+
+ return 0;
+}
+
+template<class Element, class ElementVec>
+Element *
+OpenHashTable<Element, ElementVec>::FindFirst(uint32 hash) const
+{
+ ASSERT(fElementVector);
+ hash %= fArraySize;
+ if (fHashArray[hash] < 0)
+ return 0;
+
+ return &fElementVector->At(fHashArray[hash]);
+}
+
+template<class Element, class ElementVec>
+int32
+OpenHashTable<Element, ElementVec>::ElementIndex(const Element *element) const
+{
+ return fElementVector->IndexOf(*element);
+}
+
+template<class Element, class ElementVec>
+Element *
+OpenHashTable<Element, ElementVec>::ElementAt(int32 index) const
+{
+ return &fElementVector->At(index);
+}
+
+template<class Element, class ElementVec>
+int32
+OpenHashTable<Element, ElementVec>::ArraySize() const
+{
+ return fArraySize;
+}
+
+template<class Element, class ElementVec>
+int32
+OpenHashTable<Element, ElementVec>::VectorSize() const
+{
+ return fElementVector->Size();
+}
+
+template<class Element, class ElementVec>
+int32
+OpenHashTable<Element, ElementVec>::CountElements() const
+{
+ return fElementCount;
+}
+
+
+template<class Element, class ElementVec>
+Element *
+OpenHashTable<Element, ElementVec>::Add(uint32 hash)
+{
+ ASSERT(fElementVector);
+ _RehashIfNeeded();
+ hash %= fArraySize;
+ Element *result = fElementVector->Add();
+ if (result) {
+ result->fNext = fHashArray[hash];
+ fHashArray[hash] = fElementVector->IndexOf(*result);
+ fElementCount++;
+ }
+ return result;
+}
+
+template<class Element, class ElementVec>
+void
+OpenHashTable<Element, ElementVec>::Remove(Element *element, bool dontRehash)
+{
+ if (!dontRehash)
+ _RehashIfNeeded();
+ uint32 hash = element->Hash() % fArraySize;
+ int32 next = fHashArray[hash];
+ ASSERT(next >= 0);
+
+ if (&fElementVector->At(next) == element) {
+ fHashArray[hash] = element->fNext;
+ fElementVector->Remove(next);
+ fElementCount--;
+ return;
+ }
+
+ for (int32 index = next; index >= 0; ) {
+ // look for an existing match in table
+ next = fElementVector->At(index).fNext;
+ if (next < 0) {
+ TRESPASS();
+ return;
+ }
+
+ if (&fElementVector->At(next) == element) {
+ fElementVector->At(index).fNext = element->fNext;
+ fElementVector->Remove(next);
+ fElementCount--;
+ return;
+ }
+ index = next;
+ }
+}
+
+template<class Element, class ElementVec>
+void
+OpenHashTable<Element, ElementVec>::RemoveAll()
+{
+ for (int32 i = 0; fElementCount > 0 && i < fArraySize; i++) {
+ int32 index = fHashArray[i];
+ while (index >= 0) {
+ Element* element = &fElementVector->At(index);
+ int32 next = element->fNext;
+ fElementVector->Remove(index);
+ fElementCount--;
+ index = next;
+ }
+ fHashArray[i] = -1;
+ }
+ _RehashIfNeeded();
+}
+
+template<class Element, class ElementVec>
+void
+OpenHashTable<Element, ElementVec>::SetElementVector(ElementVec *elementVector)
+{
+ fElementVector = elementVector;
+}
+
+// _RehashIfNeeded
+template<class Element, class ElementVec>
+bool
+OpenHashTable<Element, ElementVec>::_RehashIfNeeded()
+{
+ // The load factor range [fMaxLoadFactor / 3, fMaxLoadFactor] is fine,
+ // I think. After rehashing the load factor will be about
+ // fMaxLoadFactor * 2 / 3, respectively fMaxLoadFactor / 2.
+ float loadFactor = (float)fElementCount / (float)fArraySize;
+ if (loadFactor > fMaxLoadFactor
+ || (fArraySize > fInitialSize && loadFactor < fMaxLoadFactor / 3)) {
+ return _Rehash();
+ }
+ return true;
+}
+
+// _Rehash
+template<class Element, class ElementVec>
+bool
+OpenHashTable<Element, ElementVec>::_Rehash()
+{
+ bool result = true;
+ int32 newSize = int32(fElementCount * 1.73 * fMaxLoadFactor);
+ newSize = (fInitialSize > newSize ? fInitialSize : newSize);
+ if (newSize != fArraySize) {
+ // allocate a new array
+ int32 *newHashArray = (int32*)calloc(newSize, sizeof(int32));
+ if (newHashArray) {
+ // init the new hash array
+ for (int32 index = 0; index < newSize; index++)
+ newHashArray[index] = -1;
+ // iterate through all elements and put them into the new
+ // hash array
+ for (int i = 0; i < fArraySize; i++) {
+ int32 index = fHashArray[i];
+ while (index >= 0) {
+ // insert the element in the new array
+ Element &element = fElementVector->At(index);
+ int32 next = element.fNext;
+ uint32 hash = (element.Hash() % newSize);
+ element.fNext = newHashArray[hash];
+ newHashArray[hash] = index;
+ // next element in old list
+ index = next;
+ }
+ }
+ // delete the old array and set the new one
+ free(fHashArray);
+ fHashArray = newHashArray;
+ fArraySize = newSize;
+ } else
+ result = false;
+ }
+ return result;
+}
+
+
+template<class Element>
+OpenHashElementArray<Element>::OpenHashElementArray(int32 initialSize)
+ : fSize(initialSize),
+ fNextFree(0),
+ fNextDeleted(-1)
+{
+ fData = (Element*)calloc((size_t)initialSize, sizeof(Element));
+}
+
+template<class Element>
+OpenHashElementArray<Element>::~OpenHashElementArray()
+{
+ free(fData);
+}
+
+template<class Element>
+bool
+OpenHashElementArray<Element>::InitCheck() const
+{
+ return fData;
+}
+
+template<class Element>
+Element &
+OpenHashElementArray<Element>::At(int32 index)
+{
+ ASSERT(index < fSize);
+ return fData[index];
+}
+
+template<class Element>
+const Element &
+OpenHashElementArray<Element>::At(int32 index) const
+{
+ ASSERT(index < fSize);
+ return fData[index];
+}
+
+template<class Element>
+int32
+OpenHashElementArray<Element>::IndexOf(const Element &element) const
+{
+ int32 result = &element - fData;
+ if (result < 0 || result > fSize)
+ return -1;
+
+ return result;
+}
+
+template<class Element>
+int32
+OpenHashElementArray<Element>::Size() const
+{
+ return fSize;
+}
+
+
+template<class Element>
+Element *
+OpenHashElementArray<Element>::Add(const Element &newElement)
+{
+ Element *element = Add();
+ if (element)
+ element.Adopt(newElement);
+ return element;
+}
+
+#if DEBUG
+const int32 kGrowChunk = 10;
+#else
+const int32 kGrowChunk = 1024;
+#endif
+
+template<class Element>
+Element *
+OpenHashElementArray<Element>::Add()
+{
+ int32 index = fNextFree;
+ if (fNextDeleted >= 0) {
+ index = fNextDeleted;
+ fNextDeleted = At(index).fNext;
+ } else if (fNextFree >= fSize - 1) {
+ int32 newSize = fSize + kGrowChunk;
+/*
+ Element *newData = (Element *)calloc((size_t)newSize , sizeof(Element));
+ if (!newData)
+ return NULL;
+ memcpy(newData, fData, fSize * sizeof(Element));
+ free(fData);
+*/
+ Element *newData = (Element*)realloc(fData,
+ (size_t)newSize * sizeof(Element));
+ if (!newData)
+ return NULL;
+
+ fData = newData;
+ fSize = newSize;
+ index = fNextFree;
+ fNextFree++;
+ } else
+ fNextFree++;
+
+ new (&At(index)) Element;
+ // call placement new to initialize the element properly
+ ASSERT(At(index).fNext == -1);
+
+ return &At(index);
+}
+
+template<class Element>
+void
+OpenHashElementArray<Element>::Remove(int32 index)
+{
+ // delete by chaining empty elements in a single linked
+ // list, reusing the next field
+ ASSERT(index < fSize);
+ At(index).~Element();
+ // call the destructor explicitly to destroy the element
+ // properly
+ At(index).fNext = fNextDeleted;
+ fNextDeleted = index;
+}
+
+} // namespace BPrivate
+
+using BPrivate::OpenHashTable;
+
+#endif // __OPEN_HASH_TABLE__
diff --git a/src/apps/webpositive/support/SettingsMessage.cpp b/src/apps/webpositive/support/SettingsMessage.cpp
new file mode 100644
index 0000000000..15dc12394c
--- /dev/null
+++ b/src/apps/webpositive/support/SettingsMessage.cpp
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2008-2010, Stephan Aßmus <superstippi@gmx.de>.
+ * Copyright 1998, Eric Shepherd.
+ * All rights reserved. Distributed under the terms of the Be Sample Code
+ * license.
+ */
+
+//! Be Newsletter Volume II, Issue 35; September 2, 1998 (Eric Shepherd)
+
+#include "SettingsMessage.h"
+
+#include <new>
+
+#include <Autolock.h>
+#include <Entry.h>
+#include <File.h>
+#include <Messenger.h>
+#include <String.h>
+
+
+SettingsMessage::SettingsMessage(directory_which directory,
+ const char* filename)
+ :
+ BMessage('pref'),
+ fListeners(4)
+{
+ fStatus = find_directory(directory, &fPath);
+
+ if (fStatus == B_OK)
+ fStatus = fPath.Append(filename);
+
+ if (fStatus == B_OK)
+ fStatus = Load();
+}
+
+
+SettingsMessage::~SettingsMessage()
+{
+ Save();
+
+ for (int32 i = fListeners.CountItems() - 1; i >= 0; i--)
+ delete reinterpret_cast<BMessenger*>(fListeners.ItemAtFast(i));
+}
+
+
+status_t
+SettingsMessage::InitCheck() const
+{
+ return fStatus;
+}
+
+
+status_t
+SettingsMessage::Load()
+{
+ BAutolock _(this);
+
+ BFile file(fPath.Path(), B_READ_ONLY);
+ status_t status = file.InitCheck();
+
+ if (status == B_OK)
+ status = Unflatten(&file);
+
+ return status;
+}
+
+
+status_t
+SettingsMessage::Save() const
+{
+ BAutolock _(const_cast<SettingsMessage*>(this));
+
+ BFile file(fPath.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
+ status_t status = file.InitCheck();
+
+ if (status == B_OK)
+ status = Flatten(&file);
+
+ return status;
+}
+
+
+bool
+SettingsMessage::AddListener(const BMessenger& listener)
+{
+ BAutolock _(this);
+
+ BMessenger* listenerCopy = new(std::nothrow) BMessenger(listener);
+ if (listenerCopy && fListeners.AddItem(listenerCopy))
+ return true;
+ delete listenerCopy;
+ return false;
+}
+
+
+void
+SettingsMessage::RemoveListener(const BMessenger& listener)
+{
+ BAutolock _(this);
+
+ for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
+ BMessenger* listenerItem = reinterpret_cast<BMessenger*>(
+ fListeners.ItemAtFast(i));
+ if (*listenerItem == listener) {
+ fListeners.RemoveItem(i);
+ delete listenerItem;
+ return;
+ }
+ }
+}
+
+
+// #pragma mark -
+
+
+status_t
+SettingsMessage::SetValue(const char* name, bool value)
+{
+ status_t ret = ReplaceBool(name, value);
+ if (ret != B_OK)
+ ret = AddBool(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, int8 value)
+{
+ status_t ret = ReplaceInt8(name, value);
+ if (ret != B_OK)
+ ret = AddInt8(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, int16 value)
+{
+ status_t ret = ReplaceInt16(name, value);
+ if (ret != B_OK)
+ ret = AddInt16(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, int32 value)
+{
+ status_t ret = ReplaceInt32(name, value);
+ if (ret != B_OK)
+ ret = AddInt32(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, uint32 value)
+{
+ status_t ret = ReplaceUInt32(name, value);
+ if (ret != B_OK)
+ ret = AddUInt32(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, int64 value)
+{
+ status_t ret = ReplaceInt64(name, value);
+ if (ret != B_OK)
+ ret = AddInt64(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, float value)
+{
+ status_t ret = ReplaceFloat(name, value);
+ if (ret != B_OK)
+ ret = AddFloat(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, double value)
+{
+ status_t ret = ReplaceDouble(name, value);
+ if (ret != B_OK)
+ ret = AddDouble(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, const char* value)
+{
+ status_t ret = ReplaceString(name, value);
+ if (ret != B_OK)
+ ret = AddString(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, const BString& value)
+{
+ status_t ret = ReplaceString(name, value);
+ if (ret != B_OK)
+ ret = AddString(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, const BPoint& value)
+{
+ status_t ret = ReplacePoint(name, value);
+ if (ret != B_OK)
+ ret = AddPoint(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, const BRect& value)
+{
+ status_t ret = ReplaceRect(name, value);
+ if (ret != B_OK)
+ ret = AddRect(name, value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, const entry_ref& value)
+{
+ status_t ret = ReplaceRef(name, &value);
+ if (ret != B_OK)
+ ret = AddRef(name, &value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, const BMessage& value)
+{
+ status_t ret = ReplaceMessage(name, &value);
+ if (ret != B_OK)
+ ret = AddMessage(name, &value);
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, const BFlattenable* value)
+{
+ status_t ret = ReplaceFlat(name, const_cast<BFlattenable*>(value));
+ if (ret != B_OK)
+ ret = AddFlat(name, const_cast<BFlattenable*>(value));
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+status_t
+SettingsMessage::SetValue(const char* name, const BFont& value)
+{
+ font_family family;
+ font_style style;
+ value.GetFamilyAndStyle(&family, &style);
+
+ BMessage fontMessage;
+ status_t ret = fontMessage.AddString("family", family);
+ if (ret == B_OK)
+ ret = fontMessage.AddString("style", style);
+ if (ret == B_OK)
+ ret = fontMessage.AddFloat("size", value.Size());
+
+ if (ret == B_OK) {
+ if (ReplaceMessage(name, &fontMessage) != B_OK)
+ ret = AddMessage(name, &fontMessage);
+ }
+ if (ret == B_OK)
+ _NotifyValueChanged(name);
+ return ret;
+}
+
+
+// #pragma mark -
+
+
+bool
+SettingsMessage::GetValue(const char* name, bool defaultValue) const
+{
+ bool value;
+ if (FindBool(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+int8
+SettingsMessage::GetValue(const char* name, int8 defaultValue) const
+{
+ int8 value;
+ if (FindInt8(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+int16
+SettingsMessage::GetValue(const char* name, int16 defaultValue) const
+{
+ int16 value;
+ if (FindInt16(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+int32
+SettingsMessage::GetValue(const char* name, int32 defaultValue) const
+{
+ int32 value;
+ if (FindInt32(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+uint32
+SettingsMessage::GetValue(const char* name, uint32 defaultValue) const
+{
+ uint32 value;
+ if (FindUInt32(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+int64
+SettingsMessage::GetValue(const char* name, int64 defaultValue) const
+{
+ int64 value;
+ if (FindInt64(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+float
+SettingsMessage::GetValue(const char* name, float defaultValue) const
+{
+ float value;
+ if (FindFloat(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+double
+SettingsMessage::GetValue(const char* name, double defaultValue) const
+{
+ double value;
+ if (FindDouble(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+BString
+SettingsMessage::GetValue(const char* name, const BString& defaultValue) const
+{
+ BString value;
+ if (FindString(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+const char*
+SettingsMessage::GetValue(const char* name, const char* defaultValue) const
+{
+ const char* value;
+ if (FindString(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+BPoint
+SettingsMessage::GetValue(const char *name, BPoint defaultValue) const
+{
+ BPoint value;
+ if (FindPoint(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+BRect
+SettingsMessage::GetValue(const char* name, BRect defaultValue) const
+{
+ BRect value;
+ if (FindRect(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+entry_ref
+SettingsMessage::GetValue(const char* name, const entry_ref& defaultValue) const
+{
+ entry_ref value;
+ if (FindRef(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+BMessage
+SettingsMessage::GetValue(const char* name, const BMessage& defaultValue) const
+{
+ BMessage value;
+ if (FindMessage(name, &value) != B_OK)
+ return defaultValue;
+ return value;
+}
+
+
+BFont
+SettingsMessage::GetValue(const char* name, const BFont& defaultValue) const
+{
+ BMessage fontMessage;
+ if (FindMessage(name, &fontMessage) != B_OK)
+ return defaultValue;
+
+ const char* family;
+ const char* style;
+ float size;
+ if (fontMessage.FindString("family", &family) != B_OK
+ || fontMessage.FindString("style", &style) != B_OK
+ || fontMessage.FindFloat("size", &size) != B_OK) {
+ return defaultValue;
+ }
+
+ BFont value;
+ if (value.SetFamilyAndStyle(family, style) != B_OK)
+ return defaultValue;
+
+ value.SetSize(size);
+
+ return value;
+}
+
+
+// #pragma mark - private
+
+
+void
+SettingsMessage::_NotifyValueChanged(const char* name) const
+{
+ BMessage message(SETTINGS_VALUE_CHANGED);
+ message.AddString("name", name);
+
+ // Add the value of that name to the notification.
+ type_code type;
+ if (GetInfo(name, &type) == B_OK) {
+ const void* data;
+ ssize_t numBytes;
+ if (FindData(name, type, &data, &numBytes) == B_OK)
+ message.AddData("value", type, data, numBytes);
+ }
+
+ int32 count = fListeners.CountItems();
+ for (int32 i = 0; i < count; i++) {
+ BMessenger* listener = reinterpret_cast<BMessenger*>(
+ fListeners.ItemAtFast(i));
+ listener->SendMessage(&message);
+ }
+}
+
diff --git a/src/apps/webpositive/support/SettingsMessage.h b/src/apps/webpositive/support/SettingsMessage.h
new file mode 100644
index 0000000000..8f22785788
--- /dev/null
+++ b/src/apps/webpositive/support/SettingsMessage.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2008-2010 Stephan Aßmus <superstippi@gmx.de>.
+ * Copyright 1998 Eric Shepherd.
+ * All rights reserved. Distributed under the terms of the Be Sample Code
+ * license.
+ */
+#ifndef SETTINGS_MESSAGE_H
+#define SETTINGS_MESSAGE_H
+
+
+#include <FindDirectory.h>
+#include <Font.h>
+#include <List.h>
+#include <Locker.h>
+#include <Message.h>
+#include <Path.h>
+
+class BMessenger;
+class BString;
+
+
+enum {
+ SETTINGS_VALUE_CHANGED = '_svc'
+};
+
+
+class SettingsMessage : public BMessage, public BLocker {
+public:
+ SettingsMessage(directory_which directory,
+ const char* filename);
+ virtual ~SettingsMessage();
+
+
+ status_t InitCheck() const;
+ status_t Load();
+ status_t Save() const;
+
+ bool AddListener(const BMessenger& listener);
+ void RemoveListener(const BMessenger& listener);
+
+ status_t SetValue(const char* name, bool value);
+ status_t SetValue(const char* name, int8 value);
+ status_t SetValue(const char* name, int16 value);
+ status_t SetValue(const char* name, int32 value);
+ status_t SetValue(const char* name, uint32 value);
+ status_t SetValue(const char* name, int64 value);
+ status_t SetValue(const char* name, float value);
+ status_t SetValue(const char* name, double value);
+ status_t SetValue(const char* name,
+ const char* value);
+ status_t SetValue(const char* name,
+ const BString& value);
+ status_t SetValue(const char *name,
+ const BPoint& value);
+ status_t SetValue(const char* name, const BRect& value);
+ status_t SetValue(const char* name,
+ const entry_ref& value);
+ status_t SetValue(const char* name,
+ const BMessage& value);
+ status_t SetValue(const char* name,
+ const BFlattenable* value);
+ status_t SetValue(const char* name,
+ const BFont& value);
+
+ bool GetValue(const char* name,
+ bool defaultValue) const;
+ int8 GetValue(const char* name,
+ int8 defaultValue) const;
+ int16 GetValue(const char* name,
+ int16 defaultValue) const;
+ int32 GetValue(const char* name,
+ int32 defaultValue) const;
+ uint32 GetValue(const char* name,
+ uint32 defaultValue) const;
+ int64 GetValue(const char* name,
+ int64 defaultValue) const;
+ float GetValue(const char* name,
+ float defaultValue) const;
+ double GetValue(const char* name,
+ double defaultValue) const;
+ const char* GetValue(const char* name,
+ const char* defaultValue) const;
+ BString GetValue(const char* name,
+ const BString& defaultValue) const;
+ BPoint GetValue(const char *name,
+ BPoint defaultValue) const;
+ BRect GetValue(const char* name,
+ BRect defaultValue) const;
+ entry_ref GetValue(const char* name,
+ const entry_ref& defaultValue) const;
+ BMessage GetValue(const char* name,
+ const BMessage& defaultValue) const;
+ BFont GetValue(const char* name,
+ const BFont& defaultValue) const;
+
+private:
+ void _NotifyValueChanged(const char* name) const;
+
+private:
+ BPath fPath;
+ status_t fStatus;
+ BList fListeners;
+};
+
+#endif // SETTINGS_MESSAGE_H
diff --git a/src/apps/webpositive/support/SlowMenu.h b/src/apps/webpositive/support/SlowMenu.h
new file mode 100644
index 0000000000..62e9156b07
--- /dev/null
+++ b/src/apps/webpositive/support/SlowMenu.h
@@ -0,0 +1,76 @@
+/*
+Open Tracker License
+
+Terms and Conditions
+
+Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice applies to all licensees
+and shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Be Incorporated shall not be
+used in advertising or otherwise to promote the sale, use or other dealings in
+this Software without prior written authorization from Be Incorporated.
+
+Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
+of Be Incorporated in the United States and other countries. Other brand product
+names are registered trademarks or trademarks of their respective holders.
+All rights reserved.
+*/
+
+#ifndef __SLOW_MENU__
+#define __SLOW_MENU__
+
+#include <Menu.h>
+#include <MenuItem.h>
+#include <Debug.h>
+
+// SlowMenu is a convenience class that makes it easier to
+// use the AddDynamicItem callback to implement a menu that can
+// checks periodically between creating new items and quits
+// early if needed
+
+namespace BPrivate {
+
+class BSlowMenu : public BMenu {
+ public:
+ BSlowMenu(const char *title, menu_layout layout = B_ITEMS_IN_COLUMN);
+
+ protected:
+ virtual bool StartBuildingItemList();
+ // set up state to start building the item list
+ // returns false if setup failed
+ virtual bool AddNextItem() = 0;
+ // returns false if done
+ virtual void DoneBuildingItemList() = 0;
+ // default version adds items from itemList to menu and deletes
+ // the list; override to sort items first, etc.
+
+ virtual void ClearMenuBuildingState() = 0;
+
+ protected:
+ virtual bool AddDynamicItem(add_state state);
+ // this is the callback from BMenu, you shouldn't need to override this
+
+ bool fMenuBuilt;
+};
+
+} // namespace BPrivate
+
+using namespace BPrivate;
+
+#endif /* __SLOW_MENU__ */
diff --git a/src/apps/webpositive/support/StringForSize.cpp b/src/apps/webpositive/support/StringForSize.cpp
new file mode 100644
index 0000000000..ae7831e397
--- /dev/null
+++ b/src/apps/webpositive/support/StringForSize.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010 Haiku Inc. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+
+#include "StringForSize.h"
+
+#include <stdio.h>
+
+
+namespace BPrivate {
+
+
+const char*
+string_for_size(double size, char* string, size_t stringSize)
+{
+ double kib = size / 1024.0;
+ if (kib < 1.0) {
+ snprintf(string, stringSize, "%d bytes", (int)size);
+ return string;
+ }
+ double mib = kib / 1024.0;
+ if (mib < 1.0) {
+ snprintf(string, stringSize, "%3.2f KiB", kib);
+ return string;
+ }
+ double gib = mib / 1024.0;
+ if (gib < 1.0) {
+ snprintf(string, stringSize, "%3.2f MiB", mib);
+ return string;
+ }
+ double tib = gib / 1024.0;
+ if (tib < 1.0) {
+ snprintf(string, stringSize, "%3.2f GiB", gib);
+ return string;
+ }
+ snprintf(string, stringSize, "%.2f TiB", tib);
+ return string;
+}
+
+
+} // namespace BPrivate
+
diff --git a/src/apps/webpositive/support/StringForSize.h b/src/apps/webpositive/support/StringForSize.h
new file mode 100644
index 0000000000..fa3e13b115
--- /dev/null
+++ b/src/apps/webpositive/support/StringForSize.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010 Haiku Inc. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef STRING_FOR_SIZE_H
+#define STRING_FOR_SIZE_H
+
+#include <SupportDefs.h>
+
+
+namespace BPrivate {
+
+
+const char* string_for_size(double size, char* string, size_t stringSize);
+
+
+} // namespace BPrivate
+
+
+using BPrivate::string_for_size;
+
+
+#endif // COLOR_QUANTIZER_H
diff --git a/src/apps/webpositive/support/WindowIcon.h b/src/apps/webpositive/support/WindowIcon.h
new file mode 100644
index 0000000000..30d1622058
--- /dev/null
+++ b/src/apps/webpositive/support/WindowIcon.h
@@ -0,0 +1,54 @@
+
+const uint32 kWindowIconWidth = 13;
+const uint32 kWindowIconHeight = 14;
+const color_space kWindowIconFormat = B_RGBA32;
+
+const unsigned char kWindowIconBits[] = {
+ 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff,
+ 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff,
+ 0x78, 0x78, 0x78, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x78, 0x78, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff,
+ 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff,
+ 0x66, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x78, 0x78, 0xff, 0x13, 0xaf, 0xff, 0xff,
+ 0x13, 0xaf, 0xff, 0xff, 0x13, 0xaf, 0xff, 0xff, 0x13, 0xaf, 0xff, 0xff, 0x13, 0xaf, 0xff, 0xff,
+ 0x13, 0xaf, 0xff, 0xff, 0x13, 0xaf, 0xff, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff,
+ 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff,
+ 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff,
+ 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff,
+ 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff,
+ 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff,
+ 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff,
+ 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff,
+ 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff,
+ 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff,
+ 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff,
+ 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff,
+ 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff,
+ 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff
+};
+
diff --git a/src/apps/webpositive/svn_revision.cpp b/src/apps/webpositive/svn_revision.cpp
new file mode 100644
index 0000000000..489c64cdd4
--- /dev/null
+++ b/src/apps/webpositive/svn_revision.cpp
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2006-2009, Ingo Weinhold <ingo_weinhold@gmx.de>
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+
+#include "svn_revision.h"
+
+const int32 kSVNRevision =
+ #include "svn_revision"
+;
diff --git a/src/apps/webpositive/svn_revision.h b/src/apps/webpositive/svn_revision.h
new file mode 100644
index 0000000000..fff9e9b122
--- /dev/null
+++ b/src/apps/webpositive/svn_revision.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2006-2009, Ingo Weinhold <ingo_weinhold@gmx.de>
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+
+#ifndef SVN_REVISION_H
+#define SVN_REVISION_H
+
+#include <SupportDefs.h>
+
+
+extern const int32 kSVNRevision;
+
+
+#endif // SVN_REVISION_H
diff --git a/src/apps/webpositive/tabview/TabContainerView.cpp b/src/apps/webpositive/tabview/TabContainerView.cpp
new file mode 100644
index 0000000000..52217fa9c7
--- /dev/null
+++ b/src/apps/webpositive/tabview/TabContainerView.cpp
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2010 Rene Gollent <rene@gollent.com>
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "TabContainerView.h"
+
+#include <stdio.h>
+
+#include <Application.h>
+#include <AbstractLayoutItem.h>
+#include <Bitmap.h>
+#include <Button.h>
+#include <CardLayout.h>
+#include <ControlLook.h>
+#include <GroupView.h>
+#include <MenuBar.h>
+#include <SpaceLayoutItem.h>
+#include <Window.h>
+
+#include "TabView.h"
+
+
+static const float kLeftTabInset = 4;
+
+
+TabContainerView::TabContainerView(Controller* controller)
+ :
+ BGroupView(B_HORIZONTAL, 0.0),
+ fLastMouseEventTab(NULL),
+ fMouseDown(false),
+ fClickCount(0),
+ fSelectedTab(NULL),
+ fController(controller),
+ fFirstVisibleTabIndex(0)
+{
+ SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
+ SetViewColor(B_TRANSPARENT_COLOR);
+ GroupLayout()->SetInsets(kLeftTabInset, 0, 0, 1);
+ GroupLayout()->AddItem(BSpaceLayoutItem::CreateGlue(), 0.0f);
+}
+
+
+TabContainerView::~TabContainerView()
+{
+}
+
+
+BSize
+TabContainerView::MinSize()
+{
+ // Eventually, we want to be scrolling if the tabs don't fit.
+ BSize size(BGroupView::MinSize());
+ size.width = 300;
+ return size;
+}
+
+
+void
+TabContainerView::MessageReceived(BMessage* message)
+{
+ switch (message->what) {
+ default:
+ BGroupView::MessageReceived(message);
+ }
+}
+
+
+void
+TabContainerView::Draw(BRect updateRect)
+{
+ // Stroke separator line at bottom.
+ rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
+ BRect frame(Bounds());
+ SetHighColor(tint_color(base, B_DARKEN_2_TINT));
+ StrokeLine(frame.LeftBottom(), frame.RightBottom());
+ frame.bottom--;
+
+ // Draw empty area before first tab.
+ uint32 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER;
+ BRect leftFrame(frame.left, frame.top, kLeftTabInset, frame.bottom);
+ be_control_look->DrawInactiveTab(this, leftFrame, updateRect, base, 0,
+ borders);
+
+ // Draw all tabs, keeping track of where they end.
+ BGroupLayout* layout = GroupLayout();
+ int32 count = layout->CountItems() - 1;
+ for (int32 i = 0; i < count; i++) {
+ TabLayoutItem* item = dynamic_cast<TabLayoutItem*>(
+ layout->ItemAt(i));
+ if (!item || !item->IsVisible())
+ continue;
+ item->Parent()->Draw(updateRect);
+ frame.left = item->Frame().right + 1;
+ }
+
+ // Draw empty area after last tab.
+ be_control_look->DrawInactiveTab(this, frame, updateRect, base, 0, borders);
+}
+
+
+void
+TabContainerView::MouseDown(BPoint where)
+{
+ uint32 buttons;
+ if (Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons) != B_OK)
+ buttons = B_PRIMARY_MOUSE_BUTTON;
+ uint32 clicks;
+ if (Window()->CurrentMessage()->FindInt32("clicks", (int32*)&clicks) != B_OK)
+ clicks = 1;
+ fMouseDown = true;
+ SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
+ if (fLastMouseEventTab)
+ fLastMouseEventTab->MouseDown(where, buttons);
+ else {
+ if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0) {
+ // Middle click outside tabs should always open a new tab.
+ fClickCount = 2;
+ } else if (clicks > 1)
+ fClickCount = fClickCount++;
+ else
+ fClickCount = 1;
+ }
+}
+
+
+void
+TabContainerView::MouseUp(BPoint where)
+{
+ fMouseDown = false;
+ if (fLastMouseEventTab) {
+ fLastMouseEventTab->MouseUp(where);
+ fClickCount = 0;
+ } else if (fClickCount > 1) {
+ // NOTE: fClickCount is >= 1 only if the first click was outside
+ // any tab. So even if fLastMouseEventTab has been reset to NULL
+ // because this tab was removed during mouse down, we wouldn't
+ // run the "outside tabs" code below.
+ fController->DoubleClickOutsideTabs();
+ fClickCount = 0;
+ }
+ // Always check the tab under the mouse again, since we don't update
+ // it with fMouseDown == true.
+ _SendFakeMouseMoved();
+}
+
+
+void
+TabContainerView::MouseMoved(BPoint where, uint32 transit,
+ const BMessage* dragMessage)
+{
+ _MouseMoved(where, transit, dragMessage);
+}
+
+
+void
+TabContainerView::DoLayout()
+{
+ BGroupView::DoLayout();
+
+ _ValidateTabVisibility();
+ _SendFakeMouseMoved();
+}
+
+void
+TabContainerView::AddTab(const char* label, int32 index)
+{
+ TabView* tab;
+ if (fController)
+ tab = fController->CreateTabView();
+ else
+ tab = new TabView();
+ tab->SetLabel(label);
+ AddTab(tab, index);
+}
+
+
+void
+TabContainerView::AddTab(TabView* tab, int32 index)
+{
+ tab->SetContainerView(this);
+
+ if (index == -1)
+ index = GroupLayout()->CountItems() - 1;
+
+ bool hasFrames = fController != NULL && fController->HasFrames();
+ bool isFirst = index == 0 && hasFrames;
+ bool isLast = index == GroupLayout()->CountItems() - 1 && hasFrames;
+ bool isFront = fSelectedTab == NULL;
+ tab->Update(isFirst, isLast, isFront);
+
+ GroupLayout()->AddItem(index, tab->LayoutItem());
+
+ if (isFront)
+ SelectTab(tab);
+ if (isLast) {
+ TabLayoutItem* item
+ = dynamic_cast<TabLayoutItem*>(GroupLayout()->ItemAt(index - 1));
+ if (item)
+ item->Parent()->SetIsLast(false);
+ }
+
+ SetFirstVisibleTabIndex(MaxFirstVisibleTabIndex());
+ _ValidateTabVisibility();
+}
+
+TabView*
+TabContainerView::RemoveTab(int32 index)
+{
+ TabLayoutItem* item
+ = dynamic_cast<TabLayoutItem*>(GroupLayout()->RemoveItem(index));
+
+ if (!item)
+ return NULL;
+
+ BRect dirty(Bounds());
+ dirty.left = item->Frame().left;
+ TabView* removedTab = item->Parent();
+ removedTab->SetContainerView(NULL);
+
+ if (removedTab == fLastMouseEventTab)
+ fLastMouseEventTab = NULL;
+
+ // Update tabs after or before the removed tab.
+ bool hasFrames = fController != NULL && fController->HasFrames();
+ item = dynamic_cast<TabLayoutItem*>(GroupLayout()->ItemAt(index));
+ if (item) {
+ // This tab is behind the removed tab.
+ TabView* tab = item->Parent();
+ tab->Update(index == 0 && hasFrames,
+ index == GroupLayout()->CountItems() - 2 && hasFrames,
+ tab == fSelectedTab);
+ if (removedTab == fSelectedTab) {
+ fSelectedTab = NULL;
+ SelectTab(tab);
+ } else if (fController && tab == fSelectedTab)
+ fController->TabSelected(index);
+ } else {
+ // The removed tab was the last tab.
+ item = dynamic_cast<TabLayoutItem*>(GroupLayout()->ItemAt(index - 1));
+ if (item) {
+ TabView* tab = item->Parent();
+ tab->Update(index == 0 && hasFrames,
+ index == GroupLayout()->CountItems() - 2 && hasFrames,
+ tab == fSelectedTab);
+ if (removedTab == fSelectedTab) {
+ fSelectedTab = NULL;
+ SelectTab(tab);
+ }
+ }
+ }
+
+ Invalidate(dirty);
+ _ValidateTabVisibility();
+
+ return removedTab;
+}
+
+
+TabView*
+TabContainerView::TabAt(int32 index) const
+{
+ TabLayoutItem* item = dynamic_cast<TabLayoutItem*>(
+ GroupLayout()->ItemAt(index));
+ if (item)
+ return item->Parent();
+ return NULL;
+}
+
+
+int32
+TabContainerView::IndexOf(TabView* tab) const
+{
+ return GroupLayout()->IndexOfItem(tab->LayoutItem());
+}
+
+
+void
+TabContainerView::SelectTab(int32 index)
+{
+ TabView* tab = NULL;
+ TabLayoutItem* item = dynamic_cast<TabLayoutItem*>(
+ GroupLayout()->ItemAt(index));
+ if (item)
+ tab = item->Parent();
+
+ SelectTab(tab);
+}
+
+
+void
+TabContainerView::SelectTab(TabView* tab)
+{
+ if (tab == fSelectedTab)
+ return;
+
+ if (fSelectedTab)
+ fSelectedTab->SetIsFront(false);
+
+ fSelectedTab = tab;
+
+ if (fSelectedTab)
+ fSelectedTab->SetIsFront(true);
+
+ if (fController != NULL) {
+ int32 index = -1;
+ if (fSelectedTab != NULL)
+ index = GroupLayout()->IndexOfItem(tab->LayoutItem());
+
+ if (!tab->LayoutItem()->IsVisible()) {
+ SetFirstVisibleTabIndex(index);
+ }
+
+ fController->TabSelected(index);
+ }
+}
+
+
+void
+TabContainerView::SetTabLabel(int32 tabIndex, const char* label)
+{
+ TabLayoutItem* item = dynamic_cast<TabLayoutItem*>(
+ GroupLayout()->ItemAt(tabIndex));
+ if (item == NULL)
+ return;
+
+ item->Parent()->SetLabel(label);
+}
+
+
+void
+TabContainerView::SetFirstVisibleTabIndex(int32 index)
+{
+ if (index < 0)
+ index = 0;
+ if (index > MaxFirstVisibleTabIndex())
+ index = MaxFirstVisibleTabIndex();
+ if (fFirstVisibleTabIndex == index)
+ return;
+
+ fFirstVisibleTabIndex = index;
+
+ _UpdateTabVisibility();
+}
+
+
+int32
+TabContainerView::FirstVisibleTabIndex() const
+{
+ return fFirstVisibleTabIndex;
+}
+
+
+int32
+TabContainerView::MaxFirstVisibleTabIndex() const
+{
+ float availableWidth = _AvailableWidthForTabs();
+ if (availableWidth < 0)
+ return 0;
+ float visibleTabsWidth = 0;
+
+ BGroupLayout* layout = GroupLayout();
+ int32 i = layout->CountItems() - 2;
+ for (; i >= 0; i--) {
+ TabLayoutItem* item = dynamic_cast<TabLayoutItem*>(
+ layout->ItemAt(i));
+ if (item == NULL)
+ continue;
+
+ float itemWidth = item->MinSize().width;
+ if (availableWidth >= visibleTabsWidth + itemWidth)
+ visibleTabsWidth += itemWidth;
+ else {
+ // The tab before this tab is the last one that can be visible.
+ return i + 1;
+ }
+ }
+
+ return 0;
+}
+
+
+bool
+TabContainerView::CanScrollLeft() const
+{
+ return fFirstVisibleTabIndex < MaxFirstVisibleTabIndex();
+}
+
+
+bool
+TabContainerView::CanScrollRight() const
+{
+ BGroupLayout* layout = GroupLayout();
+ int32 count = layout->CountItems() - 1;
+ if (count > 0) {
+ TabLayoutItem* item = dynamic_cast<TabLayoutItem*>(
+ layout->ItemAt(count - 1));
+ return !item->IsVisible();
+ }
+ return false;
+}
+
+
+// #pragma mark -
+
+
+TabView*
+TabContainerView::_TabAt(const BPoint& where) const
+{
+ BGroupLayout* layout = GroupLayout();
+ int32 count = layout->CountItems() - 1;
+ for (int32 i = 0; i < count; i++) {
+ TabLayoutItem* item = dynamic_cast<TabLayoutItem*>(layout->ItemAt(i));
+ if (item == NULL || !item->IsVisible())
+ continue;
+ // Account for the fact that the tab frame does not contain the
+ // visible bottom border.
+ BRect frame = item->Frame();
+ frame.bottom++;
+ if (frame.Contains(where))
+ return item->Parent();
+ }
+ return NULL;
+}
+
+
+void
+TabContainerView::_MouseMoved(BPoint where, uint32 _transit,
+ const BMessage* dragMessage)
+{
+ TabView* tab = _TabAt(where);
+ if (fMouseDown) {
+ uint32 transit = tab == fLastMouseEventTab
+ ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
+ if (fLastMouseEventTab)
+ fLastMouseEventTab->MouseMoved(where, transit, dragMessage);
+ return;
+ }
+
+ if (fLastMouseEventTab != NULL && fLastMouseEventTab == tab)
+ fLastMouseEventTab->MouseMoved(where, B_INSIDE_VIEW, dragMessage);
+ else {
+ if (fLastMouseEventTab)
+ fLastMouseEventTab->MouseMoved(where, B_EXITED_VIEW, dragMessage);
+ fLastMouseEventTab = tab;
+ if (fLastMouseEventTab)
+ fLastMouseEventTab->MouseMoved(where, B_ENTERED_VIEW, dragMessage);
+ else
+ fController->SetToolTip("Double-click or middle-click to open new tab.");
+ }
+}
+
+
+void
+TabContainerView::_ValidateTabVisibility()
+{
+ if (fFirstVisibleTabIndex > MaxFirstVisibleTabIndex())
+ SetFirstVisibleTabIndex(MaxFirstVisibleTabIndex());
+ else
+ _UpdateTabVisibility();
+}
+
+
+void
+TabContainerView::_UpdateTabVisibility()
+{
+ float availableWidth = _AvailableWidthForTabs();
+ if (availableWidth < 0)
+ return;
+ float visibleTabsWidth = 0;
+
+ bool canScrollTabsLeft = fFirstVisibleTabIndex > 0;
+ bool canScrollTabsRight = false;
+
+ BGroupLayout* layout = GroupLayout();
+ int32 count = layout->CountItems() - 1;
+ for (int32 i = 0; i < count; i++) {
+ TabLayoutItem* item = dynamic_cast<TabLayoutItem*>(
+ layout->ItemAt(i));
+ if (i < fFirstVisibleTabIndex)
+ item->SetVisible(false);
+ else {
+ float itemWidth = item->MinSize().width;
+ bool visible = availableWidth >= visibleTabsWidth + itemWidth;
+ item->SetVisible(visible && !canScrollTabsRight);
+ visibleTabsWidth += itemWidth;
+ if (!visible)
+ canScrollTabsRight = true;
+ }
+ }
+ fController->UpdateTabScrollability(canScrollTabsLeft, canScrollTabsRight);
+}
+
+
+float
+TabContainerView::_AvailableWidthForTabs() const
+{
+ float width = Bounds().Width() - 10;
+ // TODO: Don't really know why -10 is needed above.
+
+ float left;
+ float right;
+ GroupLayout()->GetInsets(&left, NULL, &right, NULL);
+ width -= left + right;
+
+ return width;
+}
+
+
+void
+TabContainerView::_SendFakeMouseMoved()
+{
+ BPoint where;
+ uint32 buttons;
+ GetMouse(&where, &buttons, false);
+ if (Bounds().Contains(where))
+ _MouseMoved(where, B_INSIDE_VIEW, NULL);
+}
+
diff --git a/src/apps/webpositive/tabview/TabContainerView.h b/src/apps/webpositive/tabview/TabContainerView.h
new file mode 100644
index 0000000000..a5a7ee04fa
--- /dev/null
+++ b/src/apps/webpositive/tabview/TabContainerView.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+#ifndef TAB_CONTAINER_VIEW_H
+#define TAB_CONTAINER_VIEW_H
+
+#include <GroupView.h>
+
+
+class TabView;
+
+
+class TabContainerView : public BGroupView {
+public:
+ class Controller {
+ public:
+ virtual void TabSelected(int32 tabIndex) = 0;
+ virtual bool HasFrames() = 0;
+ virtual TabView* CreateTabView() = 0;
+ virtual void DoubleClickOutsideTabs() = 0;
+ virtual void UpdateTabScrollability(bool canScrollLeft,
+ bool canScrollRight) = 0;
+ virtual void SetToolTip(const BString& text) = 0;
+ };
+
+public:
+ TabContainerView(Controller* controller);
+ virtual ~TabContainerView();
+
+ virtual BSize MinSize();
+
+ virtual void MessageReceived(BMessage*);
+
+ virtual void Draw(BRect updateRect);
+
+ virtual void MouseDown(BPoint where);
+ virtual void MouseUp(BPoint where);
+ virtual void MouseMoved(BPoint where, uint32 transit,
+ const BMessage* dragMessage);
+
+ virtual void DoLayout();
+
+ void AddTab(const char* label, int32 index = -1);
+ void AddTab(TabView* tab, int32 index = -1);
+ TabView* RemoveTab(int32 index);
+ TabView* TabAt(int32 index) const;
+
+ int32 IndexOf(TabView* tab) const;
+
+ void SelectTab(int32 tabIndex);
+ void SelectTab(TabView* tab);
+
+ void SetTabLabel(int32 tabIndex, const char* label);
+
+ void SetFirstVisibleTabIndex(int32 index);
+ int32 FirstVisibleTabIndex() const;
+ int32 MaxFirstVisibleTabIndex() const;
+
+ bool CanScrollLeft() const;
+ bool CanScrollRight() const;
+
+private:
+ TabView* _TabAt(const BPoint& where) const;
+ void _MouseMoved(BPoint where, uint32 transit,
+ const BMessage* dragMessage);
+ void _ValidateTabVisibility();
+ void _UpdateTabVisibility();
+ float _AvailableWidthForTabs() const;
+ void _SendFakeMouseMoved();
+
+private:
+ TabView* fLastMouseEventTab;
+ bool fMouseDown;
+ uint32 fClickCount;
+ TabView* fSelectedTab;
+ Controller* fController;
+ int32 fFirstVisibleTabIndex;
+};
+
+#endif // TAB_CONTAINER_VIEW_H
diff --git a/src/apps/webpositive/tabview/TabManager.cpp b/src/apps/webpositive/tabview/TabManager.cpp
new file mode 100644
index 0000000000..f25b67769d
--- /dev/null
+++ b/src/apps/webpositive/tabview/TabManager.cpp
@@ -0,0 +1,940 @@
+/*
+ * Copyright (C) 2010 Rene Gollent <rene@gollent.com>
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "TabManager.h"
+
+#include <stdio.h>
+
+#include <Application.h>
+#include <AbstractLayoutItem.h>
+#include <Bitmap.h>
+#include <Button.h>
+#include <CardLayout.h>
+#include <ControlLook.h>
+#include <GroupView.h>
+#include <MenuBar.h>
+#include <MenuItem.h>
+#include <PopUpMenu.h>
+#include <Rect.h>
+#include <SpaceLayoutItem.h>
+#include <Window.h>
+
+#include "TabContainerView.h"
+#include "TabView.h"
+
+
+const static BString kEmptyString;
+
+
+// #pragma mark - Helper classes
+
+
+class TabButton : public BButton {
+public:
+ TabButton(BMessage* message)
+ : BButton("", message)
+ {
+ }
+
+ virtual BSize MinSize()
+ {
+ return BSize(12, 12);
+ }
+
+ virtual BSize MaxSize()
+ {
+ return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
+ }
+
+ virtual BSize PreferredSize()
+ {
+ return MinSize();
+ }
+
+ virtual void Draw(BRect updateRect)
+ {
+ BRect bounds(Bounds());
+ rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
+ SetHighColor(tint_color(base, B_DARKEN_2_TINT));
+ StrokeLine(bounds.LeftBottom(), bounds.RightBottom());
+ bounds.bottom--;
+ uint32 flags = be_control_look->Flags(this);
+ uint32 borders = BControlLook::B_TOP_BORDER
+ | BControlLook::B_BOTTOM_BORDER;
+ be_control_look->DrawInactiveTab(this, bounds, updateRect, base,
+ 0, borders);
+ if (IsEnabled()) {
+ rgb_color button = tint_color(base, 1.07);
+ be_control_look->DrawButtonBackground(this, bounds, updateRect,
+ button, flags, 0);
+ }
+
+ bounds.left = (bounds.left + bounds.right) / 2 - 6;
+ bounds.top = (bounds.top + bounds.bottom) / 2 - 6;
+ bounds.right = bounds.left + 12;
+ bounds.bottom = bounds.top + 12;
+ DrawSymbol(bounds, updateRect, base);
+ }
+
+ virtual void DrawSymbol(BRect frame, const BRect& updateRect,
+ const rgb_color& base)
+ {
+ }
+};
+
+
+class ScrollLeftTabButton : public TabButton {
+public:
+ ScrollLeftTabButton(BMessage* message)
+ : TabButton(message)
+ {
+ }
+
+ virtual void DrawSymbol(BRect frame, const BRect& updateRect,
+ const rgb_color& base)
+ {
+ float tint = IsEnabled() ? B_DARKEN_4_TINT : B_DARKEN_1_TINT;
+ be_control_look->DrawArrowShape(this, frame, updateRect,
+ base, BControlLook::B_LEFT_ARROW, 0, tint);
+ }
+};
+
+
+class ScrollRightTabButton : public TabButton {
+public:
+ ScrollRightTabButton(BMessage* message)
+ : TabButton(message)
+ {
+ }
+
+ virtual void DrawSymbol(BRect frame, const BRect& updateRect,
+ const rgb_color& base)
+ {
+ frame.OffsetBy(1, 0);
+ float tint = IsEnabled() ? B_DARKEN_4_TINT : B_DARKEN_1_TINT;
+ be_control_look->DrawArrowShape(this, frame, updateRect,
+ base, BControlLook::B_RIGHT_ARROW, 0, tint);
+ }
+};
+
+
+class NewTabButton : public TabButton {
+public:
+ NewTabButton(BMessage* message)
+ : TabButton(message)
+ {
+ SetToolTip("New tab (Cmd-T)");
+ }
+
+ virtual BSize MinSize()
+ {
+ return BSize(18, 12);
+ }
+
+ virtual void DrawSymbol(BRect frame, const BRect& updateRect,
+ const rgb_color& base)
+ {
+ SetHighColor(tint_color(base, B_DARKEN_4_TINT));
+ float inset = 3;
+ frame.InsetBy(2, 2);
+ frame.top++;
+ frame.left++;
+ FillRoundRect(BRect(frame.left, frame.top + inset,
+ frame.right, frame.bottom - inset), 1, 1);
+ FillRoundRect(BRect(frame.left + inset, frame.top,
+ frame.right - inset, frame.bottom), 1, 1);
+ }
+};
+
+
+class TabMenuTabButton : public TabButton {
+public:
+ TabMenuTabButton(BMessage* message)
+ : TabButton(message)
+ {
+ }
+
+ virtual BSize MinSize()
+ {
+ return BSize(18, 12);
+ }
+
+ virtual void DrawSymbol(BRect frame, const BRect& updateRect,
+ const rgb_color& base)
+ {
+ be_control_look->DrawArrowShape(this, frame, updateRect,
+ base, BControlLook::B_DOWN_ARROW, 0, B_DARKEN_4_TINT);
+ }
+
+ virtual void MouseDown(BPoint point)
+ {
+ if (!IsEnabled())
+ return;
+
+ // Invoke must be called before setting B_CONTROL_ON
+ // for the button to stay "down"
+ Invoke();
+ SetValue(B_CONTROL_ON);
+ }
+
+ virtual void MouseUp(BPoint point)
+ {
+ // Do nothing
+ }
+
+ void MenuClosed()
+ {
+ SetValue(B_CONTROL_OFF);
+ }
+};
+
+
+enum {
+ MSG_SCROLL_TABS_LEFT = 'stlt',
+ MSG_SCROLL_TABS_RIGHT = 'strt',
+ MSG_OPEN_TAB_MENU = 'otmn'
+};
+
+
+class TabContainerGroup : public BGroupView {
+public:
+ TabContainerGroup(TabContainerView* tabContainerView)
+ :
+ BGroupView(B_HORIZONTAL, 0.0),
+ fTabContainerView(tabContainerView),
+ fScrollLeftTabButton(NULL),
+ fScrollRightTabButton(NULL),
+ fTabMenuButton(NULL)
+ {
+ }
+
+ virtual void AttachedToWindow()
+ {
+ if (fScrollLeftTabButton != NULL)
+ fScrollLeftTabButton->SetTarget(this);
+ if (fScrollRightTabButton != NULL)
+ fScrollRightTabButton->SetTarget(this);
+ if (fTabMenuButton != NULL)
+ fTabMenuButton->SetTarget(this);
+ }
+
+ virtual void MessageReceived(BMessage* message)
+ {
+ switch (message->what) {
+ case MSG_SCROLL_TABS_LEFT:
+ fTabContainerView->SetFirstVisibleTabIndex(
+ fTabContainerView->FirstVisibleTabIndex() - 1);
+ break;
+ case MSG_SCROLL_TABS_RIGHT:
+ fTabContainerView->SetFirstVisibleTabIndex(
+ fTabContainerView->FirstVisibleTabIndex() + 1);
+ break;
+ case MSG_OPEN_TAB_MENU:
+ {
+ BPopUpMenu* tabMenu = new BPopUpMenu("tab menu", true, false);
+ int tabCount = fTabContainerView->GetLayout()->CountItems();
+ for (int i = 0; i < tabCount; i++) {
+ TabView* tab = fTabContainerView->TabAt(i);
+ if (tab) {
+ BMenuItem* item = new BMenuItem(tab->Label(), NULL);
+ tabMenu->AddItem(item);
+ if (tab->IsFront())
+ item->SetMarked(true);
+ }
+ }
+
+ // Force layout to get the final menu size. InvalidateLayout()
+ // did not seem to work here.
+ tabMenu->AttachedToWindow();
+ BRect buttonFrame = fTabMenuButton->Frame();
+ BRect menuFrame = tabMenu->Frame();
+ BPoint openPoint = ConvertToScreen(buttonFrame.LeftBottom());
+ // Open with the right side of the menu aligned with the right
+ // side of the button and a little below.
+ openPoint.x -= menuFrame.Width() - buttonFrame.Width();
+ openPoint.y += 2;
+
+ BMenuItem *selected = tabMenu->Go(openPoint, false, false,
+ ConvertToScreen(buttonFrame));
+ if (selected) {
+ selected->SetMarked(true);
+ int32 index = tabMenu->IndexOf(selected);
+ if (index != B_ERROR)
+ fTabContainerView->SelectTab(index);
+ }
+ fTabMenuButton->MenuClosed();
+ delete tabMenu;
+
+ break;
+ }
+ default:
+ BGroupView::MessageReceived(message);
+ break;
+ }
+ }
+
+ void AddScrollLeftButton(TabButton* button)
+ {
+ fScrollLeftTabButton = button;
+ GroupLayout()->AddView(button, 0.0f);
+ }
+
+ void AddScrollRightButton(TabButton* button)
+ {
+ fScrollRightTabButton = button;
+ GroupLayout()->AddView(button, 0.0f);
+ }
+
+ void AddTabMenuButton(TabMenuTabButton* button)
+ {
+ fTabMenuButton = button;
+ GroupLayout()->AddView(button, 0.0f);
+ }
+
+ void EnableScrollButtons(bool canScrollLeft, bool canScrollRight)
+ {
+ fScrollLeftTabButton->SetEnabled(canScrollLeft);
+ fScrollRightTabButton->SetEnabled(canScrollRight);
+ if (!canScrollLeft && !canScrollRight) {
+ // hide scroll buttons
+ } else {
+ // show scroll buttons
+ }
+ }
+
+private:
+ TabContainerView* fTabContainerView;
+ TabButton* fScrollLeftTabButton;
+ TabButton* fScrollRightTabButton;
+ TabMenuTabButton* fTabMenuButton;
+};
+
+
+class TabButtonContainer : public BGroupView {
+public:
+ TabButtonContainer()
+ :
+ BGroupView(B_HORIZONTAL, 0.0)
+ {
+ SetFlags(Flags() | B_WILL_DRAW);
+ SetViewColor(B_TRANSPARENT_COLOR);
+ SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
+ GroupLayout()->SetInsets(0, 6, 0, 0);
+ }
+
+ virtual void Draw(BRect updateRect)
+ {
+ BRect bounds(Bounds());
+ rgb_color base = LowColor();
+ be_control_look->DrawInactiveTab(this, bounds, updateRect,
+ base, 0, BControlLook::B_TOP_BORDER);
+ }
+};
+
+
+class TabManagerController : public TabContainerView::Controller {
+public:
+ TabManagerController(TabManager* manager);
+
+ virtual ~TabManagerController();
+
+ virtual void TabSelected(int32 index)
+ {
+ fManager->SelectTab(index);
+ }
+
+ virtual bool HasFrames()
+ {
+ return false;
+ }
+
+ virtual TabView* CreateTabView();
+
+ virtual void DoubleClickOutsideTabs();
+
+ virtual void UpdateTabScrollability(bool canScrollLeft,
+ bool canScrollRight)
+ {
+ fTabContainerGroup->EnableScrollButtons(canScrollLeft, canScrollRight);
+ }
+
+ virtual void SetToolTip(const BString& text)
+ {
+ if (fCurrentToolTip == text)
+ return;
+ fCurrentToolTip = text;
+ fManager->GetTabContainerView()->HideToolTip();
+ fManager->GetTabContainerView()->SetToolTip(
+ reinterpret_cast<BToolTip*>(NULL));
+ fManager->GetTabContainerView()->SetToolTip(fCurrentToolTip.String());
+ }
+
+ void CloseTab(int32 index);
+
+ void SetCloseButtonsAvailable(bool available)
+ {
+ fCloseButtonsAvailable = available;
+ }
+
+ bool CloseButtonsAvailable() const
+ {
+ return fCloseButtonsAvailable;
+ }
+
+ void SetDoubleClickOutsideTabsMessage(const BMessage& message,
+ const BMessenger& target);
+
+ void SetTabContainerGroup(TabContainerGroup* tabContainerGroup)
+ {
+ fTabContainerGroup = tabContainerGroup;
+ }
+
+private:
+ TabManager* fManager;
+ TabContainerGroup* fTabContainerGroup;
+ bool fCloseButtonsAvailable;
+ BMessage* fDoubleClickOutsideTabsMessage;
+ BMessenger fTarget;
+ BString fCurrentToolTip;
+};
+
+
+// #pragma mark - WebTabView
+
+
+class WebTabView : public TabView {
+public:
+ WebTabView(TabManagerController* controller);
+ ~WebTabView();
+
+ virtual BSize MaxSize();
+
+ virtual void DrawContents(BView* owner, BRect frame, const BRect& updateRect,
+ bool isFirst, bool isLast, bool isFront);
+
+ virtual void MouseDown(BPoint where, uint32 buttons);
+ virtual void MouseUp(BPoint where);
+ virtual void MouseMoved(BPoint where, uint32 transit,
+ const BMessage* dragMessage);
+
+ void SetIcon(const BBitmap* icon);
+
+private:
+ void _DrawCloseButton(BView* owner, BRect& frame, const BRect& updateRect,
+ bool isFirst, bool isLast, bool isFront);
+ BRect _CloseRectFrame(BRect frame) const;
+
+private:
+ BBitmap* fIcon;
+ TabManagerController* fController;
+ bool fOverCloseRect;
+ bool fClicked;
+ bool fCloseOnMouseUp;
+};
+
+
+WebTabView::WebTabView(TabManagerController* controller)
+ :
+ TabView(),
+ fIcon(NULL),
+ fController(controller),
+ fOverCloseRect(false),
+ fClicked(false),
+ fCloseOnMouseUp(false)
+{
+}
+
+
+WebTabView::~WebTabView()
+{
+ delete fIcon;
+}
+
+
+static const int kIconSize = 18;
+static const int kIconInset = 3;
+
+
+BSize
+WebTabView::MaxSize()
+{
+ // Account for icon.
+ BSize size(TabView::MaxSize());
+ size.height = max_c(size.height, kIconSize + kIconInset * 2);
+ if (fIcon)
+ size.width += kIconSize + kIconInset * 2;
+ // Account for close button.
+ size.width += size.height;
+ return size;
+}
+
+
+void
+WebTabView::DrawContents(BView* owner, BRect frame, const BRect& updateRect,
+ bool isFirst, bool isLast, bool isFront)
+{
+ if (fController->CloseButtonsAvailable())
+ _DrawCloseButton(owner, frame, updateRect, isFirst, isLast, isFront);
+
+ if (fIcon) {
+ BRect iconBounds(0, 0, kIconSize - 1, kIconSize - 1);
+ // clip to icon bounds, if they are smaller
+ if (iconBounds.Contains(fIcon->Bounds()))
+ iconBounds = fIcon->Bounds();
+ else {
+ // Try to scale down the icon by an even factor so the
+ // final size is between 14 and 18 pixel size. If this fails,
+ // the icon will simply be displayed at 18x18.
+ float scale = 2;
+ while ((fIcon->Bounds().Width() + 1) / scale > kIconSize)
+ scale *= 2;
+ if ((fIcon->Bounds().Width() + 1) / scale >= kIconSize - 4
+ && (fIcon->Bounds().Height() + 1) / scale >= kIconSize - 4
+ && (fIcon->Bounds().Height() + 1) / scale <= kIconSize) {
+ iconBounds.right = (fIcon->Bounds().Width() + 1) / scale - 1;
+ iconBounds.bottom = (fIcon->Bounds().Height() + 1) / scale - 1;
+ }
+ }
+ BPoint iconPos(frame.left + kIconInset - 1,
+ frame.top + floorf((frame.Height() - iconBounds.Height()) / 2));
+ iconBounds.OffsetTo(iconPos);
+ owner->SetDrawingMode(B_OP_ALPHA);
+ owner->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
+ owner->DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
+ B_FILTER_BITMAP_BILINEAR);
+ owner->SetDrawingMode(B_OP_COPY);
+ frame.left = frame.left + kIconSize + kIconInset * 2;
+ }
+
+ TabView::DrawContents(owner, frame, updateRect, isFirst, isLast, isFront);
+}
+
+
+void
+WebTabView::MouseDown(BPoint where, uint32 buttons)
+{
+ if (buttons & B_TERTIARY_MOUSE_BUTTON) {
+ fCloseOnMouseUp = true;
+ return;
+ }
+
+ BRect closeRect = _CloseRectFrame(Frame());
+ if (!fController->CloseButtonsAvailable() || !closeRect.Contains(where)) {
+ TabView::MouseDown(where, buttons);
+ return;
+ }
+
+ fClicked = true;
+ ContainerView()->Invalidate(closeRect);
+}
+
+
+void
+WebTabView::MouseUp(BPoint where)
+{
+ if (!fClicked && !fCloseOnMouseUp) {
+ TabView::MouseUp(where);
+ return;
+ }
+
+ if (fCloseOnMouseUp && Frame().Contains(where)) {
+ fCloseOnMouseUp = false;
+ fController->CloseTab(ContainerView()->IndexOf(this));
+ // Probably this object is toast now, better return here.
+ return;
+ }
+
+ fClicked = false;
+ fCloseOnMouseUp = false;
+
+ if (_CloseRectFrame(Frame()).Contains(where))
+ fController->CloseTab(ContainerView()->IndexOf(this));
+}
+
+
+void
+WebTabView::MouseMoved(BPoint where, uint32 transit,
+ const BMessage* dragMessage)
+{
+ if (fController->CloseButtonsAvailable()) {
+ BRect closeRect = _CloseRectFrame(Frame());
+ bool overCloseRect = closeRect.Contains(where);
+ if (overCloseRect != fOverCloseRect) {
+ fOverCloseRect = overCloseRect;
+ ContainerView()->Invalidate(closeRect);
+ }
+ }
+
+ fController->SetToolTip(Label());
+
+ TabView::MouseMoved(where, transit, dragMessage);
+}
+
+
+void
+WebTabView::SetIcon(const BBitmap* icon)
+{
+ delete fIcon;
+ if (icon)
+ fIcon = new BBitmap(icon);
+ else
+ fIcon = NULL;
+ LayoutItem()->InvalidateLayout();
+}
+
+
+BRect
+WebTabView::_CloseRectFrame(BRect frame) const
+{
+ frame.left = frame.right - frame.Height();
+ return frame;
+}
+
+
+void WebTabView::_DrawCloseButton(BView* owner, BRect& frame,
+ const BRect& updateRect, bool isFirst, bool isLast, bool isFront)
+{
+ BRect closeRect = _CloseRectFrame(frame);
+ frame.right = closeRect.left - be_control_look->DefaultLabelSpacing();
+
+ closeRect.left = (closeRect.left + closeRect.right) / 2 - 3;
+ closeRect.right = closeRect.left + 6;
+ closeRect.top = (closeRect.top + closeRect.bottom) / 2 - 3;
+ closeRect.bottom = closeRect.top + 6;
+
+ rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
+ float tint = B_DARKEN_1_TINT;
+ if (!IsFront()) {
+ base = tint_color(base, tint);
+ tint *= 1.02;
+ }
+
+ if (fOverCloseRect)
+ tint *= 1.2;
+
+ if (fClicked && fOverCloseRect) {
+ BRect buttonRect(closeRect.InsetByCopy(-4, -4));
+ be_control_look->DrawButtonFrame(owner, buttonRect, updateRect,
+ base, base,
+ BControlLook::B_ACTIVATED | BControlLook::B_BLEND_FRAME);
+ be_control_look->DrawButtonBackground(owner, buttonRect, updateRect,
+ base, BControlLook::B_ACTIVATED);
+ tint *= 1.2;
+ closeRect.OffsetBy(1, 1);
+ }
+
+ base = tint_color(base, tint);
+ owner->SetHighColor(base);
+ owner->SetPenSize(2);
+ owner->StrokeLine(closeRect.LeftTop(), closeRect.RightBottom());
+ owner->StrokeLine(closeRect.LeftBottom(), closeRect.RightTop());
+ owner->SetPenSize(1);
+}
+
+
+// #pragma mark - TabManagerController
+
+
+TabManagerController::TabManagerController(TabManager* manager)
+ :
+ fManager(manager),
+ fTabContainerGroup(NULL),
+ fCloseButtonsAvailable(false),
+ fDoubleClickOutsideTabsMessage(NULL)
+{
+}
+
+
+TabManagerController::~TabManagerController()
+{
+ delete fDoubleClickOutsideTabsMessage;
+}
+
+
+TabView*
+TabManagerController::CreateTabView()
+{
+ return new WebTabView(this);
+}
+
+
+void
+TabManagerController::DoubleClickOutsideTabs()
+{
+ fTarget.SendMessage(fDoubleClickOutsideTabsMessage);
+}
+
+
+void
+TabManagerController::CloseTab(int32 index)
+{
+ fManager->CloseTab(index);
+}
+
+
+void
+TabManagerController::SetDoubleClickOutsideTabsMessage(const BMessage& message,
+ const BMessenger& target)
+{
+ delete fDoubleClickOutsideTabsMessage;
+ fDoubleClickOutsideTabsMessage = new BMessage(message);
+ fTarget = target;
+}
+
+
+// #pragma mark - TabManager
+
+
+TabManager::TabManager(const BMessenger& target, BMessage* newTabMessage)
+ :
+ fController(new TabManagerController(this)),
+ fTarget(target)
+{
+ fController->SetDoubleClickOutsideTabsMessage(*newTabMessage,
+ be_app_messenger);
+
+ fContainerView = new BView("web view container", 0);
+ fCardLayout = new BCardLayout();
+ fContainerView->SetLayout(fCardLayout);
+
+ fTabContainerView = new TabContainerView(fController);
+ fTabContainerGroup = new TabContainerGroup(fTabContainerView);
+ fTabContainerGroup->GroupLayout()->SetInsets(0, 3, 0, 0);
+
+ fController->SetTabContainerGroup(fTabContainerGroup);
+
+#if INTEGRATE_MENU_INTO_TAB_BAR
+ fMenu = new BMenu("Menu");
+ BMenuBar* menuBar = new BMenuBar("Menu bar");
+ menuBar->AddItem(fMenu);
+ TabButtonContainer* menuBarContainer = new TabButtonContainer();
+ menuBarContainer->GroupLayout()->AddView(menuBar);
+ fTabContainerGroup->GroupLayout()->AddView(menuBarContainer, 0.0f);
+#endif
+
+ fTabContainerGroup->GroupLayout()->AddView(fTabContainerView);
+ fTabContainerGroup->AddScrollLeftButton(new ScrollLeftTabButton(
+ new BMessage(MSG_SCROLL_TABS_LEFT)));
+ fTabContainerGroup->AddScrollRightButton(new ScrollRightTabButton(
+ new BMessage(MSG_SCROLL_TABS_RIGHT)));
+ NewTabButton* newTabButton = new NewTabButton(newTabMessage);
+ newTabButton->SetTarget(be_app);
+ fTabContainerGroup->GroupLayout()->AddView(newTabButton, 0.0f);
+ fTabContainerGroup->AddTabMenuButton(new TabMenuTabButton(
+ new BMessage(MSG_OPEN_TAB_MENU)));
+}
+
+
+TabManager::~TabManager()
+{
+ delete fController;
+}
+
+
+void
+TabManager::SetTarget(const BMessenger& target)
+{
+ fTarget = target;
+}
+
+
+const BMessenger&
+TabManager::Target() const
+{
+ return fTarget;
+}
+
+
+#if INTEGRATE_MENU_INTO_TAB_BAR
+BMenu*
+TabManager::Menu() const
+{
+ return fMenu;
+}
+#endif
+
+
+BView*
+TabManager::TabGroup() const
+{
+ return fTabContainerGroup;
+}
+
+
+BView*
+TabManager::GetTabContainerView() const
+{
+ return fTabContainerView;
+}
+
+
+BView*
+TabManager::ContainerView() const
+{
+ return fContainerView;
+}
+
+
+BView*
+TabManager::ViewForTab(int32 tabIndex) const
+{
+ BLayoutItem* item = fCardLayout->ItemAt(tabIndex);
+ if (item != NULL)
+ return item->View();
+ return NULL;
+}
+
+
+int32
+TabManager::TabForView(const BView* containedView) const
+{
+ int32 count = fCardLayout->CountItems();
+ for (int32 i = 0; i < count; i++) {
+ BLayoutItem* item = fCardLayout->ItemAt(i);
+ if (item->View() == containedView)
+ return i;
+ }
+ return -1;
+}
+
+
+bool
+TabManager::HasView(const BView* containedView) const
+{
+ return TabForView(containedView) >= 0;
+}
+
+
+void
+TabManager::SelectTab(int32 tabIndex)
+{
+ fCardLayout->SetVisibleItem(tabIndex);
+ fTabContainerView->SelectTab(tabIndex);
+
+ BMessage message(TAB_CHANGED);
+ message.AddInt32("tab index", tabIndex);
+ fTarget.SendMessage(&message);
+}
+
+
+void
+TabManager::SelectTab(const BView* containedView)
+{
+ int32 tabIndex = TabForView(containedView);
+ if (tabIndex > 0)
+ SelectTab(tabIndex);
+}
+
+
+int32
+TabManager::SelectedTabIndex() const
+{
+ return fCardLayout->VisibleIndex();
+}
+
+
+void
+TabManager::CloseTab(int32 tabIndex)
+{
+ BMessage message(CLOSE_TAB);
+ message.AddInt32("tab index", tabIndex);
+ fTarget.SendMessage(&message);
+}
+
+
+void
+TabManager::AddTab(BView* view, const char* label, int32 index)
+{
+ fTabContainerView->AddTab(label, index);
+ fCardLayout->AddView(index, view);
+}
+
+
+BView*
+TabManager::RemoveTab(int32 index)
+{
+ // It's important to remove the view first, since
+ // removing the tab will preliminary mess with the selected tab
+ // and then item count of card layout and tab container will not
+ // match yet.
+ BLayoutItem* item = fCardLayout->RemoveItem(index);
+ if (item == NULL)
+ return NULL;
+
+ TabView* tab = fTabContainerView->RemoveTab(index);
+ delete tab;
+
+ BView* view = item->View();
+ delete item;
+ return view;
+}
+
+
+int32
+TabManager::CountTabs() const
+{
+ return fCardLayout->CountItems();
+}
+
+
+void
+TabManager::SetTabLabel(int32 tabIndex, const char* label)
+{
+ fTabContainerView->SetTabLabel(tabIndex, label);
+}
+
+const BString&
+TabManager::TabLabel(int32 tabIndex)
+{
+ TabView* tab = fTabContainerView->TabAt(tabIndex);
+ if (tab)
+ return tab->Label();
+ else
+ return kEmptyString;
+}
+
+void
+TabManager::SetTabIcon(const BView* containedView, const BBitmap* icon)
+{
+ WebTabView* tab = dynamic_cast<WebTabView*>(fTabContainerView->TabAt(
+ TabForView(containedView)));
+ if (tab)
+ tab->SetIcon(icon);
+}
+
+
+void
+TabManager::SetCloseButtonsAvailable(bool available)
+{
+ if (available == fController->CloseButtonsAvailable())
+ return;
+ fController->SetCloseButtonsAvailable(available);
+ fTabContainerView->Invalidate();
+}
+
+
diff --git a/src/apps/webpositive/tabview/TabManager.h b/src/apps/webpositive/tabview/TabManager.h
new file mode 100644
index 0000000000..97cd65c898
--- /dev/null
+++ b/src/apps/webpositive/tabview/TabManager.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+
+#ifndef TAB_MANAGER_H
+#define TAB_MANAGER_H
+
+#include <Messenger.h>
+#include <TabView.h>
+
+enum {
+ TAB_CHANGED = 'tcha',
+ CLOSE_TAB = 'cltb'
+};
+
+class BBitmap;
+class BCardLayout;
+class BGroupView;
+class BMenu;
+class TabContainerGroup;
+class TabContainerView;
+class TabManagerController;
+
+class TabManager {
+public:
+ TabManager(const BMessenger& target,
+ BMessage* newTabMessage);
+ virtual ~TabManager();
+
+ void SetTarget(const BMessenger& target);
+ const BMessenger& Target() const;
+
+#if INTEGRATE_MENU_INTO_TAB_BAR
+ BMenu* Menu() const;
+#endif
+
+ BView* TabGroup() const;
+ BView* GetTabContainerView() const;
+ BView* ContainerView() const;
+
+ BView* ViewForTab(int32 tabIndex) const;
+ int32 TabForView(const BView* containedView) const;
+ bool HasView(const BView* containedView) const;
+
+ void SelectTab(int32 tabIndex);
+ void SelectTab(const BView* containedView);
+ int32 SelectedTabIndex() const;
+ void CloseTab(int32 tabIndex);
+
+ void AddTab(BView* view, const char* label,
+ int32 index = -1);
+ BView* RemoveTab(int32 index);
+ int32 CountTabs() const;
+
+ void SetTabLabel(int32 tabIndex, const char* label);
+ const BString& TabLabel(int32);
+ void SetTabIcon(const BView* containedView,
+ const BBitmap* icon);
+ void SetCloseButtonsAvailable(bool available);
+
+private:
+#if INTEGRATE_MENU_INTO_TAB_BAR
+ BMenu* fMenu;
+#endif
+ TabContainerGroup* fTabContainerGroup;
+ TabContainerView* fTabContainerView;
+ BView* fContainerView;
+ BCardLayout* fCardLayout;
+ TabManagerController* fController;
+
+ BMessenger fTarget;
+};
+
+#endif // TAB_MANAGER_H
diff --git a/src/apps/webpositive/tabview/TabView.cpp b/src/apps/webpositive/tabview/TabView.cpp
new file mode 100644
index 0000000000..3bb78ec185
--- /dev/null
+++ b/src/apps/webpositive/tabview/TabView.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2010 Rene Gollent <rene@gollent.com>
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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 "TabView.h"
+
+#include <stdio.h>
+
+#include <Application.h>
+#include <Bitmap.h>
+#include <Button.h>
+#include <CardLayout.h>
+#include <ControlLook.h>
+#include <GroupView.h>
+#include <MenuBar.h>
+#include <SpaceLayoutItem.h>
+#include <Window.h>
+
+#include "TabContainerView.h"
+
+
+// #pragma mark - TabView
+
+
+TabView::TabView()
+ :
+ fContainerView(NULL),
+ fLayoutItem(new TabLayoutItem(this)),
+ fLabel()
+{
+}
+
+
+TabView::~TabView()
+{
+ // The layout item is deleted for us by the layout which contains it.
+ if (!fContainerView)
+ delete fLayoutItem;
+}
+
+
+BSize
+TabView::MinSize()
+{
+ BSize size(MaxSize());
+ size.width = 60.0f;
+ return size;
+}
+
+
+BSize
+TabView::PreferredSize()
+{
+ return MaxSize();
+}
+
+
+BSize
+TabView::MaxSize()
+{
+ float extra = be_control_look->DefaultLabelSpacing();
+ float labelWidth = 300.0f;
+ return BSize(labelWidth, _LabelHeight() + extra);
+}
+
+
+void
+TabView::Draw(BRect updateRect)
+{
+ BRect frame(fLayoutItem->Frame());
+ if (fIsFront) {
+ // Extend the front tab outward left/right in order to merge
+ // the frames of adjacent tabs.
+ if (!fIsFirst)
+ frame.left--;
+ if (!fIsLast)
+ frame.right++;
+
+ frame.bottom++;
+ }
+ DrawBackground(fContainerView, frame, updateRect, fIsFirst, fIsLast,
+ fIsFront);
+ if (fIsFront) {
+ frame.top += 3.0f;
+ if (!fIsFirst)
+ frame.left++;
+ if (!fIsLast)
+ frame.right--;
+ } else
+ frame.top += 6.0f;
+ float spacing = be_control_look->DefaultLabelSpacing();
+ frame.InsetBy(spacing, spacing / 2);
+ DrawContents(fContainerView, frame, updateRect, fIsFirst, fIsLast,
+ fIsFront);
+}
+
+
+void
+TabView::DrawBackground(BView* owner, BRect frame, const BRect& updateRect,
+ bool isFirst, bool isLast, bool isFront)
+{
+ rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
+ uint32 borders = BControlLook::B_TOP_BORDER
+ | BControlLook::B_BOTTOM_BORDER;
+ if (isFirst)
+ borders |= BControlLook::B_LEFT_BORDER;
+ if (isLast)
+ borders |= BControlLook::B_RIGHT_BORDER;
+ if (isFront) {
+ be_control_look->DrawActiveTab(owner, frame, updateRect, base,
+ 0, borders);
+ } else {
+ be_control_look->DrawInactiveTab(owner, frame, updateRect, base,
+ 0, borders);
+ }
+}
+
+
+void
+TabView::DrawContents(BView* owner, BRect frame, const BRect& updateRect,
+ bool isFirst, bool isLast, bool isFront)
+{
+ rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
+ be_control_look->DrawLabel(owner, fLabel.String(), frame, updateRect,
+ base, 0, BAlignment(B_ALIGN_LEFT, B_ALIGN_MIDDLE));
+}
+
+
+void
+TabView::MouseDown(BPoint where, uint32 buttons)
+{
+ fContainerView->SelectTab(this);
+}
+
+
+void
+TabView::MouseUp(BPoint where)
+{
+}
+
+
+void
+TabView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
+{
+}
+
+
+void
+TabView::SetIsFront(bool isFront)
+{
+ Update(fIsFirst, fIsLast, isFront);
+}
+
+
+bool
+TabView::IsFront() const
+{
+ return fIsFront;
+}
+
+
+void
+TabView::SetIsLast(bool isLast)
+{
+ Update(fIsFirst, isLast, fIsFront);
+}
+
+
+void
+TabView::Update(bool isFirst, bool isLast, bool isFront)
+{
+ if (fIsFirst == isFirst && fIsLast == isLast && fIsFront == isFront)
+ return;
+ fIsFirst = isFirst;
+ fIsLast = isLast;
+ fIsFront = isFront;
+
+ fLayoutItem->InvalidateContainer();
+}
+
+
+void
+TabView::SetContainerView(TabContainerView* containerView)
+{
+ fContainerView = containerView;
+}
+
+
+TabContainerView*
+TabView::ContainerView() const
+{
+ return fContainerView;
+}
+
+
+BLayoutItem*
+TabView::LayoutItem() const
+{
+ return fLayoutItem;
+}
+
+
+void
+TabView::SetLabel(const char* label)
+{
+ if (fLabel == label)
+ return;
+ fLabel = label;
+ fLayoutItem->InvalidateLayout();
+}
+
+
+const BString&
+TabView::Label() const
+{
+ return fLabel;
+}
+
+
+BRect
+TabView::Frame() const
+{
+ return fLayoutItem->Frame();
+}
+
+
+float
+TabView::_LabelHeight() const
+{
+ font_height fontHeight;
+ fContainerView->GetFontHeight(&fontHeight);
+ return ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
+}
+
+
+// #pragma mark - TabLayoutItem
+
+
+TabLayoutItem::TabLayoutItem(TabView* parent)
+ :
+ fParent(parent),
+ fVisible(true)
+{
+}
+
+
+bool
+TabLayoutItem::IsVisible()
+{
+ return fVisible;
+}
+
+
+void
+TabLayoutItem::SetVisible(bool visible)
+{
+ if (fVisible == visible)
+ return;
+
+ fVisible = visible;
+
+ InvalidateContainer();
+ fParent->ContainerView()->InvalidateLayout();
+}
+
+
+BRect
+TabLayoutItem::Frame()
+{
+ return fFrame;
+}
+
+
+void
+TabLayoutItem::SetFrame(BRect frame)
+{
+ BRect dirty = fFrame;
+ fFrame = frame;
+ dirty = dirty | fFrame;
+ InvalidateContainer(dirty);
+}
+
+
+BView*
+TabLayoutItem::View()
+{
+ return NULL;
+}
+
+
+BSize
+TabLayoutItem::BaseMinSize()
+{
+ return fParent->MinSize();
+}
+
+
+BSize
+TabLayoutItem::BaseMaxSize()
+{
+ return fParent->MaxSize();
+}
+
+
+BSize
+TabLayoutItem::BasePreferredSize()
+{
+ return fParent->PreferredSize();
+}
+
+
+BAlignment
+TabLayoutItem::BaseAlignment()
+{
+ return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
+}
+
+
+TabView*
+TabLayoutItem::Parent() const
+{
+ return fParent;
+}
+
+
+void
+TabLayoutItem::InvalidateContainer()
+{
+ InvalidateContainer(Frame());
+}
+
+
+void
+TabLayoutItem::InvalidateContainer(BRect frame)
+{
+ // Invalidate more than necessary, to help the TabContainerView
+ // redraw the parts outside any tabs...
+ frame.bottom++;
+ frame.right++;
+ fParent->ContainerView()->Invalidate(frame);
+}
diff --git a/src/apps/webpositive/tabview/TabView.h b/src/apps/webpositive/tabview/TabView.h
new file mode 100644
index 0000000000..9f73c5a994
--- /dev/null
+++ b/src/apps/webpositive/tabview/TabView.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
+ *
+ * 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.
+ */
+#ifndef TAB_VIEW_H
+#define TAB_VIEW_H
+
+#include <AbstractLayoutItem.h>
+#include <Rect.h>
+#include <String.h>
+
+
+class BMessage;
+class BView;
+class TabContainerView;
+class TabLayoutItem;
+
+
+class TabView {
+public:
+ TabView();
+ virtual ~TabView();
+
+ virtual BSize MinSize();
+ virtual BSize PreferredSize();
+ virtual BSize MaxSize();
+
+ void Draw(BRect updateRect);
+ virtual void DrawBackground(BView* owner, BRect frame,
+ const BRect& updateRect, bool isFirst,
+ bool isLast, bool isFront);
+ virtual void DrawContents(BView* owner, BRect frame,
+ const BRect& updateRect, bool isFirst,
+ bool isLast, bool isFront);
+
+ virtual void MouseDown(BPoint where, uint32 buttons);
+ virtual void MouseUp(BPoint where);
+ virtual void MouseMoved(BPoint where, uint32 transit,
+ const BMessage* dragMessage);
+
+ void SetIsFront(bool isFront);
+ bool IsFront() const;
+ void SetIsLast(bool isLast);
+ virtual void Update(bool isFirst, bool isLast,
+ bool isFront);
+
+ BLayoutItem* LayoutItem() const;
+ void SetContainerView(
+ TabContainerView* containerView);
+ TabContainerView* ContainerView() const;
+
+ void SetLabel(const char* label);
+ const BString& Label() const;
+
+ BRect Frame() const;
+
+private:
+ float _LabelHeight() const;
+
+private:
+ TabContainerView* fContainerView;
+ TabLayoutItem* fLayoutItem;
+
+ BString fLabel;
+
+ bool fIsFirst;
+ bool fIsLast;
+ bool fIsFront;
+};
+
+
+class TabLayoutItem : public BAbstractLayoutItem {
+public:
+ TabLayoutItem(TabView* parent);
+
+ virtual bool IsVisible();
+ virtual void SetVisible(bool visible);
+
+ virtual BRect Frame();
+ virtual void SetFrame(BRect frame);
+
+ virtual BView* View();
+
+ virtual BSize BaseMinSize();
+ virtual BSize BaseMaxSize();
+ virtual BSize BasePreferredSize();
+ virtual BAlignment BaseAlignment();
+
+ TabView* Parent() const;
+ void InvalidateContainer();
+ void InvalidateContainer(BRect frame);
+private:
+ TabView* fParent;
+ BRect fFrame;
+ bool fVisible;
+};
+
+
+
+#endif // TAB_VIEW_H