xref: /haiku/src/apps/webpositive/URLInputGroup.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 "TextViewCompleter.h"
31 
32 
33 #undef B_TRANSLATION_CONTEXT
34 #define B_TRANSLATION_CONTEXT "URL Bar"
35 
36 
37 class URLChoice : public BAutoCompleter::Choice {
38 public:
39 	URLChoice(const BString& choiceText, const BString& displayText,
40 			int32 matchPos, int32 matchLen, int32 priority)
41 		:
42 		BAutoCompleter::Choice(choiceText, displayText, matchPos, matchLen),
43 		fPriority(priority)
44 	{
45 	}
46 
47 	bool operator<(const URLChoice& other) const
48 	{
49 		if (fPriority > other.fPriority)
50 			return true;
51 		return DisplayText() < other.DisplayText();
52 	}
53 
54 	bool operator==(const URLChoice& other) const
55 	{
56 		return fPriority == other.fPriority
57 			&& DisplayText() < other.DisplayText();
58 	}
59 
60 private:
61 	int32 fPriority;
62 };
63 
64 
65 class BrowsingHistoryChoiceModel : public BAutoCompleter::ChoiceModel {
66 	virtual void FetchChoicesFor(const BString& pattern)
67 	{
68 		int32 count = CountChoices();
69 		for (int32 i = 0; i < count; i++) {
70 			delete reinterpret_cast<BAutoCompleter::Choice*>(
71 				fChoices.ItemAtFast(i));
72 		}
73 		fChoices.MakeEmpty();
74 
75 		// Search through BrowsingHistory for any matches.
76 		BrowsingHistory* history = BrowsingHistory::DefaultInstance();
77 		if (!history->Lock())
78 			return;
79 
80 		BString lastBaseURL;
81 		int32 priority = INT_MAX;
82 
83 		count = history->CountItems();
84 		for (int32 i = 0; i < count; i++) {
85 			BrowsingHistoryItem item = history->HistoryItemAt(i);
86 			const BString& choiceText = item.URL();
87 			int32 matchPos = choiceText.IFindFirst(pattern);
88 			if (matchPos < 0)
89 				continue;
90 			if (lastBaseURL.Length() > 0
91 				&& choiceText.FindFirst(lastBaseURL) >= 0) {
92 				priority--;
93 			} else
94 				priority = INT_MAX;
95 			lastBaseURL = baseURL(choiceText);
96 			fChoices.AddItem(new URLChoice(choiceText,
97 				choiceText, matchPos, pattern.Length(), priority));
98 		}
99 
100 		history->Unlock();
101 
102 		fChoices.SortItems(_CompareChoices);
103 	}
104 
105 	virtual int32 CountChoices() const
106 	{
107 		return fChoices.CountItems();
108 	}
109 
110 	virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const
111 	{
112 		return reinterpret_cast<BAutoCompleter::Choice*>(
113 			fChoices.ItemAt(index));
114 	}
115 
116 	static int _CompareChoices(const void* a, const void* b)
117 	{
118 		const URLChoice* aChoice
119 			= *reinterpret_cast<const URLChoice* const *>(a);
120 		const URLChoice* bChoice
121 			= *reinterpret_cast<const URLChoice* const *>(b);
122 		if (*aChoice < *bChoice)
123 			return -1;
124 		else if (*aChoice == *bChoice)
125 			return 0;
126 		return 1;
127 	}
128 
129 private:
130 	BList fChoices;
131 };
132 
133 
134 // #pragma mark - URLTextView
135 
136 
137 static const float kHorizontalTextRectInset = 4.0;
138 
139 
140 class URLInputGroup::URLTextView : public BTextView {
141 private:
142 	static const uint32 MSG_CLEAR = 'cler';
143 
144 public:
145 								URLTextView(URLInputGroup* parent);
146 	virtual						~URLTextView();
147 
148 	virtual	void				MessageReceived(BMessage* message);
149 	virtual	void				FrameResized(float width, float height);
150 	virtual	void				MouseDown(BPoint where);
151 	virtual	void				KeyDown(const char* bytes, int32 numBytes);
152 	virtual	void				MakeFocus(bool focused = true);
153 
154 	virtual	BSize				MinSize();
155 	virtual	BSize				MaxSize();
156 
157 			void				SetUpdateAutoCompleterChoices(bool update);
158 
159 protected:
160 	virtual	void				InsertText(const char* inText, int32 inLength,
161 									int32 inOffset,
162 									const text_run_array* inRuns);
163 	virtual	void				DeleteText(int32 fromOffset, int32 toOffset);
164 
165 private:
166 			void				_AlignTextRect();
167 
168 private:
169 			URLInputGroup*		fURLInputGroup;
170 			TextViewCompleter*	fURLAutoCompleter;
171 			BString				fPreviousText;
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 	fPreviousText(""),
183 	fUpdateAutoCompleterChoices(true)
184 {
185 	MakeResizable(true);
186 	SetStylable(true);
187 	fURLAutoCompleter->SetModificationsReported(true);
188 }
189 
190 
191 URLInputGroup::URLTextView::~URLTextView()
192 {
193 	delete fURLAutoCompleter;
194 }
195 
196 
197 void
198 URLInputGroup::URLTextView::MessageReceived(BMessage* message)
199 {
200 	switch (message->what) {
201 		case MSG_CLEAR:
202 			SetText("");
203 			break;
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 			// Revert to text as it was when we received keyboard focus.
286 			SetText(fPreviousText.String());
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 			BTextView::KeyDown(bytes, numBytes);
296 			break;
297 	}
298 }
299 
300 void
301 URLInputGroup::URLTextView::MakeFocus(bool focus)
302 {
303 	if (focus == IsFocus())
304 		return;
305 
306 	BTextView::MakeFocus(focus);
307 
308 	if (focus) {
309 		fPreviousText = Text();
310 		SelectAll();
311 	}
312 
313 	fURLInputGroup->Invalidate();
314 }
315 
316 
317 BSize
318 URLInputGroup::URLTextView::MinSize()
319 {
320 	BSize min;
321 	min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset);
322 		// we always add at least one pixel vertical inset top/bottom for
323 		// the text rect.
324 	min.width = min.height * 3;
325 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
326 }
327 
328 
329 BSize
330 URLInputGroup::URLTextView::MaxSize()
331 {
332 	BSize max(MinSize());
333 	max.width = B_SIZE_UNLIMITED;
334 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
335 }
336 
337 
338 void
339 URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update)
340 {
341 	fUpdateAutoCompleterChoices = update;
342 }
343 
344 
345 void
346 URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength,
347 	int32 inOffset, const text_run_array* inRuns)
348 {
349 	// Filter all line breaks, note that inText is not terminated.
350 	if (inLength == 1) {
351 		if (*inText == '\n' || *inText == '\r')
352 			BTextView::InsertText(" ", 1, inOffset, inRuns);
353 		else
354 			BTextView::InsertText(inText, 1, inOffset, inRuns);
355 	} else {
356 		BString filteredText(inText, inLength);
357 		filteredText.ReplaceAll('\n', ' ');
358 		filteredText.ReplaceAll('\r', ' ');
359 		BTextView::InsertText(filteredText.String(), inLength, inOffset,
360 			inRuns);
361 	}
362 
363 	// Make the base URL part bold.
364 	BString text(Text(), TextLength());
365 	int32 baseUrlStart = text.FindFirst("://");
366 	if (baseUrlStart >= 0)
367 		baseUrlStart += 3;
368 	else
369 		baseUrlStart = 0;
370 	int32 baseUrlEnd = text.FindFirst("/", baseUrlStart);
371 	if (baseUrlEnd < 0)
372 		baseUrlEnd = TextLength();
373 	BFont font;
374 	GetFont(&font);
375 	const rgb_color black = (rgb_color) { 0, 0, 0, 255 };
376 	const rgb_color gray = (rgb_color) { 60, 60, 60, 255 };
377 	if (baseUrlStart > 0)
378 		SetFontAndColor(0, baseUrlStart - 1, &font, B_FONT_ALL, &gray);
379 	if (baseUrlEnd > baseUrlStart) {
380 		font.SetFace(B_BOLD_FACE);
381 		SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black);
382 	}
383 	if (baseUrlEnd < TextLength()) {
384 		font.SetFace(B_REGULAR_FACE);
385 		SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray);
386 	}
387 
388 	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
389 }
390 
391 
392 void
393 URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
394 {
395 	BTextView::DeleteText(fromOffset, toOffset);
396 
397 	fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
398 }
399 
400 
401 void
402 URLInputGroup::URLTextView::_AlignTextRect()
403 {
404 	// Layout the text rect to be in the middle, normally this means there
405 	// is one pixel spacing on each side.
406 	BRect textRect(Bounds());
407 	textRect.left = 0.0;
408 	float vInset = max_c(1,
409 		floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5));
410 	float hInset = kHorizontalTextRectInset;
411 
412 	if (be_control_look)
413 		hInset = be_control_look->DefaultLabelSpacing();
414 
415 	textRect.InsetBy(hInset, vInset);
416 	SetTextRect(textRect);
417 }
418 
419 
420 const uint32 kGoBitmapWidth = 14;
421 const uint32 kGoBitmapHeight = 14;
422 const color_space kGoBitmapFormat = B_RGBA32;
423 
424 const unsigned char kGoBitmapBits[] = {
425 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
426 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
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, 0x17, 0x17, 0x17, 0x21,
437 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
438 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 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 	0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
441 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
442 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
443 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
444 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
445 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0,
447 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448 	0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed,
449 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
450 	0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
451 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
452 	0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 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 	0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
455 	0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02,
456 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
457 	0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
458 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
459 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
460 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
461 	0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 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, 0x00, 0x00, 0x00, 0x00,
464 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
468 	0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 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, 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 };
475 
476 
477 // #pragma mark - PageIconView
478 
479 
480 class URLInputGroup::PageIconView : public BView {
481 public:
482 	PageIconView()
483 		:
484 		BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
485 		fIcon(NULL)
486 	{
487 		SetDrawingMode(B_OP_ALPHA);
488 		SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
489 	}
490 
491 	~PageIconView()
492 	{
493 		delete fIcon;
494 	}
495 
496 	virtual void Draw(BRect updateRect)
497 	{
498 		if (fIcon == NULL)
499 			return;
500 
501 		BRect bounds(Bounds());
502 		BRect iconBounds(0, 0, 15, 15);
503 		iconBounds.OffsetTo(
504 			floorf((bounds.left + bounds.right
505 				- (iconBounds.left + iconBounds.right)) / 2 + 0.5f),
506 			floorf((bounds.top + bounds.bottom
507 				- (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f));
508 		DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
509 			B_FILTER_BITMAP_BILINEAR);
510 	}
511 
512 	virtual BSize MinSize()
513 	{
514 		if (fIcon != NULL)
515 			return BSize(18, 18);
516 		return BSize(0, 0);
517 	}
518 
519 	virtual BSize MaxSize()
520 	{
521 		return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
522 	}
523 
524 	virtual BSize PreferredSize()
525 	{
526 		return MinSize();
527 	}
528 
529 	void SetIcon(const BBitmap* icon)
530 	{
531 		if (icon == NULL && fIcon == NULL)
532 			return;
533 		if (!(fIcon != NULL && icon != NULL))
534 			InvalidateLayout();
535 		delete fIcon;
536 		if (icon)
537 			fIcon = new BBitmap(icon);
538 		else
539 			fIcon = NULL;
540 		Invalidate();
541 	}
542 
543 private:
544 	BBitmap* fIcon;
545 };
546 
547 
548 // #pragma mark - URLInputGroup
549 
550 
551 URLInputGroup::URLInputGroup(BMessage* goMessage)
552 	:
553 	BGroupView(B_HORIZONTAL, 0.0),
554 	fWindowActive(false)
555 {
556 	GroupLayout()->SetInsets(2, 2, 2, 2);
557 
558 	fIconView = new PageIconView();
559 	GroupLayout()->AddView(fIconView, 0.0f);
560 
561 	fTextView = new URLTextView(this);
562 	AddChild(fTextView);
563 
564 	AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER));
565 
566 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from
567 // resources?
568 //	fGoButton = new BitmapButton("kActionGo", NULL);
569 	fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth,
570 		kGoBitmapHeight, kGoBitmapFormat, goMessage);
571 	GroupLayout()->AddView(fGoButton, 0.0f);
572 
573 	SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
574 	SetLowColor(ViewColor());
575 	SetViewColor(B_TRANSPARENT_COLOR);
576 
577 	SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
578 		B_ALIGN_VERTICAL_CENTER));
579 }
580 
581 
582 URLInputGroup::~URLInputGroup()
583 {
584 }
585 
586 
587 void
588 URLInputGroup::AttachedToWindow()
589 {
590 	BGroupView::AttachedToWindow();
591 	fWindowActive = Window()->IsActive();
592 }
593 
594 
595 void
596 URLInputGroup::WindowActivated(bool active)
597 {
598 	BGroupView::WindowActivated(active);
599 	if (fWindowActive != active) {
600 		fWindowActive = active;
601 		Invalidate();
602 	}
603 }
604 
605 
606 void
607 URLInputGroup::Draw(BRect updateRect)
608 {
609 	BRect bounds(Bounds());
610 	rgb_color base(LowColor());
611 	uint32 flags = 0;
612 	if (fWindowActive && fTextView->IsFocus())
613 		flags |= BControlLook::B_FOCUSED;
614 	be_control_look->DrawTextControlBorder(this, bounds, updateRect, base,
615 		flags);
616 }
617 
618 
619 void
620 URLInputGroup::MakeFocus(bool focus)
621 {
622 	// Forward this to the text view, we never accept focus ourselves.
623 	fTextView->MakeFocus(focus);
624 }
625 
626 
627 BTextView*
628 URLInputGroup::TextView() const
629 {
630 	return fTextView;
631 }
632 
633 
634 void
635 URLInputGroup::SetText(const char* text)
636 {
637 	if (!text || !Text() || strcmp(Text(), text) != 0) {
638 		fTextView->SetUpdateAutoCompleterChoices(false);
639 		fTextView->SetText(text);
640 		fTextView->SetUpdateAutoCompleterChoices(true);
641 	}
642 }
643 
644 
645 const char*
646 URLInputGroup::Text() const
647 {
648 	return fTextView->Text();
649 }
650 
651 
652 BButton*
653 URLInputGroup::GoButton() const
654 {
655 	return fGoButton;
656 }
657 
658 
659 void
660 URLInputGroup::SetPageIcon(const BBitmap* icon)
661 {
662 	fIconView->SetIcon(icon);
663 }
664 
665