xref: /haiku/src/apps/webpositive/URLInputGroup.cpp (revision 692fe5550319c0342c9525e674b7f10105d977ee)
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 		SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
527 	}
528 
529 	~PageIconView()
530 	{
531 		delete fIcon;
532 	}
533 
534 	virtual void Draw(BRect updateRect)
535 	{
536 		BRect bounds(Bounds());
537 		BRect iconBounds(0, 0, 15, 15);
538 		iconBounds.OffsetTo(
539 			floorf((bounds.left + bounds.right
540 				- (iconBounds.left + iconBounds.right)) / 2 + 0.5f),
541 			floorf((bounds.top + bounds.bottom
542 				- (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f));
543 		DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
544 			B_FILTER_BITMAP_BILINEAR);
545 	}
546 
547 	virtual BSize MinSize()
548 	{
549 		return BSize(18, 18);
550 	}
551 
552 	virtual BSize MaxSize()
553 	{
554 		return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
555 	}
556 
557 	virtual BSize PreferredSize()
558 	{
559 		return MinSize();
560 	}
561 
562 	void SetIcon(const BBitmap* icon)
563 	{
564 		delete fIcon;
565 		if (icon)
566 			fIcon = new BBitmap(icon);
567 		else {
568 			fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32);
569 			BIconUtils::GetVectorIcon(kPlaceholderIcon,
570 				sizeof(kPlaceholderIcon), fIcon);
571 		}
572 		Invalidate();
573 	}
574 
575 private:
576 	BBitmap* fIcon;
577 };
578 
579 
580 // #pragma mark - URLInputGroup
581 
582 
583 URLInputGroup::URLInputGroup(BMessage* goMessage)
584 	:
585 	BGroupView(B_HORIZONTAL, 0.0),
586 	fWindowActive(false)
587 {
588 	GroupLayout()->SetInsets(2, 2, 2, 2);
589 
590 	fIconView = new PageIconView();
591 	GroupLayout()->AddView(fIconView, 0.0f);
592 
593 	fTextView = new URLTextView(this);
594 	AddChild(fTextView);
595 
596 	AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER));
597 
598 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from
599 // resources?
600 //	fGoButton = new BitmapButton("kActionGo", NULL);
601 	fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth,
602 		kGoBitmapHeight, kGoBitmapFormat, goMessage);
603 	GroupLayout()->AddView(fGoButton, 0.0f);
604 
605 	SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
606 	SetLowColor(ViewColor());
607 	SetViewColor(fTextView->ViewColor());
608 
609 	SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
610 		B_ALIGN_VERTICAL_CENTER));
611 }
612 
613 
614 URLInputGroup::~URLInputGroup()
615 {
616 }
617 
618 
619 void
620 URLInputGroup::AttachedToWindow()
621 {
622 	BGroupView::AttachedToWindow();
623 	fWindowActive = Window()->IsActive();
624 }
625 
626 
627 void
628 URLInputGroup::WindowActivated(bool active)
629 {
630 	BGroupView::WindowActivated(active);
631 	if (fWindowActive != active) {
632 		fWindowActive = active;
633 		Invalidate();
634 	}
635 }
636 
637 
638 void
639 URLInputGroup::Draw(BRect updateRect)
640 {
641 	BRect bounds(Bounds());
642 	rgb_color base(LowColor());
643 	uint32 flags = 0;
644 	if (fWindowActive && fTextView->IsFocus())
645 		flags |= BControlLook::B_FOCUSED;
646 	be_control_look->DrawTextControlBorder(this, bounds, updateRect, base,
647 		flags);
648 }
649 
650 
651 void
652 URLInputGroup::MakeFocus(bool focus)
653 {
654 	// Forward this to the text view, we never accept focus ourselves.
655 	fTextView->MakeFocus(focus);
656 }
657 
658 
659 BTextView*
660 URLInputGroup::TextView() const
661 {
662 	return fTextView;
663 }
664 
665 
666 void
667 URLInputGroup::SetText(const char* text)
668 {
669 	// Ignore setting the text, if the user is currently editing the URL.
670 	if (fWindowActive && fTextView->IsFocus())
671 		return;
672 
673 	if (!text || !Text() || strcmp(Text(), text) != 0) {
674 		fTextView->SetUpdateAutoCompleterChoices(false);
675 		fTextView->SetText(text);
676 		fTextView->SetUpdateAutoCompleterChoices(true);
677 	}
678 }
679 
680 
681 const char*
682 URLInputGroup::Text() const
683 {
684 	return fTextView->Text();
685 }
686 
687 
688 BButton*
689 URLInputGroup::GoButton() const
690 {
691 	return fGoButton;
692 }
693 
694 
695 void
696 URLInputGroup::SetPageIcon(const BBitmap* icon)
697 {
698 	fIconView->SetIcon(icon);
699 }
700 
701