1 /* 2 * Copyright 2010 Stephan Aßmus <superstippi@gmx.de> 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include "URLInputGroup.h" 7 8 #include <Bitmap.h> 9 #include <Button.h> 10 #include <Catalog.h> 11 #include <ControlLook.h> 12 #include <Clipboard.h> 13 #include <GroupLayout.h> 14 #include <GroupLayoutBuilder.h> 15 #include <Locale.h> 16 #include <LayoutUtils.h> 17 #include <MenuItem.h> 18 #include <PopUpMenu.h> 19 #include <SeparatorView.h> 20 #include <TextView.h> 21 #include <Window.h> 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 26 #include "BaseURL.h" 27 #include "BitmapButton.h" 28 #include "BrowsingHistory.h" 29 #include "IconButton.h" 30 #include "IconUtils.h" 31 #include "TextViewCompleter.h" 32 33 34 #undef B_TRANSLATION_CONTEXT 35 #define B_TRANSLATION_CONTEXT "URL Bar" 36 37 38 class URLChoice : public BAutoCompleter::Choice { 39 public: 40 URLChoice(const BString& choiceText, const BString& displayText, 41 int32 matchPos, int32 matchLen, int32 priority) 42 : 43 BAutoCompleter::Choice(choiceText, displayText, matchPos, matchLen), 44 fPriority(priority) 45 { 46 } 47 48 bool operator<(const URLChoice& other) const 49 { 50 if (fPriority > other.fPriority) 51 return true; 52 return DisplayText() < other.DisplayText(); 53 } 54 55 bool operator==(const URLChoice& other) const 56 { 57 return fPriority == other.fPriority 58 && DisplayText() < other.DisplayText(); 59 } 60 61 private: 62 int32 fPriority; 63 }; 64 65 66 class BrowsingHistoryChoiceModel : public BAutoCompleter::ChoiceModel { 67 virtual void FetchChoicesFor(const BString& pattern) 68 { 69 int32 count = CountChoices(); 70 for (int32 i = 0; i < count; i++) { 71 delete reinterpret_cast<BAutoCompleter::Choice*>( 72 fChoices.ItemAtFast(i)); 73 } 74 fChoices.MakeEmpty(); 75 76 // Search through BrowsingHistory for any matches. 77 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 78 if (!history->Lock()) 79 return; 80 81 BString lastBaseURL; 82 int32 priority = INT_MAX; 83 84 count = history->CountItems(); 85 for (int32 i = 0; i < count; i++) { 86 BrowsingHistoryItem item = history->HistoryItemAt(i); 87 const BString& choiceText = item.URL(); 88 int32 matchPos = choiceText.IFindFirst(pattern); 89 if (matchPos < 0) 90 continue; 91 if (lastBaseURL.Length() > 0 92 && choiceText.FindFirst(lastBaseURL) >= 0) { 93 priority--; 94 } else 95 priority = INT_MAX; 96 lastBaseURL = baseURL(choiceText); 97 fChoices.AddItem(new URLChoice(choiceText, 98 choiceText, matchPos, pattern.Length(), priority)); 99 } 100 101 history->Unlock(); 102 103 fChoices.SortItems(_CompareChoices); 104 } 105 106 virtual int32 CountChoices() const 107 { 108 return fChoices.CountItems(); 109 } 110 111 virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const 112 { 113 return reinterpret_cast<BAutoCompleter::Choice*>( 114 fChoices.ItemAt(index)); 115 } 116 117 static int _CompareChoices(const void* a, const void* b) 118 { 119 const URLChoice* aChoice 120 = *reinterpret_cast<const URLChoice* const *>(a); 121 const URLChoice* bChoice 122 = *reinterpret_cast<const URLChoice* const *>(b); 123 if (*aChoice < *bChoice) 124 return -1; 125 else if (*aChoice == *bChoice) 126 return 0; 127 return 1; 128 } 129 130 private: 131 BList fChoices; 132 }; 133 134 135 // #pragma mark - URLTextView 136 137 138 static const float kHorizontalTextRectInset = 4.0; 139 140 141 class URLInputGroup::URLTextView : public BTextView { 142 private: 143 static const uint32 MSG_CLEAR = 'cler'; 144 145 public: 146 URLTextView(URLInputGroup* parent); 147 virtual ~URLTextView(); 148 149 virtual void MessageReceived(BMessage* message); 150 virtual void FrameResized(float width, float height); 151 virtual void MouseDown(BPoint where); 152 virtual void KeyDown(const char* bytes, int32 numBytes); 153 virtual void MakeFocus(bool focused = true); 154 155 virtual BSize MinSize(); 156 virtual BSize MaxSize(); 157 158 void SetUpdateAutoCompleterChoices(bool update); 159 160 protected: 161 virtual void InsertText(const char* inText, int32 inLength, 162 int32 inOffset, 163 const text_run_array* inRuns); 164 virtual void DeleteText(int32 fromOffset, int32 toOffset); 165 166 private: 167 void _AlignTextRect(); 168 169 private: 170 URLInputGroup* fURLInputGroup; 171 TextViewCompleter* fURLAutoCompleter; 172 BString fPreviousText; 173 bool fUpdateAutoCompleterChoices; 174 }; 175 176 177 URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent) 178 : 179 BTextView("url"), 180 fURLInputGroup(parent), 181 fURLAutoCompleter(new TextViewCompleter(this, 182 new BrowsingHistoryChoiceModel())), 183 fPreviousText(""), 184 fUpdateAutoCompleterChoices(true) 185 { 186 MakeResizable(true); 187 SetStylable(true); 188 fURLAutoCompleter->SetModificationsReported(true); 189 } 190 191 192 URLInputGroup::URLTextView::~URLTextView() 193 { 194 delete fURLAutoCompleter; 195 } 196 197 198 void 199 URLInputGroup::URLTextView::MessageReceived(BMessage* message) 200 { 201 switch (message->what) { 202 case MSG_CLEAR: 203 SetText(""); 204 break; 205 206 default: 207 BTextView::MessageReceived(message); 208 break; 209 } 210 } 211 212 213 void 214 URLInputGroup::URLTextView::FrameResized(float width, float height) 215 { 216 BTextView::FrameResized(width, height); 217 _AlignTextRect(); 218 } 219 220 221 void 222 URLInputGroup::URLTextView::MouseDown(BPoint where) 223 { 224 bool wasFocus = IsFocus(); 225 if (!wasFocus) 226 MakeFocus(true); 227 228 int32 buttons; 229 if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) 230 buttons = B_PRIMARY_MOUSE_BUTTON; 231 232 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { 233 // Display context menu 234 int32 selectionStart; 235 int32 selectionEnd; 236 GetSelection(&selectionStart, &selectionEnd); 237 bool canCutOrCopy = selectionEnd > selectionStart; 238 239 bool canPaste = false; 240 if (be_clipboard->Lock()) { 241 if (BMessage* data = be_clipboard->Data()) 242 canPaste = data->HasData("text/plain", B_MIME_TYPE); 243 be_clipboard->Unlock(); 244 } 245 246 BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"), 247 new BMessage(B_CUT)); 248 BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"), 249 new BMessage(B_COPY)); 250 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 251 new BMessage(B_PASTE)); 252 BMenuItem* clearItem = new BMenuItem(B_TRANSLATE("Clear"), 253 new BMessage(MSG_CLEAR)); 254 cutItem->SetEnabled(canCutOrCopy); 255 copyItem->SetEnabled(canCutOrCopy); 256 pasteItem->SetEnabled(canPaste); 257 clearItem->SetEnabled(strlen(Text()) > 0); 258 259 BPopUpMenu* menu = new BPopUpMenu("url context"); 260 menu->AddItem(cutItem); 261 menu->AddItem(copyItem); 262 menu->AddItem(pasteItem); 263 menu->AddItem(clearItem); 264 265 menu->SetTargetForItems(this); 266 menu->Go(ConvertToScreen(where), true, true, true); 267 return; 268 } 269 270 // Only pass through to base class if we already have focus. 271 if (!wasFocus) 272 return; 273 274 BTextView::MouseDown(where); 275 } 276 277 278 void 279 URLInputGroup::URLTextView::KeyDown(const char* bytes, int32 numBytes) 280 { 281 switch (bytes[0]) { 282 case B_TAB: 283 BView::KeyDown(bytes, numBytes); 284 break; 285 286 case B_ESCAPE: 287 // Revert to text as it was when we received keyboard focus. 288 SetText(fPreviousText.String()); 289 SelectAll(); 290 break; 291 292 case B_RETURN: 293 // Don't let this through to the text view. 294 break; 295 296 default: 297 BTextView::KeyDown(bytes, numBytes); 298 break; 299 } 300 } 301 302 void 303 URLInputGroup::URLTextView::MakeFocus(bool focus) 304 { 305 if (focus == IsFocus()) 306 return; 307 308 BTextView::MakeFocus(focus); 309 310 if (focus) { 311 fPreviousText = Text(); 312 SelectAll(); 313 } 314 315 fURLInputGroup->Invalidate(); 316 } 317 318 319 BSize 320 URLInputGroup::URLTextView::MinSize() 321 { 322 BSize min; 323 min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset); 324 // we always add at least one pixel vertical inset top/bottom for 325 // the text rect. 326 min.width = min.height * 3; 327 return BLayoutUtils::ComposeSize(ExplicitMinSize(), min); 328 } 329 330 331 BSize 332 URLInputGroup::URLTextView::MaxSize() 333 { 334 BSize max(MinSize()); 335 max.width = B_SIZE_UNLIMITED; 336 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); 337 } 338 339 340 void 341 URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update) 342 { 343 fUpdateAutoCompleterChoices = update; 344 } 345 346 347 void 348 URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength, 349 int32 inOffset, const text_run_array* inRuns) 350 { 351 // Filter all line breaks, note that inText is not terminated. 352 if (inLength == 1) { 353 if (*inText == '\n' || *inText == '\r') 354 BTextView::InsertText(" ", 1, inOffset, inRuns); 355 else 356 BTextView::InsertText(inText, 1, inOffset, inRuns); 357 } else { 358 BString filteredText(inText, inLength); 359 filteredText.ReplaceAll('\n', ' '); 360 filteredText.ReplaceAll('\r', ' '); 361 BTextView::InsertText(filteredText.String(), inLength, inOffset, 362 inRuns); 363 } 364 365 // Make the base URL part bold. 366 BString text(Text(), TextLength()); 367 int32 baseUrlStart = text.FindFirst("://"); 368 if (baseUrlStart >= 0) 369 baseUrlStart += 3; 370 else 371 baseUrlStart = 0; 372 int32 baseUrlEnd = text.FindFirst("/", baseUrlStart); 373 if (baseUrlEnd < 0) 374 baseUrlEnd = TextLength(); 375 376 BFont font; 377 GetFont(&font); 378 const rgb_color black = (rgb_color) { 0, 0, 0, 255 }; 379 const rgb_color gray = (rgb_color) { 60, 60, 60, 255 }; 380 if (baseUrlStart > 0) 381 SetFontAndColor(0, baseUrlStart, &font, B_FONT_ALL, &gray); 382 if (baseUrlEnd > baseUrlStart) { 383 font.SetFace(B_BOLD_FACE); 384 SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black); 385 } 386 if (baseUrlEnd < TextLength()) { 387 font.SetFace(B_REGULAR_FACE); 388 SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray); 389 } 390 391 fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices); 392 } 393 394 395 void 396 URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset) 397 { 398 BTextView::DeleteText(fromOffset, toOffset); 399 400 fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices); 401 } 402 403 404 void 405 URLInputGroup::URLTextView::_AlignTextRect() 406 { 407 // Layout the text rect to be in the middle, normally this means there 408 // is one pixel spacing on each side. 409 BRect textRect(Bounds()); 410 textRect.left = 0.0; 411 float vInset = max_c(1, 412 floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5)); 413 float hInset = kHorizontalTextRectInset; 414 415 if (be_control_look) 416 hInset = be_control_look->DefaultLabelSpacing(); 417 418 textRect.InsetBy(hInset, vInset); 419 SetTextRect(textRect); 420 } 421 422 423 const uint32 kGoBitmapWidth = 14; 424 const uint32 kGoBitmapHeight = 14; 425 const color_space kGoBitmapFormat = B_RGBA32; 426 427 const unsigned char kGoBitmapBits[] = { 428 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 429 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 430 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 431 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 432 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 433 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 434 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 435 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 436 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 437 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 438 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 439 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 440 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 441 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 442 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 443 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 444 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 445 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 446 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 447 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 448 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 449 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 450 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 451 0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed, 452 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 453 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 454 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 455 0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 456 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 457 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 458 0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02, 459 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 460 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 461 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 462 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 464 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 465 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 466 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 467 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 468 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 469 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 471 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 477 }; 478 479 480 const unsigned char kPlaceholderIcon[] = { 481 0x6e, 0x63, 0x69, 0x66, 0x04, 0x04, 0x00, 0x66, 0x03, 0x00, 0x3f, 0x80, 482 0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x3d, 0xa6, 0x64, 0xc2, 0x19, 483 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 0x64, 0x49, 0xac, 0xcc, 0x00, 0xab, 484 0xd5, 0xff, 0xff, 0x00, 0x6c, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 485 0x00, 0x3d, 0x26, 0x64, 0xc2, 0x19, 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 486 0x64, 0x49, 0xac, 0xcc, 0x00, 0x80, 0xff, 0x80, 0xff, 0x00, 0xb2, 0x00, 487 0x04, 0x02, 0x04, 0x34, 0x22, 0xbd, 0x9b, 0x22, 0xb8, 0x53, 0x22, 0x28, 488 0x2e, 0x28, 0xb5, 0xef, 0x28, 0xbb, 0x37, 0x34, 0x3a, 0xb8, 0x53, 0x3a, 489 0xbd, 0x9b, 0x3a, 0x40, 0x2e, 0x40, 0xbb, 0x37, 0x40, 0xb5, 0xef, 0x02, 490 0x08, 0xbe, 0xb6, 0xb4, 0xac, 0xc1, 0x46, 0xb4, 0xac, 0xbc, 0x25, 0xb4, 491 0xac, 0xb8, 0x09, 0xb7, 0x35, 0xb9, 0xcf, 0xb5, 0xa0, 0xb8, 0x05, 0xbe, 492 0xb6, 0x35, 0xc2, 0xe5, 0x35, 0xbe, 0xb6, 0x35, 0xc5, 0x68, 0xb8, 0x09, 493 0xc6, 0x36, 0xb8, 0x09, 0xc6, 0x36, 0xb9, 0xcf, 0xc7, 0xca, 0xbe, 0xb6, 494 0xc8, 0xc1, 0xbc, 0x25, 0xc8, 0xc1, 0xc1, 0xb3, 0xc8, 0xc1, 0xc6, 0x3c, 495 0xc5, 0x5b, 0xc4, 0x65, 0xc7, 0x70, 0xc6, 0x3e, 0xbe, 0xb6, 0xc2, 0x0f, 496 0xba, 0x87, 0xc2, 0x0f, 0xbe, 0xb6, 0xc2, 0x0f, 0xb8, 0x05, 0xc5, 0x64, 497 0xb7, 0x37, 0xc5, 0x64, 0xb7, 0x37, 0xc3, 0x9e, 0xb5, 0xa2, 0x02, 0x04, 498 0xb8, 0x09, 0xb7, 0x35, 0xb8, 0x05, 0xbe, 0xb6, 0xb5, 0xf8, 0xb9, 0x0c, 499 0xb4, 0xac, 0xbe, 0xb6, 0xb4, 0xac, 0xbb, 0xba, 0xb4, 0xac, 0xc1, 0xb1, 500 0xb8, 0x09, 0xc6, 0x36, 0xb5, 0xf8, 0xc4, 0x5e, 0xb9, 0xcf, 0xc7, 0xca, 501 0x35, 0xc2, 0xe5, 0x35, 0xc5, 0x68, 0x35, 0xbe, 0xb6, 0x02, 0x04, 0x4d, 502 0x51, 0xc4, 0xf2, 0xbf, 0x04, 0x53, 0x4e, 0xc8, 0xc1, 0xbe, 0x58, 0xc8, 503 0xc1, 0xc1, 0x55, 0xc8, 0xc1, 0xbb, 0x5d, 0xc5, 0x64, 0xb6, 0xd9, 0xc7, 504 0x75, 0xb8, 0xb0, 0xc3, 0x9e, 0xb5, 0x44, 0xc2, 0x0f, 0xba, 0x29, 0xc2, 505 0x0f, 0xb7, 0xa6, 0xc2, 0x0f, 0xbe, 0x58, 0x04, 0x0a, 0x00, 0x01, 0x00, 506 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x19, 507 0x98, 0xc6, 0x19, 0x93, 0x44, 0x19, 0xa2, 0x01, 0x17, 0x84, 0x00, 0x04, 508 0x0a, 0x01, 0x01, 0x00, 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 509 0x00, 0x00, 0x42, 0x19, 0x98, 0xc7, 0x26, 0x5f, 0x28, 0x96, 0xf9, 0x01, 510 0x17, 0x83, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x01, 0x00, 0x0a, 0x03, 0x02, 511 0x02, 0x03, 0x00 512 }; 513 514 // #pragma mark - PageIconView 515 516 517 class URLInputGroup::PageIconView : public BView { 518 public: 519 PageIconView() 520 : 521 BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 522 fIcon(NULL) 523 { 524 SetDrawingMode(B_OP_ALPHA); 525 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 526 } 527 528 ~PageIconView() 529 { 530 delete fIcon; 531 } 532 533 virtual void Draw(BRect updateRect) 534 { 535 BRect bounds(Bounds()); 536 BRect iconBounds(0, 0, 15, 15); 537 iconBounds.OffsetTo( 538 floorf((bounds.left + bounds.right 539 - (iconBounds.left + iconBounds.right)) / 2 + 0.5f), 540 floorf((bounds.top + bounds.bottom 541 - (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f)); 542 DrawBitmap(fIcon, fIcon->Bounds(), iconBounds, 543 B_FILTER_BITMAP_BILINEAR); 544 } 545 546 virtual BSize MinSize() 547 { 548 return BSize(18, 18); 549 } 550 551 virtual BSize MaxSize() 552 { 553 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 554 } 555 556 virtual BSize PreferredSize() 557 { 558 return MinSize(); 559 } 560 561 void SetIcon(const BBitmap* icon) 562 { 563 delete fIcon; 564 if (icon) 565 fIcon = new BBitmap(icon); 566 else { 567 fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32); 568 BIconUtils::GetVectorIcon(kPlaceholderIcon, 569 sizeof(kPlaceholderIcon), fIcon); 570 } 571 Invalidate(); 572 } 573 574 private: 575 BBitmap* fIcon; 576 }; 577 578 579 // #pragma mark - URLInputGroup 580 581 582 URLInputGroup::URLInputGroup(BMessage* goMessage) 583 : 584 BGroupView(B_HORIZONTAL, 0.0), 585 fWindowActive(false) 586 { 587 GroupLayout()->SetInsets(2, 2, 2, 2); 588 589 fIconView = new PageIconView(); 590 GroupLayout()->AddView(fIconView, 0.0f); 591 592 fTextView = new URLTextView(this); 593 AddChild(fTextView); 594 595 AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER)); 596 597 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from 598 // resources? 599 // fGoButton = new BitmapButton("kActionGo", NULL); 600 fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth, 601 kGoBitmapHeight, kGoBitmapFormat, goMessage); 602 GroupLayout()->AddView(fGoButton, 0.0f); 603 604 SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); 605 SetLowColor(ViewColor()); 606 SetViewColor(fTextView->ViewColor()); 607 608 SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 609 B_ALIGN_VERTICAL_CENTER)); 610 } 611 612 613 URLInputGroup::~URLInputGroup() 614 { 615 } 616 617 618 void 619 URLInputGroup::AttachedToWindow() 620 { 621 BGroupView::AttachedToWindow(); 622 fWindowActive = Window()->IsActive(); 623 } 624 625 626 void 627 URLInputGroup::WindowActivated(bool active) 628 { 629 BGroupView::WindowActivated(active); 630 if (fWindowActive != active) { 631 fWindowActive = active; 632 Invalidate(); 633 } 634 } 635 636 637 void 638 URLInputGroup::Draw(BRect updateRect) 639 { 640 BRect bounds(Bounds()); 641 rgb_color base(LowColor()); 642 uint32 flags = 0; 643 if (fWindowActive && fTextView->IsFocus()) 644 flags |= BControlLook::B_FOCUSED; 645 be_control_look->DrawTextControlBorder(this, bounds, updateRect, base, 646 flags); 647 } 648 649 650 void 651 URLInputGroup::MakeFocus(bool focus) 652 { 653 // Forward this to the text view, we never accept focus ourselves. 654 fTextView->MakeFocus(focus); 655 } 656 657 658 BTextView* 659 URLInputGroup::TextView() const 660 { 661 return fTextView; 662 } 663 664 665 void 666 URLInputGroup::SetText(const char* text) 667 { 668 if (!text || !Text() || strcmp(Text(), text) != 0) { 669 fTextView->SetUpdateAutoCompleterChoices(false); 670 fTextView->SetText(text); 671 fTextView->SetUpdateAutoCompleterChoices(true); 672 } 673 } 674 675 676 const char* 677 URLInputGroup::Text() const 678 { 679 return fTextView->Text(); 680 } 681 682 683 BButton* 684 URLInputGroup::GoButton() const 685 { 686 return fGoButton; 687 } 688 689 690 void 691 URLInputGroup::SetPageIcon(const BBitmap* icon) 692 { 693 fIconView->SetIcon(icon); 694 } 695 696