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