1 /* 2 * Copyright (C) 2007 Andrea Anzani <andrea.anzani@gmail.com> 3 * Copyright (C) 2007, 2010 Ryan Leavengood <leavengood@gmail.com> 4 * Copyright (C) 2009 Maxime Simon <simon.maxime@gmail.com> 5 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de> 6 * Copyright (C) 2010 Michael Lotz <mmlr@mlotz.ch> 7 * Copyright (C) 2010 Rene Gollent <rene@gollent.com> 8 * 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include "BrowserWindow.h" 34 35 #include <Alert.h> 36 #include <Application.h> 37 #include <Bitmap.h> 38 #include <Button.h> 39 #include <Catalog.h> 40 #include <CheckBox.h> 41 #include <Clipboard.h> 42 #include <Directory.h> 43 #include <Entry.h> 44 #include <File.h> 45 #include <FindDirectory.h> 46 #include <GridLayoutBuilder.h> 47 #include <GroupLayout.h> 48 #include <GroupLayoutBuilder.h> 49 #include <LayoutBuilder.h> 50 #include <Locale.h> 51 #include <MenuBar.h> 52 #include <MenuItem.h> 53 #include <MessageRunner.h> 54 #include <NodeInfo.h> 55 #include <Path.h> 56 #include <Roster.h> 57 #include <Screen.h> 58 #include <SeparatorView.h> 59 #include <SpaceLayoutItem.h> 60 #include <StatusBar.h> 61 #include <StringView.h> 62 #include <TextControl.h> 63 64 #include <stdio.h> 65 66 #include "AuthenticationPanel.h" 67 #include "BaseURL.h" 68 #include "BitmapButton.h" 69 #include "BrowserApp.h" 70 #include "BrowsingHistory.h" 71 #include "CredentialsStorage.h" 72 #include "IconButton.h" 73 #include "NavMenu.h" 74 #include "SettingsKeys.h" 75 #include "SettingsMessage.h" 76 #include "TabManager.h" 77 #include "URLInputGroup.h" 78 #include "WebPage.h" 79 #include "WebView.h" 80 #include "WebViewConstants.h" 81 #include "WindowIcon.h" 82 83 84 #undef B_TRANSLATION_CONTEXT 85 #define B_TRANSLATION_CONTEXT "WebPositive Window" 86 87 enum { 88 OPEN_LOCATION = 'open', 89 GO_BACK = 'goba', 90 GO_FORWARD = 'gofo', 91 STOP = 'stop', 92 HOME = 'home', 93 GOTO_URL = 'goul', 94 RELOAD = 'reld', 95 CLEAR_HISTORY = 'clhs', 96 97 CREATE_BOOKMARK = 'crbm', 98 SHOW_BOOKMARKS = 'shbm', 99 100 ZOOM_FACTOR_INCREASE = 'zfin', 101 ZOOM_FACTOR_DECREASE = 'zfdc', 102 ZOOM_FACTOR_RESET = 'zfrs', 103 ZOOM_TEXT_ONLY = 'zfto', 104 105 TOGGLE_FULLSCREEN = 'tgfs', 106 TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN = 'tgah', 107 CHECK_AUTO_HIDE_INTERFACE = 'cahi', 108 109 SHOW_PAGE_SOURCE = 'spgs', 110 111 EDIT_SHOW_FIND_GROUP = 'sfnd', 112 EDIT_HIDE_FIND_GROUP = 'hfnd', 113 EDIT_FIND_NEXT = 'fndn', 114 EDIT_FIND_PREVIOUS = 'fndp', 115 FIND_TEXT_CHANGED = 'ftxt', 116 117 SELECT_TAB = 'sltb', 118 }; 119 120 121 static BLayoutItem* 122 layoutItemFor(BView* view) 123 { 124 BLayout* layout = view->Parent()->GetLayout(); 125 int32 index = layout->IndexOfView(view); 126 return layout->ItemAt(index); 127 } 128 129 130 class BookmarkMenu : public BNavMenu { 131 public: 132 BookmarkMenu(const char* title, BHandler* target, const entry_ref* navDir) 133 : 134 BNavMenu(title, B_REFS_RECEIVED, target) 135 { 136 // Add these items here already, so the shortcuts work even when 137 // the menu has never been opened yet. 138 _AddStaticItems(); 139 140 SetNavDir(navDir); 141 } 142 143 virtual void AttachedToWindow() 144 { 145 RemoveItems(0, CountItems(), true); 146 ForceRebuild(); 147 BNavMenu::AttachedToWindow(); 148 if (CountItems() > 0) 149 AddItem(new BSeparatorItem(), 0); 150 _AddStaticItems(); 151 DoLayout(); 152 } 153 154 private: 155 void _AddStaticItems() 156 { 157 AddItem(new BMenuItem(B_TRANSLATE("Manage bookmarks"), 158 new BMessage(SHOW_BOOKMARKS), 'M'), 0); 159 AddItem(new BMenuItem(B_TRANSLATE("Bookmark this page"), 160 new BMessage(CREATE_BOOKMARK), 'B'), 0); 161 } 162 }; 163 164 165 class PageUserData : public BWebView::UserData { 166 public: 167 PageUserData(BView* focusedView) 168 : 169 fFocusedView(focusedView), 170 fPageIcon(NULL), 171 fURLInputSelectionStart(-1), 172 fURLInputSelectionEnd(-1) 173 { 174 } 175 176 ~PageUserData() 177 { 178 delete fPageIcon; 179 } 180 181 void SetFocusedView(BView* focusedView) 182 { 183 fFocusedView = focusedView; 184 } 185 186 BView* FocusedView() const 187 { 188 return fFocusedView; 189 } 190 191 void SetPageIcon(const BBitmap* icon) 192 { 193 delete fPageIcon; 194 if (icon) 195 fPageIcon = new BBitmap(icon); 196 else 197 fPageIcon = NULL; 198 } 199 200 const BBitmap* PageIcon() const 201 { 202 return fPageIcon; 203 } 204 205 void SetURLInputContents(const char* text) 206 { 207 fURLInputContents = text; 208 } 209 210 const BString& URLInputContents() const 211 { 212 return fURLInputContents; 213 } 214 215 void SetURLInputSelection(int32 selectionStart, int32 selectionEnd) 216 { 217 fURLInputSelectionStart = selectionStart; 218 fURLInputSelectionEnd = selectionEnd; 219 } 220 221 int32 URLInputSelectionStart() const 222 { 223 return fURLInputSelectionStart; 224 } 225 226 int32 URLInputSelectionEnd() const 227 { 228 return fURLInputSelectionEnd; 229 } 230 231 private: 232 BView* fFocusedView; 233 BBitmap* fPageIcon; 234 BString fURLInputContents; 235 int32 fURLInputSelectionStart; 236 int32 fURLInputSelectionEnd; 237 }; 238 239 240 // #pragma mark - BrowserWindow 241 242 243 BrowserWindow::BrowserWindow(BRect frame, SettingsMessage* appSettings, 244 const BString& url, uint32 interfaceElements, BWebView* webView) 245 : 246 BWebWindow(frame, kApplicationName, 247 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 248 B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS), 249 fIsFullscreen(false), 250 fInterfaceVisible(false), 251 fPulseRunner(NULL), 252 fVisibleInterfaceElements(interfaceElements), 253 fAppSettings(appSettings), 254 fZoomTextOnly(true), 255 fShowTabsIfSinglePageOpen(true), 256 fAutoHideInterfaceInFullscreenMode(false), 257 fAutoHidePointer(false) 258 { 259 // Begin listening to settings changes and read some current values. 260 fAppSettings->AddListener(BMessenger(this)); 261 // fZoomTextOnly = fAppSettings->GetValue("zoom text only", fZoomTextOnly); 262 fShowTabsIfSinglePageOpen = fAppSettings->GetValue( 263 kSettingsKeyShowTabsIfSinglePageOpen, fShowTabsIfSinglePageOpen); 264 265 fAutoHidePointer = fAppSettings->GetValue(kSettingsKeyAutoHidePointer, 266 fAutoHidePointer); 267 268 fNewWindowPolicy = fAppSettings->GetValue(kSettingsKeyNewWindowPolicy, 269 (uint32)OpenStartPage); 270 fNewTabPolicy = fAppSettings->GetValue(kSettingsKeyNewTabPolicy, 271 (uint32)OpenBlankPage); 272 fStartPageURL = fAppSettings->GetValue(kSettingsKeyStartPageURL, 273 kDefaultStartPageURL); 274 fSearchPageURL = fAppSettings->GetValue(kSettingsKeySearchPageURL, 275 kDefaultSearchPageURL); 276 277 // Create the interface elements 278 BMessage* newTabMessage = new BMessage(NEW_TAB); 279 newTabMessage->AddString("url", ""); 280 newTabMessage->AddPointer("window", this); 281 newTabMessage->AddBool("select", true); 282 fTabManager = new TabManager(BMessenger(this), newTabMessage); 283 284 // Menu 285 #if INTEGRATE_MENU_INTO_TAB_BAR 286 BMenu* mainMenu = fTabManager->Menu(); 287 #else 288 BMenu* mainMenu = new BMenuBar("Main menu"); 289 #endif 290 BMenu* menu = new BMenu(B_TRANSLATE("Window")); 291 BMessage* newWindowMessage = new BMessage(NEW_WINDOW); 292 newWindowMessage->AddString("url", ""); 293 BMenuItem* newItem = new BMenuItem(B_TRANSLATE("New window"), 294 newWindowMessage, 'N'); 295 menu->AddItem(newItem); 296 newItem->SetTarget(be_app); 297 newItem = new BMenuItem(B_TRANSLATE("New tab"), 298 new BMessage(*newTabMessage), 'T'); 299 menu->AddItem(newItem); 300 newItem->SetTarget(be_app); 301 menu->AddItem(new BMenuItem(B_TRANSLATE("Open location"), 302 new BMessage(OPEN_LOCATION), 'L')); 303 menu->AddSeparatorItem(); 304 menu->AddItem(new BMenuItem(B_TRANSLATE("Close window"), 305 new BMessage(B_QUIT_REQUESTED), 'W', B_SHIFT_KEY)); 306 menu->AddItem(new BMenuItem(B_TRANSLATE("Close tab"), 307 new BMessage(CLOSE_TAB), 'W')); 308 menu->AddSeparatorItem(); 309 menu->AddItem(new BMenuItem(B_TRANSLATE("Downloads"), 310 new BMessage(SHOW_DOWNLOAD_WINDOW), 'D')); 311 menu->AddItem(new BMenuItem(B_TRANSLATE("Settings"), 312 new BMessage(SHOW_SETTINGS_WINDOW))); 313 BMenuItem* aboutItem = new BMenuItem(B_TRANSLATE("About"), 314 new BMessage(B_ABOUT_REQUESTED)); 315 menu->AddItem(aboutItem); 316 aboutItem->SetTarget(be_app); 317 menu->AddSeparatorItem(); 318 BMenuItem* quitItem = new BMenuItem(B_TRANSLATE("Quit"), 319 new BMessage(B_QUIT_REQUESTED), 'Q'); 320 menu->AddItem(quitItem); 321 quitItem->SetTarget(be_app); 322 mainMenu->AddItem(menu); 323 324 menu = new BMenu(B_TRANSLATE("Edit")); 325 menu->AddItem(fCutMenuItem = new BMenuItem(B_TRANSLATE("Cut"), 326 new BMessage(B_CUT), 'X')); 327 menu->AddItem(fCopyMenuItem = new BMenuItem(B_TRANSLATE("Copy"), 328 new BMessage(B_COPY), 'C')); 329 menu->AddItem(fPasteMenuItem = new BMenuItem(B_TRANSLATE("Paste"), 330 new BMessage(B_PASTE), 'V')); 331 menu->AddSeparatorItem(); 332 menu->AddItem(new BMenuItem(B_TRANSLATE("Find"), 333 new BMessage(EDIT_SHOW_FIND_GROUP), 'F')); 334 menu->AddItem(fFindPreviousMenuItem 335 = new BMenuItem(B_TRANSLATE("Find previous"), 336 new BMessage(EDIT_FIND_PREVIOUS), 'G', B_SHIFT_KEY)); 337 menu->AddItem(fFindNextMenuItem = new BMenuItem(B_TRANSLATE("Find next"), 338 new BMessage(EDIT_FIND_NEXT), 'G')); 339 mainMenu->AddItem(menu); 340 fFindPreviousMenuItem->SetEnabled(false); 341 fFindNextMenuItem->SetEnabled(false); 342 343 menu = new BMenu(B_TRANSLATE("View")); 344 menu->AddItem(new BMenuItem(B_TRANSLATE("Reload"), new BMessage(RELOAD), 345 'R')); 346 menu->AddSeparatorItem(); 347 menu->AddItem(new BMenuItem(B_TRANSLATE("Increase size"), 348 new BMessage(ZOOM_FACTOR_INCREASE), '+')); 349 menu->AddItem(new BMenuItem(B_TRANSLATE("Decrease size"), 350 new BMessage(ZOOM_FACTOR_DECREASE), '-')); 351 menu->AddItem(new BMenuItem(B_TRANSLATE("Reset size"), 352 new BMessage(ZOOM_FACTOR_RESET), '0')); 353 fZoomTextOnlyMenuItem = new BMenuItem(B_TRANSLATE("Zoom text only"), 354 new BMessage(ZOOM_TEXT_ONLY)); 355 fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly); 356 menu->AddItem(fZoomTextOnlyMenuItem); 357 358 menu->AddSeparatorItem(); 359 fFullscreenItem = new BMenuItem(B_TRANSLATE("Full screen"), 360 new BMessage(TOGGLE_FULLSCREEN), B_RETURN); 361 menu->AddItem(fFullscreenItem); 362 menu->AddItem(new BMenuItem(B_TRANSLATE("Page source"), 363 new BMessage(SHOW_PAGE_SOURCE), 'U')); 364 mainMenu->AddItem(menu); 365 366 fHistoryMenu = new BMenu(B_TRANSLATE("History")); 367 fHistoryMenu->AddItem(fBackMenuItem = new BMenuItem(B_TRANSLATE("Back"), 368 new BMessage(GO_BACK), B_LEFT_ARROW)); 369 fHistoryMenu->AddItem(fForwardMenuItem 370 = new BMenuItem(B_TRANSLATE("Forward"), new BMessage(GO_FORWARD), 371 B_RIGHT_ARROW)); 372 fHistoryMenu->AddSeparatorItem(); 373 fHistoryMenuFixedItemCount = fHistoryMenu->CountItems(); 374 mainMenu->AddItem(fHistoryMenu); 375 376 BPath bookmarkPath; 377 entry_ref bookmarkRef; 378 if (_BookmarkPath(bookmarkPath) == B_OK 379 && get_ref_for_path(bookmarkPath.Path(), &bookmarkRef) == B_OK) { 380 BMenu* bookmarkMenu 381 = new BookmarkMenu(B_TRANSLATE("Bookmarks"), this, &bookmarkRef); 382 mainMenu->AddItem(bookmarkMenu); 383 } 384 385 // Back, Forward, Stop & Home buttons 386 fBackButton = new IconButton("Back", 0, NULL, new BMessage(GO_BACK)); 387 fBackButton->SetIcon(201); 388 fBackButton->TrimIcon(); 389 390 fForwardButton = new IconButton("Forward", 0, NULL, new BMessage(GO_FORWARD)); 391 fForwardButton->SetIcon(202); 392 fForwardButton->TrimIcon(); 393 394 fStopButton = new IconButton("Stop", 0, NULL, new BMessage(STOP)); 395 fStopButton->SetIcon(204); 396 fStopButton->TrimIcon(); 397 398 fHomeButton = new IconButton("Home", 0, NULL, new BMessage(HOME)); 399 fHomeButton->SetIcon(206); 400 fHomeButton->TrimIcon(); 401 if (!fAppSettings->GetValue(kSettingsKeyShowHomeButton, true)) 402 fHomeButton->Hide(); 403 404 // URL input group 405 fURLInputGroup = new URLInputGroup(new BMessage(GOTO_URL)); 406 407 // Status Bar 408 fStatusText = new BStringView("status", ""); 409 fStatusText->SetAlignment(B_ALIGN_LEFT); 410 fStatusText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 411 fStatusText->SetExplicitMinSize(BSize(150, 12)); 412 // Prevent the window from growing to fit a long status message... 413 BFont font(be_plain_font); 414 font.SetSize(ceilf(font.Size() * 0.8)); 415 fStatusText->SetFont(&font, B_FONT_SIZE); 416 417 // Loading progress bar 418 fLoadingProgressBar = new BStatusBar("progress"); 419 fLoadingProgressBar->SetMaxValue(100); 420 fLoadingProgressBar->Hide(); 421 fLoadingProgressBar->SetBarHeight(12); 422 423 const float kInsetSpacing = 3; 424 const float kElementSpacing = 5; 425 426 // Find group 427 fFindTextControl = new BTextControl("find", B_TRANSLATE("Find:"), "", 428 new BMessage(EDIT_FIND_NEXT)); 429 fFindTextControl->SetModificationMessage(new BMessage(FIND_TEXT_CHANGED)); 430 fFindPreviousButton = new BButton(B_TRANSLATE("Previous"), 431 new BMessage(EDIT_FIND_PREVIOUS)); 432 fFindNextButton = new BButton(B_TRANSLATE("Next"), 433 new BMessage(EDIT_FIND_NEXT)); 434 fFindCloseButton = new BButton(B_TRANSLATE("Close"), 435 new BMessage(EDIT_HIDE_FIND_GROUP)); 436 fFindCaseSensitiveCheckBox = new BCheckBox(B_TRANSLATE("Match case")); 437 BGroupLayout* findGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 438 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 439 .Add(BGroupLayoutBuilder(B_HORIZONTAL, kElementSpacing) 440 .Add(fFindTextControl) 441 .Add(fFindPreviousButton) 442 .Add(fFindNextButton) 443 .Add(fFindCaseSensitiveCheckBox) 444 .Add(BSpaceLayoutItem::CreateGlue()) 445 .Add(fFindCloseButton) 446 .SetInsets(kInsetSpacing, kInsetSpacing, 447 kInsetSpacing, kInsetSpacing) 448 ) 449 ; 450 451 // Navigation group 452 BGroupLayout* navigationGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 453 .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing) 454 .Add(fBackButton) 455 .Add(fForwardButton) 456 .Add(fStopButton) 457 .Add(fHomeButton) 458 .Add(fURLInputGroup) 459 .SetInsets(kInsetSpacing, kInsetSpacing, kInsetSpacing, 460 kInsetSpacing) 461 ) 462 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 463 ; 464 465 // Status bar group 466 BGroupLayout* statusGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 467 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 468 .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing) 469 .Add(fStatusText) 470 .Add(fLoadingProgressBar, 0.2) 471 .AddStrut(12 - kElementSpacing) 472 .SetInsets(kInsetSpacing, 0, kInsetSpacing, 0) 473 ) 474 ; 475 476 BitmapButton* toggleFullscreenButton = new BitmapButton(kWindowIconBits, 477 kWindowIconWidth, kWindowIconHeight, kWindowIconFormat, 478 new BMessage(TOGGLE_FULLSCREEN)); 479 toggleFullscreenButton->SetBackgroundMode(BitmapButton::MENUBAR_BACKGROUND); 480 481 BGroupLayout* menuBarGroup = BLayoutBuilder::Group<>(B_HORIZONTAL, 0.0) 482 .Add(mainMenu) 483 .Add(toggleFullscreenButton, 0.0f) 484 ; 485 486 // Layout 487 AddChild(BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 488 #if !INTEGRATE_MENU_INTO_TAB_BAR 489 .Add(menuBarGroup) 490 #endif 491 .Add(fTabManager->TabGroup()) 492 .Add(navigationGroup) 493 .Add(fTabManager->ContainerView()) 494 .Add(findGroup) 495 .Add(statusGroup) 496 ); 497 498 fURLInputGroup->MakeFocus(true); 499 500 fMenuGroup = menuBarGroup; 501 fTabGroup = fTabManager->TabGroup()->GetLayout(); 502 fNavigationGroup = navigationGroup; 503 fFindGroup = findGroup; 504 fStatusGroup = statusGroup; 505 fToggleFullscreenButton = layoutItemFor(toggleFullscreenButton); 506 507 fFindGroup->SetVisible(false); 508 fToggleFullscreenButton->SetVisible(false); 509 510 CreateNewTab(url, true, webView); 511 _ShowInterface(true); 512 _SetAutoHideInterfaceInFullscreen(fAppSettings->GetValue( 513 kSettingsKeyAutoHideInterfaceInFullscreenMode, 514 fAutoHideInterfaceInFullscreenMode)); 515 516 AddShortcut('F', B_COMMAND_KEY | B_SHIFT_KEY, 517 new BMessage(EDIT_HIDE_FIND_GROUP)); 518 // TODO: Should be a different shortcut, H is usually for Find selection. 519 AddShortcut('H', B_COMMAND_KEY, new BMessage(HOME)); 520 521 // Add shortcuts to select a particular tab 522 for (int32 i = 1; i <= 9; i++) { 523 BMessage* selectTab = new BMessage(SELECT_TAB); 524 selectTab->AddInt32("tab index", i - 1); 525 char numStr[2]; 526 snprintf(numStr, sizeof(numStr), "%d", (int) i); 527 AddShortcut(numStr[0], B_COMMAND_KEY, selectTab); 528 } 529 530 be_app->PostMessage(WINDOW_OPENED); 531 } 532 533 534 BrowserWindow::~BrowserWindow() 535 { 536 fAppSettings->RemoveListener(BMessenger(this)); 537 delete fTabManager; 538 delete fPulseRunner; 539 } 540 541 542 void 543 BrowserWindow::DispatchMessage(BMessage* message, BHandler* target) 544 { 545 const char* bytes; 546 uint32 modifierKeys; 547 if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN) 548 && message->FindString("bytes", &bytes) == B_OK 549 && message->FindInt32("modifiers", (int32*)&modifierKeys) == B_OK) { 550 551 modifierKeys = modifierKeys & 0x000000ff; 552 if (bytes[0] == B_LEFT_ARROW && modifierKeys == B_COMMAND_KEY) { 553 PostMessage(GO_BACK); 554 return; 555 } else if (bytes[0] == B_RIGHT_ARROW && modifierKeys == B_COMMAND_KEY) { 556 PostMessage(GO_FORWARD); 557 return; 558 } else if (bytes[0] == B_FUNCTION_KEY) { 559 // Some function key Firefox compatibility 560 int32 key; 561 if (message->FindInt32("key", &key) == B_OK) { 562 switch (key) { 563 case B_F5_KEY: 564 PostMessage(RELOAD); 565 break; 566 case B_F11_KEY: 567 PostMessage(TOGGLE_FULLSCREEN); 568 break; 569 default: 570 break; 571 } 572 } 573 } else if (target == fURLInputGroup->TextView()) { 574 // Handle B_RETURN in the URL text control. This is the easiest 575 // way to react *only* when the user presses the return key in the 576 // address bar, as opposed to trying to load whatever is in there 577 // when the text control just goes out of focus. 578 if (bytes[0] == B_RETURN) { 579 // Do it in such a way that the user sees the Go-button go down. 580 _InvokeButtonVisibly(fURLInputGroup->GoButton()); 581 return; 582 } 583 } else if (target == fFindTextControl->TextView()) { 584 // Handle B_RETURN when the find text control has focus. 585 if (bytes[0] == B_RETURN) { 586 if ((modifierKeys & B_SHIFT_KEY) != 0) 587 _InvokeButtonVisibly(fFindPreviousButton); 588 else 589 _InvokeButtonVisibly(fFindNextButton); 590 return; 591 } else if (bytes[0] == B_ESCAPE) { 592 _InvokeButtonVisibly(fFindCloseButton); 593 return; 594 } 595 } else if (bytes[0] == B_ESCAPE) { 596 // Default escape key behavior: 597 PostMessage(STOP); 598 return; 599 } 600 } 601 if (message->what == B_MOUSE_MOVED || message->what == B_MOUSE_DOWN 602 || message->what == B_MOUSE_UP) { 603 message->FindPoint("where", &fLastMousePos); 604 if (message->FindInt64("when", &fLastMouseMovedTime) != B_OK) 605 fLastMouseMovedTime = system_time(); 606 _CheckAutoHideInterface(); 607 } 608 if (message->what == B_MOUSE_WHEEL_CHANGED) { 609 BPoint where; 610 uint32 buttons; 611 CurrentWebView()->GetMouse(&where, &buttons, false); 612 // Only do this when the mouse is over the web view 613 if (CurrentWebView()->Bounds().Contains(where)) { 614 // Zoom and unzoom text on Command + mouse wheel. 615 // This could of course (and maybe should be) implemented in the 616 // WebView, but there would need to be a way for the WebView to 617 // know the setting of the fZoomTextOnly member here. Plus other 618 // clients of the API may not want this feature. 619 if ((modifiers() & B_COMMAND_KEY) != 0) { 620 float dy; 621 if (message->FindFloat("be:wheel_delta_y", &dy) == B_OK) { 622 if (dy < 0) 623 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly); 624 else 625 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly); 626 return; 627 } 628 } 629 } else // Also don't scroll up and down if the mouse is not over the web view 630 return; 631 } 632 BWebWindow::DispatchMessage(message, target); 633 } 634 635 636 void 637 BrowserWindow::MessageReceived(BMessage* message) 638 { 639 switch (message->what) { 640 case OPEN_LOCATION: 641 _ShowInterface(true); 642 if (fURLInputGroup->TextView()->IsFocus()) 643 fURLInputGroup->TextView()->SelectAll(); 644 else 645 fURLInputGroup->MakeFocus(true); 646 break; 647 case RELOAD: 648 CurrentWebView()->Reload(); 649 break; 650 case GOTO_URL: 651 { 652 BString url; 653 if (message->FindString("url", &url) != B_OK) 654 url = fURLInputGroup->Text(); 655 656 _SetPageIcon(CurrentWebView(), NULL); 657 _SmartURLHandler(url); 658 659 break; 660 } 661 case GO_BACK: 662 CurrentWebView()->GoBack(); 663 break; 664 case GO_FORWARD: 665 CurrentWebView()->GoForward(); 666 break; 667 case STOP: 668 CurrentWebView()->StopLoading(); 669 break; 670 case HOME: 671 CurrentWebView()->LoadURL(fStartPageURL); 672 break; 673 674 case CLEAR_HISTORY: { 675 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 676 if (history->CountItems() == 0) 677 break; 678 BAlert* alert = new BAlert(B_TRANSLATE("Confirmation"), 679 B_TRANSLATE("Do you really want to " 680 "clear the browsing history?"), B_TRANSLATE("Clear"), 681 B_TRANSLATE("Cancel")); 682 alert->SetShortcut(1, B_ESCAPE); 683 684 if (alert->Go() == 0) 685 history->Clear(); 686 break; 687 } 688 689 case CREATE_BOOKMARK: 690 _CreateBookmark(); 691 break; 692 case SHOW_BOOKMARKS: 693 _ShowBookmarks(); 694 break; 695 696 case B_REFS_RECEIVED: 697 { 698 // Currently the only source of these messages is the bookmarks menu. 699 // Filter refs into URLs, this also gets rid of refs for folders. 700 // For clicks on sub-folders in the bookmarks menu, we have Tracker 701 // open the corresponding folder. 702 entry_ref ref; 703 uint32 addedCount = 0; 704 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 705 BEntry entry(&ref); 706 uint32 addedSubCount = 0; 707 if (entry.IsDirectory()) { 708 BDirectory directory(&entry); 709 _AddBookmarkURLsRecursively(directory, message, 710 addedSubCount); 711 } else { 712 BFile file(&ref, B_READ_ONLY); 713 BString url; 714 if (_ReadURLAttr(file, url)) { 715 message->AddString("url", url.String()); 716 addedSubCount++; 717 } 718 } 719 if (addedSubCount == 0) { 720 // Don't know what to do with this entry, just pass it 721 // on to the system to handle. Note that this may result 722 // in us opening other supported files via the application 723 // mechanism. 724 be_roster->Launch(&ref); 725 } 726 addedCount += addedSubCount; 727 } 728 message->RemoveName("refs"); 729 if (addedCount > 10) { 730 BString string(B_TRANSLATE_COMMENT("Do you want to open %addedCount " 731 "bookmarks all at once?", "Don't translate variable %addedCount.")); 732 string.ReplaceFirst("%addedCount", BString() << addedCount); 733 734 BAlert* alert = new BAlert(B_TRANSLATE("Open bookmarks confirmation"), 735 string.String(), B_TRANSLATE("Cancel"), B_TRANSLATE("Open all")); 736 alert->SetShortcut(0, B_ESCAPE); 737 if (alert->Go() == 0) 738 break; 739 } 740 be_app->PostMessage(message); 741 break; 742 } 743 case B_SIMPLE_DATA: 744 { 745 // User possibly dropped files on this window. 746 // If there is more than one entry_ref, let the app handle it 747 // (open one new page per ref). If there is one ref, open it in 748 // this window. 749 type_code type; 750 int32 countFound; 751 if (message->GetInfo("refs", &type, &countFound) != B_OK 752 || type != B_REF_TYPE) { 753 break; 754 } 755 if (countFound > 1) { 756 message->what = B_REFS_RECEIVED; 757 be_app->PostMessage(message); 758 break; 759 } 760 entry_ref ref; 761 if (message->FindRef("refs", &ref) != B_OK) 762 break; 763 BEntry entry(&ref, true); 764 BPath path; 765 if (!entry.Exists() || entry.GetPath(&path) != B_OK) 766 break; 767 CurrentWebView()->LoadURL(path.Path()); 768 break; 769 } 770 771 case ZOOM_FACTOR_INCREASE: 772 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly); 773 break; 774 case ZOOM_FACTOR_DECREASE: 775 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly); 776 break; 777 case ZOOM_FACTOR_RESET: 778 CurrentWebView()->ResetZoomFactor(); 779 break; 780 case ZOOM_TEXT_ONLY: 781 fZoomTextOnly = !fZoomTextOnly; 782 fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly); 783 // TODO: Would be nice to have an instant update if the page is 784 // already zoomed. 785 break; 786 787 case TOGGLE_FULLSCREEN: 788 ToggleFullscreen(); 789 break; 790 791 case TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN: 792 _SetAutoHideInterfaceInFullscreen( 793 !fAutoHideInterfaceInFullscreenMode); 794 break; 795 796 case CHECK_AUTO_HIDE_INTERFACE: 797 _CheckAutoHideInterface(); 798 break; 799 800 case SHOW_PAGE_SOURCE: 801 CurrentWebView()->WebPage()->SendPageSource(); 802 break; 803 case B_PAGE_SOURCE_RESULT: 804 _HandlePageSourceResult(message); 805 break; 806 807 case EDIT_FIND_NEXT: 808 CurrentWebView()->FindString(fFindTextControl->Text(), true, 809 fFindCaseSensitiveCheckBox->Value()); 810 break; 811 case FIND_TEXT_CHANGED: 812 { 813 bool findTextAvailable = strlen(fFindTextControl->Text()) > 0; 814 fFindPreviousMenuItem->SetEnabled(findTextAvailable); 815 fFindNextMenuItem->SetEnabled(findTextAvailable); 816 break; 817 } 818 case EDIT_FIND_PREVIOUS: 819 CurrentWebView()->FindString(fFindTextControl->Text(), false, 820 fFindCaseSensitiveCheckBox->Value()); 821 break; 822 case EDIT_SHOW_FIND_GROUP: 823 if (!fFindGroup->IsVisible()) 824 fFindGroup->SetVisible(true); 825 fFindTextControl->MakeFocus(true); 826 break; 827 case EDIT_HIDE_FIND_GROUP: 828 if (fFindGroup->IsVisible()) { 829 fFindGroup->SetVisible(false); 830 if (CurrentWebView() != NULL) 831 CurrentWebView()->MakeFocus(true); 832 } 833 break; 834 835 case B_CUT: 836 case B_COPY: 837 case B_PASTE: 838 { 839 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus()); 840 if (textView != NULL) 841 textView->MessageReceived(message); 842 else if (CurrentWebView() != NULL) 843 CurrentWebView()->MessageReceived(message); 844 break; 845 } 846 847 case B_EDITING_CAPABILITIES_RESULT: 848 { 849 BWebView* webView; 850 if (message->FindPointer("view", 851 reinterpret_cast<void**>(&webView)) != B_OK 852 || webView != CurrentWebView()) { 853 break; 854 } 855 bool canCut; 856 bool canCopy; 857 bool canPaste; 858 if (message->FindBool("can cut", &canCut) != B_OK) 859 canCut = false; 860 if (message->FindBool("can copy", &canCopy) != B_OK) 861 canCopy = false; 862 if (message->FindBool("can paste", &canPaste) != B_OK) 863 canPaste = false; 864 fCutMenuItem->SetEnabled(canCut); 865 fCopyMenuItem->SetEnabled(canCopy); 866 fPasteMenuItem->SetEnabled(canPaste); 867 break; 868 } 869 870 case SHOW_DOWNLOAD_WINDOW: 871 case SHOW_SETTINGS_WINDOW: 872 message->AddUInt32("workspaces", Workspaces()); 873 be_app->PostMessage(message); 874 break; 875 876 case CLOSE_TAB: 877 if (fTabManager->CountTabs() > 1) { 878 int32 index; 879 if (message->FindInt32("tab index", &index) != B_OK) 880 index = fTabManager->SelectedTabIndex(); 881 _ShutdownTab(index); 882 _UpdateTabGroupVisibility(); 883 } else 884 PostMessage(B_QUIT_REQUESTED); 885 break; 886 887 case SELECT_TAB: 888 { 889 int32 index; 890 if (message->FindInt32("tab index", &index) == B_OK 891 && fTabManager->SelectedTabIndex() != index 892 && fTabManager->CountTabs() > index) { 893 fTabManager->SelectTab(index); 894 } 895 896 break; 897 } 898 899 case TAB_CHANGED: 900 { 901 // This message may be received also when the last tab closed, 902 // i.e. with index == -1. 903 int32 index; 904 if (message->FindInt32("tab index", &index) != B_OK) 905 index = -1; 906 _TabChanged(index); 907 break; 908 } 909 910 case SETTINGS_VALUE_CHANGED: 911 { 912 BString name; 913 if (message->FindString("name", &name) != B_OK) 914 break; 915 bool flag; 916 BString string; 917 uint32 value; 918 if (name == kSettingsKeyShowTabsIfSinglePageOpen 919 && message->FindBool("value", &flag) == B_OK) { 920 if (fShowTabsIfSinglePageOpen != flag) { 921 fShowTabsIfSinglePageOpen = flag; 922 _UpdateTabGroupVisibility(); 923 } 924 } else if (name == kSettingsKeyAutoHidePointer 925 && message->FindBool("value", &flag) == B_OK) { 926 fAutoHidePointer = flag; 927 if (CurrentWebView()) 928 CurrentWebView()->SetAutoHidePointer(fAutoHidePointer); 929 } else if (name == kSettingsKeyStartPageURL 930 && message->FindString("value", &string) == B_OK) { 931 fStartPageURL = string; 932 } else if (name == kSettingsKeySearchPageURL 933 && message->FindString("value", &string) == B_OK) { 934 fSearchPageURL = string; 935 } else if (name == kSettingsKeyNewWindowPolicy 936 && message->FindUInt32("value", &value) == B_OK) { 937 fNewWindowPolicy = value; 938 } else if (name == kSettingsKeyNewTabPolicy 939 && message->FindUInt32("value", &value) == B_OK) { 940 fNewTabPolicy = value; 941 } else if (name == kSettingsKeyAutoHideInterfaceInFullscreenMode 942 && message->FindBool("value", &flag) == B_OK) { 943 _SetAutoHideInterfaceInFullscreen(flag); 944 } else if (name == kSettingsKeyShowHomeButton 945 && message->FindBool("value", &flag) == B_OK) { 946 if (flag) 947 fHomeButton->Show(); 948 else 949 fHomeButton->Hide(); 950 } 951 break; 952 } 953 954 default: 955 BWebWindow::MessageReceived(message); 956 break; 957 } 958 } 959 960 961 bool 962 BrowserWindow::QuitRequested() 963 { 964 // TODO: Check for modified form data and ask user for confirmation, etc. 965 966 // Iterate over all tabs to delete all BWebViews. 967 // Do this here, so WebKit tear down happens earlier. 968 SetCurrentWebView(NULL); 969 while (fTabManager->CountTabs() > 0) 970 _ShutdownTab(0); 971 972 BMessage message(WINDOW_CLOSED); 973 message.AddRect("window frame", WindowFrame()); 974 be_app->PostMessage(&message); 975 return true; 976 } 977 978 979 void 980 BrowserWindow::MenusBeginning() 981 { 982 _UpdateHistoryMenu(); 983 _UpdateClipboardItems(); 984 _ShowInterface(true); 985 } 986 987 988 void 989 BrowserWindow::ScreenChanged(BRect screenSize, color_space format) 990 { 991 if (fIsFullscreen) 992 _ResizeToScreen(); 993 } 994 995 996 void 997 BrowserWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 998 { 999 if (fIsFullscreen) 1000 _ResizeToScreen(); 1001 } 1002 1003 1004 static bool 1005 viewIsChild(const BView* parent, const BView* view) 1006 { 1007 if (parent == view) 1008 return true; 1009 1010 int32 count = parent->CountChildren(); 1011 for (int32 i = 0; i < count; i++) { 1012 BView* child = parent->ChildAt(i); 1013 if (viewIsChild(child, view)) 1014 return true; 1015 } 1016 return false; 1017 } 1018 1019 1020 void 1021 BrowserWindow::SetCurrentWebView(BWebView* webView) 1022 { 1023 if (webView == CurrentWebView()) 1024 return; 1025 1026 if (CurrentWebView() != NULL) { 1027 // Remember the currently focused view before switching tabs, 1028 // so that we can revert the focus when switching back to this tab 1029 // later. 1030 PageUserData* userData = static_cast<PageUserData*>( 1031 CurrentWebView()->GetUserData()); 1032 if (userData == NULL) { 1033 userData = new PageUserData(CurrentFocus()); 1034 CurrentWebView()->SetUserData(userData); 1035 } 1036 userData->SetFocusedView(CurrentFocus()); 1037 userData->SetURLInputContents(fURLInputGroup->Text()); 1038 int32 selectionStart; 1039 int32 selectionEnd; 1040 fURLInputGroup->TextView()->GetSelection(&selectionStart, 1041 &selectionEnd); 1042 userData->SetURLInputSelection(selectionStart, selectionEnd); 1043 } 1044 1045 BWebWindow::SetCurrentWebView(webView); 1046 1047 if (webView != NULL) { 1048 webView->SetAutoHidePointer(fAutoHidePointer); 1049 1050 _UpdateTitle(webView->MainFrameTitle()); 1051 1052 // Restore the previous focus or focus the web view. 1053 PageUserData* userData = static_cast<PageUserData*>( 1054 webView->GetUserData()); 1055 BView* focusedView = NULL; 1056 if (userData != NULL) 1057 focusedView = userData->FocusedView(); 1058 1059 if (focusedView != NULL 1060 && viewIsChild(GetLayout()->View(), focusedView)) { 1061 focusedView->MakeFocus(true); 1062 } else 1063 webView->MakeFocus(true); 1064 1065 if (userData != NULL) { 1066 fURLInputGroup->SetPageIcon(userData->PageIcon()); 1067 if (userData->URLInputContents().Length()) 1068 fURLInputGroup->SetText(userData->URLInputContents()); 1069 else 1070 fURLInputGroup->SetText(webView->MainFrameURL()); 1071 if (userData->URLInputSelectionStart() >= 0) { 1072 fURLInputGroup->TextView()->Select( 1073 userData->URLInputSelectionStart(), 1074 userData->URLInputSelectionEnd()); 1075 } 1076 } else { 1077 fURLInputGroup->SetPageIcon(NULL); 1078 fURLInputGroup->SetText(webView->MainFrameURL()); 1079 } 1080 1081 // Trigger update of the interface to the new page, by requesting 1082 // to resend all notifications. 1083 webView->WebPage()->ResendNotifications(); 1084 } else 1085 _UpdateTitle(""); 1086 } 1087 1088 1089 bool 1090 BrowserWindow::IsBlankTab() const 1091 { 1092 if (CurrentWebView() == NULL) 1093 return false; 1094 BString requestedURL = CurrentWebView()->MainFrameRequestedURL(); 1095 return requestedURL.Length() == 0 1096 || requestedURL == _NewTabURL(fTabManager->CountTabs() == 1); 1097 } 1098 1099 1100 void 1101 BrowserWindow::CreateNewTab(const BString& _url, bool select, BWebView* webView) 1102 { 1103 bool applyNewPagePolicy = webView == NULL; 1104 // Executed in app thread (new BWebPage needs to be created in app thread). 1105 if (webView == NULL) 1106 webView = new BWebView("web view"); 1107 1108 bool isNewWindow = fTabManager->CountTabs() == 0; 1109 1110 fTabManager->AddTab(webView, B_TRANSLATE("New tab")); 1111 1112 BString url(_url); 1113 if (applyNewPagePolicy && url.Length() == 0) 1114 url = _NewTabURL(isNewWindow); 1115 1116 if (url.Length() > 0) 1117 webView->LoadURL(url.String()); 1118 1119 if (select) { 1120 fTabManager->SelectTab(fTabManager->CountTabs() - 1); 1121 SetCurrentWebView(webView); 1122 webView->WebPage()->ResendNotifications(); 1123 fURLInputGroup->SetPageIcon(NULL); 1124 fURLInputGroup->SetText(url.String()); 1125 fURLInputGroup->MakeFocus(true); 1126 } 1127 1128 _ShowInterface(true); 1129 _UpdateTabGroupVisibility(); 1130 } 1131 1132 1133 BRect 1134 BrowserWindow::WindowFrame() const 1135 { 1136 if (fIsFullscreen) 1137 return fNonFullscreenWindowFrame; 1138 else 1139 return Frame(); 1140 } 1141 1142 1143 void 1144 BrowserWindow::ToggleFullscreen() 1145 { 1146 if (fIsFullscreen) { 1147 MoveTo(fNonFullscreenWindowFrame.LeftTop()); 1148 ResizeTo(fNonFullscreenWindowFrame.Width(), 1149 fNonFullscreenWindowFrame.Height()); 1150 1151 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1152 SetLook(B_DOCUMENT_WINDOW_LOOK); 1153 1154 _ShowInterface(true); 1155 } else { 1156 fNonFullscreenWindowFrame = Frame(); 1157 _ResizeToScreen(); 1158 1159 SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1160 SetLook(B_TITLED_WINDOW_LOOK); 1161 } 1162 fIsFullscreen = !fIsFullscreen; 1163 fFullscreenItem->SetMarked(fIsFullscreen); 1164 fToggleFullscreenButton->SetVisible(fIsFullscreen); 1165 } 1166 1167 1168 // #pragma mark - Notification API 1169 1170 1171 void 1172 BrowserWindow::NavigationRequested(const BString& url, BWebView* view) 1173 { 1174 } 1175 1176 1177 void 1178 BrowserWindow::NewWindowRequested(const BString& url, bool primaryAction) 1179 { 1180 // Always open new windows in the application thread, since 1181 // creating a BWebView will try to grab the application lock. 1182 // But our own WebPage may already try to lock us from within 1183 // the application thread -> dead-lock. Thus we can't wait for 1184 // a reply here. 1185 BMessage message(NEW_TAB); 1186 message.AddPointer("window", this); 1187 message.AddString("url", url); 1188 message.AddBool("select", primaryAction); 1189 be_app->PostMessage(&message); 1190 } 1191 1192 1193 void 1194 BrowserWindow::NewPageCreated(BWebView* view, BRect windowFrame, 1195 bool modalDialog, bool resizable, bool activate) 1196 { 1197 if (windowFrame.IsValid()) { 1198 BrowserWindow* window = new BrowserWindow(windowFrame, fAppSettings, 1199 BString(), INTERFACE_ELEMENT_STATUS, view); 1200 window->Show(); 1201 } else 1202 CreateNewTab(BString(), activate, view); 1203 } 1204 1205 1206 void 1207 BrowserWindow::CloseWindowRequested(BWebView* view) 1208 { 1209 int32 index = fTabManager->TabForView(view); 1210 if (index < 0) { 1211 // Tab is already gone. 1212 return; 1213 } 1214 BMessage message(CLOSE_TAB); 1215 message.AddInt32("tab index", index); 1216 PostMessage(&message, this); 1217 } 1218 1219 1220 void 1221 BrowserWindow::LoadNegotiating(const BString& url, BWebView* view) 1222 { 1223 if (view != CurrentWebView()) 1224 return; 1225 1226 fURLInputGroup->SetText(url.String()); 1227 1228 BString status(B_TRANSLATE("Requesting %url")); 1229 status.ReplaceFirst("%url", url); 1230 view->WebPage()->SetStatusMessage(status); 1231 } 1232 1233 1234 void 1235 BrowserWindow::LoadCommitted(const BString& url, BWebView* view) 1236 { 1237 if (view != CurrentWebView()) 1238 return; 1239 1240 // This hook is invoked when the load is commited. 1241 fURLInputGroup->SetText(url.String()); 1242 1243 BString status(B_TRANSLATE("Loading %url")); 1244 status.ReplaceFirst("%url", url); 1245 view->WebPage()->SetStatusMessage(status); 1246 } 1247 1248 1249 void 1250 BrowserWindow::LoadProgress(float progress, BWebView* view) 1251 { 1252 if (view != CurrentWebView()) 1253 return; 1254 1255 if (progress < 100 && fLoadingProgressBar->IsHidden()) 1256 _ShowProgressBar(true); 1257 else if (progress == 100 && !fLoadingProgressBar->IsHidden()) 1258 _ShowProgressBar(false); 1259 fLoadingProgressBar->SetTo(progress); 1260 } 1261 1262 1263 void 1264 BrowserWindow::LoadFailed(const BString& url, BWebView* view) 1265 { 1266 if (view != CurrentWebView()) 1267 return; 1268 1269 BString status(B_TRANSLATE_COMMENT("%url failed", "Loading URL failed. " 1270 "Don't translate variable %url.")); 1271 status.ReplaceFirst("%url", url); 1272 view->WebPage()->SetStatusMessage(status); 1273 if (!fLoadingProgressBar->IsHidden()) 1274 fLoadingProgressBar->Hide(); 1275 } 1276 1277 1278 void 1279 BrowserWindow::LoadFinished(const BString& url, BWebView* view) 1280 { 1281 if (view != CurrentWebView()) 1282 return; 1283 1284 BString status(B_TRANSLATE_COMMENT("%url finished", "Loading URL " 1285 "finished. Don't translate variable %url.")); 1286 status.ReplaceFirst("%url", url); 1287 view->WebPage()->SetStatusMessage(status); 1288 if (!fLoadingProgressBar->IsHidden()) 1289 fLoadingProgressBar->Hide(); 1290 1291 NavigationCapabilitiesChanged(fBackButton->IsEnabled(), 1292 fForwardButton->IsEnabled(), false, view); 1293 1294 int32 tabIndex = fTabManager->TabForView(view); 1295 if (tabIndex > 0 && strcmp(B_TRANSLATE("New tab"), 1296 fTabManager->TabLabel(tabIndex)) == 0) 1297 fTabManager->SetTabLabel(tabIndex, url); 1298 } 1299 1300 1301 void 1302 BrowserWindow::MainDocumentError(const BString& failingURL, 1303 const BString& localizedDescription, BWebView* view) 1304 { 1305 // Make sure we show the page that contains the view. 1306 if (!_ShowPage(view)) 1307 return; 1308 1309 BWebWindow::MainDocumentError(failingURL, localizedDescription, view); 1310 1311 // TODO: Remove the failing URL from the BrowsingHistory! 1312 } 1313 1314 1315 void 1316 BrowserWindow::TitleChanged(const BString& title, BWebView* view) 1317 { 1318 int32 tabIndex = fTabManager->TabForView(view); 1319 if (tabIndex < 0) 1320 return; 1321 1322 fTabManager->SetTabLabel(tabIndex, title); 1323 1324 if (view != CurrentWebView()) 1325 return; 1326 1327 _UpdateTitle(title); 1328 } 1329 1330 1331 void 1332 BrowserWindow::IconReceived(const BBitmap* icon, BWebView* view) 1333 { 1334 // The view may already be gone, since this notification arrives 1335 // asynchronously. 1336 if (!fTabManager->HasView(view)) 1337 return; 1338 1339 _SetPageIcon(view, icon); 1340 } 1341 1342 1343 void 1344 BrowserWindow::ResizeRequested(float width, float height, BWebView* view) 1345 { 1346 if (view != CurrentWebView()) 1347 return; 1348 1349 // Ignore request when there is more than one BWebView embedded. 1350 if (fTabManager->CountTabs() > 1) 1351 return; 1352 1353 // Make sure the new frame is not larger than the screen frame minus 1354 // window decorator border. 1355 BScreen screen(this); 1356 BRect screenFrame = screen.Frame(); 1357 BRect decoratorFrame = DecoratorFrame(); 1358 BRect frame = Frame(); 1359 1360 screenFrame.left += decoratorFrame.left - frame.left; 1361 screenFrame.right += decoratorFrame.right - frame.right; 1362 screenFrame.top += decoratorFrame.top - frame.top; 1363 screenFrame.bottom += decoratorFrame.bottom - frame.bottom; 1364 1365 width = min_c(width, screen.Frame().Width()); 1366 height = min_c(height, screen.Frame().Height()); 1367 1368 frame.right = frame.left + width; 1369 frame.bottom = frame.top + height; 1370 1371 // frame is now not larger than screenFrame, but may still be partly outside 1372 if (!screenFrame.Contains(frame)) { 1373 if (frame.left < screenFrame.left) 1374 frame.OffsetBy(screenFrame.left - frame.left, 0); 1375 else if (frame.right > screenFrame.right) 1376 frame.OffsetBy(screenFrame.right - frame.right, 0); 1377 if (frame.top < screenFrame.top) 1378 frame.OffsetBy(screenFrame.top - frame.top, 0); 1379 else if (frame.bottom > screenFrame.bottom) 1380 frame.OffsetBy(screenFrame.bottom - frame.bottom, 0); 1381 } 1382 1383 MoveTo(frame.left, frame.top); 1384 ResizeTo(width, height); 1385 } 1386 1387 1388 void 1389 BrowserWindow::SetToolBarsVisible(bool flag, BWebView* view) 1390 { 1391 // TODO 1392 // TODO: Ignore request when there is more than one BWebView embedded! 1393 } 1394 1395 1396 void 1397 BrowserWindow::SetStatusBarVisible(bool flag, BWebView* view) 1398 { 1399 // TODO 1400 // TODO: Ignore request when there is more than one BWebView embedded! 1401 } 1402 1403 1404 void 1405 BrowserWindow::SetMenuBarVisible(bool flag, BWebView* view) 1406 { 1407 // TODO 1408 // TODO: Ignore request when there is more than one BWebView embedded! 1409 } 1410 1411 1412 void 1413 BrowserWindow::SetResizable(bool flag, BWebView* view) 1414 { 1415 // TODO: Ignore request when there is more than one BWebView embedded! 1416 1417 if (flag) 1418 SetFlags(Flags() & ~B_NOT_RESIZABLE); 1419 else 1420 SetFlags(Flags() | B_NOT_RESIZABLE); 1421 } 1422 1423 1424 void 1425 BrowserWindow::StatusChanged(const BString& statusText, BWebView* view) 1426 { 1427 if (view != CurrentWebView()) 1428 return; 1429 1430 if (fStatusText) 1431 fStatusText->SetText(statusText.String()); 1432 } 1433 1434 1435 void 1436 BrowserWindow::NavigationCapabilitiesChanged(bool canGoBackward, 1437 bool canGoForward, bool canStop, BWebView* view) 1438 { 1439 if (view != CurrentWebView()) 1440 return; 1441 1442 fBackButton->SetEnabled(canGoBackward); 1443 fForwardButton->SetEnabled(canGoForward); 1444 fStopButton->SetEnabled(canStop); 1445 1446 fBackMenuItem->SetEnabled(canGoBackward); 1447 fForwardMenuItem->SetEnabled(canGoForward); 1448 } 1449 1450 1451 void 1452 BrowserWindow::UpdateGlobalHistory(const BString& url) 1453 { 1454 BrowsingHistory::DefaultInstance()->AddItem(BrowsingHistoryItem(url)); 1455 } 1456 1457 1458 bool 1459 BrowserWindow::AuthenticationChallenge(BString message, BString& inOutUser, 1460 BString& inOutPassword, bool& inOutRememberCredentials, 1461 uint32 failureCount, BWebView* view) 1462 { 1463 CredentialsStorage* persistentStorage 1464 = CredentialsStorage::PersistentInstance(); 1465 CredentialsStorage* sessionStorage 1466 = CredentialsStorage::SessionInstance(); 1467 1468 // TODO: Using the message as key here is not so smart. 1469 HashKeyString key(message); 1470 1471 if (failureCount == 0) { 1472 if (persistentStorage->Contains(key)) { 1473 Credentials credentials = persistentStorage->GetCredentials(key); 1474 inOutUser = credentials.Username(); 1475 inOutPassword = credentials.Password(); 1476 return true; 1477 } else if (sessionStorage->Contains(key)) { 1478 Credentials credentials = sessionStorage->GetCredentials(key); 1479 inOutUser = credentials.Username(); 1480 inOutPassword = credentials.Password(); 1481 return true; 1482 } 1483 } 1484 // Switch to the page for which this authentication is required. 1485 if (!_ShowPage(view)) 1486 return false; 1487 1488 AuthenticationPanel* panel = new AuthenticationPanel(Frame()); 1489 // Panel auto-destructs. 1490 bool success = panel->getAuthentication(message, inOutUser, inOutPassword, 1491 inOutRememberCredentials, failureCount > 0, inOutUser, inOutPassword, 1492 &inOutRememberCredentials); 1493 if (success) { 1494 Credentials credentials(inOutUser, inOutPassword); 1495 if (inOutRememberCredentials) 1496 persistentStorage->PutCredentials(key, credentials); 1497 else 1498 sessionStorage->PutCredentials(key, credentials); 1499 } 1500 return success; 1501 } 1502 1503 1504 // #pragma mark - private 1505 1506 1507 void 1508 BrowserWindow::_UpdateTitle(const BString& title) 1509 { 1510 BString windowTitle = title; 1511 if (windowTitle.Length() > 0) 1512 windowTitle << " - "; 1513 windowTitle << kApplicationName; 1514 SetTitle(windowTitle.String()); 1515 } 1516 1517 1518 void 1519 BrowserWindow::_UpdateTabGroupVisibility() 1520 { 1521 if (Lock()) { 1522 if (fInterfaceVisible) 1523 fTabGroup->SetVisible(_TabGroupShouldBeVisible()); 1524 fTabManager->SetCloseButtonsAvailable(fTabManager->CountTabs() > 1); 1525 Unlock(); 1526 } 1527 } 1528 1529 1530 bool 1531 BrowserWindow::_TabGroupShouldBeVisible() const 1532 { 1533 return (fShowTabsIfSinglePageOpen || fTabManager->CountTabs() > 1) 1534 && (fVisibleInterfaceElements & INTERFACE_ELEMENT_TABS) != 0; 1535 } 1536 1537 1538 void 1539 BrowserWindow::_ShutdownTab(int32 index) 1540 { 1541 BView* view = fTabManager->RemoveTab(index); 1542 BWebView* webView = dynamic_cast<BWebView*>(view); 1543 if (webView == CurrentWebView()) 1544 SetCurrentWebView(NULL); 1545 if (webView != NULL) 1546 webView->Shutdown(); 1547 else 1548 delete view; 1549 } 1550 1551 1552 void 1553 BrowserWindow::_TabChanged(int32 index) 1554 { 1555 SetCurrentWebView(dynamic_cast<BWebView*>(fTabManager->ViewForTab(index))); 1556 } 1557 1558 1559 status_t 1560 BrowserWindow::_BookmarkPath(BPath& path) const 1561 { 1562 status_t ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 1563 if (ret != B_OK) 1564 return ret; 1565 1566 ret = path.Append(kApplicationName); 1567 if (ret != B_OK) 1568 return ret; 1569 1570 ret = path.Append("Bookmarks"); 1571 if (ret != B_OK) 1572 return ret; 1573 1574 return create_directory(path.Path(), 0777); 1575 } 1576 1577 1578 void 1579 BrowserWindow::_CreateBookmark() 1580 { 1581 BPath path; 1582 status_t status = _BookmarkPath(path); 1583 if (status != B_OK) { 1584 BString message(B_TRANSLATE_COMMENT("There was an error retrieving " 1585 "the bookmark folder.\n\nError: %error", "Don't translate the " 1586 "variable %error")); 1587 message.ReplaceFirst("%error", strerror(status)); 1588 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1589 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1590 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1591 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1592 alert->Go(); 1593 return; 1594 } 1595 BWebView* webView = CurrentWebView(); 1596 BString url(webView->MainFrameURL()); 1597 // Create a bookmark file 1598 BFile bookmarkFile; 1599 BString bookmarkName(webView->MainFrameTitle()); 1600 if (bookmarkName.Length() == 0) { 1601 bookmarkName = url; 1602 int32 leafPos = bookmarkName.FindLast('/'); 1603 if (leafPos >= 0) 1604 bookmarkName.Remove(0, leafPos + 1); 1605 } 1606 // Make sure the bookmark title does not contain chars that are not 1607 // allowed in file names. 1608 bookmarkName.ReplaceAll('/', '-'); 1609 1610 // Check that the bookmark exists nowhere in the bookmark hierarchy, 1611 // though the intended file name must match, we don't search the stored 1612 // URLs, only for matching file names. 1613 BDirectory directory(path.Path()); 1614 if (status == B_OK && _CheckBookmarkExists(directory, bookmarkName, url)) { 1615 BString message(B_TRANSLATE_COMMENT("A bookmark for this page " 1616 "(%bookmarkName) already exists.", "Don't translate variable " 1617 "%bookmarkName")); 1618 message.ReplaceFirst("%bookmarkName", bookmarkName); 1619 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark info"), 1620 message.String(), B_TRANSLATE("OK")); 1621 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1622 alert->Go(); 1623 return; 1624 } 1625 1626 BPath entryPath(path); 1627 status = entryPath.Append(bookmarkName); 1628 BEntry entry; 1629 if (status == B_OK) 1630 status = entry.SetTo(entryPath.Path(), true); 1631 if (status == B_OK) { 1632 int32 tries = 1; 1633 while (entry.Exists()) { 1634 // Find a unique name for the bookmark, there may still be a 1635 // file in the way that stores a different URL. 1636 bookmarkName = webView->MainFrameTitle(); 1637 bookmarkName << " " << tries++; 1638 entryPath = path; 1639 status = entryPath.Append(bookmarkName); 1640 if (status == B_OK) 1641 status = entry.SetTo(entryPath.Path(), true); 1642 if (status != B_OK) 1643 break; 1644 } 1645 } 1646 if (status == B_OK) { 1647 status = bookmarkFile.SetTo(&entry, 1648 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 1649 } 1650 1651 // Write bookmark meta data 1652 if (status == B_OK) 1653 status = bookmarkFile.WriteAttrString("META:url", &url); 1654 if (status == B_OK) { 1655 BString title = webView->MainFrameTitle(); 1656 bookmarkFile.WriteAttrString("META:title", &title); 1657 } 1658 1659 BNodeInfo nodeInfo(&bookmarkFile); 1660 if (status == B_OK) { 1661 status = nodeInfo.SetType("application/x-vnd.Be-bookmark"); 1662 if (status == B_OK) { 1663 PageUserData* userData = static_cast<PageUserData*>( 1664 webView->GetUserData()); 1665 if (userData != NULL && userData->PageIcon() != NULL) { 1666 BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK, 1667 B_CMAP8); 1668 status_t ret = miniIcon.ImportBits(userData->PageIcon()); 1669 if (ret == B_OK) 1670 ret = nodeInfo.SetIcon(&miniIcon, B_MINI_ICON); 1671 if (ret != B_OK) { 1672 fprintf(stderr, "Failed to store mini icon for bookmark: " 1673 "%s\n", strerror(ret)); 1674 } 1675 BBitmap largeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK, 1676 B_CMAP8); 1677 // TODO: Store 32x32 favicon which is often provided by sites. 1678 const uint8* src = (const uint8*)miniIcon.Bits(); 1679 uint32 srcBPR = miniIcon.BytesPerRow(); 1680 uint8* dst = (uint8*)largeIcon.Bits(); 1681 uint32 dstBPR = largeIcon.BytesPerRow(); 1682 for (uint32 y = 0; y < 16; y++) { 1683 const uint8* s = src; 1684 uint8* d = dst; 1685 for (uint32 x = 0; x < 16; x++) { 1686 *d++ = *s; 1687 *d++ = *s++; 1688 } 1689 dst += dstBPR; 1690 s = src; 1691 for (uint32 x = 0; x < 16; x++) { 1692 *d++ = *s; 1693 *d++ = *s++; 1694 } 1695 dst += dstBPR; 1696 src += srcBPR; 1697 } 1698 if (ret == B_OK) 1699 ret = nodeInfo.SetIcon(&largeIcon, B_LARGE_ICON); 1700 if (ret != B_OK) { 1701 fprintf(stderr, "Failed to store large icon for bookmark: " 1702 "%s\n", strerror(ret)); 1703 } 1704 } 1705 } 1706 } 1707 1708 if (status != B_OK) { 1709 BString message(B_TRANSLATE_COMMENT("There was an error creating the " 1710 "bookmark file.\n\nError: %error", "Don't translate variable " 1711 "%error")); 1712 message.ReplaceFirst("%error", strerror(status)); 1713 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1714 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1715 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1716 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1717 alert->Go(); 1718 return; 1719 } 1720 } 1721 1722 1723 void 1724 BrowserWindow::_ShowBookmarks() 1725 { 1726 BPath path; 1727 entry_ref ref; 1728 status_t status = _BookmarkPath(path); 1729 if (status == B_OK) 1730 status = get_ref_for_path(path.Path(), &ref); 1731 if (status == B_OK) 1732 status = be_roster->Launch(&ref); 1733 1734 if (status != B_OK && status != B_ALREADY_RUNNING) { 1735 BString message(B_TRANSLATE_COMMENT("There was an error trying to " 1736 "show the Bookmarks folder.\n\nError: %error", "Don't translate variable " 1737 "%error")); 1738 message.ReplaceFirst("%error", strerror(status)); 1739 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1740 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1741 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1742 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1743 alert->Go(); 1744 return; 1745 } 1746 } 1747 1748 1749 bool BrowserWindow::_CheckBookmarkExists(BDirectory& directory, 1750 const BString& bookmarkName, const BString& url) const 1751 { 1752 BEntry entry; 1753 while (directory.GetNextEntry(&entry) == B_OK) { 1754 if (entry.IsDirectory()) { 1755 BDirectory subBirectory(&entry); 1756 // At least preserve the entry file handle when recursing into 1757 // sub-folders... eventually we will run out, though, with very 1758 // deep hierarchy. 1759 entry.Unset(); 1760 if (_CheckBookmarkExists(subBirectory, bookmarkName, url)) 1761 return true; 1762 } else { 1763 char entryName[B_FILE_NAME_LENGTH]; 1764 if (entry.GetName(entryName) != B_OK || bookmarkName != entryName) 1765 continue; 1766 BString storedURL; 1767 BFile file(&entry, B_READ_ONLY); 1768 if (_ReadURLAttr(file, storedURL)) { 1769 // Just bail if the bookmark already exists 1770 if (storedURL == url) 1771 return true; 1772 } 1773 } 1774 } 1775 return false; 1776 } 1777 1778 1779 bool 1780 BrowserWindow::_ReadURLAttr(BFile& bookmarkFile, BString& url) const 1781 { 1782 return bookmarkFile.InitCheck() == B_OK 1783 && bookmarkFile.ReadAttrString("META:url", &url) == B_OK; 1784 } 1785 1786 1787 void 1788 BrowserWindow::_AddBookmarkURLsRecursively(BDirectory& directory, 1789 BMessage* message, uint32& addedCount) const 1790 { 1791 BEntry entry; 1792 while (directory.GetNextEntry(&entry) == B_OK) { 1793 if (entry.IsDirectory()) { 1794 BDirectory subBirectory(&entry); 1795 // At least preserve the entry file handle when recursing into 1796 // sub-folders... eventually we will run out, though, with very 1797 // deep hierarchy. 1798 entry.Unset(); 1799 _AddBookmarkURLsRecursively(subBirectory, message, addedCount); 1800 } else { 1801 BString storedURL; 1802 BFile file(&entry, B_READ_ONLY); 1803 if (_ReadURLAttr(file, storedURL)) { 1804 message->AddString("url", storedURL.String()); 1805 addedCount++; 1806 } 1807 } 1808 } 1809 } 1810 1811 1812 void 1813 BrowserWindow::_SetPageIcon(BWebView* view, const BBitmap* icon) 1814 { 1815 PageUserData* userData = static_cast<PageUserData*>(view->GetUserData()); 1816 if (userData == NULL) { 1817 userData = new(std::nothrow) PageUserData(NULL); 1818 if (userData == NULL) 1819 return; 1820 view->SetUserData(userData); 1821 } 1822 // The PageUserData makes a copy of the icon, which we pass on to 1823 // the TabManager for display in the respective tab. 1824 userData->SetPageIcon(icon); 1825 fTabManager->SetTabIcon(view, userData->PageIcon()); 1826 if (view == CurrentWebView()) 1827 fURLInputGroup->SetPageIcon(icon); 1828 } 1829 1830 1831 static void 1832 addItemToMenuOrSubmenu(BMenu* menu, BMenuItem* newItem) 1833 { 1834 BString baseURLLabel = baseURL(BString(newItem->Label())); 1835 for (int32 i = menu->CountItems() - 1; i >= 0; i--) { 1836 BMenuItem* item = menu->ItemAt(i); 1837 BString label = item->Label(); 1838 if (label.FindFirst(baseURLLabel) >= 0) { 1839 if (item->Submenu()) { 1840 // Submenu was already added in previous iteration. 1841 item->Submenu()->AddItem(newItem); 1842 return; 1843 } else { 1844 menu->RemoveItem(item); 1845 BMenu* subMenu = new BMenu(baseURLLabel.String()); 1846 subMenu->AddItem(item); 1847 subMenu->AddItem(newItem); 1848 // Add common submenu for this base URL, clickable. 1849 BMessage* message = new BMessage(GOTO_URL); 1850 message->AddString("url", baseURLLabel.String()); 1851 menu->AddItem(new BMenuItem(subMenu, message), i); 1852 return; 1853 } 1854 } 1855 } 1856 menu->AddItem(newItem); 1857 } 1858 1859 1860 static void 1861 addOrDeleteMenu(BMenu* menu, BMenu* toMenu) 1862 { 1863 if (menu->CountItems() > 0) 1864 toMenu->AddItem(menu); 1865 else 1866 delete menu; 1867 } 1868 1869 1870 void 1871 BrowserWindow::_UpdateHistoryMenu() 1872 { 1873 BMenuItem* menuItem; 1874 while ((menuItem = fHistoryMenu->RemoveItem(fHistoryMenuFixedItemCount))) 1875 delete menuItem; 1876 1877 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 1878 if (!history->Lock()) 1879 return; 1880 1881 int32 count = history->CountItems(); 1882 BMenuItem* clearHistoryItem = new BMenuItem(B_TRANSLATE("Clear history"), 1883 new BMessage(CLEAR_HISTORY)); 1884 clearHistoryItem->SetEnabled(count > 0); 1885 fHistoryMenu->AddItem(clearHistoryItem); 1886 if (count == 0) { 1887 history->Unlock(); 1888 return; 1889 } 1890 fHistoryMenu->AddSeparatorItem(); 1891 1892 BDateTime todayStart = BDateTime::CurrentDateTime(B_LOCAL_TIME); 1893 todayStart.SetTime(BTime(0, 0, 0)); 1894 1895 BDateTime oneDayAgoStart = todayStart; 1896 oneDayAgoStart.Date().AddDays(-1); 1897 1898 BDateTime twoDaysAgoStart = oneDayAgoStart; 1899 twoDaysAgoStart.Date().AddDays(-1); 1900 1901 BDateTime threeDaysAgoStart = twoDaysAgoStart; 1902 threeDaysAgoStart.Date().AddDays(-1); 1903 1904 BDateTime fourDaysAgoStart = threeDaysAgoStart; 1905 fourDaysAgoStart.Date().AddDays(-1); 1906 1907 BDateTime fiveDaysAgoStart = fourDaysAgoStart; 1908 fiveDaysAgoStart.Date().AddDays(-1); 1909 1910 BMenu* todayMenu = new BMenu(B_TRANSLATE("Today")); 1911 BMenu* yesterdayMenu = new BMenu(B_TRANSLATE("Yesterday")); 1912 BMenu* twoDaysAgoMenu = new BMenu( 1913 twoDaysAgoStart.Date().LongDayName().String()); 1914 BMenu* threeDaysAgoMenu = new BMenu( 1915 threeDaysAgoStart.Date().LongDayName().String()); 1916 BMenu* fourDaysAgoMenu = new BMenu( 1917 fourDaysAgoStart.Date().LongDayName().String()); 1918 BMenu* fiveDaysAgoMenu = new BMenu( 1919 fiveDaysAgoStart.Date().LongDayName().String()); 1920 BMenu* earlierMenu = new BMenu(B_TRANSLATE("Earlier")); 1921 1922 for (int32 i = 0; i < count; i++) { 1923 BrowsingHistoryItem historyItem = history->HistoryItemAt(i); 1924 BMessage* message = new BMessage(GOTO_URL); 1925 message->AddString("url", historyItem.URL().String()); 1926 1927 BString truncatedUrl(historyItem.URL()); 1928 be_plain_font->TruncateString(&truncatedUrl, B_TRUNCATE_END, 480); 1929 menuItem = new BMenuItem(truncatedUrl, message); 1930 1931 if (historyItem.DateTime() < fiveDaysAgoStart) 1932 addItemToMenuOrSubmenu(earlierMenu, menuItem); 1933 else if (historyItem.DateTime() < fourDaysAgoStart) 1934 addItemToMenuOrSubmenu(fiveDaysAgoMenu, menuItem); 1935 else if (historyItem.DateTime() < threeDaysAgoStart) 1936 addItemToMenuOrSubmenu(fourDaysAgoMenu, menuItem); 1937 else if (historyItem.DateTime() < twoDaysAgoStart) 1938 addItemToMenuOrSubmenu(threeDaysAgoMenu, menuItem); 1939 else if (historyItem.DateTime() < oneDayAgoStart) 1940 addItemToMenuOrSubmenu(twoDaysAgoMenu, menuItem); 1941 else if (historyItem.DateTime() < todayStart) 1942 addItemToMenuOrSubmenu(yesterdayMenu, menuItem); 1943 else 1944 addItemToMenuOrSubmenu(todayMenu, menuItem); 1945 } 1946 history->Unlock(); 1947 1948 addOrDeleteMenu(todayMenu, fHistoryMenu); 1949 addOrDeleteMenu(yesterdayMenu, fHistoryMenu); 1950 addOrDeleteMenu(twoDaysAgoMenu, fHistoryMenu); 1951 addOrDeleteMenu(fourDaysAgoMenu, fHistoryMenu); 1952 addOrDeleteMenu(fiveDaysAgoMenu, fHistoryMenu); 1953 addOrDeleteMenu(earlierMenu, fHistoryMenu); 1954 } 1955 1956 1957 void 1958 BrowserWindow::_UpdateClipboardItems() 1959 { 1960 BTextView* focusTextView = dynamic_cast<BTextView*>(CurrentFocus()); 1961 if (focusTextView != NULL) { 1962 int32 selectionStart; 1963 int32 selectionEnd; 1964 focusTextView->GetSelection(&selectionStart, &selectionEnd); 1965 bool hasSelection = selectionStart < selectionEnd; 1966 bool canPaste = false; 1967 // A BTextView has the focus. 1968 if (be_clipboard->Lock()) { 1969 BMessage* data = be_clipboard->Data(); 1970 if (data != NULL) 1971 canPaste = data->HasData("text/plain", B_MIME_TYPE); 1972 be_clipboard->Unlock(); 1973 } 1974 fCutMenuItem->SetEnabled(hasSelection); 1975 fCopyMenuItem->SetEnabled(hasSelection); 1976 fPasteMenuItem->SetEnabled(canPaste); 1977 } else if (CurrentWebView() != NULL) { 1978 // Trigger update of the clipboard items, even if the 1979 // BWebView doesn't have focus, we'll dispatch these message 1980 // there anyway. This works so fast that the user can never see 1981 // the wrong enabled state when the menu opens until the result 1982 // message arrives. The initial state needs to be enabled, since 1983 // standard shortcut handling is always wrapped inside MenusBeginning() 1984 // and MenusEnded(), and since we update items asynchronously, we need 1985 // to have them enabled to begin with. 1986 fCutMenuItem->SetEnabled(true); 1987 fCopyMenuItem->SetEnabled(true); 1988 fPasteMenuItem->SetEnabled(true); 1989 1990 CurrentWebView()->WebPage()->SendEditingCapabilities(); 1991 } 1992 } 1993 1994 1995 bool 1996 BrowserWindow::_ShowPage(BWebView* view) 1997 { 1998 if (view != CurrentWebView()) { 1999 int32 tabIndex = fTabManager->TabForView(view); 2000 if (tabIndex < 0) { 2001 // Page seems to be gone already? 2002 return false; 2003 } 2004 fTabManager->SelectTab(tabIndex); 2005 _TabChanged(tabIndex); 2006 UpdateIfNeeded(); 2007 } 2008 return true; 2009 } 2010 2011 2012 void 2013 BrowserWindow::_ResizeToScreen() 2014 { 2015 BScreen screen(this); 2016 MoveTo(0, 0); 2017 ResizeTo(screen.Frame().Width(), screen.Frame().Height()); 2018 } 2019 2020 2021 void 2022 BrowserWindow::_SetAutoHideInterfaceInFullscreen(bool doIt) 2023 { 2024 if (fAutoHideInterfaceInFullscreenMode == doIt) 2025 return; 2026 2027 fAutoHideInterfaceInFullscreenMode = doIt; 2028 if (fAppSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, 2029 doIt) != doIt) { 2030 fAppSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, 2031 doIt); 2032 } 2033 2034 if (fAutoHideInterfaceInFullscreenMode) { 2035 BMessage message(CHECK_AUTO_HIDE_INTERFACE); 2036 fPulseRunner = new BMessageRunner(BMessenger(this), &message, 300000); 2037 } else { 2038 delete fPulseRunner; 2039 fPulseRunner = NULL; 2040 _ShowInterface(true); 2041 } 2042 } 2043 2044 2045 void 2046 BrowserWindow::_CheckAutoHideInterface() 2047 { 2048 if (!fIsFullscreen || !fAutoHideInterfaceInFullscreenMode 2049 || (CurrentWebView() != NULL && !CurrentWebView()->IsFocus())) { 2050 return; 2051 } 2052 2053 if (fLastMousePos.y == 0) 2054 _ShowInterface(true); 2055 else if (fNavigationGroup->IsVisible() 2056 && fLastMousePos.y > fNavigationGroup->Frame().bottom 2057 && system_time() - fLastMouseMovedTime > 1000000) { 2058 // NOTE: Do not re-use navigationGroupBottom in the above 2059 // check, since we only want to hide the interface when it is visible. 2060 _ShowInterface(false); 2061 } 2062 } 2063 2064 2065 void 2066 BrowserWindow::_ShowInterface(bool show) 2067 { 2068 if (fInterfaceVisible == show) 2069 return; 2070 2071 fInterfaceVisible = show; 2072 2073 if (show) { 2074 #if !INTEGRATE_MENU_INTO_TAB_BAR 2075 fMenuGroup->SetVisible( 2076 (fVisibleInterfaceElements & INTERFACE_ELEMENT_MENU) != 0); 2077 #endif 2078 fTabGroup->SetVisible(_TabGroupShouldBeVisible()); 2079 fNavigationGroup->SetVisible( 2080 (fVisibleInterfaceElements & INTERFACE_ELEMENT_NAVIGATION) != 0); 2081 fStatusGroup->SetVisible( 2082 (fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0); 2083 } else { 2084 fMenuGroup->SetVisible(false); 2085 fTabGroup->SetVisible(false); 2086 fNavigationGroup->SetVisible(false); 2087 fStatusGroup->SetVisible(false); 2088 } 2089 // TODO: Setting the group visible seems to unhide the status bar. 2090 // Fix in Haiku? 2091 while (!fLoadingProgressBar->IsHidden()) 2092 fLoadingProgressBar->Hide(); 2093 } 2094 2095 2096 void 2097 BrowserWindow::_ShowProgressBar(bool show) 2098 { 2099 if (show) { 2100 if (!fStatusGroup->IsVisible() && (fVisibleInterfaceElements 2101 & INTERFACE_ELEMENT_STATUS) != 0) 2102 fStatusGroup->SetVisible(true); 2103 fLoadingProgressBar->Show(); 2104 } else { 2105 if (!fInterfaceVisible) 2106 fStatusGroup->SetVisible(false); 2107 // TODO: This is also used in _ShowInterface. Without it the status bar 2108 // doesn't always hide again. It may be an Interface Kit bug. 2109 while (!fLoadingProgressBar->IsHidden()) 2110 fLoadingProgressBar->Hide(); 2111 } 2112 } 2113 2114 2115 void 2116 BrowserWindow::_InvokeButtonVisibly(BButton* button) 2117 { 2118 button->SetValue(B_CONTROL_ON); 2119 UpdateIfNeeded(); 2120 button->Invoke(); 2121 snooze(1000); 2122 button->SetValue(B_CONTROL_OFF); 2123 } 2124 2125 2126 BString 2127 BrowserWindow::_NewTabURL(bool isNewWindow) const 2128 { 2129 BString url; 2130 uint32 policy = isNewWindow ? fNewWindowPolicy : fNewTabPolicy; 2131 // Implement new page policy 2132 switch (policy) { 2133 case OpenStartPage: 2134 url = fStartPageURL; 2135 break; 2136 case OpenSearchPage: 2137 url = fSearchPageURL; 2138 break; 2139 case CloneCurrentPage: 2140 if (CurrentWebView() != NULL) 2141 url = CurrentWebView()->MainFrameURL(); 2142 break; 2143 case OpenBlankPage: 2144 default: 2145 break; 2146 } 2147 return url; 2148 } 2149 2150 BString 2151 BrowserWindow::_EncodeURIComponent(const BString& search) 2152 { 2153 const BString escCharList = " $&`:<>[]{}\"+#%@/;=?\\^|~\',"; 2154 BString result = search; 2155 char hexcode[4]; 2156 2157 for (int32 i = 0; i < result.Length(); i++) { 2158 if (escCharList.FindFirst(result[i]) != B_ERROR) { 2159 sprintf(hexcode, "%02X", (unsigned int)result[i]); 2160 result[i] = '%'; 2161 result.Insert(hexcode, i + 1); 2162 i += 2; 2163 } 2164 } 2165 2166 return result; 2167 } 2168 2169 2170 void 2171 BrowserWindow::_VisitURL(const BString& url) 2172 { 2173 //fURLInputGroup->TextView()->SetText(url); 2174 CurrentWebView()->LoadURL(url.String()); 2175 } 2176 2177 2178 void 2179 BrowserWindow::_VisitSearchEngine(const BString& search) 2180 { 2181 // TODO: Google Code-In Task to make default search 2182 // engine modifiable from Settings? :) 2183 2184 BString engine = "http://www.google.com/search?q="; 2185 engine += _EncodeURIComponent(search); 2186 // We have to take care of some of the escaping before 2187 // we hand over the string to WebKit, if we want queries 2188 // like "4+3" to not be searched as "4 3". 2189 2190 _VisitURL(engine); 2191 } 2192 2193 2194 inline bool 2195 BrowserWindow::_IsValidDomainChar(char ch) 2196 { 2197 // TODO: Currenlty, only a whitespace character 2198 // breaks a domain name. It might be 2199 // a good idea (or a bad one) to make 2200 // character filtering based on the 2201 // IDNA 2008 standard. 2202 2203 return ch != ' '; 2204 } 2205 2206 2207 void 2208 BrowserWindow::_SmartURLHandler(const BString& url) 2209 { 2210 // Only process if this doesn't look like a full URL (http:// or 2211 // file://, etc.) 2212 2213 BString temp; 2214 int32 at = url.FindFirst(":"); 2215 2216 if (at != B_ERROR) { 2217 BString proto; 2218 url.CopyInto(proto, 0, at); 2219 2220 if (proto == "http" || proto == "https" || proto == "file") 2221 _VisitURL(url); 2222 else { 2223 temp = "application/x-vnd.Be.URL."; 2224 temp += proto; 2225 2226 char* argv[1] = { (char*)url.String() }; 2227 2228 if (be_roster->Launch(temp.String(), 1, argv) != B_OK) 2229 _VisitSearchEngine(url); 2230 } 2231 } else if (url == "localhost") 2232 _VisitURL("http://localhost/"); 2233 else { 2234 const char* localhostPrefix = "localhost/"; 2235 2236 if(url.Compare(localhostPrefix, strlen(localhostPrefix)) == 0) 2237 _VisitURL(url); 2238 else { 2239 bool isURL = false; 2240 2241 for (int32 i = 0; i < url.CountChars(); i++) { 2242 if (url[i] == '.') 2243 isURL = true; 2244 else if (url[i] == '/') 2245 break; 2246 else if (!_IsValidDomainChar(url[i])) { 2247 isURL = false; 2248 2249 break; 2250 } 2251 } 2252 2253 if (isURL) 2254 _VisitURL(url); 2255 else 2256 _VisitSearchEngine(url); 2257 } 2258 } 2259 } 2260 2261 2262 void 2263 BrowserWindow::_HandlePageSourceResult(const BMessage* message) 2264 { 2265 // TODO: This should be done in an extra thread perhaps. Doing it in 2266 // the application thread is not much better, since it actually draws 2267 // the pages... 2268 2269 BPath pathToPageSource; 2270 2271 BString url; 2272 status_t ret = message->FindString("url", &url); 2273 if (ret == B_OK && url.FindFirst("file://") == 0) { 2274 // Local file 2275 url.Remove(0, strlen("file://")); 2276 pathToPageSource.SetTo(url.String()); 2277 } else { 2278 // Something else, store it. 2279 // TODO: What if it isn't HTML, but for example SVG? 2280 BString source; 2281 ret = message->FindString("source", &source); 2282 2283 if (ret == B_OK) 2284 ret = find_directory(B_COMMON_TEMP_DIRECTORY, &pathToPageSource); 2285 2286 BString tmpFileName("PageSource_"); 2287 tmpFileName << system_time() << ".html"; 2288 if (ret == B_OK) 2289 ret = pathToPageSource.Append(tmpFileName.String()); 2290 2291 BFile pageSourceFile(pathToPageSource.Path(), 2292 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 2293 if (ret == B_OK) 2294 ret = pageSourceFile.InitCheck(); 2295 2296 if (ret == B_OK) { 2297 ssize_t written = pageSourceFile.Write(source.String(), 2298 source.Length()); 2299 if (written != source.Length()) 2300 ret = (status_t)written; 2301 } 2302 2303 if (ret == B_OK) { 2304 const char* type = "text/html"; 2305 size_t size = strlen(type); 2306 pageSourceFile.WriteAttr("BEOS:TYPE", B_STRING_TYPE, 0, type, size); 2307 // If it fails we don't care. 2308 } 2309 } 2310 2311 entry_ref ref; 2312 if (ret == B_OK) 2313 ret = get_ref_for_path(pathToPageSource.Path(), &ref); 2314 2315 if (ret == B_OK) { 2316 BMessage refsMessage(B_REFS_RECEIVED); 2317 ret = refsMessage.AddRef("refs", &ref); 2318 if (ret == B_OK) { 2319 ret = be_roster->Launch("text/x-source-code", &refsMessage); 2320 if (ret == B_ALREADY_RUNNING) 2321 ret = B_OK; 2322 } 2323 } 2324 2325 if (ret != B_OK) { 2326 char buffer[1024]; 2327 snprintf(buffer, sizeof(buffer), "Failed to show the " 2328 "page source: %s\n", strerror(ret)); 2329 BAlert* alert = new BAlert(B_TRANSLATE("Page source error"), buffer, 2330 B_TRANSLATE("OK")); 2331 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2332 alert->Go(NULL); 2333 } 2334 } 2335 2336 2337