⛏️ index : haiku.git

/*
 * 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 <Catalog.h>
#include <ControlLook.h>
#include <Clipboard.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <Locale.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 "BrowserWindow.h"
#include "BrowsingHistory.h"
#include "IconButton.h"
#include "IconUtils.h"
#include "TextViewCompleter.h"
#include "WebView.h"
#include "WebWindow.h"


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "URL Bar"


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				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:
			URLInputGroup*		fURLInputGroup;
			TextViewCompleter*	fURLAutoCompleter;
			bool				fUpdateAutoCompleterChoices;
};


URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent)
	:
	BTextView("url"),
	fURLInputGroup(parent),
	fURLAutoCompleter(new TextViewCompleter(this,
		new BrowsingHistoryChoiceModel())),
	fUpdateAutoCompleterChoices(true)
{
	MakeResizable(true);
	SetStylable(true);
	SetInsets(be_control_look->DefaultLabelSpacing(), 2, 0, 2);
	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::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(B_TRANSLATE("Cut"),
			new BMessage(B_CUT));
		BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"),
			new BMessage(B_COPY));
		BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
			new BMessage(B_PASTE));
		BMenuItem* clearItem = new BMenuItem(B_TRANSLATE("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:
			// Text already unlocked && replaced in BrowserWindow,
			// now select it.
			SelectAll();
			break;

		case B_RETURN:
			// Don't let this through to the text view.
			break;

		default:
		{
			BString currentText = Text();
			BTextView::KeyDown(bytes, numBytes);
			// Lock the URL input if it was modified
			if (!fURLInputGroup->IsURLInputLocked()
				&& Text() != currentText)
				fURLInputGroup->LockURLInput();
			break;
		}
	}
}

void
URLInputGroup::URLTextView::MakeFocus(bool focus)
{
	// Unlock the URL input if focus was lost.
	if (!focus)
		fURLInputGroup->LockURLInput(false);

	if (focus == IsFocus())
		return;

	BTextView::MakeFocus(focus);

	if (focus)
		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 hostColor = ui_color(B_DOCUMENT_TEXT_COLOR);
	const rgb_color urlColor = tint_color(hostColor,
		(hostColor.IsDark() ? B_LIGHTEN_1_TINT : B_DARKEN_1_TINT));
	if (baseUrlStart > 0)
		SetFontAndColor(0, baseUrlStart, &font, B_FONT_ALL, &urlColor);
	if (baseUrlEnd > baseUrlStart) {
		font.SetFace(B_BOLD_FACE);
		SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL,
			&hostColor);
	}
	if (baseUrlEnd < TextLength()) {
		font.SetFace(B_REGULAR_FACE);
		SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL,
			&urlColor);
	}

	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
}


void
URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
{
	BTextView::DeleteText(fromOffset, toOffset);

	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
}


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
};


const unsigned char kPlaceholderIcon[] = {
	0x6e, 0x63, 0x69, 0x66, 0x04, 0x04, 0x00, 0x66, 0x03, 0x00, 0x3f, 0x80,
	0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x3d, 0xa6, 0x64, 0xc2, 0x19,
	0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 0x64, 0x49, 0xac, 0xcc, 0x00, 0xab,
	0xd5, 0xff, 0xff, 0x00, 0x6c, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x00, 0x00,
	0x00, 0x3d, 0x26, 0x64, 0xc2, 0x19, 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce,
	0x64, 0x49, 0xac, 0xcc, 0x00, 0x80, 0xff, 0x80, 0xff, 0x00, 0xb2, 0x00,
	0x04, 0x02, 0x04, 0x34, 0x22, 0xbd, 0x9b, 0x22, 0xb8, 0x53, 0x22, 0x28,
	0x2e, 0x28, 0xb5, 0xef, 0x28, 0xbb, 0x37, 0x34, 0x3a, 0xb8, 0x53, 0x3a,
	0xbd, 0x9b, 0x3a, 0x40, 0x2e, 0x40, 0xbb, 0x37, 0x40, 0xb5, 0xef, 0x02,
	0x08, 0xbe, 0xb6, 0xb4, 0xac, 0xc1, 0x46, 0xb4, 0xac, 0xbc, 0x25, 0xb4,
	0xac, 0xb8, 0x09, 0xb7, 0x35, 0xb9, 0xcf, 0xb5, 0xa0, 0xb8, 0x05, 0xbe,
	0xb6, 0x35, 0xc2, 0xe5, 0x35, 0xbe, 0xb6, 0x35, 0xc5, 0x68, 0xb8, 0x09,
	0xc6, 0x36, 0xb8, 0x09, 0xc6, 0x36, 0xb9, 0xcf, 0xc7, 0xca, 0xbe, 0xb6,
	0xc8, 0xc1, 0xbc, 0x25, 0xc8, 0xc1, 0xc1, 0xb3, 0xc8, 0xc1, 0xc6, 0x3c,
	0xc5, 0x5b, 0xc4, 0x65, 0xc7, 0x70, 0xc6, 0x3e, 0xbe, 0xb6, 0xc2, 0x0f,
	0xba, 0x87, 0xc2, 0x0f, 0xbe, 0xb6, 0xc2, 0x0f, 0xb8, 0x05, 0xc5, 0x64,
	0xb7, 0x37, 0xc5, 0x64, 0xb7, 0x37, 0xc3, 0x9e, 0xb5, 0xa2, 0x02, 0x04,
	0xb8, 0x09, 0xb7, 0x35, 0xb8, 0x05, 0xbe, 0xb6, 0xb5, 0xf8, 0xb9, 0x0c,
	0xb4, 0xac, 0xbe, 0xb6, 0xb4, 0xac, 0xbb, 0xba, 0xb4, 0xac, 0xc1, 0xb1,
	0xb8, 0x09, 0xc6, 0x36, 0xb5, 0xf8, 0xc4, 0x5e, 0xb9, 0xcf, 0xc7, 0xca,
	0x35, 0xc2, 0xe5, 0x35, 0xc5, 0x68, 0x35, 0xbe, 0xb6, 0x02, 0x04, 0x4d,
	0x51, 0xc4, 0xf2, 0xbf, 0x04, 0x53, 0x4e, 0xc8, 0xc1, 0xbe, 0x58, 0xc8,
	0xc1, 0xc1, 0x55, 0xc8, 0xc1, 0xbb, 0x5d, 0xc5, 0x64, 0xb6, 0xd9, 0xc7,
	0x75, 0xb8, 0xb0, 0xc3, 0x9e, 0xb5, 0x44, 0xc2, 0x0f, 0xba, 0x29, 0xc2,
	0x0f, 0xb7, 0xa6, 0xc2, 0x0f, 0xbe, 0x58, 0x04, 0x0a, 0x00, 0x01, 0x00,
	0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x19,
	0x98, 0xc6, 0x19, 0x93, 0x44, 0x19, 0xa2, 0x01, 0x17, 0x84, 0x00, 0x04,
	0x0a, 0x01, 0x01, 0x00, 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x42, 0x19, 0x98, 0xc7, 0x26, 0x5f, 0x28, 0x96, 0xf9, 0x01,
	0x17, 0x83, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x01, 0x00, 0x0a, 0x03, 0x02,
	0x02, 0x03, 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),
		fClickPoint(-1, 0),
		fPageIconSet(false)
	{
		SetDrawingMode(B_OP_ALPHA);
		SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
		SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
	}

	~PageIconView()
	{
		delete fIcon;
	}

	virtual void Draw(BRect updateRect)
	{
		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()
	{
		return BSize(18, 18);
	}

	virtual BSize MaxSize()
	{
		return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
	}

	virtual BSize PreferredSize()
	{
		return MinSize();
	}

	void MouseDown(BPoint where)
	{
		int32 buttons;
		if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) == B_OK) {
			if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0) {
				// Memorize click point for dragging
				fClickPoint = where;
			}
		}
		return;
	}

	void MouseUp(BPoint where)
	{
		fClickPoint.x = -1;
	}

	virtual void MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
	{
		if (dragMessage != NULL)
			return;

		if (fClickPoint.x >= 0
			&& (fabs(where.x - fClickPoint.x) > 4 || fabs(where.y - fClickPoint.y) > 4)) {
			// Start dragging
			BPoint offset = fClickPoint - Frame().LeftTop();

			const char* url = static_cast<URLInputGroup*>(Parent())->Text();
			const char* title =
				static_cast<BWebWindow*>(Window())->CurrentWebView()->MainFrameTitle();

			// Validate the file name to be set for the clipping if user drags to Tracker.
			BString fileName(title);
			if (fileName.Length() == 0) {
				fileName = url;
				int32 leafPos = fileName.FindLast('/');
				if (leafPos >= 0)
					fileName.Remove(0, leafPos + 1);
			}
			fileName.ReplaceAll('/', '-');
			fileName.Truncate(B_FILE_NAME_LENGTH - 1);

			BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK,
				B_CMAP8);
			miniIcon.ImportBits(fIcon);
			// TODO:  obtain and send the large icon in addition to the mini icon.
			// Currently PageUserData does not provide a function that returns this.

			BMessage drag(B_SIMPLE_DATA);
			drag.AddInt32("be:actions", B_COPY_TARGET);
			drag.AddString("be:clip_name", fileName.String());
			drag.AddString("be:filetypes", "application/x-vnd.Be-bookmark");
			// Support the "Passing Data via File" protocol
			drag.AddString("be:types", B_FILE_MIME_TYPE);
			BMessage data(B_SIMPLE_DATA);
			data.AddString("url", url);
			data.AddString("title", title);
				// The title may differ from the validated filename
			if (fPageIconSet == true) {
				// Don't bother sending the placeholder web icon, if that is all we have.
				data.AddData("miniIcon", B_COLOR_8_BIT_TYPE, &miniIcon, sizeof(miniIcon));
			}
			drag.AddMessage("be:originator-data", &data);

			BBitmap* iconClone = new BBitmap(fIcon);
				// Needed because DragMessage will delete the bitmap when it's done.

			DragMessage(&drag, iconClone, B_OP_ALPHA, offset);
		}
		return;
	}

	void SetIcon(const BBitmap* icon)
	{
		delete fIcon;
		if (icon) {
			fIcon = new BBitmap(icon);
			fPageIconSet = true;
		} else {
			fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32);
			BIconUtils::GetVectorIcon(kPlaceholderIcon,
				sizeof(kPlaceholderIcon), fIcon);
			fPageIconSet = false;
		}
		Invalidate();
	}

private:
	BBitmap* fIcon;
	BPoint fClickPoint;
	bool fPageIconSet;
};


// #pragma mark - URLInputGroup


URLInputGroup::URLInputGroup(BMessage* goMessage)
	:
	BGroupView(B_HORIZONTAL, 0.0),
	fWindowActive(false),
	fURLLocked(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 BBitmapButton(kGoBitmapBits, kGoBitmapWidth,
		kGoBitmapHeight, kGoBitmapFormat, goMessage);
	GroupLayout()->AddView(fGoButton, 0.0f);

	SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
	SetLowColor(ViewColor());
	SetViewColor(fTextView->ViewColor());

	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)
{
	// Ignore setting the text, if the input is locked.
	if (fURLLocked)
		return;

	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);
}


bool
URLInputGroup::IsURLInputLocked() const
{
	return fURLLocked;
}


void
URLInputGroup::LockURLInput(bool lock)
{
	fURLLocked = lock;
}