xref: /haiku/src/apps/webpositive/URLInputGroup.cpp (revision 37fedaf8494b34aad811abcc49e79aa32943f880)
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 "BrowsingHistory.h"
29 #include "IconButton.h"
30 #include "IconUtils.h"
31 #include "TextViewCompleter.h"
32 
33 
34 #undef B_TRANSLATION_CONTEXT
35 #define B_TRANSLATION_CONTEXT "URL Bar"
36 
37 
38 class URLChoice : public BAutoCompleter::Choice {
39 public:
40 	URLChoice(const BString& choiceText, const BString& displayText,
41 			int32 matchPos, int32 matchLen, int32 priority)
42 		:
43 		BAutoCompleter::Choice(choiceText, displayText, matchPos, matchLen),
44 		fPriority(priority)
45 	{
46 	}
47 
48 	bool operator<(const URLChoice& other) const
49 	{
50 		if (fPriority > other.fPriority)
51 			return true;
52 		return DisplayText() < other.DisplayText();
53 	}
54 
55 	bool operator==(const URLChoice& other) const
56 	{
57 		return fPriority == other.fPriority
58 			&& DisplayText() < other.DisplayText();
59 	}
60 
61 private:
62 	int32 fPriority;
63 };
64 
65 
66 class BrowsingHistoryChoiceModel : public BAutoCompleter::ChoiceModel {
67 	virtual void FetchChoicesFor(const BString& pattern)
68 	{
69 		int32 count = CountChoices();
70 		for (int32 i = 0; i < count; i++) {
71 			delete reinterpret_cast<BAutoCompleter::Choice*>(
72 				fChoices.ItemAtFast(i));
73 		}
74 		fChoices.MakeEmpty();
75 
76 		// Search through BrowsingHistory for any matches.
77 		BrowsingHistory* history = BrowsingHistory::DefaultInstance();
78 		if (!history->Lock())
79 			return;
80 
81 		BString lastBaseURL;
82 		int32 priority = INT_MAX;
83 
84 		count = history->CountItems();
85 		for (int32 i = 0; i < count; i++) {
86 			BrowsingHistoryItem item = history->HistoryItemAt(i);
87 			const BString& choiceText = item.URL();
88 			int32 matchPos = choiceText.IFindFirst(pattern);
89 			if (matchPos < 0)
90 				continue;
91 			if (lastBaseURL.Length() > 0
92 				&& choiceText.FindFirst(lastBaseURL) >= 0) {
93 				priority--;
94 			} else
95 				priority = INT_MAX;
96 			lastBaseURL = baseURL(choiceText);
97 			fChoices.AddItem(new URLChoice(choiceText,
98 				choiceText, matchPos, pattern.Length(), priority));
99 		}
100 
101 		history->Unlock();
102 
103 		fChoices.SortItems(_CompareChoices);
104 	}
105 
106 	virtual int32 CountChoices() const
107 	{
108 		return fChoices.CountItems();
109 	}
110 
111 	virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const
112 	{
113 		return reinterpret_cast<BAutoCompleter::Choice*>(
114 			fChoices.ItemAt(index));
115 	}
116 
117 	static int _CompareChoices(const void* a, const void* b)
118 	{
119 		const URLChoice* aChoice
120 			= *reinterpret_cast<const URLChoice* const *>(a);
121 		const URLChoice* bChoice
122 			= *reinterpret_cast<const URLChoice* const *>(b);
123 		if (*aChoice < *bChoice)
124 			return -1;
125 		else if (*aChoice == *bChoice)
126 			return 0;
127 		return 1;
128 	}
129 
130 private:
131 	BList fChoices;
132 };
133 
134 
135 // #pragma mark - URLTextView
136 
137 
138 static const float kHorizontalTextRectInset = 4.0;
139 
140 
141 class URLInputGroup::URLTextView : public BTextView {
142 private:
143 	static const uint32 MSG_CLEAR = 'cler';
144 
145 public:
146 								URLTextView(URLInputGroup* parent);
147 	virtual						~URLTextView();
148 
149 	virtual	void				MessageReceived(BMessage* message);
150 	virtual	void				FrameResized(float width, float height);
151 	virtual	void				MouseDown(BPoint where);
152 	virtual	void				KeyDown(const char* bytes, int32 numBytes);
153 	virtual	void				MakeFocus(bool focused = true);
154 
155 	virtual	BSize				MinSize();
156 	virtual	BSize				MaxSize();
157 
158 			void				SetUpdateAutoCompleterChoices(bool update);
159 
160 protected:
161 	virtual	void				InsertText(const char* inText, int32 inLength,
162 									int32 inOffset,
163 									const text_run_array* inRuns);
164 	virtual	void				DeleteText(int32 fromOffset, int32 toOffset);
165 
166 private:
167 			void				_AlignTextRect();
168 
169 private:
170 			URLInputGroup*		fURLInputGroup;
171 			TextViewCompleter*	fURLAutoCompleter;
172 			BString				fPreviousText;
173 			bool				fUpdateAutoCompleterChoices;
174 };
175 
176 
177 URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent)
178 	:
179 	BTextView("url"),
180 	fURLInputGroup(parent),
181 	fURLAutoCompleter(new TextViewCompleter(this,
182 		new BrowsingHistoryChoiceModel())),
183 	fPreviousText(""),
184 	fUpdateAutoCompleterChoices(true)
185 {
186 	MakeResizable(true);
187 	SetStylable(true);
188 	fURLAutoCompleter->SetModificationsReported(true);
189 }
190 
191 
192 URLInputGroup::URLTextView::~URLTextView()
193 {
194 	delete fURLAutoCompleter;
195 }
196 
197 
198 void
199 URLInputGroup::URLTextView::MessageReceived(BMessage* message)
200 {
201 	switch (message->what) {
202 		case MSG_CLEAR:
203 			SetText("");
204 			break;
205 
206 		default:
207 			BTextView::MessageReceived(message);
208 			break;
209 	}
210 }
211 
212 
213 void
214 URLInputGroup::URLTextView::FrameResized(float width, float height)
215 {
216 	BTextView::FrameResized(width, height);
217 	_AlignTextRect();
218 }
219 
220 
221 void
222 URLInputGroup::URLTextView::MouseDown(BPoint where)
223 {
224 	bool wasFocus = IsFocus();
225 	if (!wasFocus)
226 		MakeFocus(true);
227 
228 	int32 buttons;
229 	if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
230 		buttons = B_PRIMARY_MOUSE_BUTTON;
231 
232 	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
233 		// Display context menu
234 		int32 selectionStart;
235 		int32 selectionEnd;
236 		GetSelection(&selectionStart, &selectionEnd);
237 		bool canCutOrCopy = selectionEnd > selectionStart;
238 
239 		bool canPaste = false;
240 		if (be_clipboard->Lock()) {
241 			if (BMessage* data = be_clipboard->Data())
242 				canPaste = data->HasData("text/plain", B_MIME_TYPE);
243 			be_clipboard->Unlock();
244 		}
245 
246 		BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"),
247 			new BMessage(B_CUT));
248 		BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"),
249 			new BMessage(B_COPY));
250 		BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
251 			new BMessage(B_PASTE));
252 		BMenuItem* clearItem = new BMenuItem(B_TRANSLATE("Clear"),
253 			new BMessage(MSG_CLEAR));
254 		cutItem->SetEnabled(canCutOrCopy);
255 		copyItem->SetEnabled(canCutOrCopy);
256 		pasteItem->SetEnabled(canPaste);
257 		clearItem->SetEnabled(strlen(Text()) > 0);
258 
259 		BPopUpMenu* menu = new BPopUpMenu("url context");
260 		menu->AddItem(cutItem);
261 		menu->AddItem(copyItem);
262 		menu->AddItem(pasteItem);
263 		menu->AddItem(clearItem);
264 
265 		menu->SetTargetForItems(this);
266 		menu->Go(ConvertToScreen(where), true, true, true);
267 		return;
268 	}
269 
270 	// Only pass through to base class if we already have focus.
271 	if (!wasFocus)
272 		return;
273 
274 	BTextView::MouseDown(where);
275 }
276 
277 
278 void
279 URLInputGroup::URLTextView::KeyDown(const char* bytes, int32 numBytes)
280 {
281 	switch (bytes[0]) {
282 		case B_TAB:
283 			BView::KeyDown(bytes, numBytes);
284 			break;
285 
286 		case B_ESCAPE:
287 			// Revert to text as it was when we received keyboard focus.
288 			SetText(fPreviousText.String());
289 			SelectAll();
290 			break;
291 
292 		case B_RETURN:
293 			// Don't let this through to the text view.
294 			break;
295 
296 		default:
297 			BTextView::KeyDown(bytes, numBytes);
298 			break;
299 	}
300 }
301 
302 void
303 URLInputGroup::URLTextView::MakeFocus(bool focus)
304 {
305 	if (focus == IsFocus())
306 		return;
307 
308 	BTextView::MakeFocus(focus);
309 
310 	if (focus) {
311 		fPreviousText = Text();
312 		SelectAll();
313 	}
314 
315 	fURLInputGroup->Invalidate();
316 }
317 
318 
319 BSize
320 URLInputGroup::URLTextView::MinSize()
321 {
322 	BSize min;
323 	min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset);
324 		// we always add at least one pixel vertical inset top/bottom for
325 		// the text rect.
326 	min.width = min.height * 3;
327 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
328 }
329 
330 
331 BSize
332 URLInputGroup::URLTextView::MaxSize()
333 {
334 	BSize max(MinSize());
335 	max.width = B_SIZE_UNLIMITED;
336 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
337 }
338 
339 
340 void
341 URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update)
342 {
343 	fUpdateAutoCompleterChoices = update;
344 }
345 
346 
347 void
348 URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength,
349 	int32 inOffset, const text_run_array* inRuns)
350 {
351 	// Filter all line breaks, note that inText is not terminated.
352 	if (inLength == 1) {
353 		if (*inText == '\n' || *inText == '\r')
354 			BTextView::InsertText(" ", 1, inOffset, inRuns);
355 		else
356 			BTextView::InsertText(inText, 1, inOffset, inRuns);
357 	} else {
358 		BString filteredText(inText, inLength);
359 		filteredText.ReplaceAll('\n', ' ');
360 		filteredText.ReplaceAll('\r', ' ');
361 		BTextView::InsertText(filteredText.String(), inLength, inOffset,
362 			inRuns);
363 	}
364 
365 	// Make the base URL part bold.
366 	BString text(Text(), TextLength());
367 	int32 baseUrlStart = text.FindFirst("://");
368 	if (baseUrlStart >= 0)
369 		baseUrlStart += 3;
370 	else
371 		baseUrlStart = 0;
372 	int32 baseUrlEnd = text.FindFirst("/", baseUrlStart);
373 	if (baseUrlEnd < 0)
374 		baseUrlEnd = TextLength();
375 	BFont font;
376 	GetFont(&font);
377 	const rgb_color black = (rgb_color) { 0, 0, 0, 255 };
378 	const rgb_color gray = (rgb_color) { 60, 60, 60, 255 };
379 	if (baseUrlStart > 0)
380 		SetFontAndColor(0, baseUrlStart - 1, &font, B_FONT_ALL, &gray);
381 	if (baseUrlEnd > baseUrlStart) {
382 		font.SetFace(B_BOLD_FACE);
383 		SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black);
384 	}
385 	if (baseUrlEnd < TextLength()) {
386 		font.SetFace(B_REGULAR_FACE);
387 		SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray);
388 	}
389 
390 	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
391 }
392 
393 
394 void
395 URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
396 {
397 	BTextView::DeleteText(fromOffset, toOffset);
398 
399 	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
400 }
401 
402 
403 void
404 URLInputGroup::URLTextView::_AlignTextRect()
405 {
406 	// Layout the text rect to be in the middle, normally this means there
407 	// is one pixel spacing on each side.
408 	BRect textRect(Bounds());
409 	textRect.left = 0.0;
410 	float vInset = max_c(1,
411 		floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5));
412 	float hInset = kHorizontalTextRectInset;
413 
414 	if (be_control_look)
415 		hInset = be_control_look->DefaultLabelSpacing();
416 
417 	textRect.InsetBy(hInset, vInset);
418 	SetTextRect(textRect);
419 }
420 
421 
422 const uint32 kGoBitmapWidth = 14;
423 const uint32 kGoBitmapHeight = 14;
424 const color_space kGoBitmapFormat = B_RGBA32;
425 
426 const unsigned char kGoBitmapBits[] = {
427 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
431 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
432 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
433 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
434 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
435 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
436 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
437 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
438 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
439 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
440 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
441 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
442 	0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
443 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
446 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
447 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0,
449 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
450 	0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed,
451 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
452 	0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
453 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
454 	0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
455 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
456 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
457 	0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02,
458 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
459 	0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
460 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
461 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
462 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
463 	0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
464 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
465 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
466 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1,
467 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
468 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
469 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
470 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
476 };
477 
478 
479 const unsigned char kPlaceholderIcon[] = {
480 	0x6e, 0x63, 0x69, 0x66, 0x04, 0x04, 0x00, 0x66, 0x03, 0x00, 0x3f, 0x80,
481 	0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x3d, 0xa6, 0x64, 0xc2, 0x19,
482 	0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 0x64, 0x49, 0xac, 0xcc, 0x00, 0xab,
483 	0xd5, 0xff, 0xff, 0x00, 0x6c, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x00, 0x00,
484 	0x00, 0x3d, 0x26, 0x64, 0xc2, 0x19, 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce,
485 	0x64, 0x49, 0xac, 0xcc, 0x00, 0x80, 0xff, 0x80, 0xff, 0x00, 0xb2, 0x00,
486 	0x04, 0x02, 0x04, 0x34, 0x22, 0xbd, 0x9b, 0x22, 0xb8, 0x53, 0x22, 0x28,
487 	0x2e, 0x28, 0xb5, 0xef, 0x28, 0xbb, 0x37, 0x34, 0x3a, 0xb8, 0x53, 0x3a,
488 	0xbd, 0x9b, 0x3a, 0x40, 0x2e, 0x40, 0xbb, 0x37, 0x40, 0xb5, 0xef, 0x02,
489 	0x08, 0xbe, 0xb6, 0xb4, 0xac, 0xc1, 0x46, 0xb4, 0xac, 0xbc, 0x25, 0xb4,
490 	0xac, 0xb8, 0x09, 0xb7, 0x35, 0xb9, 0xcf, 0xb5, 0xa0, 0xb8, 0x05, 0xbe,
491 	0xb6, 0x35, 0xc2, 0xe5, 0x35, 0xbe, 0xb6, 0x35, 0xc5, 0x68, 0xb8, 0x09,
492 	0xc6, 0x36, 0xb8, 0x09, 0xc6, 0x36, 0xb9, 0xcf, 0xc7, 0xca, 0xbe, 0xb6,
493 	0xc8, 0xc1, 0xbc, 0x25, 0xc8, 0xc1, 0xc1, 0xb3, 0xc8, 0xc1, 0xc6, 0x3c,
494 	0xc5, 0x5b, 0xc4, 0x65, 0xc7, 0x70, 0xc6, 0x3e, 0xbe, 0xb6, 0xc2, 0x0f,
495 	0xba, 0x87, 0xc2, 0x0f, 0xbe, 0xb6, 0xc2, 0x0f, 0xb8, 0x05, 0xc5, 0x64,
496 	0xb7, 0x37, 0xc5, 0x64, 0xb7, 0x37, 0xc3, 0x9e, 0xb5, 0xa2, 0x02, 0x04,
497 	0xb8, 0x09, 0xb7, 0x35, 0xb8, 0x05, 0xbe, 0xb6, 0xb5, 0xf8, 0xb9, 0x0c,
498 	0xb4, 0xac, 0xbe, 0xb6, 0xb4, 0xac, 0xbb, 0xba, 0xb4, 0xac, 0xc1, 0xb1,
499 	0xb8, 0x09, 0xc6, 0x36, 0xb5, 0xf8, 0xc4, 0x5e, 0xb9, 0xcf, 0xc7, 0xca,
500 	0x35, 0xc2, 0xe5, 0x35, 0xc5, 0x68, 0x35, 0xbe, 0xb6, 0x02, 0x04, 0x4d,
501 	0x51, 0xc4, 0xf2, 0xbf, 0x04, 0x53, 0x4e, 0xc8, 0xc1, 0xbe, 0x58, 0xc8,
502 	0xc1, 0xc1, 0x55, 0xc8, 0xc1, 0xbb, 0x5d, 0xc5, 0x64, 0xb6, 0xd9, 0xc7,
503 	0x75, 0xb8, 0xb0, 0xc3, 0x9e, 0xb5, 0x44, 0xc2, 0x0f, 0xba, 0x29, 0xc2,
504 	0x0f, 0xb7, 0xa6, 0xc2, 0x0f, 0xbe, 0x58, 0x04, 0x0a, 0x00, 0x01, 0x00,
505 	0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x19,
506 	0x98, 0xc6, 0x19, 0x93, 0x44, 0x19, 0xa2, 0x01, 0x17, 0x84, 0x00, 0x04,
507 	0x0a, 0x01, 0x01, 0x00, 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00,
508 	0x00, 0x00, 0x42, 0x19, 0x98, 0xc7, 0x26, 0x5f, 0x28, 0x96, 0xf9, 0x01,
509 	0x17, 0x83, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x01, 0x00, 0x0a, 0x03, 0x02,
510 	0x02, 0x03, 0x00
511 };
512 
513 // #pragma mark - PageIconView
514 
515 
516 class URLInputGroup::PageIconView : public BView {
517 public:
518 	PageIconView()
519 		:
520 		BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
521 		fIcon(NULL)
522 	{
523 		SetDrawingMode(B_OP_ALPHA);
524 		SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
525 	}
526 
527 	~PageIconView()
528 	{
529 		delete fIcon;
530 	}
531 
532 	virtual void Draw(BRect updateRect)
533 	{
534 		BRect bounds(Bounds());
535 		BRect iconBounds(0, 0, 15, 15);
536 		iconBounds.OffsetTo(
537 			floorf((bounds.left + bounds.right
538 				- (iconBounds.left + iconBounds.right)) / 2 + 0.5f),
539 			floorf((bounds.top + bounds.bottom
540 				- (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f));
541 		DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
542 			B_FILTER_BITMAP_BILINEAR);
543 	}
544 
545 	virtual BSize MinSize()
546 	{
547 		return BSize(18, 18);
548 	}
549 
550 	virtual BSize MaxSize()
551 	{
552 		return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
553 	}
554 
555 	virtual BSize PreferredSize()
556 	{
557 		return MinSize();
558 	}
559 
560 	void SetIcon(const BBitmap* icon)
561 	{
562 		delete fIcon;
563 		if (icon)
564 			fIcon = new BBitmap(icon);
565 		else {
566 			fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32);
567 			BIconUtils::GetVectorIcon(kPlaceholderIcon,
568 				sizeof(kPlaceholderIcon), fIcon);
569 		}
570 		Invalidate();
571 	}
572 
573 private:
574 	BBitmap* fIcon;
575 };
576 
577 
578 // #pragma mark - URLInputGroup
579 
580 
581 URLInputGroup::URLInputGroup(BMessage* goMessage)
582 	:
583 	BGroupView(B_HORIZONTAL, 0.0),
584 	fWindowActive(false)
585 {
586 	GroupLayout()->SetInsets(2, 2, 2, 2);
587 
588 	fIconView = new PageIconView();
589 	GroupLayout()->AddView(fIconView, 0.0f);
590 
591 	fTextView = new URLTextView(this);
592 	AddChild(fTextView);
593 
594 	AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER));
595 
596 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from
597 // resources?
598 //	fGoButton = new BitmapButton("kActionGo", NULL);
599 	fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth,
600 		kGoBitmapHeight, kGoBitmapFormat, goMessage);
601 	GroupLayout()->AddView(fGoButton, 0.0f);
602 
603 	SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
604 	SetLowColor(ViewColor());
605 	SetViewColor(fTextView->ViewColor());
606 
607 	SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
608 		B_ALIGN_VERTICAL_CENTER));
609 }
610 
611 
612 URLInputGroup::~URLInputGroup()
613 {
614 }
615 
616 
617 void
618 URLInputGroup::AttachedToWindow()
619 {
620 	BGroupView::AttachedToWindow();
621 	fWindowActive = Window()->IsActive();
622 }
623 
624 
625 void
626 URLInputGroup::WindowActivated(bool active)
627 {
628 	BGroupView::WindowActivated(active);
629 	if (fWindowActive != active) {
630 		fWindowActive = active;
631 		Invalidate();
632 	}
633 }
634 
635 
636 void
637 URLInputGroup::Draw(BRect updateRect)
638 {
639 	BRect bounds(Bounds());
640 	rgb_color base(LowColor());
641 	uint32 flags = 0;
642 	if (fWindowActive && fTextView->IsFocus())
643 		flags |= BControlLook::B_FOCUSED;
644 	be_control_look->DrawTextControlBorder(this, bounds, updateRect, base,
645 		flags);
646 }
647 
648 
649 void
650 URLInputGroup::MakeFocus(bool focus)
651 {
652 	// Forward this to the text view, we never accept focus ourselves.
653 	fTextView->MakeFocus(focus);
654 }
655 
656 
657 BTextView*
658 URLInputGroup::TextView() const
659 {
660 	return fTextView;
661 }
662 
663 
664 void
665 URLInputGroup::SetText(const char* text)
666 {
667 	if (!text || !Text() || strcmp(Text(), text) != 0) {
668 		fTextView->SetUpdateAutoCompleterChoices(false);
669 		fTextView->SetText(text);
670 		fTextView->SetUpdateAutoCompleterChoices(true);
671 	}
672 }
673 
674 
675 const char*
676 URLInputGroup::Text() const
677 {
678 	return fTextView->Text();
679 }
680 
681 
682 BButton*
683 URLInputGroup::GoButton() const
684 {
685 	return fGoButton;
686 }
687 
688 
689 void
690 URLInputGroup::SetPageIcon(const BBitmap* icon)
691 {
692 	fIconView->SetIcon(icon);
693 }
694 
695