1/*
2 * Copyright 2010 Stephan A��mus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include "URLInputGroup.h"
7
8#include <Bitmap.h>
9#include <Button.h>
10#include <Catalog.h>
11#include <ControlLook.h>
12#include <Clipboard.h>
13#include <GroupLayout.h>
14#include <GroupLayoutBuilder.h>
15#include <Locale.h>
16#include <LayoutUtils.h>
17#include <MenuItem.h>
18#include <PopUpMenu.h>
19#include <SeparatorView.h>
20#include <TextView.h>
21#include <Window.h>
22
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "BaseURL.h"
27#include "BitmapButton.h"
28#include "BrowserWindow.h"
29#include "BrowsingHistory.h"
30#include "IconButton.h"
31#include "IconUtils.h"
32#include "TextViewCompleter.h"
33#include "WebView.h"
34#include "WebWindow.h"
35
36
37#undef B_TRANSLATION_CONTEXT
38#define B_TRANSLATION_CONTEXT "URL Bar"
39
40
41class URLChoice : public BAutoCompleter::Choice {
42public:
43	URLChoice(const BString& choiceText, const BString& displayText,
44			int32 matchPos, int32 matchLen, int32 priority)
45		:
46		BAutoCompleter::Choice(choiceText, displayText, matchPos, matchLen),
47		fPriority(priority)
48	{
49	}
50
51	bool operator<(const URLChoice& other) const
52	{
53		if (fPriority > other.fPriority)
54			return true;
55		return DisplayText() < other.DisplayText();
56	}
57
58	bool operator==(const URLChoice& other) const
59	{
60		return fPriority == other.fPriority
61			&& DisplayText() < other.DisplayText();
62	}
63
64private:
65	int32 fPriority;
66};
67
68
69class BrowsingHistoryChoiceModel : public BAutoCompleter::ChoiceModel {
70	virtual void FetchChoicesFor(const BString& pattern)
71	{
72		int32 count = CountChoices();
73		for (int32 i = 0; i < count; i++) {
74			delete reinterpret_cast<BAutoCompleter::Choice*>(
75				fChoices.ItemAtFast(i));
76		}
77		fChoices.MakeEmpty();
78
79		// Search through BrowsingHistory for any matches.
80		BrowsingHistory* history = BrowsingHistory::DefaultInstance();
81		if (!history->Lock())
82			return;
83
84		BString lastBaseURL;
85		int32 priority = INT_MAX;
86
87		count = history->CountItems();
88		for (int32 i = 0; i < count; i++) {
89			BrowsingHistoryItem item = history->HistoryItemAt(i);
90			const BString& choiceText = item.URL();
91			int32 matchPos = choiceText.IFindFirst(pattern);
92			if (matchPos < 0)
93				continue;
94			if (lastBaseURL.Length() > 0
95				&& choiceText.FindFirst(lastBaseURL) >= 0) {
96				priority--;
97			} else
98				priority = INT_MAX;
99			lastBaseURL = baseURL(choiceText);
100			fChoices.AddItem(new URLChoice(choiceText,
101				choiceText, matchPos, pattern.Length(), priority));
102		}
103
104		history->Unlock();
105
106		fChoices.SortItems(_CompareChoices);
107	}
108
109	virtual int32 CountChoices() const
110	{
111		return fChoices.CountItems();
112	}
113
114	virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const
115	{
116		return reinterpret_cast<BAutoCompleter::Choice*>(
117			fChoices.ItemAt(index));
118	}
119
120	static int _CompareChoices(const void* a, const void* b)
121	{
122		const URLChoice* aChoice
123			= *reinterpret_cast<const URLChoice* const *>(a);
124		const URLChoice* bChoice
125			= *reinterpret_cast<const URLChoice* const *>(b);
126		if (*aChoice < *bChoice)
127			return -1;
128		else if (*aChoice == *bChoice)
129			return 0;
130		return 1;
131	}
132
133private:
134	BList fChoices;
135};
136
137
138// #pragma mark - URLTextView
139
140
141static const float kHorizontalTextRectInset = 4.0;
142
143
144class URLInputGroup::URLTextView : public BTextView {
145private:
146	static const uint32 MSG_CLEAR = 'cler';
147
148public:
149								URLTextView(URLInputGroup* parent);
150	virtual						~URLTextView();
151
152	virtual	void				MessageReceived(BMessage* message);
153	virtual	void				MouseDown(BPoint where);
154	virtual	void				KeyDown(const char* bytes, int32 numBytes);
155	virtual	void				MakeFocus(bool focused = true);
156
157	virtual	BSize				MinSize();
158	virtual	BSize				MaxSize();
159
160			void				SetUpdateAutoCompleterChoices(bool update);
161
162protected:
163	virtual	void				InsertText(const char* inText, int32 inLength,
164									int32 inOffset,
165									const text_run_array* inRuns);
166	virtual	void				DeleteText(int32 fromOffset, int32 toOffset);
167
168private:
169			URLInputGroup*		fURLInputGroup;
170			TextViewCompleter*	fURLAutoCompleter;
171			bool				fUpdateAutoCompleterChoices;
172};
173
174
175URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent)
176	:
177	BTextView("url"),
178	fURLInputGroup(parent),
179	fURLAutoCompleter(new TextViewCompleter(this,
180		new BrowsingHistoryChoiceModel())),
181	fUpdateAutoCompleterChoices(true)
182{
183	MakeResizable(true);
184	SetStylable(true);
185	SetInsets(be_control_look->DefaultLabelSpacing(), 2, 0, 2);
186	fURLAutoCompleter->SetModificationsReported(true);
187}
188
189
190URLInputGroup::URLTextView::~URLTextView()
191{
192	delete fURLAutoCompleter;
193}
194
195
196void
197URLInputGroup::URLTextView::MessageReceived(BMessage* message)
198{
199	switch (message->what) {
200		case MSG_CLEAR:
201			SetText("");
202			break;
203
204		default:
205			BTextView::MessageReceived(message);
206			break;
207	}
208}
209
210
211void
212URLInputGroup::URLTextView::MouseDown(BPoint where)
213{
214	bool wasFocus = IsFocus();
215	if (!wasFocus)
216		MakeFocus(true);
217
218	int32 buttons;
219	if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
220		buttons = B_PRIMARY_MOUSE_BUTTON;
221
222	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
223		// Display context menu
224		int32 selectionStart;
225		int32 selectionEnd;
226		GetSelection(&selectionStart, &selectionEnd);
227		bool canCutOrCopy = selectionEnd > selectionStart;
228
229		bool canPaste = false;
230		if (be_clipboard->Lock()) {
231			if (BMessage* data = be_clipboard->Data())
232				canPaste = data->HasData("text/plain", B_MIME_TYPE);
233			be_clipboard->Unlock();
234		}
235
236		BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"),
237			new BMessage(B_CUT));
238		BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"),
239			new BMessage(B_COPY));
240		BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
241			new BMessage(B_PASTE));
242		BMenuItem* clearItem = new BMenuItem(B_TRANSLATE("Clear"),
243			new BMessage(MSG_CLEAR));
244		cutItem->SetEnabled(canCutOrCopy);
245		copyItem->SetEnabled(canCutOrCopy);
246		pasteItem->SetEnabled(canPaste);
247		clearItem->SetEnabled(strlen(Text()) > 0);
248
249		BPopUpMenu* menu = new BPopUpMenu("url context");
250		menu->AddItem(cutItem);
251		menu->AddItem(copyItem);
252		menu->AddItem(pasteItem);
253		menu->AddItem(clearItem);
254
255		menu->SetTargetForItems(this);
256		menu->Go(ConvertToScreen(where), true, true, true);
257		return;
258	}
259
260	// Only pass through to base class if we already have focus.
261	if (!wasFocus)
262		return;
263
264	BTextView::MouseDown(where);
265}
266
267
268void
269URLInputGroup::URLTextView::KeyDown(const char* bytes, int32 numBytes)
270{
271	switch (bytes[0]) {
272		case B_TAB:
273			BView::KeyDown(bytes, numBytes);
274			break;
275
276		case B_ESCAPE:
277			// Text already unlocked && replaced in BrowserWindow,
278			// now select it.
279			SelectAll();
280			break;
281
282		case B_RETURN:
283			// Don't let this through to the text view.
284			break;
285
286		default:
287		{
288			BString currentText = Text();
289			BTextView::KeyDown(bytes, numBytes);
290			// Lock the URL input if it was modified
291			if (!fURLInputGroup->IsURLInputLocked()
292				&& Text() != currentText)
293				fURLInputGroup->LockURLInput();
294			break;
295		}
296	}
297}
298
299void
300URLInputGroup::URLTextView::MakeFocus(bool focus)
301{
302	// Unlock the URL input if focus was lost.
303	if (!focus)
304		fURLInputGroup->LockURLInput(false);
305
306	if (focus == IsFocus())
307		return;
308
309	BTextView::MakeFocus(focus);
310
311	if (focus)
312		SelectAll();
313
314	fURLInputGroup->Invalidate();
315}
316
317
318BSize
319URLInputGroup::URLTextView::MinSize()
320{
321	BSize min;
322	min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset);
323		// we always add at least one pixel vertical inset top/bottom for
324		// the text rect.
325	min.width = min.height * 3;
326	return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
327}
328
329
330BSize
331URLInputGroup::URLTextView::MaxSize()
332{
333	BSize max(MinSize());
334	max.width = B_SIZE_UNLIMITED;
335	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
336}
337
338
339void
340URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update)
341{
342	fUpdateAutoCompleterChoices = update;
343}
344
345
346void
347URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength,
348	int32 inOffset, const text_run_array* inRuns)
349{
350	// Filter all line breaks, note that inText is not terminated.
351	if (inLength == 1) {
352		if (*inText == '\n' || *inText == '\r')
353			BTextView::InsertText(" ", 1, inOffset, inRuns);
354		else
355			BTextView::InsertText(inText, 1, inOffset, inRuns);
356	} else {
357		BString filteredText(inText, inLength);
358		filteredText.ReplaceAll('\n', ' ');
359		filteredText.ReplaceAll('\r', ' ');
360		BTextView::InsertText(filteredText.String(), inLength, inOffset,
361			inRuns);
362	}
363
364	// Make the base URL part bold.
365	BString text(Text(), TextLength());
366	int32 baseUrlStart = text.FindFirst("://");
367	if (baseUrlStart >= 0)
368		baseUrlStart += 3;
369	else
370		baseUrlStart = 0;
371	int32 baseUrlEnd = text.FindFirst("/", baseUrlStart);
372	if (baseUrlEnd < 0)
373		baseUrlEnd = TextLength();
374
375	BFont font;
376	GetFont(&font);
377	const rgb_color hostColor = ui_color(B_DOCUMENT_TEXT_COLOR);
378	const rgb_color urlColor = tint_color(hostColor,
379		(hostColor.IsDark() ? B_LIGHTEN_1_TINT : B_DARKEN_1_TINT));
380	if (baseUrlStart > 0)
381		SetFontAndColor(0, baseUrlStart, &font, B_FONT_ALL, &urlColor);
382	if (baseUrlEnd > baseUrlStart) {
383		font.SetFace(B_BOLD_FACE);
384		SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL,
385			&hostColor);
386	}
387	if (baseUrlEnd < TextLength()) {
388		font.SetFace(B_REGULAR_FACE);
389		SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL,
390			&urlColor);
391	}
392
393	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
394}
395
396
397void
398URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
399{
400	BTextView::DeleteText(fromOffset, toOffset);
401
402	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
403}
404
405
406const uint32 kGoBitmapWidth = 14;
407const uint32 kGoBitmapHeight = 14;
408const color_space kGoBitmapFormat = B_RGBA32;
409
410const unsigned char kGoBitmapBits[] = {
411	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
412	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
414	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
415	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
416	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
417	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
418	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
419	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
420	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
421	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
422	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
423	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
424	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
425	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
426	0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
427	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
428	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
430	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
431	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
432	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0,
433	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
434	0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed,
435	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
436	0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
437	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
438	0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
439	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
440	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
441	0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02,
442	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
443	0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
444	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
445	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
447	0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
449	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
450	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1,
451	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
452	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
453	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
454	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
455	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
456	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
457	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
458	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
459	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
460};
461
462
463const unsigned char kPlaceholderIcon[] = {
464	0x6e, 0x63, 0x69, 0x66, 0x04, 0x04, 0x00, 0x66, 0x03, 0x00, 0x3f, 0x80,
465	0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x3d, 0xa6, 0x64, 0xc2, 0x19,
466	0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 0x64, 0x49, 0xac, 0xcc, 0x00, 0xab,
467	0xd5, 0xff, 0xff, 0x00, 0x6c, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x00, 0x00,
468	0x00, 0x3d, 0x26, 0x64, 0xc2, 0x19, 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce,
469	0x64, 0x49, 0xac, 0xcc, 0x00, 0x80, 0xff, 0x80, 0xff, 0x00, 0xb2, 0x00,
470	0x04, 0x02, 0x04, 0x34, 0x22, 0xbd, 0x9b, 0x22, 0xb8, 0x53, 0x22, 0x28,
471	0x2e, 0x28, 0xb5, 0xef, 0x28, 0xbb, 0x37, 0x34, 0x3a, 0xb8, 0x53, 0x3a,
472	0xbd, 0x9b, 0x3a, 0x40, 0x2e, 0x40, 0xbb, 0x37, 0x40, 0xb5, 0xef, 0x02,
473	0x08, 0xbe, 0xb6, 0xb4, 0xac, 0xc1, 0x46, 0xb4, 0xac, 0xbc, 0x25, 0xb4,
474	0xac, 0xb8, 0x09, 0xb7, 0x35, 0xb9, 0xcf, 0xb5, 0xa0, 0xb8, 0x05, 0xbe,
475	0xb6, 0x35, 0xc2, 0xe5, 0x35, 0xbe, 0xb6, 0x35, 0xc5, 0x68, 0xb8, 0x09,
476	0xc6, 0x36, 0xb8, 0x09, 0xc6, 0x36, 0xb9, 0xcf, 0xc7, 0xca, 0xbe, 0xb6,
477	0xc8, 0xc1, 0xbc, 0x25, 0xc8, 0xc1, 0xc1, 0xb3, 0xc8, 0xc1, 0xc6, 0x3c,
478	0xc5, 0x5b, 0xc4, 0x65, 0xc7, 0x70, 0xc6, 0x3e, 0xbe, 0xb6, 0xc2, 0x0f,
479	0xba, 0x87, 0xc2, 0x0f, 0xbe, 0xb6, 0xc2, 0x0f, 0xb8, 0x05, 0xc5, 0x64,
480	0xb7, 0x37, 0xc5, 0x64, 0xb7, 0x37, 0xc3, 0x9e, 0xb5, 0xa2, 0x02, 0x04,
481	0xb8, 0x09, 0xb7, 0x35, 0xb8, 0x05, 0xbe, 0xb6, 0xb5, 0xf8, 0xb9, 0x0c,
482	0xb4, 0xac, 0xbe, 0xb6, 0xb4, 0xac, 0xbb, 0xba, 0xb4, 0xac, 0xc1, 0xb1,
483	0xb8, 0x09, 0xc6, 0x36, 0xb5, 0xf8, 0xc4, 0x5e, 0xb9, 0xcf, 0xc7, 0xca,
484	0x35, 0xc2, 0xe5, 0x35, 0xc5, 0x68, 0x35, 0xbe, 0xb6, 0x02, 0x04, 0x4d,
485	0x51, 0xc4, 0xf2, 0xbf, 0x04, 0x53, 0x4e, 0xc8, 0xc1, 0xbe, 0x58, 0xc8,
486	0xc1, 0xc1, 0x55, 0xc8, 0xc1, 0xbb, 0x5d, 0xc5, 0x64, 0xb6, 0xd9, 0xc7,
487	0x75, 0xb8, 0xb0, 0xc3, 0x9e, 0xb5, 0x44, 0xc2, 0x0f, 0xba, 0x29, 0xc2,
488	0x0f, 0xb7, 0xa6, 0xc2, 0x0f, 0xbe, 0x58, 0x04, 0x0a, 0x00, 0x01, 0x00,
489	0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x19,
490	0x98, 0xc6, 0x19, 0x93, 0x44, 0x19, 0xa2, 0x01, 0x17, 0x84, 0x00, 0x04,
491	0x0a, 0x01, 0x01, 0x00, 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00,
492	0x00, 0x00, 0x42, 0x19, 0x98, 0xc7, 0x26, 0x5f, 0x28, 0x96, 0xf9, 0x01,
493	0x17, 0x83, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x01, 0x00, 0x0a, 0x03, 0x02,
494	0x02, 0x03, 0x00
495};
496
497// #pragma mark - PageIconView
498
499
500class URLInputGroup::PageIconView : public BView {
501public:
502	PageIconView()
503		:
504		BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
505		fIcon(NULL),
506		fClickPoint(-1, 0),
507		fPageIconSet(false)
508	{
509		SetDrawingMode(B_OP_ALPHA);
510		SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
511		SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
512	}
513
514	~PageIconView()
515	{
516		delete fIcon;
517	}
518
519	virtual void Draw(BRect updateRect)
520	{
521		BRect bounds(Bounds());
522		BRect iconBounds(0, 0, 15, 15);
523		iconBounds.OffsetTo(
524			floorf((bounds.left + bounds.right
525				- (iconBounds.left + iconBounds.right)) / 2 + 0.5f),
526			floorf((bounds.top + bounds.bottom
527				- (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f));
528		DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
529			B_FILTER_BITMAP_BILINEAR);
530	}
531
532	virtual BSize MinSize()
533	{
534		return BSize(18, 18);
535	}
536
537	virtual BSize MaxSize()
538	{
539		return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
540	}
541
542	virtual BSize PreferredSize()
543	{
544		return MinSize();
545	}
546
547	void MouseDown(BPoint where)
548	{
549		int32 buttons;
550		if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) == B_OK) {
551			if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0) {
552				// Memorize click point for dragging
553				fClickPoint = where;
554			}
555		}
556		return;
557	}
558
559	void MouseUp(BPoint where)
560	{
561		fClickPoint.x = -1;
562	}
563
564	virtual void MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
565	{
566		if (dragMessage != NULL)
567			return;
568
569		if (fClickPoint.x >= 0
570			&& (fabs(where.x - fClickPoint.x) > 4 || fabs(where.y - fClickPoint.y) > 4)) {
571			// Start dragging
572			BPoint offset = fClickPoint - Frame().LeftTop();
573
574			const char* url = static_cast<URLInputGroup*>(Parent())->Text();
575			const char* title =
576				static_cast<BWebWindow*>(Window())->CurrentWebView()->MainFrameTitle();
577
578			// Validate the file name to be set for the clipping if user drags to Tracker.
579			BString fileName(title);
580			if (fileName.Length() == 0) {
581				fileName = url;
582				int32 leafPos = fileName.FindLast('/');
583				if (leafPos >= 0)
584					fileName.Remove(0, leafPos + 1);
585			}
586			fileName.ReplaceAll('/', '-');
587			fileName.Truncate(B_FILE_NAME_LENGTH - 1);
588
589			BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK,
590				B_CMAP8);
591			miniIcon.ImportBits(fIcon);
592			// TODO:  obtain and send the large icon in addition to the mini icon.
593			// Currently PageUserData does not provide a function that returns this.
594
595			BMessage drag(B_SIMPLE_DATA);
596			drag.AddInt32("be:actions", B_COPY_TARGET);
597			drag.AddString("be:clip_name", fileName.String());
598			drag.AddString("be:filetypes", "application/x-vnd.Be-bookmark");
599			// Support the "Passing Data via File" protocol
600			drag.AddString("be:types", B_FILE_MIME_TYPE);
601			BMessage data(B_SIMPLE_DATA);
602			data.AddString("url", url);
603			data.AddString("title", title);
604				// The title may differ from the validated filename
605			if (fPageIconSet == true) {
606				// Don't bother sending the placeholder web icon, if that is all we have.
607				data.AddData("miniIcon", B_COLOR_8_BIT_TYPE, &miniIcon, sizeof(miniIcon));
608			}
609			drag.AddMessage("be:originator-data", &data);
610
611			BBitmap* iconClone = new BBitmap(fIcon);
612				// Needed because DragMessage will delete the bitmap when it's done.
613
614			DragMessage(&drag, iconClone, B_OP_ALPHA, offset);
615		}
616		return;
617	}
618
619	void SetIcon(const BBitmap* icon)
620	{
621		delete fIcon;
622		if (icon) {
623			fIcon = new BBitmap(icon);
624			fPageIconSet = true;
625		} else {
626			fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32);
627			BIconUtils::GetVectorIcon(kPlaceholderIcon,
628				sizeof(kPlaceholderIcon), fIcon);
629			fPageIconSet = false;
630		}
631		Invalidate();
632	}
633
634private:
635	BBitmap* fIcon;
636	BPoint fClickPoint;
637	bool fPageIconSet;
638};
639
640
641// #pragma mark - URLInputGroup
642
643
644URLInputGroup::URLInputGroup(BMessage* goMessage)
645	:
646	BGroupView(B_HORIZONTAL, 0.0),
647	fWindowActive(false),
648	fURLLocked(false)
649{
650	GroupLayout()->SetInsets(2, 2, 2, 2);
651
652	fIconView = new PageIconView();
653	GroupLayout()->AddView(fIconView, 0.0f);
654
655	fTextView = new URLTextView(this);
656	AddChild(fTextView);
657
658	AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER));
659
660// TODO: Fix in Haiku, no in-built support for archived BBitmaps from
661// resources?
662//	fGoButton = new BitmapButton("kActionGo", NULL);
663	fGoButton = new BBitmapButton(kGoBitmapBits, kGoBitmapWidth,
664		kGoBitmapHeight, kGoBitmapFormat, goMessage);
665	GroupLayout()->AddView(fGoButton, 0.0f);
666
667	SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
668	SetLowColor(ViewColor());
669	SetViewColor(fTextView->ViewColor());
670
671	SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
672		B_ALIGN_VERTICAL_CENTER));
673
674}
675
676
677URLInputGroup::~URLInputGroup()
678{
679}
680
681
682void
683URLInputGroup::AttachedToWindow()
684{
685	BGroupView::AttachedToWindow();
686	fWindowActive = Window()->IsActive();
687}
688
689
690void
691URLInputGroup::WindowActivated(bool active)
692{
693	BGroupView::WindowActivated(active);
694	if (fWindowActive != active) {
695		fWindowActive = active;
696		Invalidate();
697	}
698}
699
700
701void
702URLInputGroup::Draw(BRect updateRect)
703{
704	BRect bounds(Bounds());
705	rgb_color base(LowColor());
706	uint32 flags = 0;
707	if (fWindowActive && fTextView->IsFocus())
708		flags |= BControlLook::B_FOCUSED;
709	be_control_look->DrawTextControlBorder(this, bounds, updateRect, base,
710		flags);
711}
712
713
714void
715URLInputGroup::MakeFocus(bool focus)
716{
717	// Forward this to the text view, we never accept focus ourselves.
718	fTextView->MakeFocus(focus);
719}
720
721
722BTextView*
723URLInputGroup::TextView() const
724{
725	return fTextView;
726}
727
728
729void
730URLInputGroup::SetText(const char* text)
731{
732	// Ignore setting the text, if the input is locked.
733	if (fURLLocked)
734		return;
735
736	if (!text || !Text() || strcmp(Text(), text) != 0) {
737		fTextView->SetUpdateAutoCompleterChoices(false);
738		fTextView->SetText(text);
739		fTextView->SetUpdateAutoCompleterChoices(true);
740	}
741}
742
743
744const char*
745URLInputGroup::Text() const
746{
747	return fTextView->Text();
748}
749
750
751BButton*
752URLInputGroup::GoButton() const
753{
754	return fGoButton;
755}
756
757
758void
759URLInputGroup::SetPageIcon(const BBitmap* icon)
760{
761	fIconView->SetIcon(icon);
762}
763
764
765bool
766URLInputGroup::IsURLInputLocked() const
767{
768	return fURLLocked;
769}
770
771
772void
773URLInputGroup::LockURLInput(bool lock)
774{
775	fURLLocked = lock;
776}
777