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 "BrowserWindow.h" 29 #include "BrowsingHistory.h" 30 #include "IconButton.h" 31 #include "IconUtils.h" 32 #include "TextViewCompleter.h" 33 #include "WebView.h" 34 #include "WebWindow.h" 35 36 37 #undef B_TRANSLATION_CONTEXT 38 #define B_TRANSLATION_CONTEXT "URL Bar" 39 40 41 class URLChoice : public BAutoCompleter::Choice { 42 public: 43 URLChoice(const BString& choiceText, const BString& displayText, 44 int32 matchPos, int32 matchLen, int32 priority) 45 : 46 BAutoCompleter::Choice(choiceText, displayText, matchPos, matchLen), 47 fPriority(priority) 48 { 49 } 50 51 bool operator<(const URLChoice& other) const 52 { 53 if (fPriority > other.fPriority) 54 return true; 55 return DisplayText() < other.DisplayText(); 56 } 57 58 bool operator==(const URLChoice& other) const 59 { 60 return fPriority == other.fPriority 61 && DisplayText() < other.DisplayText(); 62 } 63 64 private: 65 int32 fPriority; 66 }; 67 68 69 class BrowsingHistoryChoiceModel : public BAutoCompleter::ChoiceModel { 70 virtual void FetchChoicesFor(const BString& pattern) 71 { 72 int32 count = CountChoices(); 73 for (int32 i = 0; i < count; i++) { 74 delete reinterpret_cast<BAutoCompleter::Choice*>( 75 fChoices.ItemAtFast(i)); 76 } 77 fChoices.MakeEmpty(); 78 79 // Search through BrowsingHistory for any matches. 80 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 81 if (!history->Lock()) 82 return; 83 84 BString lastBaseURL; 85 int32 priority = INT_MAX; 86 87 count = history->CountItems(); 88 for (int32 i = 0; i < count; i++) { 89 BrowsingHistoryItem item = history->HistoryItemAt(i); 90 const BString& choiceText = item.URL(); 91 int32 matchPos = choiceText.IFindFirst(pattern); 92 if (matchPos < 0) 93 continue; 94 if (lastBaseURL.Length() > 0 95 && choiceText.FindFirst(lastBaseURL) >= 0) { 96 priority--; 97 } else 98 priority = INT_MAX; 99 lastBaseURL = baseURL(choiceText); 100 fChoices.AddItem(new URLChoice(choiceText, 101 choiceText, matchPos, pattern.Length(), priority)); 102 } 103 104 history->Unlock(); 105 106 fChoices.SortItems(_CompareChoices); 107 } 108 109 virtual int32 CountChoices() const 110 { 111 return fChoices.CountItems(); 112 } 113 114 virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const 115 { 116 return reinterpret_cast<BAutoCompleter::Choice*>( 117 fChoices.ItemAt(index)); 118 } 119 120 static int _CompareChoices(const void* a, const void* b) 121 { 122 const URLChoice* aChoice 123 = *reinterpret_cast<const URLChoice* const *>(a); 124 const URLChoice* bChoice 125 = *reinterpret_cast<const URLChoice* const *>(b); 126 if (*aChoice < *bChoice) 127 return -1; 128 else if (*aChoice == *bChoice) 129 return 0; 130 return 1; 131 } 132 133 private: 134 BList fChoices; 135 }; 136 137 138 // #pragma mark - URLTextView 139 140 141 static const float kHorizontalTextRectInset = 4.0; 142 143 144 class URLInputGroup::URLTextView : public BTextView { 145 private: 146 static const uint32 MSG_CLEAR = 'cler'; 147 148 public: 149 URLTextView(URLInputGroup* parent); 150 virtual ~URLTextView(); 151 152 virtual void MessageReceived(BMessage* message); 153 virtual void MouseDown(BPoint where); 154 virtual void KeyDown(const char* bytes, int32 numBytes); 155 virtual void MakeFocus(bool focused = true); 156 157 virtual BSize MinSize(); 158 virtual BSize MaxSize(); 159 160 void SetUpdateAutoCompleterChoices(bool update); 161 162 protected: 163 virtual void InsertText(const char* inText, int32 inLength, 164 int32 inOffset, 165 const text_run_array* inRuns); 166 virtual void DeleteText(int32 fromOffset, int32 toOffset); 167 168 private: 169 URLInputGroup* fURLInputGroup; 170 TextViewCompleter* fURLAutoCompleter; 171 bool fUpdateAutoCompleterChoices; 172 }; 173 174 175 URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent) 176 : 177 BTextView("url"), 178 fURLInputGroup(parent), 179 fURLAutoCompleter(new TextViewCompleter(this, 180 new BrowsingHistoryChoiceModel())), 181 fUpdateAutoCompleterChoices(true) 182 { 183 MakeResizable(true); 184 SetStylable(true); 185 SetInsets(be_control_look->DefaultLabelSpacing(), 2, 0, 2); 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::MouseDown(BPoint where) 213 { 214 bool wasFocus = IsFocus(); 215 if (!wasFocus) 216 MakeFocus(true); 217 218 int32 buttons; 219 if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) 220 buttons = B_PRIMARY_MOUSE_BUTTON; 221 222 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { 223 // Display context menu 224 int32 selectionStart; 225 int32 selectionEnd; 226 GetSelection(&selectionStart, &selectionEnd); 227 bool canCutOrCopy = selectionEnd > selectionStart; 228 229 bool canPaste = false; 230 if (be_clipboard->Lock()) { 231 if (BMessage* data = be_clipboard->Data()) 232 canPaste = data->HasData("text/plain", B_MIME_TYPE); 233 be_clipboard->Unlock(); 234 } 235 236 BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"), 237 new BMessage(B_CUT)); 238 BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"), 239 new BMessage(B_COPY)); 240 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 241 new BMessage(B_PASTE)); 242 BMenuItem* clearItem = new BMenuItem(B_TRANSLATE("Clear"), 243 new BMessage(MSG_CLEAR)); 244 cutItem->SetEnabled(canCutOrCopy); 245 copyItem->SetEnabled(canCutOrCopy); 246 pasteItem->SetEnabled(canPaste); 247 clearItem->SetEnabled(strlen(Text()) > 0); 248 249 BPopUpMenu* menu = new BPopUpMenu("url context"); 250 menu->AddItem(cutItem); 251 menu->AddItem(copyItem); 252 menu->AddItem(pasteItem); 253 menu->AddItem(clearItem); 254 255 menu->SetTargetForItems(this); 256 menu->Go(ConvertToScreen(where), true, true, true); 257 return; 258 } 259 260 // Only pass through to base class if we already have focus. 261 if (!wasFocus) 262 return; 263 264 BTextView::MouseDown(where); 265 } 266 267 268 void 269 URLInputGroup::URLTextView::KeyDown(const char* bytes, int32 numBytes) 270 { 271 switch (bytes[0]) { 272 case B_TAB: 273 BView::KeyDown(bytes, numBytes); 274 break; 275 276 case B_ESCAPE: 277 // Text already unlocked && replaced in BrowserWindow, 278 // now select it. 279 SelectAll(); 280 break; 281 282 case B_RETURN: 283 // Don't let this through to the text view. 284 break; 285 286 default: 287 { 288 BString currentText = Text(); 289 BTextView::KeyDown(bytes, numBytes); 290 // Lock the URL input if it was modified 291 if (!fURLInputGroup->IsURLInputLocked() 292 && Text() != currentText) 293 fURLInputGroup->LockURLInput(); 294 break; 295 } 296 } 297 } 298 299 void 300 URLInputGroup::URLTextView::MakeFocus(bool focus) 301 { 302 // Unlock the URL input if focus was lost. 303 if (!focus) 304 fURLInputGroup->LockURLInput(false); 305 306 if (focus == IsFocus()) 307 return; 308 309 BTextView::MakeFocus(focus); 310 311 if (focus) 312 SelectAll(); 313 314 fURLInputGroup->Invalidate(); 315 } 316 317 318 BSize 319 URLInputGroup::URLTextView::MinSize() 320 { 321 BSize min; 322 min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset); 323 // we always add at least one pixel vertical inset top/bottom for 324 // the text rect. 325 min.width = min.height * 3; 326 return BLayoutUtils::ComposeSize(ExplicitMinSize(), min); 327 } 328 329 330 BSize 331 URLInputGroup::URLTextView::MaxSize() 332 { 333 BSize max(MinSize()); 334 max.width = B_SIZE_UNLIMITED; 335 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); 336 } 337 338 339 void 340 URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update) 341 { 342 fUpdateAutoCompleterChoices = update; 343 } 344 345 346 void 347 URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength, 348 int32 inOffset, const text_run_array* inRuns) 349 { 350 // Filter all line breaks, note that inText is not terminated. 351 if (inLength == 1) { 352 if (*inText == '\n' || *inText == '\r') 353 BTextView::InsertText(" ", 1, inOffset, inRuns); 354 else 355 BTextView::InsertText(inText, 1, inOffset, inRuns); 356 } else { 357 BString filteredText(inText, inLength); 358 filteredText.ReplaceAll('\n', ' '); 359 filteredText.ReplaceAll('\r', ' '); 360 BTextView::InsertText(filteredText.String(), inLength, inOffset, 361 inRuns); 362 } 363 364 // Make the base URL part bold. 365 BString text(Text(), TextLength()); 366 int32 baseUrlStart = text.FindFirst("://"); 367 if (baseUrlStart >= 0) 368 baseUrlStart += 3; 369 else 370 baseUrlStart = 0; 371 int32 baseUrlEnd = text.FindFirst("/", baseUrlStart); 372 if (baseUrlEnd < 0) 373 baseUrlEnd = TextLength(); 374 375 BFont font; 376 GetFont(&font); 377 const rgb_color hostColor = ui_color(B_DOCUMENT_TEXT_COLOR); 378 const rgb_color urlColor = tint_color(hostColor, 379 (hostColor.IsDark() ? B_LIGHTEN_1_TINT : B_DARKEN_1_TINT)); 380 if (baseUrlStart > 0) 381 SetFontAndColor(0, baseUrlStart, &font, B_FONT_ALL, &urlColor); 382 if (baseUrlEnd > baseUrlStart) { 383 font.SetFace(B_BOLD_FACE); 384 SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, 385 &hostColor); 386 } 387 if (baseUrlEnd < TextLength()) { 388 font.SetFace(B_REGULAR_FACE); 389 SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, 390 &urlColor); 391 } 392 393 fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices); 394 } 395 396 397 void 398 URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset) 399 { 400 BTextView::DeleteText(fromOffset, toOffset); 401 402 fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices); 403 } 404 405 406 const uint32 kGoBitmapWidth = 14; 407 const uint32 kGoBitmapHeight = 14; 408 const color_space kGoBitmapFormat = B_RGBA32; 409 410 const unsigned char kGoBitmapBits[] = { 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 421 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 423 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 424 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 425 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 426 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 427 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 428 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 429 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 430 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 431 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 432 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 433 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 434 0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed, 435 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 436 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 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, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 439 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 440 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 441 0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02, 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, 0xe1, 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, 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, 0x17, 0x17, 0x17, 0x21, 447 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 451 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x17, 0x17, 0x17, 0x21, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 }; 461 462 463 const unsigned char kPlaceholderIcon[] = { 464 0x6e, 0x63, 0x69, 0x66, 0x04, 0x04, 0x00, 0x66, 0x03, 0x00, 0x3f, 0x80, 465 0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x3d, 0xa6, 0x64, 0xc2, 0x19, 466 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 0x64, 0x49, 0xac, 0xcc, 0x00, 0xab, 467 0xd5, 0xff, 0xff, 0x00, 0x6c, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x00, 0x00, 468 0x00, 0x3d, 0x26, 0x64, 0xc2, 0x19, 0x98, 0x00, 0x00, 0x00, 0x4d, 0xce, 469 0x64, 0x49, 0xac, 0xcc, 0x00, 0x80, 0xff, 0x80, 0xff, 0x00, 0xb2, 0x00, 470 0x04, 0x02, 0x04, 0x34, 0x22, 0xbd, 0x9b, 0x22, 0xb8, 0x53, 0x22, 0x28, 471 0x2e, 0x28, 0xb5, 0xef, 0x28, 0xbb, 0x37, 0x34, 0x3a, 0xb8, 0x53, 0x3a, 472 0xbd, 0x9b, 0x3a, 0x40, 0x2e, 0x40, 0xbb, 0x37, 0x40, 0xb5, 0xef, 0x02, 473 0x08, 0xbe, 0xb6, 0xb4, 0xac, 0xc1, 0x46, 0xb4, 0xac, 0xbc, 0x25, 0xb4, 474 0xac, 0xb8, 0x09, 0xb7, 0x35, 0xb9, 0xcf, 0xb5, 0xa0, 0xb8, 0x05, 0xbe, 475 0xb6, 0x35, 0xc2, 0xe5, 0x35, 0xbe, 0xb6, 0x35, 0xc5, 0x68, 0xb8, 0x09, 476 0xc6, 0x36, 0xb8, 0x09, 0xc6, 0x36, 0xb9, 0xcf, 0xc7, 0xca, 0xbe, 0xb6, 477 0xc8, 0xc1, 0xbc, 0x25, 0xc8, 0xc1, 0xc1, 0xb3, 0xc8, 0xc1, 0xc6, 0x3c, 478 0xc5, 0x5b, 0xc4, 0x65, 0xc7, 0x70, 0xc6, 0x3e, 0xbe, 0xb6, 0xc2, 0x0f, 479 0xba, 0x87, 0xc2, 0x0f, 0xbe, 0xb6, 0xc2, 0x0f, 0xb8, 0x05, 0xc5, 0x64, 480 0xb7, 0x37, 0xc5, 0x64, 0xb7, 0x37, 0xc3, 0x9e, 0xb5, 0xa2, 0x02, 0x04, 481 0xb8, 0x09, 0xb7, 0x35, 0xb8, 0x05, 0xbe, 0xb6, 0xb5, 0xf8, 0xb9, 0x0c, 482 0xb4, 0xac, 0xbe, 0xb6, 0xb4, 0xac, 0xbb, 0xba, 0xb4, 0xac, 0xc1, 0xb1, 483 0xb8, 0x09, 0xc6, 0x36, 0xb5, 0xf8, 0xc4, 0x5e, 0xb9, 0xcf, 0xc7, 0xca, 484 0x35, 0xc2, 0xe5, 0x35, 0xc5, 0x68, 0x35, 0xbe, 0xb6, 0x02, 0x04, 0x4d, 485 0x51, 0xc4, 0xf2, 0xbf, 0x04, 0x53, 0x4e, 0xc8, 0xc1, 0xbe, 0x58, 0xc8, 486 0xc1, 0xc1, 0x55, 0xc8, 0xc1, 0xbb, 0x5d, 0xc5, 0x64, 0xb6, 0xd9, 0xc7, 487 0x75, 0xb8, 0xb0, 0xc3, 0x9e, 0xb5, 0x44, 0xc2, 0x0f, 0xba, 0x29, 0xc2, 488 0x0f, 0xb7, 0xa6, 0xc2, 0x0f, 0xbe, 0x58, 0x04, 0x0a, 0x00, 0x01, 0x00, 489 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x19, 490 0x98, 0xc6, 0x19, 0x93, 0x44, 0x19, 0xa2, 0x01, 0x17, 0x84, 0x00, 0x04, 491 0x0a, 0x01, 0x01, 0x00, 0x12, 0x42, 0x19, 0x98, 0x00, 0x00, 0x00, 0x00, 492 0x00, 0x00, 0x42, 0x19, 0x98, 0xc7, 0x26, 0x5f, 0x28, 0x96, 0xf9, 0x01, 493 0x17, 0x83, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x01, 0x00, 0x0a, 0x03, 0x02, 494 0x02, 0x03, 0x00 495 }; 496 497 // #pragma mark - PageIconView 498 499 500 class URLInputGroup::PageIconView : public BView { 501 public: 502 PageIconView() 503 : 504 BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 505 fIcon(NULL), 506 fClickPoint(-1, 0), 507 fPageIconSet(false) 508 { 509 SetDrawingMode(B_OP_ALPHA); 510 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 511 SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR); 512 } 513 514 ~PageIconView() 515 { 516 delete fIcon; 517 } 518 519 virtual void Draw(BRect updateRect) 520 { 521 BRect bounds(Bounds()); 522 BRect iconBounds(0, 0, 15, 15); 523 iconBounds.OffsetTo( 524 floorf((bounds.left + bounds.right 525 - (iconBounds.left + iconBounds.right)) / 2 + 0.5f), 526 floorf((bounds.top + bounds.bottom 527 - (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f)); 528 DrawBitmap(fIcon, fIcon->Bounds(), iconBounds, 529 B_FILTER_BITMAP_BILINEAR); 530 } 531 532 virtual BSize MinSize() 533 { 534 return BSize(18, 18); 535 } 536 537 virtual BSize MaxSize() 538 { 539 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 540 } 541 542 virtual BSize PreferredSize() 543 { 544 return MinSize(); 545 } 546 547 void MouseDown(BPoint where) 548 { 549 int32 buttons; 550 if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) == B_OK) { 551 if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0) { 552 // Memorize click point for dragging 553 fClickPoint = where; 554 } 555 } 556 return; 557 } 558 559 void MouseUp(BPoint where) 560 { 561 fClickPoint.x = -1; 562 } 563 564 virtual void MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage) 565 { 566 if (dragMessage != NULL) 567 return; 568 569 if (fClickPoint.x >= 0 570 && (fabs(where.x - fClickPoint.x) > 4 || fabs(where.y - fClickPoint.y) > 4)) { 571 // Start dragging 572 BPoint offset = fClickPoint - Frame().LeftTop(); 573 574 const char* url = static_cast<URLInputGroup*>(Parent())->Text(); 575 const char* title = 576 static_cast<BWebWindow*>(Window())->CurrentWebView()->MainFrameTitle(); 577 578 // Validate the file name to be set for the clipping if user drags to Tracker. 579 BString fileName(title); 580 if (fileName.Length() == 0) { 581 fileName = url; 582 int32 leafPos = fileName.FindLast('/'); 583 if (leafPos >= 0) 584 fileName.Remove(0, leafPos + 1); 585 } 586 fileName.ReplaceAll('/', '-'); 587 fileName.Truncate(B_FILE_NAME_LENGTH - 1); 588 589 BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK, 590 B_CMAP8); 591 miniIcon.ImportBits(fIcon); 592 // TODO: obtain and send the large icon in addition to the mini icon. 593 // Currently PageUserData does not provide a function that returns this. 594 595 BMessage drag(B_SIMPLE_DATA); 596 drag.AddInt32("be:actions", B_COPY_TARGET); 597 drag.AddString("be:clip_name", fileName.String()); 598 drag.AddString("be:filetypes", "application/x-vnd.Be-bookmark"); 599 // Support the "Passing Data via File" protocol 600 drag.AddString("be:types", B_FILE_MIME_TYPE); 601 BMessage data(B_SIMPLE_DATA); 602 data.AddString("url", url); 603 data.AddString("title", title); 604 // The title may differ from the validated filename 605 if (fPageIconSet == true) { 606 // Don't bother sending the placeholder web icon, if that is all we have. 607 data.AddData("miniIcon", B_COLOR_8_BIT_TYPE, &miniIcon, sizeof(miniIcon)); 608 } 609 drag.AddMessage("be:originator-data", &data); 610 611 BBitmap* iconClone = new BBitmap(fIcon); 612 // Needed because DragMessage will delete the bitmap when it's done. 613 614 DragMessage(&drag, iconClone, B_OP_ALPHA, offset); 615 } 616 return; 617 } 618 619 void SetIcon(const BBitmap* icon) 620 { 621 delete fIcon; 622 if (icon) { 623 fIcon = new BBitmap(icon); 624 fPageIconSet = true; 625 } else { 626 fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32); 627 BIconUtils::GetVectorIcon(kPlaceholderIcon, 628 sizeof(kPlaceholderIcon), fIcon); 629 fPageIconSet = false; 630 } 631 Invalidate(); 632 } 633 634 private: 635 BBitmap* fIcon; 636 BPoint fClickPoint; 637 bool fPageIconSet; 638 }; 639 640 641 // #pragma mark - URLInputGroup 642 643 644 URLInputGroup::URLInputGroup(BMessage* goMessage) 645 : 646 BGroupView(B_HORIZONTAL, 0.0), 647 fWindowActive(false), 648 fURLLocked(false) 649 { 650 GroupLayout()->SetInsets(2, 2, 2, 2); 651 652 fIconView = new PageIconView(); 653 GroupLayout()->AddView(fIconView, 0.0f); 654 655 fTextView = new URLTextView(this); 656 AddChild(fTextView); 657 658 AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER)); 659 660 // TODO: Fix in Haiku, no in-built support for archived BBitmaps from 661 // resources? 662 // fGoButton = new BitmapButton("kActionGo", NULL); 663 fGoButton = new BBitmapButton(kGoBitmapBits, kGoBitmapWidth, 664 kGoBitmapHeight, kGoBitmapFormat, goMessage); 665 GroupLayout()->AddView(fGoButton, 0.0f); 666 667 SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); 668 SetLowColor(ViewColor()); 669 SetViewColor(fTextView->ViewColor()); 670 671 SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 672 B_ALIGN_VERTICAL_CENTER)); 673 674 } 675 676 677 URLInputGroup::~URLInputGroup() 678 { 679 } 680 681 682 void 683 URLInputGroup::AttachedToWindow() 684 { 685 BGroupView::AttachedToWindow(); 686 fWindowActive = Window()->IsActive(); 687 } 688 689 690 void 691 URLInputGroup::WindowActivated(bool active) 692 { 693 BGroupView::WindowActivated(active); 694 if (fWindowActive != active) { 695 fWindowActive = active; 696 Invalidate(); 697 } 698 } 699 700 701 void 702 URLInputGroup::Draw(BRect updateRect) 703 { 704 BRect bounds(Bounds()); 705 rgb_color base(LowColor()); 706 uint32 flags = 0; 707 if (fWindowActive && fTextView->IsFocus()) 708 flags |= BControlLook::B_FOCUSED; 709 be_control_look->DrawTextControlBorder(this, bounds, updateRect, base, 710 flags); 711 } 712 713 714 void 715 URLInputGroup::MakeFocus(bool focus) 716 { 717 // Forward this to the text view, we never accept focus ourselves. 718 fTextView->MakeFocus(focus); 719 } 720 721 722 BTextView* 723 URLInputGroup::TextView() const 724 { 725 return fTextView; 726 } 727 728 729 void 730 URLInputGroup::SetText(const char* text) 731 { 732 // Ignore setting the text, if the input is locked. 733 if (fURLLocked) 734 return; 735 736 if (!text || !Text() || strcmp(Text(), text) != 0) { 737 fTextView->SetUpdateAutoCompleterChoices(false); 738 fTextView->SetText(text); 739 fTextView->SetUpdateAutoCompleterChoices(true); 740 } 741 } 742 743 744 const char* 745 URLInputGroup::Text() const 746 { 747 return fTextView->Text(); 748 } 749 750 751 BButton* 752 URLInputGroup::GoButton() const 753 { 754 return fGoButton; 755 } 756 757 758 void 759 URLInputGroup::SetPageIcon(const BBitmap* icon) 760 { 761 fIconView->SetIcon(icon); 762 } 763 764 765 bool 766 URLInputGroup::IsURLInputLocked() const 767 { 768 return fURLLocked; 769 } 770 771 772 void 773 URLInputGroup::LockURLInput(bool lock) 774 { 775 fURLLocked = lock; 776 } 777