xref: /haiku/src/apps/webpositive/URLInputGroup.cpp (revision dd2a1e350b303b855a50fd64e6cb55618be1ae6a)
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 
41 class URLChoice : public BAutoCompleter::Choice {
42 public:
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 
64 private:
65 	int32 fPriority;
66 };
67 
68 
69 class 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 
133 private:
134 	BList fChoices;
135 };
136 
137 
138 // #pragma mark - URLTextView
139 
140 
141 static const float kHorizontalTextRectInset = 4.0;
142 
143 
144 class URLInputGroup::URLTextView : public BTextView {
145 private:
146 	static const uint32 MSG_CLEAR = 'cler';
147 
148 public:
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 
162 protected:
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 
168 private:
169 			URLInputGroup*		fURLInputGroup;
170 			TextViewCompleter*	fURLAutoCompleter;
171 			bool				fUpdateAutoCompleterChoices;
172 };
173 
174 
175 URLInputGroup::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 
190 URLInputGroup::URLTextView::~URLTextView()
191 {
192 	delete fURLAutoCompleter;
193 }
194 
195 
196 void
197 URLInputGroup::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 
211 void
212 URLInputGroup::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 
268 void
269 URLInputGroup::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 
299 void
300 URLInputGroup::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 
318 BSize
319 URLInputGroup::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 
330 BSize
331 URLInputGroup::URLTextView::MaxSize()
332 {
333 	BSize max(MinSize());
334 	max.width = B_SIZE_UNLIMITED;
335 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
336 }
337 
338 
339 void
340 URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update)
341 {
342 	fUpdateAutoCompleterChoices = update;
343 }
344 
345 
346 void
347 URLInputGroup::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 
397 void
398 URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
399 {
400 	BTextView::DeleteText(fromOffset, toOffset);
401 
402 	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
403 }
404 
405 
406 const uint32 kGoBitmapWidth = 14;
407 const uint32 kGoBitmapHeight = 14;
408 const color_space kGoBitmapFormat = B_RGBA32;
409 
410 const 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 
463 const 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 
500 class URLInputGroup::PageIconView : public BView {
501 public:
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 
634 private:
635 	BBitmap* fIcon;
636 	BPoint fClickPoint;
637 	bool fPageIconSet;
638 };
639 
640 
641 // #pragma mark - URLInputGroup
642 
643 
644 URLInputGroup::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 
677 URLInputGroup::~URLInputGroup()
678 {
679 }
680 
681 
682 void
683 URLInputGroup::AttachedToWindow()
684 {
685 	BGroupView::AttachedToWindow();
686 	fWindowActive = Window()->IsActive();
687 }
688 
689 
690 void
691 URLInputGroup::WindowActivated(bool active)
692 {
693 	BGroupView::WindowActivated(active);
694 	if (fWindowActive != active) {
695 		fWindowActive = active;
696 		Invalidate();
697 	}
698 }
699 
700 
701 void
702 URLInputGroup::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 
714 void
715 URLInputGroup::MakeFocus(bool focus)
716 {
717 	// Forward this to the text view, we never accept focus ourselves.
718 	fTextView->MakeFocus(focus);
719 }
720 
721 
722 BTextView*
723 URLInputGroup::TextView() const
724 {
725 	return fTextView;
726 }
727 
728 
729 void
730 URLInputGroup::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 
744 const char*
745 URLInputGroup::Text() const
746 {
747 	return fTextView->Text();
748 }
749 
750 
751 BButton*
752 URLInputGroup::GoButton() const
753 {
754 	return fGoButton;
755 }
756 
757 
758 void
759 URLInputGroup::SetPageIcon(const BBitmap* icon)
760 {
761 	fIconView->SetIcon(icon);
762 }
763 
764 
765 bool
766 URLInputGroup::IsURLInputLocked() const
767 {
768 	return fURLLocked;
769 }
770 
771 
772 void
773 URLInputGroup::LockURLInput(bool lock)
774 {
775 	fURLLocked = lock;
776 }
777