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 _SetPageIcon(CurrentWebView(), NULL); 656 BString newUrl = _SmartURLHandler(url); 657 if (newUrl != url) 658 fURLInputGroup->TextView()->SetText(newUrl); 659 CurrentWebView()->LoadURL(newUrl.String()); 660 break; 661 } 662 case GO_BACK: 663 CurrentWebView()->GoBack(); 664 break; 665 case GO_FORWARD: 666 CurrentWebView()->GoForward(); 667 break; 668 case STOP: 669 CurrentWebView()->StopLoading(); 670 break; 671 case HOME: 672 CurrentWebView()->LoadURL(fStartPageURL); 673 break; 674 675 case CLEAR_HISTORY: { 676 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 677 if (history->CountItems() == 0) 678 break; 679 BAlert* alert = new BAlert(B_TRANSLATE("Confirmation"), 680 B_TRANSLATE("Do you really want to " 681 "clear the browsing history?"), B_TRANSLATE("Clear"), 682 B_TRANSLATE("Cancel")); 683 alert->SetShortcut(1, B_ESCAPE); 684 685 if (alert->Go() == 0) 686 history->Clear(); 687 break; 688 } 689 690 case CREATE_BOOKMARK: 691 _CreateBookmark(); 692 break; 693 case SHOW_BOOKMARKS: 694 _ShowBookmarks(); 695 break; 696 697 case B_REFS_RECEIVED: 698 { 699 // Currently the only source of these messages is the bookmarks menu. 700 // Filter refs into URLs, this also gets rid of refs for folders. 701 // For clicks on sub-folders in the bookmarks menu, we have Tracker 702 // open the corresponding folder. 703 entry_ref ref; 704 uint32 addedCount = 0; 705 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 706 BEntry entry(&ref); 707 uint32 addedSubCount = 0; 708 if (entry.IsDirectory()) { 709 BDirectory directory(&entry); 710 _AddBookmarkURLsRecursively(directory, message, 711 addedSubCount); 712 } else { 713 BFile file(&ref, B_READ_ONLY); 714 BString url; 715 if (_ReadURLAttr(file, url)) { 716 message->AddString("url", url.String()); 717 addedSubCount++; 718 } 719 } 720 if (addedSubCount == 0) { 721 // Don't know what to do with this entry, just pass it 722 // on to the system to handle. Note that this may result 723 // in us opening other supported files via the application 724 // mechanism. 725 be_roster->Launch(&ref); 726 } 727 addedCount += addedSubCount; 728 } 729 message->RemoveName("refs"); 730 if (addedCount > 10) { 731 BString string(B_TRANSLATE_COMMENT("Do you want to open %addedCount " 732 "bookmarks all at once?", "Don't translate variable %addedCount.")); 733 string.ReplaceFirst("%addedCount", BString() << addedCount); 734 735 BAlert* alert = new BAlert(B_TRANSLATE("Open bookmarks confirmation"), 736 string.String(), B_TRANSLATE("Cancel"), B_TRANSLATE("Open all")); 737 alert->SetShortcut(0, B_ESCAPE); 738 if (alert->Go() == 0) 739 break; 740 } 741 be_app->PostMessage(message); 742 break; 743 } 744 case B_SIMPLE_DATA: 745 { 746 // User possibly dropped files on this window. 747 // If there is more than one entry_ref, let the app handle it 748 // (open one new page per ref). If there is one ref, open it in 749 // this window. 750 type_code type; 751 int32 countFound; 752 if (message->GetInfo("refs", &type, &countFound) != B_OK 753 || type != B_REF_TYPE) { 754 break; 755 } 756 if (countFound > 1) { 757 message->what = B_REFS_RECEIVED; 758 be_app->PostMessage(message); 759 break; 760 } 761 entry_ref ref; 762 if (message->FindRef("refs", &ref) != B_OK) 763 break; 764 BEntry entry(&ref, true); 765 BPath path; 766 if (!entry.Exists() || entry.GetPath(&path) != B_OK) 767 break; 768 CurrentWebView()->LoadURL(path.Path()); 769 break; 770 } 771 772 case ZOOM_FACTOR_INCREASE: 773 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly); 774 break; 775 case ZOOM_FACTOR_DECREASE: 776 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly); 777 break; 778 case ZOOM_FACTOR_RESET: 779 CurrentWebView()->ResetZoomFactor(); 780 break; 781 case ZOOM_TEXT_ONLY: 782 fZoomTextOnly = !fZoomTextOnly; 783 fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly); 784 // TODO: Would be nice to have an instant update if the page is 785 // already zoomed. 786 break; 787 788 case TOGGLE_FULLSCREEN: 789 ToggleFullscreen(); 790 break; 791 792 case TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN: 793 _SetAutoHideInterfaceInFullscreen( 794 !fAutoHideInterfaceInFullscreenMode); 795 break; 796 797 case CHECK_AUTO_HIDE_INTERFACE: 798 _CheckAutoHideInterface(); 799 break; 800 801 case SHOW_PAGE_SOURCE: 802 CurrentWebView()->WebPage()->SendPageSource(); 803 break; 804 case B_PAGE_SOURCE_RESULT: 805 _HandlePageSourceResult(message); 806 break; 807 808 case EDIT_FIND_NEXT: 809 CurrentWebView()->FindString(fFindTextControl->Text(), true, 810 fFindCaseSensitiveCheckBox->Value()); 811 break; 812 case FIND_TEXT_CHANGED: 813 { 814 bool findTextAvailable = strlen(fFindTextControl->Text()) > 0; 815 fFindPreviousMenuItem->SetEnabled(findTextAvailable); 816 fFindNextMenuItem->SetEnabled(findTextAvailable); 817 break; 818 } 819 case EDIT_FIND_PREVIOUS: 820 CurrentWebView()->FindString(fFindTextControl->Text(), false, 821 fFindCaseSensitiveCheckBox->Value()); 822 break; 823 case EDIT_SHOW_FIND_GROUP: 824 if (!fFindGroup->IsVisible()) 825 fFindGroup->SetVisible(true); 826 fFindTextControl->MakeFocus(true); 827 break; 828 case EDIT_HIDE_FIND_GROUP: 829 if (fFindGroup->IsVisible()) { 830 fFindGroup->SetVisible(false); 831 if (CurrentWebView() != NULL) 832 CurrentWebView()->MakeFocus(true); 833 } 834 break; 835 836 case B_CUT: 837 case B_COPY: 838 case B_PASTE: 839 { 840 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus()); 841 if (textView != NULL) 842 textView->MessageReceived(message); 843 else if (CurrentWebView() != NULL) 844 CurrentWebView()->MessageReceived(message); 845 break; 846 } 847 848 case B_EDITING_CAPABILITIES_RESULT: 849 { 850 BWebView* webView; 851 if (message->FindPointer("view", 852 reinterpret_cast<void**>(&webView)) != B_OK 853 || webView != CurrentWebView()) { 854 break; 855 } 856 bool canCut; 857 bool canCopy; 858 bool canPaste; 859 if (message->FindBool("can cut", &canCut) != B_OK) 860 canCut = false; 861 if (message->FindBool("can copy", &canCopy) != B_OK) 862 canCopy = false; 863 if (message->FindBool("can paste", &canPaste) != B_OK) 864 canPaste = false; 865 fCutMenuItem->SetEnabled(canCut); 866 fCopyMenuItem->SetEnabled(canCopy); 867 fPasteMenuItem->SetEnabled(canPaste); 868 break; 869 } 870 871 case SHOW_DOWNLOAD_WINDOW: 872 case SHOW_SETTINGS_WINDOW: 873 message->AddUInt32("workspaces", Workspaces()); 874 be_app->PostMessage(message); 875 break; 876 877 case CLOSE_TAB: 878 if (fTabManager->CountTabs() > 1) { 879 int32 index; 880 if (message->FindInt32("tab index", &index) != B_OK) 881 index = fTabManager->SelectedTabIndex(); 882 _ShutdownTab(index); 883 _UpdateTabGroupVisibility(); 884 } else 885 PostMessage(B_QUIT_REQUESTED); 886 break; 887 888 case SELECT_TAB: 889 { 890 int32 index; 891 if (message->FindInt32("tab index", &index) == B_OK 892 && fTabManager->SelectedTabIndex() != index 893 && fTabManager->CountTabs() > index) { 894 fTabManager->SelectTab(index); 895 } 896 897 break; 898 } 899 900 case TAB_CHANGED: 901 { 902 // This message may be received also when the last tab closed, 903 // i.e. with index == -1. 904 int32 index; 905 if (message->FindInt32("tab index", &index) != B_OK) 906 index = -1; 907 _TabChanged(index); 908 break; 909 } 910 911 case SETTINGS_VALUE_CHANGED: 912 { 913 BString name; 914 if (message->FindString("name", &name) != B_OK) 915 break; 916 bool flag; 917 BString string; 918 uint32 value; 919 if (name == kSettingsKeyShowTabsIfSinglePageOpen 920 && message->FindBool("value", &flag) == B_OK) { 921 if (fShowTabsIfSinglePageOpen != flag) { 922 fShowTabsIfSinglePageOpen = flag; 923 _UpdateTabGroupVisibility(); 924 } 925 } else if (name == kSettingsKeyAutoHidePointer 926 && message->FindBool("value", &flag) == B_OK) { 927 fAutoHidePointer = flag; 928 if (CurrentWebView()) 929 CurrentWebView()->SetAutoHidePointer(fAutoHidePointer); 930 } else if (name == kSettingsKeyStartPageURL 931 && message->FindString("value", &string) == B_OK) { 932 fStartPageURL = string; 933 } else if (name == kSettingsKeySearchPageURL 934 && message->FindString("value", &string) == B_OK) { 935 fSearchPageURL = string; 936 } else if (name == kSettingsKeyNewWindowPolicy 937 && message->FindUInt32("value", &value) == B_OK) { 938 fNewWindowPolicy = value; 939 } else if (name == kSettingsKeyNewTabPolicy 940 && message->FindUInt32("value", &value) == B_OK) { 941 fNewTabPolicy = value; 942 } else if (name == kSettingsKeyAutoHideInterfaceInFullscreenMode 943 && message->FindBool("value", &flag) == B_OK) { 944 _SetAutoHideInterfaceInFullscreen(flag); 945 } else if (name == kSettingsKeyShowHomeButton 946 && message->FindBool("value", &flag) == B_OK) { 947 if (flag) 948 fHomeButton->Show(); 949 else 950 fHomeButton->Hide(); 951 } 952 break; 953 } 954 955 default: 956 BWebWindow::MessageReceived(message); 957 break; 958 } 959 } 960 961 962 bool 963 BrowserWindow::QuitRequested() 964 { 965 // TODO: Check for modified form data and ask user for confirmation, etc. 966 967 // Iterate over all tabs to delete all BWebViews. 968 // Do this here, so WebKit tear down happens earlier. 969 SetCurrentWebView(NULL); 970 while (fTabManager->CountTabs() > 0) 971 _ShutdownTab(0); 972 973 BMessage message(WINDOW_CLOSED); 974 message.AddRect("window frame", WindowFrame()); 975 be_app->PostMessage(&message); 976 return true; 977 } 978 979 980 void 981 BrowserWindow::MenusBeginning() 982 { 983 _UpdateHistoryMenu(); 984 _UpdateClipboardItems(); 985 _ShowInterface(true); 986 } 987 988 989 void 990 BrowserWindow::ScreenChanged(BRect screenSize, color_space format) 991 { 992 if (fIsFullscreen) 993 _ResizeToScreen(); 994 } 995 996 997 void 998 BrowserWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 999 { 1000 if (fIsFullscreen) 1001 _ResizeToScreen(); 1002 } 1003 1004 1005 static bool 1006 viewIsChild(const BView* parent, const BView* view) 1007 { 1008 if (parent == view) 1009 return true; 1010 1011 int32 count = parent->CountChildren(); 1012 for (int32 i = 0; i < count; i++) { 1013 BView* child = parent->ChildAt(i); 1014 if (viewIsChild(child, view)) 1015 return true; 1016 } 1017 return false; 1018 } 1019 1020 1021 void 1022 BrowserWindow::SetCurrentWebView(BWebView* webView) 1023 { 1024 if (webView == CurrentWebView()) 1025 return; 1026 1027 if (CurrentWebView() != NULL) { 1028 // Remember the currently focused view before switching tabs, 1029 // so that we can revert the focus when switching back to this tab 1030 // later. 1031 PageUserData* userData = static_cast<PageUserData*>( 1032 CurrentWebView()->GetUserData()); 1033 if (userData == NULL) { 1034 userData = new PageUserData(CurrentFocus()); 1035 CurrentWebView()->SetUserData(userData); 1036 } 1037 userData->SetFocusedView(CurrentFocus()); 1038 userData->SetURLInputContents(fURLInputGroup->Text()); 1039 int32 selectionStart; 1040 int32 selectionEnd; 1041 fURLInputGroup->TextView()->GetSelection(&selectionStart, 1042 &selectionEnd); 1043 userData->SetURLInputSelection(selectionStart, selectionEnd); 1044 } 1045 1046 BWebWindow::SetCurrentWebView(webView); 1047 1048 if (webView != NULL) { 1049 webView->SetAutoHidePointer(fAutoHidePointer); 1050 1051 _UpdateTitle(webView->MainFrameTitle()); 1052 1053 // Restore the previous focus or focus the web view. 1054 PageUserData* userData = static_cast<PageUserData*>( 1055 webView->GetUserData()); 1056 BView* focusedView = NULL; 1057 if (userData != NULL) 1058 focusedView = userData->FocusedView(); 1059 1060 if (focusedView != NULL 1061 && viewIsChild(GetLayout()->View(), focusedView)) { 1062 focusedView->MakeFocus(true); 1063 } else 1064 webView->MakeFocus(true); 1065 1066 if (userData != NULL) { 1067 fURLInputGroup->SetPageIcon(userData->PageIcon()); 1068 if (userData->URLInputContents().Length()) 1069 fURLInputGroup->SetText(userData->URLInputContents()); 1070 else 1071 fURLInputGroup->SetText(webView->MainFrameURL()); 1072 if (userData->URLInputSelectionStart() >= 0) { 1073 fURLInputGroup->TextView()->Select( 1074 userData->URLInputSelectionStart(), 1075 userData->URLInputSelectionEnd()); 1076 } 1077 } else { 1078 fURLInputGroup->SetPageIcon(NULL); 1079 fURLInputGroup->SetText(webView->MainFrameURL()); 1080 } 1081 1082 // Trigger update of the interface to the new page, by requesting 1083 // to resend all notifications. 1084 webView->WebPage()->ResendNotifications(); 1085 } else 1086 _UpdateTitle(""); 1087 } 1088 1089 1090 bool 1091 BrowserWindow::IsBlankTab() const 1092 { 1093 if (CurrentWebView() == NULL) 1094 return false; 1095 BString requestedURL = CurrentWebView()->MainFrameRequestedURL(); 1096 return requestedURL.Length() == 0 1097 || requestedURL == _NewTabURL(fTabManager->CountTabs() == 1); 1098 } 1099 1100 1101 void 1102 BrowserWindow::CreateNewTab(const BString& _url, bool select, BWebView* webView) 1103 { 1104 bool applyNewPagePolicy = webView == NULL; 1105 // Executed in app thread (new BWebPage needs to be created in app thread). 1106 if (webView == NULL) 1107 webView = new BWebView("web view"); 1108 1109 bool isNewWindow = fTabManager->CountTabs() == 0; 1110 1111 fTabManager->AddTab(webView, B_TRANSLATE("New tab")); 1112 1113 BString url(_url); 1114 if (applyNewPagePolicy && url.Length() == 0) 1115 url = _NewTabURL(isNewWindow); 1116 1117 if (url.Length() > 0) 1118 webView->LoadURL(url.String()); 1119 1120 if (select) { 1121 fTabManager->SelectTab(fTabManager->CountTabs() - 1); 1122 SetCurrentWebView(webView); 1123 webView->WebPage()->ResendNotifications(); 1124 fURLInputGroup->SetPageIcon(NULL); 1125 fURLInputGroup->SetText(url.String()); 1126 fURLInputGroup->MakeFocus(true); 1127 } 1128 1129 _ShowInterface(true); 1130 _UpdateTabGroupVisibility(); 1131 } 1132 1133 1134 BRect 1135 BrowserWindow::WindowFrame() const 1136 { 1137 if (fIsFullscreen) 1138 return fNonFullscreenWindowFrame; 1139 else 1140 return Frame(); 1141 } 1142 1143 1144 void 1145 BrowserWindow::ToggleFullscreen() 1146 { 1147 if (fIsFullscreen) { 1148 MoveTo(fNonFullscreenWindowFrame.LeftTop()); 1149 ResizeTo(fNonFullscreenWindowFrame.Width(), 1150 fNonFullscreenWindowFrame.Height()); 1151 1152 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1153 SetLook(B_DOCUMENT_WINDOW_LOOK); 1154 1155 _ShowInterface(true); 1156 } else { 1157 fNonFullscreenWindowFrame = Frame(); 1158 _ResizeToScreen(); 1159 1160 SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1161 SetLook(B_TITLED_WINDOW_LOOK); 1162 } 1163 fIsFullscreen = !fIsFullscreen; 1164 fFullscreenItem->SetMarked(fIsFullscreen); 1165 fToggleFullscreenButton->SetVisible(fIsFullscreen); 1166 } 1167 1168 1169 // #pragma mark - Notification API 1170 1171 1172 void 1173 BrowserWindow::NavigationRequested(const BString& url, BWebView* view) 1174 { 1175 } 1176 1177 1178 void 1179 BrowserWindow::NewWindowRequested(const BString& url, bool primaryAction) 1180 { 1181 // Always open new windows in the application thread, since 1182 // creating a BWebView will try to grab the application lock. 1183 // But our own WebPage may already try to lock us from within 1184 // the application thread -> dead-lock. Thus we can't wait for 1185 // a reply here. 1186 BMessage message(NEW_TAB); 1187 message.AddPointer("window", this); 1188 message.AddString("url", url); 1189 message.AddBool("select", primaryAction); 1190 be_app->PostMessage(&message); 1191 } 1192 1193 1194 void 1195 BrowserWindow::NewPageCreated(BWebView* view, BRect windowFrame, 1196 bool modalDialog, bool resizable, bool activate) 1197 { 1198 if (windowFrame.IsValid()) { 1199 BrowserWindow* window = new BrowserWindow(windowFrame, fAppSettings, 1200 BString(), INTERFACE_ELEMENT_STATUS, view); 1201 window->Show(); 1202 } else 1203 CreateNewTab(BString(), activate, view); 1204 } 1205 1206 1207 void 1208 BrowserWindow::CloseWindowRequested(BWebView* view) 1209 { 1210 int32 index = fTabManager->TabForView(view); 1211 if (index < 0) { 1212 // Tab is already gone. 1213 return; 1214 } 1215 BMessage message(CLOSE_TAB); 1216 message.AddInt32("tab index", index); 1217 PostMessage(&message, this); 1218 } 1219 1220 1221 void 1222 BrowserWindow::LoadNegotiating(const BString& url, BWebView* view) 1223 { 1224 if (view != CurrentWebView()) 1225 return; 1226 1227 fURLInputGroup->SetText(url.String()); 1228 1229 BString status(B_TRANSLATE("Requesting %url")); 1230 status.ReplaceFirst("%url", url); 1231 view->WebPage()->SetStatusMessage(status); 1232 } 1233 1234 1235 void 1236 BrowserWindow::LoadCommitted(const BString& url, BWebView* view) 1237 { 1238 if (view != CurrentWebView()) 1239 return; 1240 1241 // This hook is invoked when the load is commited. 1242 fURLInputGroup->SetText(url.String()); 1243 1244 BString status(B_TRANSLATE("Loading %url")); 1245 status.ReplaceFirst("%url", url); 1246 view->WebPage()->SetStatusMessage(status); 1247 } 1248 1249 1250 void 1251 BrowserWindow::LoadProgress(float progress, BWebView* view) 1252 { 1253 if (view != CurrentWebView()) 1254 return; 1255 1256 if (progress < 100 && fLoadingProgressBar->IsHidden()) 1257 _ShowProgressBar(true); 1258 else if (progress == 100 && !fLoadingProgressBar->IsHidden()) 1259 _ShowProgressBar(false); 1260 fLoadingProgressBar->SetTo(progress); 1261 } 1262 1263 1264 void 1265 BrowserWindow::LoadFailed(const BString& url, BWebView* view) 1266 { 1267 if (view != CurrentWebView()) 1268 return; 1269 1270 BString status(B_TRANSLATE_COMMENT("%url failed", "Loading URL failed. " 1271 "Don't translate variable %url.")); 1272 status.ReplaceFirst("%url", url); 1273 view->WebPage()->SetStatusMessage(status); 1274 if (!fLoadingProgressBar->IsHidden()) 1275 fLoadingProgressBar->Hide(); 1276 } 1277 1278 1279 void 1280 BrowserWindow::LoadFinished(const BString& url, BWebView* view) 1281 { 1282 if (view != CurrentWebView()) 1283 return; 1284 1285 BString status(B_TRANSLATE_COMMENT("%url finished", "Loading URL " 1286 "finished. Don't translate variable %url.")); 1287 status.ReplaceFirst("%url", url); 1288 view->WebPage()->SetStatusMessage(status); 1289 if (!fLoadingProgressBar->IsHidden()) 1290 fLoadingProgressBar->Hide(); 1291 1292 NavigationCapabilitiesChanged(fBackButton->IsEnabled(), 1293 fForwardButton->IsEnabled(), false, view); 1294 1295 int32 tabIndex = fTabManager->TabForView(view); 1296 if (tabIndex > 0 && strcmp(B_TRANSLATE("New tab"), 1297 fTabManager->TabLabel(tabIndex)) == 0) 1298 fTabManager->SetTabLabel(tabIndex, url); 1299 } 1300 1301 1302 void 1303 BrowserWindow::MainDocumentError(const BString& failingURL, 1304 const BString& localizedDescription, BWebView* view) 1305 { 1306 // Make sure we show the page that contains the view. 1307 if (!_ShowPage(view)) 1308 return; 1309 1310 BWebWindow::MainDocumentError(failingURL, localizedDescription, view); 1311 1312 // TODO: Remove the failing URL from the BrowsingHistory! 1313 } 1314 1315 1316 void 1317 BrowserWindow::TitleChanged(const BString& title, BWebView* view) 1318 { 1319 int32 tabIndex = fTabManager->TabForView(view); 1320 if (tabIndex < 0) 1321 return; 1322 1323 fTabManager->SetTabLabel(tabIndex, title); 1324 1325 if (view != CurrentWebView()) 1326 return; 1327 1328 _UpdateTitle(title); 1329 } 1330 1331 1332 void 1333 BrowserWindow::IconReceived(const BBitmap* icon, BWebView* view) 1334 { 1335 // The view may already be gone, since this notification arrives 1336 // asynchronously. 1337 if (!fTabManager->HasView(view)) 1338 return; 1339 1340 _SetPageIcon(view, icon); 1341 } 1342 1343 1344 void 1345 BrowserWindow::ResizeRequested(float width, float height, BWebView* view) 1346 { 1347 if (view != CurrentWebView()) 1348 return; 1349 1350 // Ignore request when there is more than one BWebView embedded. 1351 if (fTabManager->CountTabs() > 1) 1352 return; 1353 1354 // Make sure the new frame is not larger than the screen frame minus 1355 // window decorator border. 1356 BScreen screen(this); 1357 BRect screenFrame = screen.Frame(); 1358 BRect decoratorFrame = DecoratorFrame(); 1359 BRect frame = Frame(); 1360 1361 screenFrame.left += decoratorFrame.left - frame.left; 1362 screenFrame.right += decoratorFrame.right - frame.right; 1363 screenFrame.top += decoratorFrame.top - frame.top; 1364 screenFrame.bottom += decoratorFrame.bottom - frame.bottom; 1365 1366 width = min_c(width, screen.Frame().Width()); 1367 height = min_c(height, screen.Frame().Height()); 1368 1369 frame.right = frame.left + width; 1370 frame.bottom = frame.top + height; 1371 1372 // frame is now not larger than screenFrame, but may still be partly outside 1373 if (!screenFrame.Contains(frame)) { 1374 if (frame.left < screenFrame.left) 1375 frame.OffsetBy(screenFrame.left - frame.left, 0); 1376 else if (frame.right > screenFrame.right) 1377 frame.OffsetBy(screenFrame.right - frame.right, 0); 1378 if (frame.top < screenFrame.top) 1379 frame.OffsetBy(screenFrame.top - frame.top, 0); 1380 else if (frame.bottom > screenFrame.bottom) 1381 frame.OffsetBy(screenFrame.bottom - frame.bottom, 0); 1382 } 1383 1384 MoveTo(frame.left, frame.top); 1385 ResizeTo(width, height); 1386 } 1387 1388 1389 void 1390 BrowserWindow::SetToolBarsVisible(bool flag, BWebView* view) 1391 { 1392 // TODO 1393 // TODO: Ignore request when there is more than one BWebView embedded! 1394 } 1395 1396 1397 void 1398 BrowserWindow::SetStatusBarVisible(bool flag, BWebView* view) 1399 { 1400 // TODO 1401 // TODO: Ignore request when there is more than one BWebView embedded! 1402 } 1403 1404 1405 void 1406 BrowserWindow::SetMenuBarVisible(bool flag, BWebView* view) 1407 { 1408 // TODO 1409 // TODO: Ignore request when there is more than one BWebView embedded! 1410 } 1411 1412 1413 void 1414 BrowserWindow::SetResizable(bool flag, BWebView* view) 1415 { 1416 // TODO: Ignore request when there is more than one BWebView embedded! 1417 1418 if (flag) 1419 SetFlags(Flags() & ~B_NOT_RESIZABLE); 1420 else 1421 SetFlags(Flags() | B_NOT_RESIZABLE); 1422 } 1423 1424 1425 void 1426 BrowserWindow::StatusChanged(const BString& statusText, BWebView* view) 1427 { 1428 if (view != CurrentWebView()) 1429 return; 1430 1431 if (fStatusText) 1432 fStatusText->SetText(statusText.String()); 1433 } 1434 1435 1436 void 1437 BrowserWindow::NavigationCapabilitiesChanged(bool canGoBackward, 1438 bool canGoForward, bool canStop, BWebView* view) 1439 { 1440 if (view != CurrentWebView()) 1441 return; 1442 1443 fBackButton->SetEnabled(canGoBackward); 1444 fForwardButton->SetEnabled(canGoForward); 1445 fStopButton->SetEnabled(canStop); 1446 1447 fBackMenuItem->SetEnabled(canGoBackward); 1448 fForwardMenuItem->SetEnabled(canGoForward); 1449 } 1450 1451 1452 void 1453 BrowserWindow::UpdateGlobalHistory(const BString& url) 1454 { 1455 BrowsingHistory::DefaultInstance()->AddItem(BrowsingHistoryItem(url)); 1456 } 1457 1458 1459 bool 1460 BrowserWindow::AuthenticationChallenge(BString message, BString& inOutUser, 1461 BString& inOutPassword, bool& inOutRememberCredentials, 1462 uint32 failureCount, BWebView* view) 1463 { 1464 CredentialsStorage* persistentStorage 1465 = CredentialsStorage::PersistentInstance(); 1466 CredentialsStorage* sessionStorage 1467 = CredentialsStorage::SessionInstance(); 1468 1469 // TODO: Using the message as key here is not so smart. 1470 HashKeyString key(message); 1471 1472 if (failureCount == 0) { 1473 if (persistentStorage->Contains(key)) { 1474 Credentials credentials = persistentStorage->GetCredentials(key); 1475 inOutUser = credentials.Username(); 1476 inOutPassword = credentials.Password(); 1477 return true; 1478 } else if (sessionStorage->Contains(key)) { 1479 Credentials credentials = sessionStorage->GetCredentials(key); 1480 inOutUser = credentials.Username(); 1481 inOutPassword = credentials.Password(); 1482 return true; 1483 } 1484 } 1485 // Switch to the page for which this authentication is required. 1486 if (!_ShowPage(view)) 1487 return false; 1488 1489 AuthenticationPanel* panel = new AuthenticationPanel(Frame()); 1490 // Panel auto-destructs. 1491 bool success = panel->getAuthentication(message, inOutUser, inOutPassword, 1492 inOutRememberCredentials, failureCount > 0, inOutUser, inOutPassword, 1493 &inOutRememberCredentials); 1494 if (success) { 1495 Credentials credentials(inOutUser, inOutPassword); 1496 if (inOutRememberCredentials) 1497 persistentStorage->PutCredentials(key, credentials); 1498 else 1499 sessionStorage->PutCredentials(key, credentials); 1500 } 1501 return success; 1502 } 1503 1504 1505 // #pragma mark - private 1506 1507 1508 void 1509 BrowserWindow::_UpdateTitle(const BString& title) 1510 { 1511 BString windowTitle = title; 1512 if (windowTitle.Length() > 0) 1513 windowTitle << " - "; 1514 windowTitle << kApplicationName; 1515 SetTitle(windowTitle.String()); 1516 } 1517 1518 1519 void 1520 BrowserWindow::_UpdateTabGroupVisibility() 1521 { 1522 if (Lock()) { 1523 if (fInterfaceVisible) 1524 fTabGroup->SetVisible(_TabGroupShouldBeVisible()); 1525 fTabManager->SetCloseButtonsAvailable(fTabManager->CountTabs() > 1); 1526 Unlock(); 1527 } 1528 } 1529 1530 1531 bool 1532 BrowserWindow::_TabGroupShouldBeVisible() const 1533 { 1534 return (fShowTabsIfSinglePageOpen || fTabManager->CountTabs() > 1) 1535 && (fVisibleInterfaceElements & INTERFACE_ELEMENT_TABS) != 0; 1536 } 1537 1538 1539 void 1540 BrowserWindow::_ShutdownTab(int32 index) 1541 { 1542 BView* view = fTabManager->RemoveTab(index); 1543 BWebView* webView = dynamic_cast<BWebView*>(view); 1544 if (webView == CurrentWebView()) 1545 SetCurrentWebView(NULL); 1546 if (webView != NULL) 1547 webView->Shutdown(); 1548 else 1549 delete view; 1550 } 1551 1552 1553 void 1554 BrowserWindow::_TabChanged(int32 index) 1555 { 1556 SetCurrentWebView(dynamic_cast<BWebView*>(fTabManager->ViewForTab(index))); 1557 } 1558 1559 1560 status_t 1561 BrowserWindow::_BookmarkPath(BPath& path) const 1562 { 1563 status_t ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 1564 if (ret != B_OK) 1565 return ret; 1566 1567 ret = path.Append(kApplicationName); 1568 if (ret != B_OK) 1569 return ret; 1570 1571 ret = path.Append("Bookmarks"); 1572 if (ret != B_OK) 1573 return ret; 1574 1575 return create_directory(path.Path(), 0777); 1576 } 1577 1578 1579 void 1580 BrowserWindow::_CreateBookmark() 1581 { 1582 BPath path; 1583 status_t status = _BookmarkPath(path); 1584 if (status != B_OK) { 1585 BString message(B_TRANSLATE_COMMENT("There was an error retrieving " 1586 "the bookmark folder.\n\nError: %error", "Don't translate the " 1587 "variable %error")); 1588 message.ReplaceFirst("%error", strerror(status)); 1589 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1590 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1591 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1592 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1593 alert->Go(); 1594 return; 1595 } 1596 BWebView* webView = CurrentWebView(); 1597 BString url(webView->MainFrameURL()); 1598 // Create a bookmark file 1599 BFile bookmarkFile; 1600 BString bookmarkName(webView->MainFrameTitle()); 1601 if (bookmarkName.Length() == 0) { 1602 bookmarkName = url; 1603 int32 leafPos = bookmarkName.FindLast('/'); 1604 if (leafPos >= 0) 1605 bookmarkName.Remove(0, leafPos + 1); 1606 } 1607 // Make sure the bookmark title does not contain chars that are not 1608 // allowed in file names. 1609 bookmarkName.ReplaceAll('/', '-'); 1610 1611 // Check that the bookmark exists nowhere in the bookmark hierarchy, 1612 // though the intended file name must match, we don't search the stored 1613 // URLs, only for matching file names. 1614 BDirectory directory(path.Path()); 1615 if (status == B_OK && _CheckBookmarkExists(directory, bookmarkName, url)) { 1616 BString message(B_TRANSLATE_COMMENT("A bookmark for this page " 1617 "(%bookmarkName) already exists.", "Don't translate variable " 1618 "%bookmarkName")); 1619 message.ReplaceFirst("%bookmarkName", bookmarkName); 1620 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark info"), 1621 message.String(), B_TRANSLATE("OK")); 1622 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1623 alert->Go(); 1624 return; 1625 } 1626 1627 BPath entryPath(path); 1628 status = entryPath.Append(bookmarkName); 1629 BEntry entry; 1630 if (status == B_OK) 1631 status = entry.SetTo(entryPath.Path(), true); 1632 if (status == B_OK) { 1633 int32 tries = 1; 1634 while (entry.Exists()) { 1635 // Find a unique name for the bookmark, there may still be a 1636 // file in the way that stores a different URL. 1637 bookmarkName = webView->MainFrameTitle(); 1638 bookmarkName << " " << tries++; 1639 entryPath = path; 1640 status = entryPath.Append(bookmarkName); 1641 if (status == B_OK) 1642 status = entry.SetTo(entryPath.Path(), true); 1643 if (status != B_OK) 1644 break; 1645 } 1646 } 1647 if (status == B_OK) { 1648 status = bookmarkFile.SetTo(&entry, 1649 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 1650 } 1651 1652 // Write bookmark meta data 1653 if (status == B_OK) 1654 status = bookmarkFile.WriteAttrString("META:url", &url); 1655 if (status == B_OK) { 1656 BString title = webView->MainFrameTitle(); 1657 bookmarkFile.WriteAttrString("META:title", &title); 1658 } 1659 1660 BNodeInfo nodeInfo(&bookmarkFile); 1661 if (status == B_OK) { 1662 status = nodeInfo.SetType("application/x-vnd.Be-bookmark"); 1663 if (status == B_OK) { 1664 PageUserData* userData = static_cast<PageUserData*>( 1665 webView->GetUserData()); 1666 if (userData != NULL && userData->PageIcon() != NULL) { 1667 BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK, 1668 B_CMAP8); 1669 status_t ret = miniIcon.ImportBits(userData->PageIcon()); 1670 if (ret == B_OK) 1671 ret = nodeInfo.SetIcon(&miniIcon, B_MINI_ICON); 1672 if (ret != B_OK) { 1673 fprintf(stderr, "Failed to store mini icon for bookmark: " 1674 "%s\n", strerror(ret)); 1675 } 1676 BBitmap largeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK, 1677 B_CMAP8); 1678 // TODO: Store 32x32 favicon which is often provided by sites. 1679 const uint8* src = (const uint8*)miniIcon.Bits(); 1680 uint32 srcBPR = miniIcon.BytesPerRow(); 1681 uint8* dst = (uint8*)largeIcon.Bits(); 1682 uint32 dstBPR = largeIcon.BytesPerRow(); 1683 for (uint32 y = 0; y < 16; y++) { 1684 const uint8* s = src; 1685 uint8* d = dst; 1686 for (uint32 x = 0; x < 16; x++) { 1687 *d++ = *s; 1688 *d++ = *s++; 1689 } 1690 dst += dstBPR; 1691 s = src; 1692 for (uint32 x = 0; x < 16; x++) { 1693 *d++ = *s; 1694 *d++ = *s++; 1695 } 1696 dst += dstBPR; 1697 src += srcBPR; 1698 } 1699 if (ret == B_OK) 1700 ret = nodeInfo.SetIcon(&largeIcon, B_LARGE_ICON); 1701 if (ret != B_OK) { 1702 fprintf(stderr, "Failed to store large icon for bookmark: " 1703 "%s\n", strerror(ret)); 1704 } 1705 } 1706 } 1707 } 1708 1709 if (status != B_OK) { 1710 BString message(B_TRANSLATE_COMMENT("There was an error creating the " 1711 "bookmark file.\n\nError: %error", "Don't translate variable " 1712 "%error")); 1713 message.ReplaceFirst("%error", strerror(status)); 1714 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1715 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1716 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1717 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1718 alert->Go(); 1719 return; 1720 } 1721 } 1722 1723 1724 void 1725 BrowserWindow::_ShowBookmarks() 1726 { 1727 BPath path; 1728 entry_ref ref; 1729 status_t status = _BookmarkPath(path); 1730 if (status == B_OK) 1731 status = get_ref_for_path(path.Path(), &ref); 1732 if (status == B_OK) 1733 status = be_roster->Launch(&ref); 1734 1735 if (status != B_OK && status != B_ALREADY_RUNNING) { 1736 BString message(B_TRANSLATE_COMMENT("There was an error trying to " 1737 "show the Bookmarks folder.\n\nError: %error", "Don't translate variable " 1738 "%error")); 1739 message.ReplaceFirst("%error", strerror(status)); 1740 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1741 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1742 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1743 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1744 alert->Go(); 1745 return; 1746 } 1747 } 1748 1749 1750 bool BrowserWindow::_CheckBookmarkExists(BDirectory& directory, 1751 const BString& bookmarkName, const BString& url) const 1752 { 1753 BEntry entry; 1754 while (directory.GetNextEntry(&entry) == B_OK) { 1755 if (entry.IsDirectory()) { 1756 BDirectory subBirectory(&entry); 1757 // At least preserve the entry file handle when recursing into 1758 // sub-folders... eventually we will run out, though, with very 1759 // deep hierarchy. 1760 entry.Unset(); 1761 if (_CheckBookmarkExists(subBirectory, bookmarkName, url)) 1762 return true; 1763 } else { 1764 char entryName[B_FILE_NAME_LENGTH]; 1765 if (entry.GetName(entryName) != B_OK || bookmarkName != entryName) 1766 continue; 1767 BString storedURL; 1768 BFile file(&entry, B_READ_ONLY); 1769 if (_ReadURLAttr(file, storedURL)) { 1770 // Just bail if the bookmark already exists 1771 if (storedURL == url) 1772 return true; 1773 } 1774 } 1775 } 1776 return false; 1777 } 1778 1779 1780 bool 1781 BrowserWindow::_ReadURLAttr(BFile& bookmarkFile, BString& url) const 1782 { 1783 return bookmarkFile.InitCheck() == B_OK 1784 && bookmarkFile.ReadAttrString("META:url", &url) == B_OK; 1785 } 1786 1787 1788 void 1789 BrowserWindow::_AddBookmarkURLsRecursively(BDirectory& directory, 1790 BMessage* message, uint32& addedCount) const 1791 { 1792 BEntry entry; 1793 while (directory.GetNextEntry(&entry) == B_OK) { 1794 if (entry.IsDirectory()) { 1795 BDirectory subBirectory(&entry); 1796 // At least preserve the entry file handle when recursing into 1797 // sub-folders... eventually we will run out, though, with very 1798 // deep hierarchy. 1799 entry.Unset(); 1800 _AddBookmarkURLsRecursively(subBirectory, message, addedCount); 1801 } else { 1802 BString storedURL; 1803 BFile file(&entry, B_READ_ONLY); 1804 if (_ReadURLAttr(file, storedURL)) { 1805 message->AddString("url", storedURL.String()); 1806 addedCount++; 1807 } 1808 } 1809 } 1810 } 1811 1812 1813 void 1814 BrowserWindow::_SetPageIcon(BWebView* view, const BBitmap* icon) 1815 { 1816 PageUserData* userData = static_cast<PageUserData*>(view->GetUserData()); 1817 if (userData == NULL) { 1818 userData = new(std::nothrow) PageUserData(NULL); 1819 if (userData == NULL) 1820 return; 1821 view->SetUserData(userData); 1822 } 1823 // The PageUserData makes a copy of the icon, which we pass on to 1824 // the TabManager for display in the respective tab. 1825 userData->SetPageIcon(icon); 1826 fTabManager->SetTabIcon(view, userData->PageIcon()); 1827 if (view == CurrentWebView()) 1828 fURLInputGroup->SetPageIcon(icon); 1829 } 1830 1831 1832 static void 1833 addItemToMenuOrSubmenu(BMenu* menu, BMenuItem* newItem) 1834 { 1835 BString baseURLLabel = baseURL(BString(newItem->Label())); 1836 for (int32 i = menu->CountItems() - 1; i >= 0; i--) { 1837 BMenuItem* item = menu->ItemAt(i); 1838 BString label = item->Label(); 1839 if (label.FindFirst(baseURLLabel) >= 0) { 1840 if (item->Submenu()) { 1841 // Submenu was already added in previous iteration. 1842 item->Submenu()->AddItem(newItem); 1843 return; 1844 } else { 1845 menu->RemoveItem(item); 1846 BMenu* subMenu = new BMenu(baseURLLabel.String()); 1847 subMenu->AddItem(item); 1848 subMenu->AddItem(newItem); 1849 // Add common submenu for this base URL, clickable. 1850 BMessage* message = new BMessage(GOTO_URL); 1851 message->AddString("url", baseURLLabel.String()); 1852 menu->AddItem(new BMenuItem(subMenu, message), i); 1853 return; 1854 } 1855 } 1856 } 1857 menu->AddItem(newItem); 1858 } 1859 1860 1861 static void 1862 addOrDeleteMenu(BMenu* menu, BMenu* toMenu) 1863 { 1864 if (menu->CountItems() > 0) 1865 toMenu->AddItem(menu); 1866 else 1867 delete menu; 1868 } 1869 1870 1871 void 1872 BrowserWindow::_UpdateHistoryMenu() 1873 { 1874 BMenuItem* menuItem; 1875 while ((menuItem = fHistoryMenu->RemoveItem(fHistoryMenuFixedItemCount))) 1876 delete menuItem; 1877 1878 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 1879 if (!history->Lock()) 1880 return; 1881 1882 int32 count = history->CountItems(); 1883 BMenuItem* clearHistoryItem = new BMenuItem(B_TRANSLATE("Clear history"), 1884 new BMessage(CLEAR_HISTORY)); 1885 clearHistoryItem->SetEnabled(count > 0); 1886 fHistoryMenu->AddItem(clearHistoryItem); 1887 if (count == 0) { 1888 history->Unlock(); 1889 return; 1890 } 1891 fHistoryMenu->AddSeparatorItem(); 1892 1893 BDateTime todayStart = BDateTime::CurrentDateTime(B_LOCAL_TIME); 1894 todayStart.SetTime(BTime(0, 0, 0)); 1895 1896 BDateTime oneDayAgoStart = todayStart; 1897 oneDayAgoStart.Date().AddDays(-1); 1898 1899 BDateTime twoDaysAgoStart = oneDayAgoStart; 1900 twoDaysAgoStart.Date().AddDays(-1); 1901 1902 BDateTime threeDaysAgoStart = twoDaysAgoStart; 1903 threeDaysAgoStart.Date().AddDays(-1); 1904 1905 BDateTime fourDaysAgoStart = threeDaysAgoStart; 1906 fourDaysAgoStart.Date().AddDays(-1); 1907 1908 BDateTime fiveDaysAgoStart = fourDaysAgoStart; 1909 fiveDaysAgoStart.Date().AddDays(-1); 1910 1911 BMenu* todayMenu = new BMenu(B_TRANSLATE("Today")); 1912 BMenu* yesterdayMenu = new BMenu(B_TRANSLATE("Yesterday")); 1913 BMenu* twoDaysAgoMenu = new BMenu( 1914 twoDaysAgoStart.Date().LongDayName().String()); 1915 BMenu* threeDaysAgoMenu = new BMenu( 1916 threeDaysAgoStart.Date().LongDayName().String()); 1917 BMenu* fourDaysAgoMenu = new BMenu( 1918 fourDaysAgoStart.Date().LongDayName().String()); 1919 BMenu* fiveDaysAgoMenu = new BMenu( 1920 fiveDaysAgoStart.Date().LongDayName().String()); 1921 BMenu* earlierMenu = new BMenu(B_TRANSLATE("Earlier")); 1922 1923 for (int32 i = 0; i < count; i++) { 1924 BrowsingHistoryItem historyItem = history->HistoryItemAt(i); 1925 BMessage* message = new BMessage(GOTO_URL); 1926 message->AddString("url", historyItem.URL().String()); 1927 1928 BString truncatedUrl(historyItem.URL()); 1929 be_plain_font->TruncateString(&truncatedUrl, B_TRUNCATE_END, 480); 1930 menuItem = new BMenuItem(truncatedUrl, message); 1931 1932 if (historyItem.DateTime() < fiveDaysAgoStart) 1933 addItemToMenuOrSubmenu(earlierMenu, menuItem); 1934 else if (historyItem.DateTime() < fourDaysAgoStart) 1935 addItemToMenuOrSubmenu(fiveDaysAgoMenu, menuItem); 1936 else if (historyItem.DateTime() < threeDaysAgoStart) 1937 addItemToMenuOrSubmenu(fourDaysAgoMenu, menuItem); 1938 else if (historyItem.DateTime() < twoDaysAgoStart) 1939 addItemToMenuOrSubmenu(threeDaysAgoMenu, menuItem); 1940 else if (historyItem.DateTime() < oneDayAgoStart) 1941 addItemToMenuOrSubmenu(twoDaysAgoMenu, menuItem); 1942 else if (historyItem.DateTime() < todayStart) 1943 addItemToMenuOrSubmenu(yesterdayMenu, menuItem); 1944 else 1945 addItemToMenuOrSubmenu(todayMenu, menuItem); 1946 } 1947 history->Unlock(); 1948 1949 addOrDeleteMenu(todayMenu, fHistoryMenu); 1950 addOrDeleteMenu(yesterdayMenu, fHistoryMenu); 1951 addOrDeleteMenu(twoDaysAgoMenu, fHistoryMenu); 1952 addOrDeleteMenu(fourDaysAgoMenu, fHistoryMenu); 1953 addOrDeleteMenu(fiveDaysAgoMenu, fHistoryMenu); 1954 addOrDeleteMenu(earlierMenu, fHistoryMenu); 1955 } 1956 1957 1958 void 1959 BrowserWindow::_UpdateClipboardItems() 1960 { 1961 BTextView* focusTextView = dynamic_cast<BTextView*>(CurrentFocus()); 1962 if (focusTextView != NULL) { 1963 int32 selectionStart; 1964 int32 selectionEnd; 1965 focusTextView->GetSelection(&selectionStart, &selectionEnd); 1966 bool hasSelection = selectionStart < selectionEnd; 1967 bool canPaste = false; 1968 // A BTextView has the focus. 1969 if (be_clipboard->Lock()) { 1970 BMessage* data = be_clipboard->Data(); 1971 if (data != NULL) 1972 canPaste = data->HasData("text/plain", B_MIME_TYPE); 1973 be_clipboard->Unlock(); 1974 } 1975 fCutMenuItem->SetEnabled(hasSelection); 1976 fCopyMenuItem->SetEnabled(hasSelection); 1977 fPasteMenuItem->SetEnabled(canPaste); 1978 } else if (CurrentWebView() != NULL) { 1979 // Trigger update of the clipboard items, even if the 1980 // BWebView doesn't have focus, we'll dispatch these message 1981 // there anyway. This works so fast that the user can never see 1982 // the wrong enabled state when the menu opens until the result 1983 // message arrives. The initial state needs to be enabled, since 1984 // standard shortcut handling is always wrapped inside MenusBeginning() 1985 // and MenusEnded(), and since we update items asynchronously, we need 1986 // to have them enabled to begin with. 1987 fCutMenuItem->SetEnabled(true); 1988 fCopyMenuItem->SetEnabled(true); 1989 fPasteMenuItem->SetEnabled(true); 1990 1991 CurrentWebView()->WebPage()->SendEditingCapabilities(); 1992 } 1993 } 1994 1995 1996 bool 1997 BrowserWindow::_ShowPage(BWebView* view) 1998 { 1999 if (view != CurrentWebView()) { 2000 int32 tabIndex = fTabManager->TabForView(view); 2001 if (tabIndex < 0) { 2002 // Page seems to be gone already? 2003 return false; 2004 } 2005 fTabManager->SelectTab(tabIndex); 2006 _TabChanged(tabIndex); 2007 UpdateIfNeeded(); 2008 } 2009 return true; 2010 } 2011 2012 2013 void 2014 BrowserWindow::_ResizeToScreen() 2015 { 2016 BScreen screen(this); 2017 MoveTo(0, 0); 2018 ResizeTo(screen.Frame().Width(), screen.Frame().Height()); 2019 } 2020 2021 2022 void 2023 BrowserWindow::_SetAutoHideInterfaceInFullscreen(bool doIt) 2024 { 2025 if (fAutoHideInterfaceInFullscreenMode == doIt) 2026 return; 2027 2028 fAutoHideInterfaceInFullscreenMode = doIt; 2029 if (fAppSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, 2030 doIt) != doIt) { 2031 fAppSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, 2032 doIt); 2033 } 2034 2035 if (fAutoHideInterfaceInFullscreenMode) { 2036 BMessage message(CHECK_AUTO_HIDE_INTERFACE); 2037 fPulseRunner = new BMessageRunner(BMessenger(this), &message, 300000); 2038 } else { 2039 delete fPulseRunner; 2040 fPulseRunner = NULL; 2041 _ShowInterface(true); 2042 } 2043 } 2044 2045 2046 void 2047 BrowserWindow::_CheckAutoHideInterface() 2048 { 2049 if (!fIsFullscreen || !fAutoHideInterfaceInFullscreenMode 2050 || (CurrentWebView() != NULL && !CurrentWebView()->IsFocus())) { 2051 return; 2052 } 2053 2054 if (fLastMousePos.y == 0) 2055 _ShowInterface(true); 2056 else if (fNavigationGroup->IsVisible() 2057 && fLastMousePos.y > fNavigationGroup->Frame().bottom 2058 && system_time() - fLastMouseMovedTime > 1000000) { 2059 // NOTE: Do not re-use navigationGroupBottom in the above 2060 // check, since we only want to hide the interface when it is visible. 2061 _ShowInterface(false); 2062 } 2063 } 2064 2065 2066 void 2067 BrowserWindow::_ShowInterface(bool show) 2068 { 2069 if (fInterfaceVisible == show) 2070 return; 2071 2072 fInterfaceVisible = show; 2073 2074 if (show) { 2075 #if !INTEGRATE_MENU_INTO_TAB_BAR 2076 fMenuGroup->SetVisible( 2077 (fVisibleInterfaceElements & INTERFACE_ELEMENT_MENU) != 0); 2078 #endif 2079 fTabGroup->SetVisible(_TabGroupShouldBeVisible()); 2080 fNavigationGroup->SetVisible( 2081 (fVisibleInterfaceElements & INTERFACE_ELEMENT_NAVIGATION) != 0); 2082 fStatusGroup->SetVisible( 2083 (fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0); 2084 } else { 2085 fMenuGroup->SetVisible(false); 2086 fTabGroup->SetVisible(false); 2087 fNavigationGroup->SetVisible(false); 2088 fStatusGroup->SetVisible(false); 2089 } 2090 // TODO: Setting the group visible seems to unhide the status bar. 2091 // Fix in Haiku? 2092 while (!fLoadingProgressBar->IsHidden()) 2093 fLoadingProgressBar->Hide(); 2094 } 2095 2096 2097 void 2098 BrowserWindow::_ShowProgressBar(bool show) 2099 { 2100 if (show) { 2101 if (!fStatusGroup->IsVisible() && (fVisibleInterfaceElements 2102 & INTERFACE_ELEMENT_STATUS) != 0) 2103 fStatusGroup->SetVisible(true); 2104 fLoadingProgressBar->Show(); 2105 } else { 2106 if (!fInterfaceVisible) 2107 fStatusGroup->SetVisible(false); 2108 // TODO: This is also used in _ShowInterface. Without it the status bar 2109 // doesn't always hide again. It may be an Interface Kit bug. 2110 while (!fLoadingProgressBar->IsHidden()) 2111 fLoadingProgressBar->Hide(); 2112 } 2113 } 2114 2115 2116 void 2117 BrowserWindow::_InvokeButtonVisibly(BButton* button) 2118 { 2119 button->SetValue(B_CONTROL_ON); 2120 UpdateIfNeeded(); 2121 button->Invoke(); 2122 snooze(1000); 2123 button->SetValue(B_CONTROL_OFF); 2124 } 2125 2126 2127 BString 2128 BrowserWindow::_NewTabURL(bool isNewWindow) const 2129 { 2130 BString url; 2131 uint32 policy = isNewWindow ? fNewWindowPolicy : fNewTabPolicy; 2132 // Implement new page policy 2133 switch (policy) { 2134 case OpenStartPage: 2135 url = fStartPageURL; 2136 break; 2137 case OpenSearchPage: 2138 url = fSearchPageURL; 2139 break; 2140 case CloneCurrentPage: 2141 if (CurrentWebView() != NULL) 2142 url = CurrentWebView()->MainFrameURL(); 2143 break; 2144 case OpenBlankPage: 2145 default: 2146 break; 2147 } 2148 return url; 2149 } 2150 2151 2152 BString 2153 BrowserWindow::_SmartURLHandler(const BString& url) const 2154 { 2155 BString result = url; 2156 2157 // Only process if this doesn't look like a full URL (http:// or 2158 // file://, etc.) 2159 if (url.FindFirst("://") == B_ERROR) { 2160 if (url.FindFirst(".") == B_ERROR || url.FindFirst(" ") != B_ERROR) 2161 result.Prepend("http://www.google.com/search?q="); 2162 } 2163 return result; 2164 } 2165 2166 2167 void 2168 BrowserWindow::_HandlePageSourceResult(const BMessage* message) 2169 { 2170 // TODO: This should be done in an extra thread perhaps. Doing it in 2171 // the application thread is not much better, since it actually draws 2172 // the pages... 2173 2174 BPath pathToPageSource; 2175 2176 BString url; 2177 status_t ret = message->FindString("url", &url); 2178 if (ret == B_OK && url.FindFirst("file://") == 0) { 2179 // Local file 2180 url.Remove(0, strlen("file://")); 2181 pathToPageSource.SetTo(url.String()); 2182 } else { 2183 // Something else, store it. 2184 // TODO: What if it isn't HTML, but for example SVG? 2185 BString source; 2186 ret = message->FindString("source", &source); 2187 2188 if (ret == B_OK) 2189 ret = find_directory(B_COMMON_TEMP_DIRECTORY, &pathToPageSource); 2190 2191 BString tmpFileName("PageSource_"); 2192 tmpFileName << system_time() << ".html"; 2193 if (ret == B_OK) 2194 ret = pathToPageSource.Append(tmpFileName.String()); 2195 2196 BFile pageSourceFile(pathToPageSource.Path(), 2197 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 2198 if (ret == B_OK) 2199 ret = pageSourceFile.InitCheck(); 2200 2201 if (ret == B_OK) { 2202 ssize_t written = pageSourceFile.Write(source.String(), 2203 source.Length()); 2204 if (written != source.Length()) 2205 ret = (status_t)written; 2206 } 2207 2208 if (ret == B_OK) { 2209 const char* type = "text/html"; 2210 size_t size = strlen(type); 2211 pageSourceFile.WriteAttr("BEOS:TYPE", B_STRING_TYPE, 0, type, size); 2212 // If it fails we don't care. 2213 } 2214 } 2215 2216 entry_ref ref; 2217 if (ret == B_OK) 2218 ret = get_ref_for_path(pathToPageSource.Path(), &ref); 2219 2220 if (ret == B_OK) { 2221 BMessage refsMessage(B_REFS_RECEIVED); 2222 ret = refsMessage.AddRef("refs", &ref); 2223 if (ret == B_OK) { 2224 ret = be_roster->Launch("text/x-source-code", &refsMessage); 2225 if (ret == B_ALREADY_RUNNING) 2226 ret = B_OK; 2227 } 2228 } 2229 2230 if (ret != B_OK) { 2231 char buffer[1024]; 2232 snprintf(buffer, sizeof(buffer), "Failed to show the " 2233 "page source: %s\n", strerror(ret)); 2234 BAlert* alert = new BAlert(B_TRANSLATE("Page source error"), buffer, 2235 B_TRANSLATE("OK")); 2236 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2237 alert->Go(NULL); 2238 } 2239 } 2240 2241 2242