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