xref: /haiku/src/apps/webpositive/URLInputGroup.cpp (revision 3995592cdf304335132305e27c40cbb0b1ac46e3)
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 			bool				fUpdateAutoCompleterChoices;
173 };
174 
175 
176 URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent)
177 	:
178 	BTextView("url"),
179 	fURLInputGroup(parent),
180 	fURLAutoCompleter(new TextViewCompleter(this,
181 		new BrowsingHistoryChoiceModel())),
182 	fUpdateAutoCompleterChoices(true)
183 {
184 	MakeResizable(true);
185 	SetStylable(true);
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::FrameResized(float width, float height)
213 {
214 	BTextView::FrameResized(width, height);
215 	_AlignTextRect();
216 }
217 
218 
219 void
220 URLInputGroup::URLTextView::MouseDown(BPoint where)
221 {
222 	bool wasFocus = IsFocus();
223 	if (!wasFocus)
224 		MakeFocus(true);
225 
226 	int32 buttons;
227 	if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
228 		buttons = B_PRIMARY_MOUSE_BUTTON;
229 
230 	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
231 		// Display context menu
232 		int32 selectionStart;
233 		int32 selectionEnd;
234 		GetSelection(&selectionStart, &selectionEnd);
235 		bool canCutOrCopy = selectionEnd > selectionStart;
236 
237 		bool canPaste = false;
238 		if (be_clipboard->Lock()) {
239 			if (BMessage* data = be_clipboard->Data())
240 				canPaste = data->HasData("text/plain", B_MIME_TYPE);
241 			be_clipboard->Unlock();
242 		}
243 
244 		BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"),
245 			new BMessage(B_CUT));
246 		BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"),
247 			new BMessage(B_COPY));
248 		BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
249 			new BMessage(B_PASTE));
250 		BMenuItem* clearItem = new BMenuItem(B_TRANSLATE("Clear"),
251 			new BMessage(MSG_CLEAR));
252 		cutItem->SetEnabled(canCutOrCopy);
253 		copyItem->SetEnabled(canCutOrCopy);
254 		pasteItem->SetEnabled(canPaste);
255 		clearItem->SetEnabled(strlen(Text()) > 0);
256 
257 		BPopUpMenu* menu = new BPopUpMenu("url context");
258 		menu->AddItem(cutItem);
259 		menu->AddItem(copyItem);
260 		menu->AddItem(pasteItem);
261 		menu->AddItem(clearItem);
262 
263 		menu->SetTargetForItems(this);
264 		menu->Go(ConvertToScreen(where), true, true, true);
265 		return;
266 	}
267 
268 	// Only pass through to base class if we already have focus.
269 	if (!wasFocus)
270 		return;
271 
272 	BTextView::MouseDown(where);
273 }
274 
275 
276 void
277 URLInputGroup::URLTextView::KeyDown(const char* bytes, int32 numBytes)
278 {
279 	switch (bytes[0]) {
280 		case B_TAB:
281 			BView::KeyDown(bytes, numBytes);
282 			break;
283 
284 		case B_ESCAPE:
285 			// Text already unlocked && replaced in BrowserWindow,
286 			// now select it.
287 			SelectAll();
288 			break;
289 
290 		case B_RETURN:
291 			// Don't let this through to the text view.
292 			break;
293 
294 		default:
295 		{
296 			BString currentText = Text();
297 			BTextView::KeyDown(bytes, numBytes);
298 			// Lock the URL input if it was modified
299 			if (!fURLInputGroup->IsURLInputLocked()
300 				&& Text() != currentText)
301 				fURLInputGroup->LockURLInput();
302 			break;
303 		}
304 	}
305 }
306 
307 void
308 URLInputGroup::URLTextView::MakeFocus(bool focus)
309 {
310 	// Unlock the URL input if focus was lost.
311 	if (!focus)
312 		fURLInputGroup->LockURLInput(false);
313 
314 	if (focus == IsFocus())
315 		return;
316 
317 	BTextView::MakeFocus(focus);
318 
319 	if (focus)
320 		SelectAll();
321 
322 	fURLInputGroup->Invalidate();
323 }
324 
325 
326 BSize
327 URLInputGroup::URLTextView::MinSize()
328 {
329 	BSize min;
330 	min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset);
331 		// we always add at least one pixel vertical inset top/bottom for
332 		// the text rect.
333 	min.width = min.height * 3;
334 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
335 }
336 
337 
338 BSize
339 URLInputGroup::URLTextView::MaxSize()
340 {
341 	BSize max(MinSize());
342 	max.width = B_SIZE_UNLIMITED;
343 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
344 }
345 
346 
347 void
348 URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update)
349 {
350 	fUpdateAutoCompleterChoices = update;
351 }
352 
353 
354 void
355 URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength,
356 	int32 inOffset, const text_run_array* inRuns)
357 {
358 	// Filter all line breaks, note that inText is not terminated.
359 	if (inLength == 1) {
360 		if (*inText == '\n' || *inText == '\r')
361 			BTextView::InsertText(" ", 1, inOffset, inRuns);
362 		else
363 			BTextView::InsertText(inText, 1, inOffset, inRuns);
364 	} else {
365 		BString filteredText(inText, inLength);
366 		filteredText.ReplaceAll('\n', ' ');
367 		filteredText.ReplaceAll('\r', ' ');
368 		BTextView::InsertText(filteredText.String(), inLength, inOffset,
369 			inRuns);
370 	}
371 
372 	// Make the base URL part bold.
373 	BString text(Text(), TextLength());
374 	int32 baseUrlStart = text.FindFirst("://");
375 	if (baseUrlStart >= 0)
376 		baseUrlStart += 3;
377 	else
378 		baseUrlStart = 0;
379 	int32 baseUrlEnd = text.FindFirst("/", baseUrlStart);
380 	if (baseUrlEnd < 0)
381 		baseUrlEnd = TextLength();
382 
383 	BFont font;
384 	GetFont(&font);
385 	const rgb_color black = (rgb_color) { 0, 0, 0, 255 };
386 	const rgb_color gray = (rgb_color) { 60, 60, 60, 255 };
387 	if (baseUrlStart > 0)
388 		SetFontAndColor(0, baseUrlStart, &font, B_FONT_ALL, &gray);
389 	if (baseUrlEnd > baseUrlStart) {
390 		font.SetFace(B_BOLD_FACE);
391 		SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black);
392 	}
393 	if (baseUrlEnd < TextLength()) {
394 		font.SetFace(B_REGULAR_FACE);
395 		SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray);
396 	}
397 
398 	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
399 }
400 
401 
402 void
403 URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
404 {
405 	BTextView::DeleteText(fromOffset, toOffset);
406 
407 	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
408 }
409 
410 
411 void
412 URLInputGroup::URLTextView::_AlignTextRect()
413 {
414 	// Layout the text rect to be in the middle, normally this means there
415 	// is one pixel spacing on each side.
416 	BRect textRect(Bounds());
417 	textRect.left = 0.0;
418 	float vInset = max_c(1,
419 		floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5));
420 	float hInset = kHorizontalTextRectInset;
421 
422 	if (be_control_look)
423 		hInset = be_control_look->DefaultLabelSpacing();
424 
425 	textRect.InsetBy(hInset, vInset);
426 	SetTextRect(textRect);
427 }
428 
429 
430 const uint32 kGoBitmapWidth = 14;
431 const uint32 kGoBitmapHeight = 14;
432 const color_space kGoBitmapFormat = B_RGBA32;
433 
434 const unsigned char kGoBitmapBits[] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x00, 0x00, 0x00, 0x00,
440 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
443 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
450 	0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
451 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
452 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
453 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
454 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
455 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
456 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0,
457 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
458 	0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed,
459 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
460 	0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
461 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
462 	0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
463 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
464 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
465 	0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02,
466 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
467 	0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
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, 0x17, 0x17, 0x17, 0x21,
471 	0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 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, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1,
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 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
484 };
485 
486 
487 const unsigned char kPlaceholderIcon[] = {
488 	0x6e, 0x63, 0x69, 0x66, 0x04, 0x04, 0x00, 0x66, 0x03, 0x00, 0x3f, 0x80,
489 	0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x3d, 0xa6, 0x64, 0xc2, 0x19,
490 	0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 0x64, 0x49, 0xac, 0xcc, 0x00, 0xab,
491 	0xd5, 0xff, 0xff, 0x00, 0x6c, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x00, 0x00,
492 	0x00, 0x3d, 0x26, 0x64, 0xc2, 0x19, 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce,
493 	0x64, 0x49, 0xac, 0xcc, 0x00, 0x80, 0xff, 0x80, 0xff, 0x00, 0xb2, 0x00,
494 	0x04, 0x02, 0x04, 0x34, 0x22, 0xbd, 0x9b, 0x22, 0xb8, 0x53, 0x22, 0x28,
495 	0x2e, 0x28, 0xb5, 0xef, 0x28, 0xbb, 0x37, 0x34, 0x3a, 0xb8, 0x53, 0x3a,
496 	0xbd, 0x9b, 0x3a, 0x40, 0x2e, 0x40, 0xbb, 0x37, 0x40, 0xb5, 0xef, 0x02,
497 	0x08, 0xbe, 0xb6, 0xb4, 0xac, 0xc1, 0x46, 0xb4, 0xac, 0xbc, 0x25, 0xb4,
498 	0xac, 0xb8, 0x09, 0xb7, 0x35, 0xb9, 0xcf, 0xb5, 0xa0, 0xb8, 0x05, 0xbe,
499 	0xb6, 0x35, 0xc2, 0xe5, 0x35, 0xbe, 0xb6, 0x35, 0xc5, 0x68, 0xb8, 0x09,
500 	0xc6, 0x36, 0xb8, 0x09, 0xc6, 0x36, 0xb9, 0xcf, 0xc7, 0xca, 0xbe, 0xb6,
501 	0xc8, 0xc1, 0xbc, 0x25, 0xc8, 0xc1, 0xc1, 0xb3, 0xc8, 0xc1, 0xc6, 0x3c,
502 	0xc5, 0x5b, 0xc4, 0x65, 0xc7, 0x70, 0xc6, 0x3e, 0xbe, 0xb6, 0xc2, 0x0f,
503 	0xba, 0x87, 0xc2, 0x0f, 0xbe, 0xb6, 0xc2, 0x0f, 0xb8, 0x05, 0xc5, 0x64,
504 	0xb7, 0x37, 0xc5, 0x64, 0xb7, 0x37, 0xc3, 0x9e, 0xb5, 0xa2, 0x02, 0x04,
505 	0xb8, 0x09, 0xb7, 0x35, 0xb8, 0x05, 0xbe, 0xb6, 0xb5, 0xf8, 0xb9, 0x0c,
506 	0xb4, 0xac, 0xbe, 0xb6, 0xb4, 0xac, 0xbb, 0xba, 0xb4, 0xac, 0xc1, 0xb1,
507 	0xb8, 0x09, 0xc6, 0x36, 0xb5, 0xf8, 0xc4, 0x5e, 0xb9, 0xcf, 0xc7, 0xca,
508 	0x35, 0xc2, 0xe5, 0x35, 0xc5, 0x68, 0x35, 0xbe, 0xb6, 0x02, 0x04, 0x4d,
509 	0x51, 0xc4, 0xf2, 0xbf, 0x04, 0x53, 0x4e, 0xc8, 0xc1, 0xbe, 0x58, 0xc8,
510 	0xc1, 0xc1, 0x55, 0xc8, 0xc1, 0xbb, 0x5d, 0xc5, 0x64, 0xb6, 0xd9, 0xc7,
511 	0x75, 0xb8, 0xb0, 0xc3, 0x9e, 0xb5, 0x44, 0xc2, 0x0f, 0xba, 0x29, 0xc2,
512 	0x0f, 0xb7, 0xa6, 0xc2, 0x0f, 0xbe, 0x58, 0x04, 0x0a, 0x00, 0x01, 0x00,
513 	0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x19,
514 	0x98, 0xc6, 0x19, 0x93, 0x44, 0x19, 0xa2, 0x01, 0x17, 0x84, 0x00, 0x04,
515 	0x0a, 0x01, 0x01, 0x00, 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00,
516 	0x00, 0x00, 0x42, 0x19, 0x98, 0xc7, 0x26, 0x5f, 0x28, 0x96, 0xf9, 0x01,
517 	0x17, 0x83, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x01, 0x00, 0x0a, 0x03, 0x02,
518 	0x02, 0x03, 0x00
519 };
520 
521 // #pragma mark - PageIconView
522 
523 
524 class URLInputGroup::PageIconView : public BView {
525 public:
526 	PageIconView()
527 		:
528 		BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
529 		fIcon(NULL)
530 	{
531 		SetDrawingMode(B_OP_ALPHA);
532 		SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
533 		SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
534 	}
535 
536 	~PageIconView()
537 	{
538 		delete fIcon;
539 	}
540 
541 	virtual void Draw(BRect updateRect)
542 	{
543 		BRect bounds(Bounds());
544 		BRect iconBounds(0, 0, 15, 15);
545 		iconBounds.OffsetTo(
546 			floorf((bounds.left + bounds.right
547 				- (iconBounds.left + iconBounds.right)) / 2 + 0.5f),
548 			floorf((bounds.top + bounds.bottom
549 				- (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f));
550 		DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
551 			B_FILTER_BITMAP_BILINEAR);
552 	}
553 
554 	virtual BSize MinSize()
555 	{
556 		return BSize(18, 18);
557 	}
558 
559 	virtual BSize MaxSize()
560 	{
561 		return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
562 	}
563 
564 	virtual BSize PreferredSize()
565 	{
566 		return MinSize();
567 	}
568 
569 	void SetIcon(const BBitmap* icon)
570 	{
571 		delete fIcon;
572 		if (icon)
573 			fIcon = new BBitmap(icon);
574 		else {
575 			fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32);
576 			BIconUtils::GetVectorIcon(kPlaceholderIcon,
577 				sizeof(kPlaceholderIcon), fIcon);
578 		}
579 		Invalidate();
580 	}
581 
582 private:
583 	BBitmap* fIcon;
584 };
585 
586 
587 // #pragma mark - URLInputGroup
588 
589 
590 URLInputGroup::URLInputGroup(BMessage* goMessage)
591 	:
592 	BGroupView(B_HORIZONTAL, 0.0),
593 	fWindowActive(false),
594 	fURLLocked(false)
595 {
596 	GroupLayout()->SetInsets(2, 2, 2, 2);
597 
598 	fIconView = new PageIconView();
599 	GroupLayout()->AddView(fIconView, 0.0f);
600 
601 	fTextView = new URLTextView(this);
602 	AddChild(fTextView);
603 
604 	AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER));
605 
606 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from
607 // resources?
608 //	fGoButton = new BitmapButton("kActionGo", NULL);
609 	fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth,
610 		kGoBitmapHeight, kGoBitmapFormat, goMessage);
611 	GroupLayout()->AddView(fGoButton, 0.0f);
612 
613 	SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
614 	SetLowColor(ViewColor());
615 	SetViewColor(fTextView->ViewColor());
616 
617 	SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
618 		B_ALIGN_VERTICAL_CENTER));
619 
620 }
621 
622 
623 URLInputGroup::~URLInputGroup()
624 {
625 }
626 
627 
628 void
629 URLInputGroup::AttachedToWindow()
630 {
631 	BGroupView::AttachedToWindow();
632 	fWindowActive = Window()->IsActive();
633 }
634 
635 
636 void
637 URLInputGroup::WindowActivated(bool active)
638 {
639 	BGroupView::WindowActivated(active);
640 	if (fWindowActive != active) {
641 		fWindowActive = active;
642 		Invalidate();
643 	}
644 }
645 
646 
647 void
648 URLInputGroup::Draw(BRect updateRect)
649 {
650 	BRect bounds(Bounds());
651 	rgb_color base(LowColor());
652 	uint32 flags = 0;
653 	if (fWindowActive && fTextView->IsFocus())
654 		flags |= BControlLook::B_FOCUSED;
655 	be_control_look->DrawTextControlBorder(this, bounds, updateRect, base,
656 		flags);
657 }
658 
659 
660 void
661 URLInputGroup::MakeFocus(bool focus)
662 {
663 	// Forward this to the text view, we never accept focus ourselves.
664 	fTextView->MakeFocus(focus);
665 }
666 
667 
668 BTextView*
669 URLInputGroup::TextView() const
670 {
671 	return fTextView;
672 }
673 
674 
675 void
676 URLInputGroup::SetText(const char* text)
677 {
678 	// Ignore setting the text, if the input is locked.
679 	if (fURLLocked)
680 		return;
681 
682 	if (!text || !Text() || strcmp(Text(), text) != 0) {
683 		fTextView->SetUpdateAutoCompleterChoices(false);
684 		fTextView->SetText(text);
685 		fTextView->SetUpdateAutoCompleterChoices(true);
686 	}
687 }
688 
689 
690 const char*
691 URLInputGroup::Text() const
692 {
693 	return fTextView->Text();
694 }
695 
696 
697 BButton*
698 URLInputGroup::GoButton() const
699 {
700 	return fGoButton;
701 }
702 
703 
704 void
705 URLInputGroup::SetPageIcon(const BBitmap* icon)
706 {
707 	fIconView->SetIcon(icon);
708 }
709 
710 
711 bool
712 URLInputGroup::IsURLInputLocked() const
713 {
714 	return fURLLocked;
715 }
716 
717 
718 void
719 URLInputGroup::LockURLInput(bool lock)
720 {
721 	fURLLocked = lock;
722 }
723