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 BFont font; 376 GetFont(&font); 377 const rgb_color black = (rgb_color) { 0, 0, 0, 255 }; 378 const rgb_color gray = (rgb_color) { 60, 60, 60, 255 }; 379 if (baseUrlStart > 0) 380 SetFontAndColor(0, baseUrlStart - 1, &font, B_FONT_ALL, &gray); 381 if (baseUrlEnd > baseUrlStart) { 382 font.SetFace(B_BOLD_FACE); 383 SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black); 384 } 385 if (baseUrlEnd < TextLength()) { 386 font.SetFace(B_REGULAR_FACE); 387 SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray); 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 void 404 URLInputGroup::URLTextView::_AlignTextRect() 405 { 406 // Layout the text rect to be in the middle, normally this means there 407 // is one pixel spacing on each side. 408 BRect textRect(Bounds()); 409 textRect.left = 0.0; 410 float vInset = max_c(1, 411 floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5)); 412 float hInset = kHorizontalTextRectInset; 413 414 if (be_control_look) 415 hInset = be_control_look->DefaultLabelSpacing(); 416 417 textRect.InsetBy(hInset, vInset); 418 SetTextRect(textRect); 419 } 420 421 422 const uint32 kGoBitmapWidth = 14; 423 const uint32 kGoBitmapHeight = 14; 424 const color_space kGoBitmapFormat = B_RGBA32; 425 426 const unsigned char kGoBitmapBits[] = { 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, 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, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 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, 0x17, 0x17, 0x17, 0x21, 439 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 440 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 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 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 443 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 446 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 447 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 448 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 449 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 450 0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed, 451 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 452 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 453 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 454 0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 455 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 456 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 457 0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02, 458 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 459 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 460 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x17, 0x17, 0x17, 0x21, 463 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 464 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 467 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x17, 0x17, 0x17, 0x21, 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 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 }; 477 478 479 const unsigned char kPlaceholderIcon[] = { 480 0x6e, 0x63, 0x69, 0x66, 0x04, 0x04, 0x00, 0x66, 0x03, 0x00, 0x3f, 0x80, 481 0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x3d, 0xa6, 0x64, 0xc2, 0x19, 482 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 0x64, 0x49, 0xac, 0xcc, 0x00, 0xab, 483 0xd5, 0xff, 0xff, 0x00, 0x6c, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 484 0x00, 0x3d, 0x26, 0x64, 0xc2, 0x19, 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 485 0x64, 0x49, 0xac, 0xcc, 0x00, 0x80, 0xff, 0x80, 0xff, 0x00, 0xb2, 0x00, 486 0x04, 0x02, 0x04, 0x34, 0x22, 0xbd, 0x9b, 0x22, 0xb8, 0x53, 0x22, 0x28, 487 0x2e, 0x28, 0xb5, 0xef, 0x28, 0xbb, 0x37, 0x34, 0x3a, 0xb8, 0x53, 0x3a, 488 0xbd, 0x9b, 0x3a, 0x40, 0x2e, 0x40, 0xbb, 0x37, 0x40, 0xb5, 0xef, 0x02, 489 0x08, 0xbe, 0xb6, 0xb4, 0xac, 0xc1, 0x46, 0xb4, 0xac, 0xbc, 0x25, 0xb4, 490 0xac, 0xb8, 0x09, 0xb7, 0x35, 0xb9, 0xcf, 0xb5, 0xa0, 0xb8, 0x05, 0xbe, 491 0xb6, 0x35, 0xc2, 0xe5, 0x35, 0xbe, 0xb6, 0x35, 0xc5, 0x68, 0xb8, 0x09, 492 0xc6, 0x36, 0xb8, 0x09, 0xc6, 0x36, 0xb9, 0xcf, 0xc7, 0xca, 0xbe, 0xb6, 493 0xc8, 0xc1, 0xbc, 0x25, 0xc8, 0xc1, 0xc1, 0xb3, 0xc8, 0xc1, 0xc6, 0x3c, 494 0xc5, 0x5b, 0xc4, 0x65, 0xc7, 0x70, 0xc6, 0x3e, 0xbe, 0xb6, 0xc2, 0x0f, 495 0xba, 0x87, 0xc2, 0x0f, 0xbe, 0xb6, 0xc2, 0x0f, 0xb8, 0x05, 0xc5, 0x64, 496 0xb7, 0x37, 0xc5, 0x64, 0xb7, 0x37, 0xc3, 0x9e, 0xb5, 0xa2, 0x02, 0x04, 497 0xb8, 0x09, 0xb7, 0x35, 0xb8, 0x05, 0xbe, 0xb6, 0xb5, 0xf8, 0xb9, 0x0c, 498 0xb4, 0xac, 0xbe, 0xb6, 0xb4, 0xac, 0xbb, 0xba, 0xb4, 0xac, 0xc1, 0xb1, 499 0xb8, 0x09, 0xc6, 0x36, 0xb5, 0xf8, 0xc4, 0x5e, 0xb9, 0xcf, 0xc7, 0xca, 500 0x35, 0xc2, 0xe5, 0x35, 0xc5, 0x68, 0x35, 0xbe, 0xb6, 0x02, 0x04, 0x4d, 501 0x51, 0xc4, 0xf2, 0xbf, 0x04, 0x53, 0x4e, 0xc8, 0xc1, 0xbe, 0x58, 0xc8, 502 0xc1, 0xc1, 0x55, 0xc8, 0xc1, 0xbb, 0x5d, 0xc5, 0x64, 0xb6, 0xd9, 0xc7, 503 0x75, 0xb8, 0xb0, 0xc3, 0x9e, 0xb5, 0x44, 0xc2, 0x0f, 0xba, 0x29, 0xc2, 504 0x0f, 0xb7, 0xa6, 0xc2, 0x0f, 0xbe, 0x58, 0x04, 0x0a, 0x00, 0x01, 0x00, 505 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x19, 506 0x98, 0xc6, 0x19, 0x93, 0x44, 0x19, 0xa2, 0x01, 0x17, 0x84, 0x00, 0x04, 507 0x0a, 0x01, 0x01, 0x00, 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 508 0x00, 0x00, 0x42, 0x19, 0x98, 0xc7, 0x26, 0x5f, 0x28, 0x96, 0xf9, 0x01, 509 0x17, 0x83, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x01, 0x00, 0x0a, 0x03, 0x02, 510 0x02, 0x03, 0x00 511 }; 512 513 // #pragma mark - PageIconView 514 515 516 class URLInputGroup::PageIconView : public BView { 517 public: 518 PageIconView() 519 : 520 BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 521 fIcon(NULL) 522 { 523 SetDrawingMode(B_OP_ALPHA); 524 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 525 } 526 527 ~PageIconView() 528 { 529 delete fIcon; 530 } 531 532 virtual void Draw(BRect updateRect) 533 { 534 BRect bounds(Bounds()); 535 BRect iconBounds(0, 0, 15, 15); 536 iconBounds.OffsetTo( 537 floorf((bounds.left + bounds.right 538 - (iconBounds.left + iconBounds.right)) / 2 + 0.5f), 539 floorf((bounds.top + bounds.bottom 540 - (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f)); 541 DrawBitmap(fIcon, fIcon->Bounds(), iconBounds, 542 B_FILTER_BITMAP_BILINEAR); 543 } 544 545 virtual BSize MinSize() 546 { 547 return BSize(18, 18); 548 } 549 550 virtual BSize MaxSize() 551 { 552 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 553 } 554 555 virtual BSize PreferredSize() 556 { 557 return MinSize(); 558 } 559 560 void SetIcon(const BBitmap* icon) 561 { 562 delete fIcon; 563 if (icon) 564 fIcon = new BBitmap(icon); 565 else { 566 fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32); 567 BIconUtils::GetVectorIcon(kPlaceholderIcon, 568 sizeof(kPlaceholderIcon), fIcon); 569 } 570 Invalidate(); 571 } 572 573 private: 574 BBitmap* fIcon; 575 }; 576 577 578 // #pragma mark - URLInputGroup 579 580 581 URLInputGroup::URLInputGroup(BMessage* goMessage) 582 : 583 BGroupView(B_HORIZONTAL, 0.0), 584 fWindowActive(false) 585 { 586 GroupLayout()->SetInsets(2, 2, 2, 2); 587 588 fIconView = new PageIconView(); 589 GroupLayout()->AddView(fIconView, 0.0f); 590 591 fTextView = new URLTextView(this); 592 AddChild(fTextView); 593 594 AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER)); 595 596 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from 597 // resources? 598 // fGoButton = new BitmapButton("kActionGo", NULL); 599 fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth, 600 kGoBitmapHeight, kGoBitmapFormat, goMessage); 601 GroupLayout()->AddView(fGoButton, 0.0f); 602 603 SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); 604 SetLowColor(ViewColor()); 605 SetViewColor(fTextView->ViewColor()); 606 607 SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 608 B_ALIGN_VERTICAL_CENTER)); 609 } 610 611 612 URLInputGroup::~URLInputGroup() 613 { 614 } 615 616 617 void 618 URLInputGroup::AttachedToWindow() 619 { 620 BGroupView::AttachedToWindow(); 621 fWindowActive = Window()->IsActive(); 622 } 623 624 625 void 626 URLInputGroup::WindowActivated(bool active) 627 { 628 BGroupView::WindowActivated(active); 629 if (fWindowActive != active) { 630 fWindowActive = active; 631 Invalidate(); 632 } 633 } 634 635 636 void 637 URLInputGroup::Draw(BRect updateRect) 638 { 639 BRect bounds(Bounds()); 640 rgb_color base(LowColor()); 641 uint32 flags = 0; 642 if (fWindowActive && fTextView->IsFocus()) 643 flags |= BControlLook::B_FOCUSED; 644 be_control_look->DrawTextControlBorder(this, bounds, updateRect, base, 645 flags); 646 } 647 648 649 void 650 URLInputGroup::MakeFocus(bool focus) 651 { 652 // Forward this to the text view, we never accept focus ourselves. 653 fTextView->MakeFocus(focus); 654 } 655 656 657 BTextView* 658 URLInputGroup::TextView() const 659 { 660 return fTextView; 661 } 662 663 664 void 665 URLInputGroup::SetText(const char* text) 666 { 667 if (!text || !Text() || strcmp(Text(), text) != 0) { 668 fTextView->SetUpdateAutoCompleterChoices(false); 669 fTextView->SetText(text); 670 fTextView->SetUpdateAutoCompleterChoices(true); 671 } 672 } 673 674 675 const char* 676 URLInputGroup::Text() const 677 { 678 return fTextView->Text(); 679 } 680 681 682 BButton* 683 URLInputGroup::GoButton() const 684 { 685 return fGoButton; 686 } 687 688 689 void 690 URLInputGroup::SetPageIcon(const BBitmap* icon) 691 { 692 fIconView->SetIcon(icon); 693 } 694 695