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