xref: /haiku/src/apps/webpositive/URLInputGroup.cpp (revision ab4411e89a079bc0a40d901995f3418d998c51b3)
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 
376 	BFont font;
377 	GetFont(&font);
378 	const rgb_color black = (rgb_color) { 0, 0, 0, 255 };
379 	const rgb_color gray = (rgb_color) { 60, 60, 60, 255 };
380 	if (baseUrlStart > 0)
381 		SetFontAndColor(0, baseUrlStart, &font, B_FONT_ALL, &gray);
382 	if (baseUrlEnd > baseUrlStart) {
383 		font.SetFace(B_BOLD_FACE);
384 		SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black);
385 	}
386 	if (baseUrlEnd < TextLength()) {
387 		font.SetFace(B_REGULAR_FACE);
388 		SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray);
389 	}
390 
391 	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
392 }
393 
394 
395 void
396 URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
397 {
398 	BTextView::DeleteText(fromOffset, toOffset);
399 
400 	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
401 }
402 
403 
404 void
405 URLInputGroup::URLTextView::_AlignTextRect()
406 {
407 	// Layout the text rect to be in the middle, normally this means there
408 	// is one pixel spacing on each side.
409 	BRect textRect(Bounds());
410 	textRect.left = 0.0;
411 	float vInset = max_c(1,
412 		floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5));
413 	float hInset = kHorizontalTextRectInset;
414 
415 	if (be_control_look)
416 		hInset = be_control_look->DefaultLabelSpacing();
417 
418 	textRect.InsetBy(hInset, vInset);
419 	SetTextRect(textRect);
420 }
421 
422 
423 const uint32 kGoBitmapWidth = 14;
424 const uint32 kGoBitmapHeight = 14;
425 const color_space kGoBitmapFormat = B_RGBA32;
426 
427 const unsigned char kGoBitmapBits[] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
437 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
438 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
439 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
440 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
441 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
444 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
445 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
447 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
449 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0,
450 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
451 	0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed,
452 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
453 	0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
454 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
455 	0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
456 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
457 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
458 	0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02,
459 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
460 	0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
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, 0x00, 0x00, 0x00, 0x00,
463 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
464 	0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
467 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 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 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
477 };
478 
479 
480 const unsigned char kPlaceholderIcon[] = {
481 	0x6e, 0x63, 0x69, 0x66, 0x04, 0x04, 0x00, 0x66, 0x03, 0x00, 0x3f, 0x80,
482 	0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x3d, 0xa6, 0x64, 0xc2, 0x19,
483 	0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 0x64, 0x49, 0xac, 0xcc, 0x00, 0xab,
484 	0xd5, 0xff, 0xff, 0x00, 0x6c, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x00, 0x00,
485 	0x00, 0x3d, 0x26, 0x64, 0xc2, 0x19, 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce,
486 	0x64, 0x49, 0xac, 0xcc, 0x00, 0x80, 0xff, 0x80, 0xff, 0x00, 0xb2, 0x00,
487 	0x04, 0x02, 0x04, 0x34, 0x22, 0xbd, 0x9b, 0x22, 0xb8, 0x53, 0x22, 0x28,
488 	0x2e, 0x28, 0xb5, 0xef, 0x28, 0xbb, 0x37, 0x34, 0x3a, 0xb8, 0x53, 0x3a,
489 	0xbd, 0x9b, 0x3a, 0x40, 0x2e, 0x40, 0xbb, 0x37, 0x40, 0xb5, 0xef, 0x02,
490 	0x08, 0xbe, 0xb6, 0xb4, 0xac, 0xc1, 0x46, 0xb4, 0xac, 0xbc, 0x25, 0xb4,
491 	0xac, 0xb8, 0x09, 0xb7, 0x35, 0xb9, 0xcf, 0xb5, 0xa0, 0xb8, 0x05, 0xbe,
492 	0xb6, 0x35, 0xc2, 0xe5, 0x35, 0xbe, 0xb6, 0x35, 0xc5, 0x68, 0xb8, 0x09,
493 	0xc6, 0x36, 0xb8, 0x09, 0xc6, 0x36, 0xb9, 0xcf, 0xc7, 0xca, 0xbe, 0xb6,
494 	0xc8, 0xc1, 0xbc, 0x25, 0xc8, 0xc1, 0xc1, 0xb3, 0xc8, 0xc1, 0xc6, 0x3c,
495 	0xc5, 0x5b, 0xc4, 0x65, 0xc7, 0x70, 0xc6, 0x3e, 0xbe, 0xb6, 0xc2, 0x0f,
496 	0xba, 0x87, 0xc2, 0x0f, 0xbe, 0xb6, 0xc2, 0x0f, 0xb8, 0x05, 0xc5, 0x64,
497 	0xb7, 0x37, 0xc5, 0x64, 0xb7, 0x37, 0xc3, 0x9e, 0xb5, 0xa2, 0x02, 0x04,
498 	0xb8, 0x09, 0xb7, 0x35, 0xb8, 0x05, 0xbe, 0xb6, 0xb5, 0xf8, 0xb9, 0x0c,
499 	0xb4, 0xac, 0xbe, 0xb6, 0xb4, 0xac, 0xbb, 0xba, 0xb4, 0xac, 0xc1, 0xb1,
500 	0xb8, 0x09, 0xc6, 0x36, 0xb5, 0xf8, 0xc4, 0x5e, 0xb9, 0xcf, 0xc7, 0xca,
501 	0x35, 0xc2, 0xe5, 0x35, 0xc5, 0x68, 0x35, 0xbe, 0xb6, 0x02, 0x04, 0x4d,
502 	0x51, 0xc4, 0xf2, 0xbf, 0x04, 0x53, 0x4e, 0xc8, 0xc1, 0xbe, 0x58, 0xc8,
503 	0xc1, 0xc1, 0x55, 0xc8, 0xc1, 0xbb, 0x5d, 0xc5, 0x64, 0xb6, 0xd9, 0xc7,
504 	0x75, 0xb8, 0xb0, 0xc3, 0x9e, 0xb5, 0x44, 0xc2, 0x0f, 0xba, 0x29, 0xc2,
505 	0x0f, 0xb7, 0xa6, 0xc2, 0x0f, 0xbe, 0x58, 0x04, 0x0a, 0x00, 0x01, 0x00,
506 	0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x19,
507 	0x98, 0xc6, 0x19, 0x93, 0x44, 0x19, 0xa2, 0x01, 0x17, 0x84, 0x00, 0x04,
508 	0x0a, 0x01, 0x01, 0x00, 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00,
509 	0x00, 0x00, 0x42, 0x19, 0x98, 0xc7, 0x26, 0x5f, 0x28, 0x96, 0xf9, 0x01,
510 	0x17, 0x83, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x01, 0x00, 0x0a, 0x03, 0x02,
511 	0x02, 0x03, 0x00
512 };
513 
514 // #pragma mark - PageIconView
515 
516 
517 class URLInputGroup::PageIconView : public BView {
518 public:
519 	PageIconView()
520 		:
521 		BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
522 		fIcon(NULL)
523 	{
524 		SetDrawingMode(B_OP_ALPHA);
525 		SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
526 	}
527 
528 	~PageIconView()
529 	{
530 		delete fIcon;
531 	}
532 
533 	virtual void Draw(BRect updateRect)
534 	{
535 		BRect bounds(Bounds());
536 		BRect iconBounds(0, 0, 15, 15);
537 		iconBounds.OffsetTo(
538 			floorf((bounds.left + bounds.right
539 				- (iconBounds.left + iconBounds.right)) / 2 + 0.5f),
540 			floorf((bounds.top + bounds.bottom
541 				- (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f));
542 		DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
543 			B_FILTER_BITMAP_BILINEAR);
544 	}
545 
546 	virtual BSize MinSize()
547 	{
548 		return BSize(18, 18);
549 	}
550 
551 	virtual BSize MaxSize()
552 	{
553 		return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
554 	}
555 
556 	virtual BSize PreferredSize()
557 	{
558 		return MinSize();
559 	}
560 
561 	void SetIcon(const BBitmap* icon)
562 	{
563 		delete fIcon;
564 		if (icon)
565 			fIcon = new BBitmap(icon);
566 		else {
567 			fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32);
568 			BIconUtils::GetVectorIcon(kPlaceholderIcon,
569 				sizeof(kPlaceholderIcon), fIcon);
570 		}
571 		Invalidate();
572 	}
573 
574 private:
575 	BBitmap* fIcon;
576 };
577 
578 
579 // #pragma mark - URLInputGroup
580 
581 
582 URLInputGroup::URLInputGroup(BMessage* goMessage)
583 	:
584 	BGroupView(B_HORIZONTAL, 0.0),
585 	fWindowActive(false)
586 {
587 	GroupLayout()->SetInsets(2, 2, 2, 2);
588 
589 	fIconView = new PageIconView();
590 	GroupLayout()->AddView(fIconView, 0.0f);
591 
592 	fTextView = new URLTextView(this);
593 	AddChild(fTextView);
594 
595 	AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER));
596 
597 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from
598 // resources?
599 //	fGoButton = new BitmapButton("kActionGo", NULL);
600 	fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth,
601 		kGoBitmapHeight, kGoBitmapFormat, goMessage);
602 	GroupLayout()->AddView(fGoButton, 0.0f);
603 
604 	SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
605 	SetLowColor(ViewColor());
606 	SetViewColor(fTextView->ViewColor());
607 
608 	SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
609 		B_ALIGN_VERTICAL_CENTER));
610 }
611 
612 
613 URLInputGroup::~URLInputGroup()
614 {
615 }
616 
617 
618 void
619 URLInputGroup::AttachedToWindow()
620 {
621 	BGroupView::AttachedToWindow();
622 	fWindowActive = Window()->IsActive();
623 }
624 
625 
626 void
627 URLInputGroup::WindowActivated(bool active)
628 {
629 	BGroupView::WindowActivated(active);
630 	if (fWindowActive != active) {
631 		fWindowActive = active;
632 		Invalidate();
633 	}
634 }
635 
636 
637 void
638 URLInputGroup::Draw(BRect updateRect)
639 {
640 	BRect bounds(Bounds());
641 	rgb_color base(LowColor());
642 	uint32 flags = 0;
643 	if (fWindowActive && fTextView->IsFocus())
644 		flags |= BControlLook::B_FOCUSED;
645 	be_control_look->DrawTextControlBorder(this, bounds, updateRect, base,
646 		flags);
647 }
648 
649 
650 void
651 URLInputGroup::MakeFocus(bool focus)
652 {
653 	// Forward this to the text view, we never accept focus ourselves.
654 	fTextView->MakeFocus(focus);
655 }
656 
657 
658 BTextView*
659 URLInputGroup::TextView() const
660 {
661 	return fTextView;
662 }
663 
664 
665 void
666 URLInputGroup::SetText(const char* text)
667 {
668 	if (!text || !Text() || strcmp(Text(), text) != 0) {
669 		fTextView->SetUpdateAutoCompleterChoices(false);
670 		fTextView->SetText(text);
671 		fTextView->SetUpdateAutoCompleterChoices(true);
672 	}
673 }
674 
675 
676 const char*
677 URLInputGroup::Text() const
678 {
679 	return fTextView->Text();
680 }
681 
682 
683 BButton*
684 URLInputGroup::GoButton() const
685 {
686 	return fGoButton;
687 }
688 
689 
690 void
691 URLInputGroup::SetPageIcon(const BBitmap* icon)
692 {
693 	fIconView->SetIcon(icon);
694 }
695 
696