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 "TextViewCompleter.h" 31 32 33 #undef B_TRANSLATION_CONTEXT 34 #define B_TRANSLATION_CONTEXT "URL Bar" 35 36 37 class URLChoice : public BAutoCompleter::Choice { 38 public: 39 URLChoice(const BString& choiceText, const BString& displayText, 40 int32 matchPos, int32 matchLen, int32 priority) 41 : 42 BAutoCompleter::Choice(choiceText, displayText, matchPos, matchLen), 43 fPriority(priority) 44 { 45 } 46 47 bool operator<(const URLChoice& other) const 48 { 49 if (fPriority > other.fPriority) 50 return true; 51 return DisplayText() < other.DisplayText(); 52 } 53 54 bool operator==(const URLChoice& other) const 55 { 56 return fPriority == other.fPriority 57 && DisplayText() < other.DisplayText(); 58 } 59 60 private: 61 int32 fPriority; 62 }; 63 64 65 class BrowsingHistoryChoiceModel : public BAutoCompleter::ChoiceModel { 66 virtual void FetchChoicesFor(const BString& pattern) 67 { 68 int32 count = CountChoices(); 69 for (int32 i = 0; i < count; i++) { 70 delete reinterpret_cast<BAutoCompleter::Choice*>( 71 fChoices.ItemAtFast(i)); 72 } 73 fChoices.MakeEmpty(); 74 75 // Search through BrowsingHistory for any matches. 76 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 77 if (!history->Lock()) 78 return; 79 80 BString lastBaseURL; 81 int32 priority = INT_MAX; 82 83 count = history->CountItems(); 84 for (int32 i = 0; i < count; i++) { 85 BrowsingHistoryItem item = history->HistoryItemAt(i); 86 const BString& choiceText = item.URL(); 87 int32 matchPos = choiceText.IFindFirst(pattern); 88 if (matchPos < 0) 89 continue; 90 if (lastBaseURL.Length() > 0 91 && choiceText.FindFirst(lastBaseURL) >= 0) { 92 priority--; 93 } else 94 priority = INT_MAX; 95 lastBaseURL = baseURL(choiceText); 96 fChoices.AddItem(new URLChoice(choiceText, 97 choiceText, matchPos, pattern.Length(), priority)); 98 } 99 100 history->Unlock(); 101 102 fChoices.SortItems(_CompareChoices); 103 } 104 105 virtual int32 CountChoices() const 106 { 107 return fChoices.CountItems(); 108 } 109 110 virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const 111 { 112 return reinterpret_cast<BAutoCompleter::Choice*>( 113 fChoices.ItemAt(index)); 114 } 115 116 static int _CompareChoices(const void* a, const void* b) 117 { 118 const URLChoice* aChoice 119 = *reinterpret_cast<const URLChoice* const *>(a); 120 const URLChoice* bChoice 121 = *reinterpret_cast<const URLChoice* const *>(b); 122 if (*aChoice < *bChoice) 123 return -1; 124 else if (*aChoice == *bChoice) 125 return 0; 126 return 1; 127 } 128 129 private: 130 BList fChoices; 131 }; 132 133 134 // #pragma mark - URLTextView 135 136 137 static const float kHorizontalTextRectInset = 4.0; 138 139 140 class URLInputGroup::URLTextView : public BTextView { 141 private: 142 static const uint32 MSG_CLEAR = 'cler'; 143 144 public: 145 URLTextView(URLInputGroup* parent); 146 virtual ~URLTextView(); 147 148 virtual void MessageReceived(BMessage* message); 149 virtual void FrameResized(float width, float height); 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 void _AlignTextRect(); 167 168 private: 169 URLInputGroup* fURLInputGroup; 170 TextViewCompleter* fURLAutoCompleter; 171 BString fPreviousText; 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 fPreviousText(""), 183 fUpdateAutoCompleterChoices(true) 184 { 185 MakeResizable(true); 186 SetStylable(true); 187 fURLAutoCompleter->SetModificationsReported(true); 188 } 189 190 191 URLInputGroup::URLTextView::~URLTextView() 192 { 193 delete fURLAutoCompleter; 194 } 195 196 197 void 198 URLInputGroup::URLTextView::MessageReceived(BMessage* message) 199 { 200 switch (message->what) { 201 case MSG_CLEAR: 202 SetText(""); 203 break; 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 // Revert to text as it was when we received keyboard focus. 286 SetText(fPreviousText.String()); 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 BTextView::KeyDown(bytes, numBytes); 296 break; 297 } 298 } 299 300 void 301 URLInputGroup::URLTextView::MakeFocus(bool focus) 302 { 303 if (focus == IsFocus()) 304 return; 305 306 BTextView::MakeFocus(focus); 307 308 if (focus) { 309 fPreviousText = Text(); 310 SelectAll(); 311 } 312 313 fURLInputGroup->Invalidate(); 314 } 315 316 317 BSize 318 URLInputGroup::URLTextView::MinSize() 319 { 320 BSize min; 321 min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset); 322 // we always add at least one pixel vertical inset top/bottom for 323 // the text rect. 324 min.width = min.height * 3; 325 return BLayoutUtils::ComposeSize(ExplicitMinSize(), min); 326 } 327 328 329 BSize 330 URLInputGroup::URLTextView::MaxSize() 331 { 332 BSize max(MinSize()); 333 max.width = B_SIZE_UNLIMITED; 334 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); 335 } 336 337 338 void 339 URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update) 340 { 341 fUpdateAutoCompleterChoices = update; 342 } 343 344 345 void 346 URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength, 347 int32 inOffset, const text_run_array* inRuns) 348 { 349 // Filter all line breaks, note that inText is not terminated. 350 if (inLength == 1) { 351 if (*inText == '\n' || *inText == '\r') 352 BTextView::InsertText(" ", 1, inOffset, inRuns); 353 else 354 BTextView::InsertText(inText, 1, inOffset, inRuns); 355 } else { 356 BString filteredText(inText, inLength); 357 filteredText.ReplaceAll('\n', ' '); 358 filteredText.ReplaceAll('\r', ' '); 359 BTextView::InsertText(filteredText.String(), inLength, inOffset, 360 inRuns); 361 } 362 363 // Make the base URL part bold. 364 BString text(Text(), TextLength()); 365 int32 baseUrlStart = text.FindFirst("://"); 366 if (baseUrlStart >= 0) 367 baseUrlStart += 3; 368 else 369 baseUrlStart = 0; 370 int32 baseUrlEnd = text.FindFirst("/", baseUrlStart); 371 if (baseUrlEnd < 0) 372 baseUrlEnd = TextLength(); 373 BFont font; 374 GetFont(&font); 375 const rgb_color black = (rgb_color) { 0, 0, 0, 255 }; 376 const rgb_color gray = (rgb_color) { 60, 60, 60, 255 }; 377 if (baseUrlStart > 0) 378 SetFontAndColor(0, baseUrlStart - 1, &font, B_FONT_ALL, &gray); 379 if (baseUrlEnd > baseUrlStart) { 380 font.SetFace(B_BOLD_FACE); 381 SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black); 382 } 383 if (baseUrlEnd < TextLength()) { 384 font.SetFace(B_REGULAR_FACE); 385 SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray); 386 } 387 388 fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices); 389 } 390 391 392 void 393 URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset) 394 { 395 BTextView::DeleteText(fromOffset, toOffset); 396 397 fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices); 398 } 399 400 401 void 402 URLInputGroup::URLTextView::_AlignTextRect() 403 { 404 // Layout the text rect to be in the middle, normally this means there 405 // is one pixel spacing on each side. 406 BRect textRect(Bounds()); 407 textRect.left = 0.0; 408 float vInset = max_c(1, 409 floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5)); 410 float hInset = kHorizontalTextRectInset; 411 412 if (be_control_look) 413 hInset = be_control_look->DefaultLabelSpacing(); 414 415 textRect.InsetBy(hInset, vInset); 416 SetTextRect(textRect); 417 } 418 419 420 const uint32 kGoBitmapWidth = 14; 421 const uint32 kGoBitmapHeight = 14; 422 const color_space kGoBitmapFormat = B_RGBA32; 423 424 const unsigned char kGoBitmapBits[] = { 425 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 426 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 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, 0x17, 0x17, 0x17, 0x21, 437 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 438 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 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 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 441 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 442 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 443 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 444 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 445 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 446 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 447 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 448 0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed, 449 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 450 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 451 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 452 0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 453 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 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, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02, 456 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 457 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 458 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 459 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 460 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 461 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 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, 0x00, 0x00, 0x00, 0x00, 464 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 468 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 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, 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 }; 475 476 477 // #pragma mark - PageIconView 478 479 480 class URLInputGroup::PageIconView : public BView { 481 public: 482 PageIconView() 483 : 484 BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 485 fIcon(NULL) 486 { 487 SetDrawingMode(B_OP_ALPHA); 488 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 489 } 490 491 ~PageIconView() 492 { 493 delete fIcon; 494 } 495 496 virtual void Draw(BRect updateRect) 497 { 498 if (fIcon == NULL) 499 return; 500 501 BRect bounds(Bounds()); 502 BRect iconBounds(0, 0, 15, 15); 503 iconBounds.OffsetTo( 504 floorf((bounds.left + bounds.right 505 - (iconBounds.left + iconBounds.right)) / 2 + 0.5f), 506 floorf((bounds.top + bounds.bottom 507 - (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f)); 508 DrawBitmap(fIcon, fIcon->Bounds(), iconBounds, 509 B_FILTER_BITMAP_BILINEAR); 510 } 511 512 virtual BSize MinSize() 513 { 514 if (fIcon != NULL) 515 return BSize(18, 18); 516 return BSize(0, 0); 517 } 518 519 virtual BSize MaxSize() 520 { 521 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 522 } 523 524 virtual BSize PreferredSize() 525 { 526 return MinSize(); 527 } 528 529 void SetIcon(const BBitmap* icon) 530 { 531 if (icon == NULL && fIcon == NULL) 532 return; 533 if (!(fIcon != NULL && icon != NULL)) 534 InvalidateLayout(); 535 delete fIcon; 536 if (icon) 537 fIcon = new BBitmap(icon); 538 else 539 fIcon = NULL; 540 Invalidate(); 541 } 542 543 private: 544 BBitmap* fIcon; 545 }; 546 547 548 // #pragma mark - URLInputGroup 549 550 551 URLInputGroup::URLInputGroup(BMessage* goMessage) 552 : 553 BGroupView(B_HORIZONTAL, 0.0), 554 fWindowActive(false) 555 { 556 GroupLayout()->SetInsets(2, 2, 2, 2); 557 558 fIconView = new PageIconView(); 559 GroupLayout()->AddView(fIconView, 0.0f); 560 561 fTextView = new URLTextView(this); 562 AddChild(fTextView); 563 564 AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER)); 565 566 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from 567 // resources? 568 // fGoButton = new BitmapButton("kActionGo", NULL); 569 fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth, 570 kGoBitmapHeight, kGoBitmapFormat, goMessage); 571 GroupLayout()->AddView(fGoButton, 0.0f); 572 573 SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); 574 SetLowColor(ViewColor()); 575 SetViewColor(B_TRANSPARENT_COLOR); 576 577 SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 578 B_ALIGN_VERTICAL_CENTER)); 579 } 580 581 582 URLInputGroup::~URLInputGroup() 583 { 584 } 585 586 587 void 588 URLInputGroup::AttachedToWindow() 589 { 590 BGroupView::AttachedToWindow(); 591 fWindowActive = Window()->IsActive(); 592 } 593 594 595 void 596 URLInputGroup::WindowActivated(bool active) 597 { 598 BGroupView::WindowActivated(active); 599 if (fWindowActive != active) { 600 fWindowActive = active; 601 Invalidate(); 602 } 603 } 604 605 606 void 607 URLInputGroup::Draw(BRect updateRect) 608 { 609 BRect bounds(Bounds()); 610 rgb_color base(LowColor()); 611 uint32 flags = 0; 612 if (fWindowActive && fTextView->IsFocus()) 613 flags |= BControlLook::B_FOCUSED; 614 be_control_look->DrawTextControlBorder(this, bounds, updateRect, base, 615 flags); 616 } 617 618 619 void 620 URLInputGroup::MakeFocus(bool focus) 621 { 622 // Forward this to the text view, we never accept focus ourselves. 623 fTextView->MakeFocus(focus); 624 } 625 626 627 BTextView* 628 URLInputGroup::TextView() const 629 { 630 return fTextView; 631 } 632 633 634 void 635 URLInputGroup::SetText(const char* text) 636 { 637 if (!text || !Text() || strcmp(Text(), text) != 0) { 638 fTextView->SetUpdateAutoCompleterChoices(false); 639 fTextView->SetText(text); 640 fTextView->SetUpdateAutoCompleterChoices(true); 641 } 642 } 643 644 645 const char* 646 URLInputGroup::Text() const 647 { 648 return fTextView->Text(); 649 } 650 651 652 BButton* 653 URLInputGroup::GoButton() const 654 { 655 return fGoButton; 656 } 657 658 659 void 660 URLInputGroup::SetPageIcon(const BBitmap* icon) 661 { 662 fIconView->SetIcon(icon); 663 } 664 665