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 SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR); 527 } 528 529 ~PageIconView() 530 { 531 delete fIcon; 532 } 533 534 virtual void Draw(BRect updateRect) 535 { 536 BRect bounds(Bounds()); 537 BRect iconBounds(0, 0, 15, 15); 538 iconBounds.OffsetTo( 539 floorf((bounds.left + bounds.right 540 - (iconBounds.left + iconBounds.right)) / 2 + 0.5f), 541 floorf((bounds.top + bounds.bottom 542 - (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f)); 543 DrawBitmap(fIcon, fIcon->Bounds(), iconBounds, 544 B_FILTER_BITMAP_BILINEAR); 545 } 546 547 virtual BSize MinSize() 548 { 549 return BSize(18, 18); 550 } 551 552 virtual BSize MaxSize() 553 { 554 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 555 } 556 557 virtual BSize PreferredSize() 558 { 559 return MinSize(); 560 } 561 562 void SetIcon(const BBitmap* icon) 563 { 564 delete fIcon; 565 if (icon) 566 fIcon = new BBitmap(icon); 567 else { 568 fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32); 569 BIconUtils::GetVectorIcon(kPlaceholderIcon, 570 sizeof(kPlaceholderIcon), fIcon); 571 } 572 Invalidate(); 573 } 574 575 private: 576 BBitmap* fIcon; 577 }; 578 579 580 // #pragma mark - URLInputGroup 581 582 583 URLInputGroup::URLInputGroup(BMessage* goMessage) 584 : 585 BGroupView(B_HORIZONTAL, 0.0), 586 fWindowActive(false) 587 { 588 GroupLayout()->SetInsets(2, 2, 2, 2); 589 590 fIconView = new PageIconView(); 591 GroupLayout()->AddView(fIconView, 0.0f); 592 593 fTextView = new URLTextView(this); 594 AddChild(fTextView); 595 596 AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER)); 597 598 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from 599 // resources? 600 // fGoButton = new BitmapButton("kActionGo", NULL); 601 fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth, 602 kGoBitmapHeight, kGoBitmapFormat, goMessage); 603 GroupLayout()->AddView(fGoButton, 0.0f); 604 605 SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); 606 SetLowColor(ViewColor()); 607 SetViewColor(fTextView->ViewColor()); 608 609 SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 610 B_ALIGN_VERTICAL_CENTER)); 611 } 612 613 614 URLInputGroup::~URLInputGroup() 615 { 616 } 617 618 619 void 620 URLInputGroup::AttachedToWindow() 621 { 622 BGroupView::AttachedToWindow(); 623 fWindowActive = Window()->IsActive(); 624 } 625 626 627 void 628 URLInputGroup::WindowActivated(bool active) 629 { 630 BGroupView::WindowActivated(active); 631 if (fWindowActive != active) { 632 fWindowActive = active; 633 Invalidate(); 634 } 635 } 636 637 638 void 639 URLInputGroup::Draw(BRect updateRect) 640 { 641 BRect bounds(Bounds()); 642 rgb_color base(LowColor()); 643 uint32 flags = 0; 644 if (fWindowActive && fTextView->IsFocus()) 645 flags |= BControlLook::B_FOCUSED; 646 be_control_look->DrawTextControlBorder(this, bounds, updateRect, base, 647 flags); 648 } 649 650 651 void 652 URLInputGroup::MakeFocus(bool focus) 653 { 654 // Forward this to the text view, we never accept focus ourselves. 655 fTextView->MakeFocus(focus); 656 } 657 658 659 BTextView* 660 URLInputGroup::TextView() const 661 { 662 return fTextView; 663 } 664 665 666 void 667 URLInputGroup::SetText(const char* text) 668 { 669 // Ignore setting the text, if the user is currently editing the URL. 670 if (fWindowActive && fTextView->IsFocus()) 671 return; 672 673 if (!text || !Text() || strcmp(Text(), text) != 0) { 674 fTextView->SetUpdateAutoCompleterChoices(false); 675 fTextView->SetText(text); 676 fTextView->SetUpdateAutoCompleterChoices(true); 677 } 678 } 679 680 681 const char* 682 URLInputGroup::Text() const 683 { 684 return fTextView->Text(); 685 } 686 687 688 BButton* 689 URLInputGroup::GoButton() const 690 { 691 return fGoButton; 692 } 693 694 695 void 696 URLInputGroup::SetPageIcon(const BBitmap* icon) 697 { 698 fIconView->SetIcon(icon); 699 } 700 701