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