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