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