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 "BrowserWindow.h"
29 #include "BrowsingHistory.h"
30 #include "IconButton.h"
31 #include "IconUtils.h"
32 #include "TextViewCompleter.h"
33 #include "WebView.h"
34 #include "WebWindow.h"
35
36
37 #undef B_TRANSLATION_CONTEXT
38 #define B_TRANSLATION_CONTEXT "URL Bar"
39
40
41 class URLChoice : public BAutoCompleter::Choice {
42 public:
URLChoice(const BString & choiceText,const BString & displayText,int32 matchPos,int32 matchLen,int32 priority)43 URLChoice(const BString& choiceText, const BString& displayText,
44 int32 matchPos, int32 matchLen, int32 priority)
45 :
46 BAutoCompleter::Choice(choiceText, displayText, matchPos, matchLen),
47 fPriority(priority)
48 {
49 }
50
operator <(const URLChoice & other) const51 bool operator<(const URLChoice& other) const
52 {
53 if (fPriority > other.fPriority)
54 return true;
55 return DisplayText() < other.DisplayText();
56 }
57
operator ==(const URLChoice & other) const58 bool operator==(const URLChoice& other) const
59 {
60 return fPriority == other.fPriority
61 && DisplayText() < other.DisplayText();
62 }
63
64 private:
65 int32 fPriority;
66 };
67
68
69 class BrowsingHistoryChoiceModel : public BAutoCompleter::ChoiceModel {
FetchChoicesFor(const BString & pattern)70 virtual void FetchChoicesFor(const BString& pattern)
71 {
72 int32 count = CountChoices();
73 for (int32 i = 0; i < count; i++) {
74 delete reinterpret_cast<BAutoCompleter::Choice*>(
75 fChoices.ItemAtFast(i));
76 }
77 fChoices.MakeEmpty();
78
79 // Search through BrowsingHistory for any matches.
80 BrowsingHistory* history = BrowsingHistory::DefaultInstance();
81 if (!history->Lock())
82 return;
83
84 BString lastBaseURL;
85 int32 priority = INT_MAX;
86
87 count = history->CountItems();
88 for (int32 i = 0; i < count; i++) {
89 BrowsingHistoryItem item = history->HistoryItemAt(i);
90 const BString& choiceText = item.URL();
91 int32 matchPos = choiceText.IFindFirst(pattern);
92 if (matchPos < 0)
93 continue;
94 if (lastBaseURL.Length() > 0
95 && choiceText.FindFirst(lastBaseURL) >= 0) {
96 priority--;
97 } else
98 priority = INT_MAX;
99 lastBaseURL = baseURL(choiceText);
100 fChoices.AddItem(new URLChoice(choiceText,
101 choiceText, matchPos, pattern.Length(), priority));
102 }
103
104 history->Unlock();
105
106 fChoices.SortItems(_CompareChoices);
107 }
108
CountChoices() const109 virtual int32 CountChoices() const
110 {
111 return fChoices.CountItems();
112 }
113
ChoiceAt(int32 index) const114 virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const
115 {
116 return reinterpret_cast<BAutoCompleter::Choice*>(
117 fChoices.ItemAt(index));
118 }
119
_CompareChoices(const void * a,const void * b)120 static int _CompareChoices(const void* a, const void* b)
121 {
122 const URLChoice* aChoice
123 = *reinterpret_cast<const URLChoice* const *>(a);
124 const URLChoice* bChoice
125 = *reinterpret_cast<const URLChoice* const *>(b);
126 if (*aChoice < *bChoice)
127 return -1;
128 else if (*aChoice == *bChoice)
129 return 0;
130 return 1;
131 }
132
133 private:
134 BList fChoices;
135 };
136
137
138 // #pragma mark - URLTextView
139
140
141 static const float kHorizontalTextRectInset = 4.0;
142
143
144 class URLInputGroup::URLTextView : public BTextView {
145 private:
146 static const uint32 MSG_CLEAR = 'cler';
147
148 public:
149 URLTextView(URLInputGroup* parent);
150 virtual ~URLTextView();
151
152 virtual void MessageReceived(BMessage* message);
153 virtual void MouseDown(BPoint where);
154 virtual void KeyDown(const char* bytes, int32 numBytes);
155 virtual void MakeFocus(bool focused = true);
156
157 virtual BSize MinSize();
158 virtual BSize MaxSize();
159
160 void SetUpdateAutoCompleterChoices(bool update);
161
162 protected:
163 virtual void InsertText(const char* inText, int32 inLength,
164 int32 inOffset,
165 const text_run_array* inRuns);
166 virtual void DeleteText(int32 fromOffset, int32 toOffset);
167
168 private:
169 URLInputGroup* fURLInputGroup;
170 TextViewCompleter* fURLAutoCompleter;
171 bool fUpdateAutoCompleterChoices;
172 };
173
174
URLTextView(URLInputGroup * parent)175 URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent)
176 :
177 BTextView("url"),
178 fURLInputGroup(parent),
179 fURLAutoCompleter(new TextViewCompleter(this,
180 new BrowsingHistoryChoiceModel())),
181 fUpdateAutoCompleterChoices(true)
182 {
183 MakeResizable(true);
184 SetStylable(true);
185 SetInsets(be_control_look->DefaultLabelSpacing(), 2, 0, 2);
186 fURLAutoCompleter->SetModificationsReported(true);
187 }
188
189
~URLTextView()190 URLInputGroup::URLTextView::~URLTextView()
191 {
192 delete fURLAutoCompleter;
193 }
194
195
196 void
MessageReceived(BMessage * message)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
MouseDown(BPoint where)212 URLInputGroup::URLTextView::MouseDown(BPoint where)
213 {
214 bool wasFocus = IsFocus();
215 if (!wasFocus)
216 MakeFocus(true);
217
218 int32 buttons;
219 if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
220 buttons = B_PRIMARY_MOUSE_BUTTON;
221
222 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
223 // Display context menu
224 int32 selectionStart;
225 int32 selectionEnd;
226 GetSelection(&selectionStart, &selectionEnd);
227 bool canCutOrCopy = selectionEnd > selectionStart;
228
229 bool canPaste = false;
230 if (be_clipboard->Lock()) {
231 if (BMessage* data = be_clipboard->Data())
232 canPaste = data->HasData("text/plain", B_MIME_TYPE);
233 be_clipboard->Unlock();
234 }
235
236 BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"),
237 new BMessage(B_CUT));
238 BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"),
239 new BMessage(B_COPY));
240 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
241 new BMessage(B_PASTE));
242 BMenuItem* clearItem = new BMenuItem(B_TRANSLATE("Clear"),
243 new BMessage(MSG_CLEAR));
244 cutItem->SetEnabled(canCutOrCopy);
245 copyItem->SetEnabled(canCutOrCopy);
246 pasteItem->SetEnabled(canPaste);
247 clearItem->SetEnabled(strlen(Text()) > 0);
248
249 BPopUpMenu* menu = new BPopUpMenu("url context");
250 menu->AddItem(cutItem);
251 menu->AddItem(copyItem);
252 menu->AddItem(pasteItem);
253 menu->AddItem(clearItem);
254
255 menu->SetTargetForItems(this);
256 menu->Go(ConvertToScreen(where), true, true, true);
257 return;
258 }
259
260 // Only pass through to base class if we already have focus.
261 if (!wasFocus)
262 return;
263
264 BTextView::MouseDown(where);
265 }
266
267
268 void
KeyDown(const char * bytes,int32 numBytes)269 URLInputGroup::URLTextView::KeyDown(const char* bytes, int32 numBytes)
270 {
271 switch (bytes[0]) {
272 case B_TAB:
273 BView::KeyDown(bytes, numBytes);
274 break;
275
276 case B_ESCAPE:
277 // Text already unlocked && replaced in BrowserWindow,
278 // now select it.
279 SelectAll();
280 break;
281
282 case B_RETURN:
283 // Don't let this through to the text view.
284 break;
285
286 default:
287 {
288 BString currentText = Text();
289 BTextView::KeyDown(bytes, numBytes);
290 // Lock the URL input if it was modified
291 if (!fURLInputGroup->IsURLInputLocked()
292 && Text() != currentText)
293 fURLInputGroup->LockURLInput();
294 break;
295 }
296 }
297 }
298
299 void
MakeFocus(bool focus)300 URLInputGroup::URLTextView::MakeFocus(bool focus)
301 {
302 // Unlock the URL input if focus was lost.
303 if (!focus)
304 fURLInputGroup->LockURLInput(false);
305
306 if (focus == IsFocus())
307 return;
308
309 BTextView::MakeFocus(focus);
310
311 if (focus)
312 SelectAll();
313
314 fURLInputGroup->Invalidate();
315 }
316
317
318 BSize
MinSize()319 URLInputGroup::URLTextView::MinSize()
320 {
321 BSize min;
322 min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset);
323 // we always add at least one pixel vertical inset top/bottom for
324 // the text rect.
325 min.width = min.height * 3;
326 return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
327 }
328
329
330 BSize
MaxSize()331 URLInputGroup::URLTextView::MaxSize()
332 {
333 BSize max(MinSize());
334 max.width = B_SIZE_UNLIMITED;
335 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
336 }
337
338
339 void
SetUpdateAutoCompleterChoices(bool update)340 URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update)
341 {
342 fUpdateAutoCompleterChoices = update;
343 }
344
345
346 void
InsertText(const char * inText,int32 inLength,int32 inOffset,const text_run_array * inRuns)347 URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength,
348 int32 inOffset, const text_run_array* inRuns)
349 {
350 // Filter all line breaks, note that inText is not terminated.
351 if (inLength == 1) {
352 if (*inText == '\n' || *inText == '\r')
353 BTextView::InsertText(" ", 1, inOffset, inRuns);
354 else
355 BTextView::InsertText(inText, 1, inOffset, inRuns);
356 } else {
357 BString filteredText(inText, inLength);
358 filteredText.ReplaceAll('\n', ' ');
359 filteredText.ReplaceAll('\r', ' ');
360 BTextView::InsertText(filteredText.String(), inLength, inOffset,
361 inRuns);
362 }
363
364 // Make the base URL part bold.
365 BString text(Text(), TextLength());
366 int32 baseUrlStart = text.FindFirst("://");
367 if (baseUrlStart >= 0)
368 baseUrlStart += 3;
369 else
370 baseUrlStart = 0;
371 int32 baseUrlEnd = text.FindFirst("/", baseUrlStart);
372 if (baseUrlEnd < 0)
373 baseUrlEnd = TextLength();
374
375 BFont font;
376 GetFont(&font);
377 const rgb_color hostColor = ui_color(B_DOCUMENT_TEXT_COLOR);
378 const rgb_color urlColor = tint_color(hostColor,
379 (hostColor.IsDark() ? B_LIGHTEN_1_TINT : B_DARKEN_1_TINT));
380 if (baseUrlStart > 0)
381 SetFontAndColor(0, baseUrlStart, &font, B_FONT_ALL, &urlColor);
382 if (baseUrlEnd > baseUrlStart) {
383 font.SetFace(B_BOLD_FACE);
384 SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL,
385 &hostColor);
386 }
387 if (baseUrlEnd < TextLength()) {
388 font.SetFace(B_REGULAR_FACE);
389 SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL,
390 &urlColor);
391 }
392
393 fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
394 }
395
396
397 void
DeleteText(int32 fromOffset,int32 toOffset)398 URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
399 {
400 BTextView::DeleteText(fromOffset, toOffset);
401
402 fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
403 }
404
405
406 const uint32 kGoBitmapWidth = 14;
407 const uint32 kGoBitmapHeight = 14;
408 const color_space kGoBitmapFormat = B_RGBA32;
409
410 const unsigned char kGoBitmapBits[] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x00, 0x00, 0x00, 0x00,
420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
421 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
423 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
424 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
425 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
426 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
427 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
428 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
430 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
431 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
432 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0,
433 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
434 0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed,
435 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21,
436 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 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, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
439 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff,
440 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
441 0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02,
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, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff,
444 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x17, 0x17, 0x17, 0x21,
447 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1,
451 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x17, 0x17, 0x17, 0x21, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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 };
461
462
463 const unsigned char kPlaceholderIcon[] = {
464 0x6e, 0x63, 0x69, 0x66, 0x04, 0x04, 0x00, 0x66, 0x03, 0x00, 0x3f, 0x80,
465 0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x3d, 0xa6, 0x64, 0xc2, 0x19,
466 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 0x64, 0x49, 0xac, 0xcc, 0x00, 0xab,
467 0xd5, 0xff, 0xff, 0x00, 0x6c, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x00, 0x00,
468 0x00, 0x3d, 0x26, 0x64, 0xc2, 0x19, 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce,
469 0x64, 0x49, 0xac, 0xcc, 0x00, 0x80, 0xff, 0x80, 0xff, 0x00, 0xb2, 0x00,
470 0x04, 0x02, 0x04, 0x34, 0x22, 0xbd, 0x9b, 0x22, 0xb8, 0x53, 0x22, 0x28,
471 0x2e, 0x28, 0xb5, 0xef, 0x28, 0xbb, 0x37, 0x34, 0x3a, 0xb8, 0x53, 0x3a,
472 0xbd, 0x9b, 0x3a, 0x40, 0x2e, 0x40, 0xbb, 0x37, 0x40, 0xb5, 0xef, 0x02,
473 0x08, 0xbe, 0xb6, 0xb4, 0xac, 0xc1, 0x46, 0xb4, 0xac, 0xbc, 0x25, 0xb4,
474 0xac, 0xb8, 0x09, 0xb7, 0x35, 0xb9, 0xcf, 0xb5, 0xa0, 0xb8, 0x05, 0xbe,
475 0xb6, 0x35, 0xc2, 0xe5, 0x35, 0xbe, 0xb6, 0x35, 0xc5, 0x68, 0xb8, 0x09,
476 0xc6, 0x36, 0xb8, 0x09, 0xc6, 0x36, 0xb9, 0xcf, 0xc7, 0xca, 0xbe, 0xb6,
477 0xc8, 0xc1, 0xbc, 0x25, 0xc8, 0xc1, 0xc1, 0xb3, 0xc8, 0xc1, 0xc6, 0x3c,
478 0xc5, 0x5b, 0xc4, 0x65, 0xc7, 0x70, 0xc6, 0x3e, 0xbe, 0xb6, 0xc2, 0x0f,
479 0xba, 0x87, 0xc2, 0x0f, 0xbe, 0xb6, 0xc2, 0x0f, 0xb8, 0x05, 0xc5, 0x64,
480 0xb7, 0x37, 0xc5, 0x64, 0xb7, 0x37, 0xc3, 0x9e, 0xb5, 0xa2, 0x02, 0x04,
481 0xb8, 0x09, 0xb7, 0x35, 0xb8, 0x05, 0xbe, 0xb6, 0xb5, 0xf8, 0xb9, 0x0c,
482 0xb4, 0xac, 0xbe, 0xb6, 0xb4, 0xac, 0xbb, 0xba, 0xb4, 0xac, 0xc1, 0xb1,
483 0xb8, 0x09, 0xc6, 0x36, 0xb5, 0xf8, 0xc4, 0x5e, 0xb9, 0xcf, 0xc7, 0xca,
484 0x35, 0xc2, 0xe5, 0x35, 0xc5, 0x68, 0x35, 0xbe, 0xb6, 0x02, 0x04, 0x4d,
485 0x51, 0xc4, 0xf2, 0xbf, 0x04, 0x53, 0x4e, 0xc8, 0xc1, 0xbe, 0x58, 0xc8,
486 0xc1, 0xc1, 0x55, 0xc8, 0xc1, 0xbb, 0x5d, 0xc5, 0x64, 0xb6, 0xd9, 0xc7,
487 0x75, 0xb8, 0xb0, 0xc3, 0x9e, 0xb5, 0x44, 0xc2, 0x0f, 0xba, 0x29, 0xc2,
488 0x0f, 0xb7, 0xa6, 0xc2, 0x0f, 0xbe, 0x58, 0x04, 0x0a, 0x00, 0x01, 0x00,
489 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x19,
490 0x98, 0xc6, 0x19, 0x93, 0x44, 0x19, 0xa2, 0x01, 0x17, 0x84, 0x00, 0x04,
491 0x0a, 0x01, 0x01, 0x00, 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x42, 0x19, 0x98, 0xc7, 0x26, 0x5f, 0x28, 0x96, 0xf9, 0x01,
493 0x17, 0x83, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x01, 0x00, 0x0a, 0x03, 0x02,
494 0x02, 0x03, 0x00
495 };
496
497 // #pragma mark - PageIconView
498
499
500 class URLInputGroup::PageIconView : public BView {
501 public:
PageIconView()502 PageIconView()
503 :
504 BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
505 fIcon(NULL),
506 fClickPoint(-1, 0),
507 fPageIconSet(false)
508 {
509 SetDrawingMode(B_OP_ALPHA);
510 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
511 SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
512 }
513
~PageIconView()514 ~PageIconView()
515 {
516 delete fIcon;
517 }
518
Draw(BRect updateRect)519 virtual void Draw(BRect updateRect)
520 {
521 BRect bounds(Bounds());
522 BRect iconBounds(0, 0, 15, 15);
523 iconBounds.OffsetTo(
524 floorf((bounds.left + bounds.right
525 - (iconBounds.left + iconBounds.right)) / 2 + 0.5f),
526 floorf((bounds.top + bounds.bottom
527 - (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f));
528 DrawBitmap(fIcon, fIcon->Bounds(), iconBounds,
529 B_FILTER_BITMAP_BILINEAR);
530 }
531
MinSize()532 virtual BSize MinSize()
533 {
534 return BSize(18, 18);
535 }
536
MaxSize()537 virtual BSize MaxSize()
538 {
539 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
540 }
541
PreferredSize()542 virtual BSize PreferredSize()
543 {
544 return MinSize();
545 }
546
MouseDown(BPoint where)547 void MouseDown(BPoint where)
548 {
549 int32 buttons;
550 if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) == B_OK) {
551 if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0) {
552 // Memorize click point for dragging
553 fClickPoint = where;
554 }
555 }
556 return;
557 }
558
MouseUp(BPoint where)559 void MouseUp(BPoint where)
560 {
561 fClickPoint.x = -1;
562 }
563
MouseMoved(BPoint where,uint32 code,const BMessage * dragMessage)564 virtual void MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
565 {
566 if (dragMessage != NULL)
567 return;
568
569 if (fClickPoint.x >= 0
570 && (fabs(where.x - fClickPoint.x) > 4 || fabs(where.y - fClickPoint.y) > 4)) {
571 // Start dragging
572 BPoint offset = fClickPoint - Frame().LeftTop();
573
574 const char* url = static_cast<URLInputGroup*>(Parent())->Text();
575 const char* title =
576 static_cast<BWebWindow*>(Window())->CurrentWebView()->MainFrameTitle();
577
578 // Validate the file name to be set for the clipping if user drags to Tracker.
579 BString fileName(title);
580 if (fileName.Length() == 0) {
581 fileName = url;
582 int32 leafPos = fileName.FindLast('/');
583 if (leafPos >= 0)
584 fileName.Remove(0, leafPos + 1);
585 }
586 fileName.ReplaceAll('/', '-');
587 fileName.Truncate(B_FILE_NAME_LENGTH - 1);
588
589 BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK,
590 B_CMAP8);
591 miniIcon.ImportBits(fIcon);
592 // TODO: obtain and send the large icon in addition to the mini icon.
593 // Currently PageUserData does not provide a function that returns this.
594
595 BMessage drag(B_SIMPLE_DATA);
596 drag.AddInt32("be:actions", B_COPY_TARGET);
597 drag.AddString("be:clip_name", fileName.String());
598 drag.AddString("be:filetypes", "application/x-vnd.Be-bookmark");
599 // Support the "Passing Data via File" protocol
600 drag.AddString("be:types", B_FILE_MIME_TYPE);
601 BMessage data(B_SIMPLE_DATA);
602 data.AddString("url", url);
603 data.AddString("title", title);
604 // The title may differ from the validated filename
605 if (fPageIconSet == true) {
606 // Don't bother sending the placeholder web icon, if that is all we have.
607 data.AddData("miniIcon", B_COLOR_8_BIT_TYPE, &miniIcon, sizeof(miniIcon));
608 }
609 drag.AddMessage("be:originator-data", &data);
610
611 BBitmap* iconClone = new BBitmap(fIcon);
612 // Needed because DragMessage will delete the bitmap when it's done.
613
614 DragMessage(&drag, iconClone, B_OP_ALPHA, offset);
615 }
616 return;
617 }
618
SetIcon(const BBitmap * icon)619 void SetIcon(const BBitmap* icon)
620 {
621 delete fIcon;
622 if (icon) {
623 fIcon = new BBitmap(icon);
624 fPageIconSet = true;
625 } else {
626 fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32);
627 BIconUtils::GetVectorIcon(kPlaceholderIcon,
628 sizeof(kPlaceholderIcon), fIcon);
629 fPageIconSet = false;
630 }
631 Invalidate();
632 }
633
634 private:
635 BBitmap* fIcon;
636 BPoint fClickPoint;
637 bool fPageIconSet;
638 };
639
640
641 // #pragma mark - URLInputGroup
642
643
URLInputGroup(BMessage * goMessage)644 URLInputGroup::URLInputGroup(BMessage* goMessage)
645 :
646 BGroupView(B_HORIZONTAL, 0.0),
647 fWindowActive(false),
648 fURLLocked(false)
649 {
650 GroupLayout()->SetInsets(2, 2, 2, 2);
651
652 fIconView = new PageIconView();
653 GroupLayout()->AddView(fIconView, 0.0f);
654
655 fTextView = new URLTextView(this);
656 AddChild(fTextView);
657
658 AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER));
659
660 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from
661 // resources?
662 // fGoButton = new BitmapButton("kActionGo", NULL);
663 fGoButton = new BBitmapButton(kGoBitmapBits, kGoBitmapWidth,
664 kGoBitmapHeight, kGoBitmapFormat, goMessage);
665 GroupLayout()->AddView(fGoButton, 0.0f);
666
667 SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
668 SetLowColor(ViewColor());
669 SetViewColor(fTextView->ViewColor());
670
671 SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
672 B_ALIGN_VERTICAL_CENTER));
673
674 }
675
676
~URLInputGroup()677 URLInputGroup::~URLInputGroup()
678 {
679 }
680
681
682 void
AttachedToWindow()683 URLInputGroup::AttachedToWindow()
684 {
685 BGroupView::AttachedToWindow();
686 fWindowActive = Window()->IsActive();
687 }
688
689
690 void
WindowActivated(bool active)691 URLInputGroup::WindowActivated(bool active)
692 {
693 BGroupView::WindowActivated(active);
694 if (fWindowActive != active) {
695 fWindowActive = active;
696 Invalidate();
697 }
698 }
699
700
701 void
Draw(BRect updateRect)702 URLInputGroup::Draw(BRect updateRect)
703 {
704 BRect bounds(Bounds());
705 rgb_color base(LowColor());
706 uint32 flags = 0;
707 if (fWindowActive && fTextView->IsFocus())
708 flags |= BControlLook::B_FOCUSED;
709 be_control_look->DrawTextControlBorder(this, bounds, updateRect, base,
710 flags);
711 }
712
713
714 void
MakeFocus(bool focus)715 URLInputGroup::MakeFocus(bool focus)
716 {
717 // Forward this to the text view, we never accept focus ourselves.
718 fTextView->MakeFocus(focus);
719 }
720
721
722 BTextView*
TextView() const723 URLInputGroup::TextView() const
724 {
725 return fTextView;
726 }
727
728
729 void
SetText(const char * text)730 URLInputGroup::SetText(const char* text)
731 {
732 // Ignore setting the text, if the input is locked.
733 if (fURLLocked)
734 return;
735
736 if (!text || !Text() || strcmp(Text(), text) != 0) {
737 fTextView->SetUpdateAutoCompleterChoices(false);
738 fTextView->SetText(text);
739 fTextView->SetUpdateAutoCompleterChoices(true);
740 }
741 }
742
743
744 const char*
Text() const745 URLInputGroup::Text() const
746 {
747 return fTextView->Text();
748 }
749
750
751 BButton*
GoButton() const752 URLInputGroup::GoButton() const
753 {
754 return fGoButton;
755 }
756
757
758 void
SetPageIcon(const BBitmap * icon)759 URLInputGroup::SetPageIcon(const BBitmap* icon)
760 {
761 fIconView->SetIcon(icon);
762 }
763
764
765 bool
IsURLInputLocked() const766 URLInputGroup::IsURLInputLocked() const
767 {
768 return fURLLocked;
769 }
770
771
772 void
LockURLInput(bool lock)773 URLInputGroup::LockURLInput(bool lock)
774 {
775 fURLLocked = lock;
776 }
777