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-2015 Haiku, Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
32 #include "BrowserWindow.h"
35 #include <Application.h>
40 #include <Clipboard.h>
41 #include <ControlLook.h>
43 #include <Directory.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>
53 #include <LayoutBuilder.h>
55 #include <ObjectList.h>
58 #include <MessageRunner.h>
60 #include <NodeMonitor.h>
64 #include <SeparatorView.h>
66 #include <SpaceLayoutItem.h>
67 #include <StatusBar.h>
68 #include <StringView.h>
69 #include <TextControl.h>
70 #include <UnicodeChar.h>
76 #include "AuthenticationPanel.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"
85 #include "SettingsKeys.h"
86 #include "SettingsMessage.h"
87 #include "TabManager.h"
88 #include "URLInputGroup.h"
91 #include "WebViewConstants.h"
92 #include "WindowIcon.h"
95 #undef B_TRANSLATION_CONTEXT
96 #define B_TRANSLATION_CONTEXT "WebPositive Window"
100 OPEN_LOCATION
= 'open',
108 SHOW_HIDE_BOOKMARK_BAR
= 'shbb',
109 CLEAR_HISTORY
= 'clhs',
111 CREATE_BOOKMARK
= 'crbm',
112 SHOW_BOOKMARKS
= 'shbm',
114 ZOOM_FACTOR_INCREASE
= 'zfin',
115 ZOOM_FACTOR_DECREASE
= 'zfdc',
116 ZOOM_FACTOR_RESET
= 'zfrs',
117 ZOOM_TEXT_ONLY
= 'zfto',
119 TOGGLE_FULLSCREEN
= 'tgfs',
120 TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN
= 'tgah',
121 CHECK_AUTO_HIDE_INTERFACE
= 'cahi',
123 SHOW_PAGE_SOURCE
= 'spgs',
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',
135 static const int32 kModifiers
= B_SHIFT_KEY
| B_COMMAND_KEY
136 | B_CONTROL_KEY
| B_OPTION_KEY
| B_MENU_KEY
;
139 static const char* kHandledProtocols
[] = {
150 layoutItemFor(BView
* view
)
152 BLayout
* layout
= view
->Parent()->GetLayout();
153 int32 index
= layout
->IndexOfView(view
);
154 return layout
->ItemAt(index
);
158 class BookmarkMenu
: public BNavMenu
{
160 BookmarkMenu(const char* title
, BHandler
* target
, const entry_ref
* navDir
)
162 BNavMenu(title
, B_REFS_RECEIVED
, target
)
164 // Add these items here already, so the shortcuts work even when
165 // the menu has never been opened yet.
171 virtual void AttachedToWindow()
173 RemoveItems(0, CountItems(), true);
175 BNavMenu::AttachedToWindow();
176 if (CountItems() > 0)
177 AddItem(new BSeparatorItem(), 0);
183 void _AddStaticItems()
185 AddItem(new BMenuItem(B_TRANSLATE("Manage bookmarks"),
186 new BMessage(SHOW_BOOKMARKS
), 'M'), 0);
187 AddItem(new BMenuItem(B_TRANSLATE("Bookmark this page"),
188 new BMessage(CREATE_BOOKMARK
), 'B'), 0);
193 class PageUserData
: public BWebView::UserData
{
195 PageUserData(BView
* focusedView
)
197 fFocusedView(focusedView
),
199 fURLInputSelectionStart(-1),
200 fURLInputSelectionEnd(-1)
209 void SetFocusedView(BView
* focusedView
)
211 fFocusedView
= focusedView
;
214 BView
* FocusedView() const
219 void SetPageIcon(const BBitmap
* icon
)
223 fPageIcon
= new BBitmap(icon
);
228 const BBitmap
* PageIcon() const
233 void SetURLInputContents(const char* text
)
235 fURLInputContents
= text
;
238 const BString
& URLInputContents() const
240 return fURLInputContents
;
243 void SetURLInputSelection(int32 selectionStart
, int32 selectionEnd
)
245 fURLInputSelectionStart
= selectionStart
;
246 fURLInputSelectionEnd
= selectionEnd
;
249 int32
URLInputSelectionStart() const
251 return fURLInputSelectionStart
;
254 int32
URLInputSelectionEnd() const
256 return fURLInputSelectionEnd
;
262 BString fURLInputContents
;
263 int32 fURLInputSelectionStart
;
264 int32 fURLInputSelectionEnd
;
268 class CloseButton
: public BButton
{
270 CloseButton(BMessage
* message
)
272 BButton("close button", NULL
, message
),
273 fOverCloseRect(false)
275 // Button is 16x16 regardless of font size
276 SetExplicitMinSize(BSize(15, 15));
277 SetExplicitMaxSize(BSize(15, 15));
280 virtual void Draw(BRect updateRect
)
282 BRect frame
= Bounds();
283 BRect
closeRect(frame
.InsetByCopy(4, 4));
284 rgb_color base
= ui_color(B_PANEL_BACKGROUND_COLOR
);
285 float tint
= B_DARKEN_1_TINT
;
292 if (Value() == B_CONTROL_ON
&& fOverCloseRect
) {
293 // Draw the button frame
294 be_control_look
->DrawButtonFrame(this, frame
, updateRect
,
295 base
, base
, BControlLook::B_ACTIVATED
296 | BControlLook::B_BLEND_FRAME
);
297 be_control_look
->DrawButtonBackground(this, frame
,
298 updateRect
, base
, BControlLook::B_ACTIVATED
);
299 closeRect
.OffsetBy(1, 1);
303 FillRect(updateRect
);
307 base
= tint_color(base
, tint
);
310 StrokeLine(closeRect
.LeftTop(), closeRect
.RightBottom());
311 StrokeLine(closeRect
.LeftBottom(), closeRect
.RightTop());
315 virtual void MouseMoved(BPoint where
, uint32 transit
,
316 const BMessage
* dragMessage
)
320 fOverCloseRect
= true;
324 fOverCloseRect
= false;
328 fOverCloseRect
= true;
331 fOverCloseRect
= false;
335 BButton::MouseMoved(where
, transit
, dragMessage
);
343 // #pragma mark - BrowserWindow
346 BrowserWindow::BrowserWindow(BRect frame
, SettingsMessage
* appSettings
,
347 const BString
& url
, BUrlContext
* context
, uint32 interfaceElements
,
350 BWebWindow(frame
, kApplicationName
,
351 B_DOCUMENT_WINDOW_LOOK
, B_NORMAL_WINDOW_FEEL
,
352 B_AUTO_UPDATE_SIZE_LIMITS
| B_ASYNCHRONOUS_CONTROLS
),
353 fIsFullscreen(false),
354 fInterfaceVisible(false),
355 fMenusRunning(false),
357 fVisibleInterfaceElements(interfaceElements
),
359 fAppSettings(appSettings
),
361 fShowTabsIfSinglePageOpen(true),
362 fAutoHideInterfaceInFullscreenMode(false),
363 fAutoHidePointer(false),
366 // Begin listening to settings changes and read some current values.
367 fAppSettings
->AddListener(BMessenger(this));
368 // fZoomTextOnly = fAppSettings->GetValue("zoom text only", fZoomTextOnly);
369 fShowTabsIfSinglePageOpen
= fAppSettings
->GetValue(
370 kSettingsKeyShowTabsIfSinglePageOpen
, fShowTabsIfSinglePageOpen
);
372 fAutoHidePointer
= fAppSettings
->GetValue(kSettingsKeyAutoHidePointer
,
375 fNewWindowPolicy
= fAppSettings
->GetValue(kSettingsKeyNewWindowPolicy
,
376 (uint32
)OpenStartPage
);
377 fNewTabPolicy
= fAppSettings
->GetValue(kSettingsKeyNewTabPolicy
,
378 (uint32
)OpenBlankPage
);
379 fStartPageURL
= fAppSettings
->GetValue(kSettingsKeyStartPageURL
,
380 kDefaultStartPageURL
);
381 fSearchPageURL
= fAppSettings
->GetValue(kSettingsKeySearchPageURL
,
382 kDefaultSearchPageURL
);
384 // Create the interface elements
385 BMessage
* newTabMessage
= new BMessage(NEW_TAB
);
386 newTabMessage
->AddString("url", "");
387 newTabMessage
->AddPointer("window", this);
388 newTabMessage
->AddBool("select", true);
389 fTabManager
= new TabManager(BMessenger(this), newTabMessage
);
392 #if INTEGRATE_MENU_INTO_TAB_BAR
393 BMenu
* mainMenu
= fTabManager
->Menu();
395 BMenu
* mainMenu
= new BMenuBar("Main menu");
397 BMenu
* menu
= new BMenu(B_TRANSLATE("Window"));
398 BMessage
* newWindowMessage
= new BMessage(NEW_WINDOW
);
399 newWindowMessage
->AddString("url", "");
400 BMenuItem
* newItem
= new BMenuItem(B_TRANSLATE("New window"),
401 newWindowMessage
, 'N');
402 menu
->AddItem(newItem
);
403 newItem
->SetTarget(be_app
);
404 newItem
= new BMenuItem(B_TRANSLATE("New tab"),
405 new BMessage(*newTabMessage
), 'T');
406 menu
->AddItem(newItem
);
407 newItem
->SetTarget(be_app
);
408 menu
->AddItem(new BMenuItem(B_TRANSLATE("Open location"),
409 new BMessage(OPEN_LOCATION
), 'L'));
410 menu
->AddSeparatorItem();
411 menu
->AddItem(new BMenuItem(B_TRANSLATE("Close window"),
412 new BMessage(B_QUIT_REQUESTED
), 'W', B_SHIFT_KEY
));
413 menu
->AddItem(new BMenuItem(B_TRANSLATE("Close tab"),
414 new BMessage(CLOSE_TAB
), 'W'));
415 menu
->AddItem(new BMenuItem(B_TRANSLATE("Save page as" B_UTF8_ELLIPSIS
),
416 new BMessage(SAVE_PAGE
), 'S'));
417 menu
->AddSeparatorItem();
418 menu
->AddItem(new BMenuItem(B_TRANSLATE("Downloads"),
419 new BMessage(SHOW_DOWNLOAD_WINDOW
), 'D'));
420 menu
->AddItem(new BMenuItem(B_TRANSLATE("Settings"),
421 new BMessage(SHOW_SETTINGS_WINDOW
)));
422 menu
->AddItem(new BMenuItem(B_TRANSLATE("Cookie manager"),
423 new BMessage(SHOW_COOKIE_WINDOW
)));
424 menu
->AddItem(new BMenuItem(B_TRANSLATE("Script console"),
425 new BMessage(SHOW_CONSOLE_WINDOW
)));
426 BMenuItem
* aboutItem
= new BMenuItem(B_TRANSLATE("About"),
427 new BMessage(B_ABOUT_REQUESTED
));
428 menu
->AddItem(aboutItem
);
429 aboutItem
->SetTarget(be_app
);
430 menu
->AddSeparatorItem();
431 BMenuItem
* quitItem
= new BMenuItem(B_TRANSLATE("Quit"),
432 new BMessage(B_QUIT_REQUESTED
), 'Q');
433 menu
->AddItem(quitItem
);
434 quitItem
->SetTarget(be_app
);
435 mainMenu
->AddItem(menu
);
437 menu
= new BMenu(B_TRANSLATE("Edit"));
438 menu
->AddItem(fCutMenuItem
= new BMenuItem(B_TRANSLATE("Cut"),
439 new BMessage(B_CUT
), 'X'));
440 menu
->AddItem(fCopyMenuItem
= new BMenuItem(B_TRANSLATE("Copy"),
441 new BMessage(B_COPY
), 'C'));
442 menu
->AddItem(fPasteMenuItem
= new BMenuItem(B_TRANSLATE("Paste"),
443 new BMessage(B_PASTE
), 'V'));
444 menu
->AddSeparatorItem();
445 menu
->AddItem(new BMenuItem(B_TRANSLATE("Find"),
446 new BMessage(EDIT_SHOW_FIND_GROUP
), 'F'));
447 menu
->AddItem(fFindPreviousMenuItem
448 = new BMenuItem(B_TRANSLATE("Find previous"),
449 new BMessage(EDIT_FIND_PREVIOUS
), 'G', B_SHIFT_KEY
));
450 menu
->AddItem(fFindNextMenuItem
= new BMenuItem(B_TRANSLATE("Find next"),
451 new BMessage(EDIT_FIND_NEXT
), 'G'));
452 mainMenu
->AddItem(menu
);
453 fFindPreviousMenuItem
->SetEnabled(false);
454 fFindNextMenuItem
->SetEnabled(false);
456 menu
= new BMenu(B_TRANSLATE("View"));
457 menu
->AddItem(new BMenuItem(B_TRANSLATE("Reload"), new BMessage(RELOAD
),
459 // the label will be replaced with the appropriate text later on
460 fBookmarkBarMenuItem
= new BMenuItem(B_TRANSLATE("Show bookmark bar"),
461 new BMessage(SHOW_HIDE_BOOKMARK_BAR
));
462 menu
->AddItem(fBookmarkBarMenuItem
);
463 menu
->AddSeparatorItem();
464 menu
->AddItem(new BMenuItem(B_TRANSLATE("Increase size"),
465 new BMessage(ZOOM_FACTOR_INCREASE
), '+'));
466 menu
->AddItem(new BMenuItem(B_TRANSLATE("Decrease size"),
467 new BMessage(ZOOM_FACTOR_DECREASE
), '-'));
468 menu
->AddItem(new BMenuItem(B_TRANSLATE("Reset size"),
469 new BMessage(ZOOM_FACTOR_RESET
), '0'));
470 fZoomTextOnlyMenuItem
= new BMenuItem(B_TRANSLATE("Zoom text only"),
471 new BMessage(ZOOM_TEXT_ONLY
));
472 fZoomTextOnlyMenuItem
->SetMarked(fZoomTextOnly
);
473 menu
->AddItem(fZoomTextOnlyMenuItem
);
475 menu
->AddSeparatorItem();
476 fFullscreenItem
= new BMenuItem(B_TRANSLATE("Full screen"),
477 new BMessage(TOGGLE_FULLSCREEN
), B_RETURN
);
478 menu
->AddItem(fFullscreenItem
);
479 menu
->AddItem(new BMenuItem(B_TRANSLATE("Page source"),
480 new BMessage(SHOW_PAGE_SOURCE
), 'U'));
481 mainMenu
->AddItem(menu
);
483 fHistoryMenu
= new BMenu(B_TRANSLATE("History"));
484 fHistoryMenu
->AddItem(fBackMenuItem
= new BMenuItem(B_TRANSLATE("Back"),
485 new BMessage(GO_BACK
), B_LEFT_ARROW
));
486 fHistoryMenu
->AddItem(fForwardMenuItem
487 = new BMenuItem(B_TRANSLATE("Forward"), new BMessage(GO_FORWARD
),
489 fHistoryMenu
->AddSeparatorItem();
490 fHistoryMenuFixedItemCount
= fHistoryMenu
->CountItems();
491 mainMenu
->AddItem(fHistoryMenu
);
494 entry_ref bookmarkRef
;
495 if (_BookmarkPath(bookmarkPath
) == B_OK
496 && get_ref_for_path(bookmarkPath
.Path(), &bookmarkRef
) == B_OK
) {
498 = new BookmarkMenu(B_TRANSLATE("Bookmarks"), this, &bookmarkRef
);
499 mainMenu
->AddItem(bookmarkMenu
);
501 BDirectory
barDir(&bookmarkRef
);
502 BEntry
bookmarkBar(&barDir
, "Bookmark bar");
503 entry_ref bookmarkBarRef
;
504 // TODO we could also check if the folder is empty here.
505 if (bookmarkBar
.Exists() && bookmarkBar
.GetRef(&bookmarkBarRef
)
507 fBookmarkBar
= new BookmarkBar("Bookmarks", this, &bookmarkBarRef
);
508 fBookmarkBarMenuItem
->SetEnabled(true);
510 fBookmarkBarMenuItem
->SetEnabled(false);
512 fBookmarkBarMenuItem
->SetEnabled(false);
514 // Back, Forward, Stop & Home buttons
515 fBackButton
= new BIconButton("Back", NULL
, new BMessage(GO_BACK
));
516 fBackButton
->SetIcon(201);
517 fBackButton
->TrimIcon();
519 fForwardButton
= new BIconButton("Forward", NULL
, new BMessage(GO_FORWARD
));
520 fForwardButton
->SetIcon(202);
521 fForwardButton
->TrimIcon();
523 fStopButton
= new BIconButton("Stop", NULL
, new BMessage(STOP
));
524 fStopButton
->SetIcon(204);
525 fStopButton
->TrimIcon();
527 fHomeButton
= new BIconButton("Home", NULL
, new BMessage(HOME
));
528 fHomeButton
->SetIcon(206);
529 fHomeButton
->TrimIcon();
530 if (!fAppSettings
->GetValue(kSettingsKeyShowHomeButton
, true))
534 fURLInputGroup
= new URLInputGroup(new BMessage(GOTO_URL
));
537 fStatusText
= new BStringView("status", "");
538 fStatusText
->SetAlignment(B_ALIGN_LEFT
);
539 fStatusText
->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
540 fStatusText
->SetExplicitMinSize(BSize(150, 12));
541 // Prevent the window from growing to fit a long status message...
542 BFont
font(be_plain_font
);
543 font
.SetSize(ceilf(font
.Size() * 0.8));
544 fStatusText
->SetFont(&font
, B_FONT_SIZE
);
546 // Loading progress bar
547 fLoadingProgressBar
= new BStatusBar("progress");
548 fLoadingProgressBar
->SetMaxValue(100);
549 fLoadingProgressBar
->Hide();
550 fLoadingProgressBar
->SetBarHeight(12);
552 const float kInsetSpacing
= 3;
553 const float kElementSpacing
= 5;
556 fFindCloseButton
= new CloseButton(new BMessage(EDIT_HIDE_FIND_GROUP
));
557 fFindTextControl
= new BTextControl("find", B_TRANSLATE("Find:"), "", NULL
);
558 fFindTextControl
->SetModificationMessage(new BMessage(FIND_TEXT_CHANGED
));
559 fFindPreviousButton
= new BButton(B_TRANSLATE("Previous"),
560 new BMessage(EDIT_FIND_PREVIOUS
));
561 fFindPreviousButton
->SetToolTip(
562 B_TRANSLATE_COMMENT("Find previous occurrence of search terms",
563 "find bar previous button tooltip"));
564 fFindNextButton
= new BButton(B_TRANSLATE("Next"),
565 new BMessage(EDIT_FIND_NEXT
));
566 fFindNextButton
->SetToolTip(
567 B_TRANSLATE_COMMENT("Find next occurrence of search terms",
568 "find bar next button tooltip"));
569 fFindCaseSensitiveCheckBox
= new BCheckBox(B_TRANSLATE("Match case"));
570 BGroupLayout
* findGroup
= BLayoutBuilder::Group
<>(B_VERTICAL
, 0.0)
571 .Add(new BSeparatorView(B_HORIZONTAL
, B_PLAIN_BORDER
))
572 .Add(BGroupLayoutBuilder(B_HORIZONTAL
, B_USE_SMALL_SPACING
)
573 .Add(fFindCloseButton
)
574 .Add(fFindTextControl
)
575 .Add(fFindPreviousButton
)
576 .Add(fFindNextButton
)
577 .Add(fFindCaseSensitiveCheckBox
)
578 .SetInsets(kInsetSpacing
, kInsetSpacing
,
579 kInsetSpacing
, kInsetSpacing
)
584 BGroupLayout
* navigationGroup
= BLayoutBuilder::Group
<>(B_VERTICAL
, 0.0)
585 .Add(BLayoutBuilder::Group
<>(B_HORIZONTAL
, kElementSpacing
)
591 .SetInsets(kInsetSpacing
, kInsetSpacing
, kInsetSpacing
,
594 .Add(new BSeparatorView(B_HORIZONTAL
, B_PLAIN_BORDER
))
598 BGroupLayout
* statusGroup
= BLayoutBuilder::Group
<>(B_VERTICAL
, 0.0)
599 .Add(new BSeparatorView(B_HORIZONTAL
, B_PLAIN_BORDER
))
600 .Add(BLayoutBuilder::Group
<>(B_HORIZONTAL
, kElementSpacing
)
602 .Add(fLoadingProgressBar
, 0.2)
603 .AddStrut(12 - kElementSpacing
)
604 .SetInsets(kInsetSpacing
, 0, kInsetSpacing
, 0)
608 BitmapButton
* toggleFullscreenButton
= new BitmapButton(kWindowIconBits
,
609 kWindowIconWidth
, kWindowIconHeight
, kWindowIconFormat
,
610 new BMessage(TOGGLE_FULLSCREEN
));
611 toggleFullscreenButton
->SetBackgroundMode(BitmapButton::MENUBAR_BACKGROUND
);
613 BGroupLayout
* menuBarGroup
= BLayoutBuilder::Group
<>(B_HORIZONTAL
, 0.0)
615 .Add(toggleFullscreenButton
, 0.0f
)
618 if (fAppSettings
->GetValue(kSettingsShowBookmarkBar
, true))
619 _ShowBookmarkBar(true);
621 _ShowBookmarkBar(false);
623 fSavePanel
= new BFilePanel(B_SAVE_PANEL
, new BMessenger(this), NULL
, 0,
627 BGroupView
* topView
= new BGroupView(B_VERTICAL
, 0.0);
629 #if !INTEGRATE_MENU_INTO_TAB_BAR
630 topView
->AddChild(menuBarGroup
);
632 topView
->AddChild(fTabManager
->TabGroup());
633 topView
->AddChild(navigationGroup
);
634 if (fBookmarkBar
!= NULL
)
635 topView
->AddChild(fBookmarkBar
);
636 topView
->AddChild(fTabManager
->ContainerView());
637 topView
->AddChild(findGroup
);
638 topView
->AddChild(statusGroup
);
642 fURLInputGroup
->MakeFocus(true);
644 fMenuGroup
= menuBarGroup
;
645 fTabGroup
= fTabManager
->TabGroup()->GetLayout();
646 fNavigationGroup
= navigationGroup
;
647 fFindGroup
= findGroup
;
648 fStatusGroup
= statusGroup
;
649 fToggleFullscreenButton
= layoutItemFor(toggleFullscreenButton
);
651 fFindGroup
->SetVisible(false);
652 fToggleFullscreenButton
->SetVisible(false);
654 CreateNewTab(url
, true, webView
);
655 _ShowInterface(true);
656 _SetAutoHideInterfaceInFullscreen(fAppSettings
->GetValue(
657 kSettingsKeyAutoHideInterfaceInFullscreenMode
,
658 fAutoHideInterfaceInFullscreenMode
));
660 AddShortcut('F', B_COMMAND_KEY
| B_SHIFT_KEY
,
661 new BMessage(EDIT_HIDE_FIND_GROUP
));
662 // TODO: Should be a different shortcut, H is usually for Find selection.
663 AddShortcut('H', B_COMMAND_KEY
, new BMessage(HOME
));
665 // Add shortcuts to select a particular tab
666 for (int32 i
= 1; i
<= 9; i
++) {
667 BMessage
* selectTab
= new BMessage(SELECT_TAB
);
668 selectTab
->AddInt32("tab index", i
- 1);
670 snprintf(numStr
, sizeof(numStr
), "%d", (int) i
);
671 AddShortcut(numStr
[0], B_COMMAND_KEY
, selectTab
);
675 keymap
.SetToCurrent();
676 BObjectList
<const char> unmodified(3, true);
677 if (keymap
.GetModifiedCharacters("+", B_SHIFT_KEY
, 0, &unmodified
)
679 int32 count
= unmodified
.CountItems();
680 for (int32 i
= 0; i
< count
; i
++) {
681 uint32 key
= BUnicodeChar::FromUTF8(unmodified
.ItemAt(i
));
682 if (!HasShortcut(key
, 0)) {
683 // Add semantic zoom in shortcut, bug #7428
684 AddShortcut(key
, B_COMMAND_KEY
,
685 new BMessage(ZOOM_FACTOR_INCREASE
));
689 unmodified
.MakeEmpty();
691 be_app
->PostMessage(WINDOW_OPENED
);
695 BrowserWindow::~BrowserWindow()
697 fAppSettings
->RemoveListener(BMessenger(this));
705 BrowserWindow::DispatchMessage(BMessage
* message
, BHandler
* target
)
709 if ((message
->what
== B_KEY_DOWN
|| message
->what
== B_UNMAPPED_KEY_DOWN
)
710 && message
->FindString("bytes", &bytes
) == B_OK
711 && message
->FindInt32("modifiers", &modifierKeys
) == B_OK
) {
712 if (bytes
[0] == B_FUNCTION_KEY
) {
713 // Some function key Firefox compatibility
715 if (message
->FindInt32("key", &key
) == B_OK
) {
722 PostMessage(TOGGLE_FULLSCREEN
);
729 } else if (target
== fURLInputGroup
->TextView()) {
730 // Handle B_RETURN in the URL text control. This is the easiest
731 // way to react *only* when the user presses the return key in the
732 // address bar, as opposed to trying to load whatever is in there
733 // when the text control just goes out of focus.
734 if (bytes
[0] == B_RETURN
) {
735 // Do it in such a way that the user sees the Go-button go down.
736 _InvokeButtonVisibly(fURLInputGroup
->GoButton());
738 } else if (bytes
[0] == B_ESCAPE
) {
739 // Replace edited text with the current URL.
740 fURLInputGroup
->LockURLInput(false);
741 fURLInputGroup
->SetText(CurrentWebView()->MainFrameURL());
743 } else if (target
== fFindTextControl
->TextView()) {
744 // Handle B_RETURN when the find text control has focus.
745 if (bytes
[0] == B_RETURN
) {
746 if ((modifierKeys
& B_SHIFT_KEY
) != 0)
747 _InvokeButtonVisibly(fFindPreviousButton
);
749 _InvokeButtonVisibly(fFindNextButton
);
751 } else if (bytes
[0] == B_ESCAPE
) {
752 _InvokeButtonVisibly(fFindCloseButton
);
755 } else if (bytes
[0] == B_ESCAPE
&& !fMenusRunning
) {
756 if (modifierKeys
== B_COMMAND_KEY
)
757 _ShowInterface(true);
759 // Default escape key behavior:
766 if (message
->what
== B_MOUSE_MOVED
|| message
->what
== B_MOUSE_DOWN
767 || message
->what
== B_MOUSE_UP
) {
768 message
->FindPoint("where", &fLastMousePos
);
769 if (message
->FindInt64("when", &fLastMouseMovedTime
) != B_OK
)
770 fLastMouseMovedTime
= system_time();
771 _CheckAutoHideInterface();
774 if (message
->what
== B_MOUSE_WHEEL_CHANGED
) {
777 CurrentWebView()->GetMouse(&where
, &buttons
, false);
778 // Only do this when the mouse is over the web view
779 if (CurrentWebView()->Bounds().Contains(where
)) {
780 // Zoom and unzoom text on Command + mouse wheel.
781 // This could of course (and maybe should be) implemented in the
782 // WebView, but there would need to be a way for the WebView to
783 // know the setting of the fZoomTextOnly member here. Plus other
784 // clients of the API may not want this feature.
785 if ((modifiers() & B_COMMAND_KEY
) != 0) {
787 if (message
->FindFloat("be:wheel_delta_y", &deltaY
) == B_OK
) {
789 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly
);
791 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly
);
797 // Also don't scroll up and down if the mouse is not over the
803 BWebWindow::DispatchMessage(message
, target
);
808 BrowserWindow::MessageReceived(BMessage
* message
)
810 switch (message
->what
) {
812 _ShowInterface(true);
813 if (fURLInputGroup
->TextView()->IsFocus())
814 fURLInputGroup
->TextView()->SelectAll();
816 fURLInputGroup
->MakeFocus(true);
820 CurrentWebView()->Reload();
823 case SHOW_HIDE_BOOKMARK_BAR
:
824 _ShowBookmarkBar(fBookmarkBar
->IsHidden());
830 if (message
->FindString("url", &url
) != B_OK
)
831 url
= fURLInputGroup
->Text();
833 _SetPageIcon(CurrentWebView(), NULL
);
834 _SmartURLHandler(url
);
841 fSavePanel
->SetSaveText(CurrentWebView()->MainFrameTitle());
846 case B_SAVE_REQUESTED
:
851 if (message
->FindRef("directory", &ref
) == B_OK
852 && message
->FindString("name", &name
) == B_OK
) {
853 BDirectory
dir(&ref
);
854 BFile
output(&dir
, name
,
855 B_WRITE_ONLY
| B_CREATE_FILE
| B_ERASE_FILE
);
856 CurrentWebView()->WebPage()->GetContentsAsMHTML(output
);
863 CurrentWebView()->GoBack();
867 CurrentWebView()->GoForward();
871 CurrentWebView()->StopLoading();
875 CurrentWebView()->LoadURL(fStartPageURL
);
878 case CLEAR_HISTORY
: {
879 BrowsingHistory
* history
= BrowsingHistory::DefaultInstance();
880 if (history
->CountItems() == 0)
882 BAlert
* alert
= new BAlert(B_TRANSLATE("Confirmation"),
883 B_TRANSLATE("Do you really want to "
884 "clear the browsing history?"), B_TRANSLATE("Clear"),
885 B_TRANSLATE("Cancel"));
886 alert
->SetShortcut(1, B_ESCAPE
);
888 if (alert
->Go() == 0)
893 case CREATE_BOOKMARK
:
901 case B_REFS_RECEIVED
:
903 // Currently the only source of these messages is the bookmarks
905 // Filter refs into URLs, this also gets rid of refs for folders.
906 // For clicks on sub-folders in the bookmarks menu, we have Tracker
907 // open the corresponding folder.
909 uint32 addedCount
= 0;
910 for (int32 i
= 0; message
->FindRef("refs", i
, &ref
) == B_OK
; i
++) {
912 uint32 addedSubCount
= 0;
913 if (entry
.IsDirectory()) {
914 BDirectory
directory(&entry
);
915 _AddBookmarkURLsRecursively(directory
, message
,
918 BFile
file(&ref
, B_READ_ONLY
);
920 if (_ReadURLAttr(file
, url
)) {
921 message
->AddString("url", url
.String());
925 if (addedSubCount
== 0) {
926 // Don't know what to do with this entry, just pass it
927 // on to the system to handle. Note that this may result
928 // in us opening other supported files via the application
930 be_roster
->Launch(&ref
);
932 addedCount
+= addedSubCount
;
934 message
->RemoveName("refs");
935 if (addedCount
> 10) {
936 BString
string(B_TRANSLATE_COMMENT("Do you want to open "
937 "%addedCount bookmarks all at once?", "Don't translate "
938 "variable %addedCount."));
939 string
.ReplaceFirst("%addedCount", BString() << addedCount
);
941 BAlert
* alert
= new BAlert(
942 B_TRANSLATE("Open bookmarks confirmation"),
943 string
.String(), B_TRANSLATE("Cancel"),
944 B_TRANSLATE("Open all"));
945 alert
->SetShortcut(0, B_ESCAPE
);
946 if (alert
->Go() == 0)
949 message
->AddPointer("window", this);
950 be_app
->PostMessage(message
);
956 // User possibly dropped files on this window.
957 // If there is more than one entry_ref, let the app handle it
958 // (open one new page per ref). If there is one ref, open it in
962 if (message
->GetInfo("refs", &type
, &countFound
) != B_OK
963 || type
!= B_REF_TYPE
) {
966 if (countFound
> 1) {
967 message
->what
= B_REFS_RECEIVED
;
968 be_app
->PostMessage(message
);
972 if (message
->FindRef("refs", &ref
) != B_OK
)
974 BEntry
entry(&ref
, true);
976 if (!entry
.Exists() || entry
.GetPath(&path
) != B_OK
)
980 CurrentWebView()->LoadURL(url
);
984 case ZOOM_FACTOR_INCREASE
:
985 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly
);
987 case ZOOM_FACTOR_DECREASE
:
988 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly
);
990 case ZOOM_FACTOR_RESET
:
991 CurrentWebView()->ResetZoomFactor();
994 fZoomTextOnly
= !fZoomTextOnly
;
995 fZoomTextOnlyMenuItem
->SetMarked(fZoomTextOnly
);
996 // TODO: Would be nice to have an instant update if the page is
1000 case TOGGLE_FULLSCREEN
:
1004 case TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN
:
1005 _SetAutoHideInterfaceInFullscreen(
1006 !fAutoHideInterfaceInFullscreenMode
);
1009 case CHECK_AUTO_HIDE_INTERFACE
:
1010 _CheckAutoHideInterface();
1013 case SHOW_PAGE_SOURCE
:
1014 CurrentWebView()->WebPage()->SendPageSource();
1016 case B_PAGE_SOURCE_RESULT
:
1017 _HandlePageSourceResult(message
);
1020 case EDIT_FIND_NEXT
:
1021 CurrentWebView()->FindString(fFindTextControl
->Text(), true,
1022 fFindCaseSensitiveCheckBox
->Value());
1024 case FIND_TEXT_CHANGED
:
1026 bool findTextAvailable
= strlen(fFindTextControl
->Text()) > 0;
1027 fFindPreviousMenuItem
->SetEnabled(findTextAvailable
);
1028 fFindNextMenuItem
->SetEnabled(findTextAvailable
);
1031 case EDIT_FIND_PREVIOUS
:
1032 CurrentWebView()->FindString(fFindTextControl
->Text(), false,
1033 fFindCaseSensitiveCheckBox
->Value());
1035 case EDIT_SHOW_FIND_GROUP
:
1036 if (!fFindGroup
->IsVisible())
1037 fFindGroup
->SetVisible(true);
1038 fFindTextControl
->MakeFocus(true);
1040 case EDIT_HIDE_FIND_GROUP
:
1041 if (fFindGroup
->IsVisible()) {
1042 fFindGroup
->SetVisible(false);
1043 if (CurrentWebView() != NULL
)
1044 CurrentWebView()->MakeFocus(true);
1052 BTextView
* textView
= dynamic_cast<BTextView
*>(CurrentFocus());
1053 if (textView
!= NULL
)
1054 textView
->MessageReceived(message
);
1055 else if (CurrentWebView() != NULL
)
1056 CurrentWebView()->MessageReceived(message
);
1060 case B_EDITING_CAPABILITIES_RESULT
:
1063 if (message
->FindPointer("view",
1064 reinterpret_cast<void**>(&webView
)) != B_OK
1065 || webView
!= CurrentWebView()) {
1071 if (message
->FindBool("can cut", &canCut
) != B_OK
)
1073 if (message
->FindBool("can copy", &canCopy
) != B_OK
)
1075 if (message
->FindBool("can paste", &canPaste
) != B_OK
)
1077 fCutMenuItem
->SetEnabled(canCut
);
1078 fCopyMenuItem
->SetEnabled(canCopy
);
1079 fPasteMenuItem
->SetEnabled(canPaste
);
1083 case SHOW_DOWNLOAD_WINDOW
:
1084 case SHOW_SETTINGS_WINDOW
:
1085 case SHOW_CONSOLE_WINDOW
:
1086 case SHOW_COOKIE_WINDOW
:
1087 message
->AddUInt32("workspaces", Workspaces());
1088 be_app
->PostMessage(message
);
1092 if (fTabManager
->CountTabs() > 1) {
1094 if (message
->FindInt32("tab index", &index
) != B_OK
)
1095 index
= fTabManager
->SelectedTabIndex();
1096 _ShutdownTab(index
);
1097 _UpdateTabGroupVisibility();
1099 PostMessage(B_QUIT_REQUESTED
);
1105 if (message
->FindInt32("tab index", &index
) == B_OK
1106 && fTabManager
->SelectedTabIndex() != index
1107 && fTabManager
->CountTabs() > index
) {
1108 fTabManager
->SelectTab(index
);
1116 // This message may be received also when the last tab closed,
1117 // i.e. with index == -1.
1119 if (message
->FindInt32("tab index", &index
) != B_OK
)
1125 case SETTINGS_VALUE_CHANGED
:
1128 if (message
->FindString("name", &name
) != B_OK
)
1133 if (name
== kSettingsKeyShowTabsIfSinglePageOpen
1134 && message
->FindBool("value", &flag
) == B_OK
) {
1135 if (fShowTabsIfSinglePageOpen
!= flag
) {
1136 fShowTabsIfSinglePageOpen
= flag
;
1137 _UpdateTabGroupVisibility();
1139 } else if (name
== kSettingsKeyAutoHidePointer
1140 && message
->FindBool("value", &flag
) == B_OK
) {
1141 fAutoHidePointer
= flag
;
1142 if (CurrentWebView())
1143 CurrentWebView()->SetAutoHidePointer(fAutoHidePointer
);
1144 } else if (name
== kSettingsKeyStartPageURL
1145 && message
->FindString("value", &string
) == B_OK
) {
1146 fStartPageURL
= string
;
1147 } else if (name
== kSettingsKeySearchPageURL
1148 && message
->FindString("value", &string
) == B_OK
) {
1149 fSearchPageURL
= string
;
1150 } else if (name
== kSettingsKeyNewWindowPolicy
1151 && message
->FindUInt32("value", &value
) == B_OK
) {
1152 fNewWindowPolicy
= value
;
1153 } else if (name
== kSettingsKeyNewTabPolicy
1154 && message
->FindUInt32("value", &value
) == B_OK
) {
1155 fNewTabPolicy
= value
;
1156 } else if (name
== kSettingsKeyAutoHideInterfaceInFullscreenMode
1157 && message
->FindBool("value", &flag
) == B_OK
) {
1158 _SetAutoHideInterfaceInFullscreen(flag
);
1159 } else if (name
== kSettingsKeyShowHomeButton
1160 && message
->FindBool("value", &flag
) == B_OK
) {
1162 fHomeButton
->Show();
1164 fHomeButton
->Hide();
1165 } else if (name
== kSettingsShowBookmarkBar
1166 && message
->FindBool("value", &flag
) == B_OK
) {
1167 _ShowBookmarkBar(flag
);
1171 case ADD_CONSOLE_MESSAGE
:
1172 be_app
->PostMessage(message
);
1173 BWebWindow::MessageReceived(message
);
1177 BWebWindow::MessageReceived(message
);
1184 BrowserWindow::Archive(BMessage
* archive
, bool deep
) const
1186 status_t ret
= archive
->AddRect("window frame", Frame());
1188 for (int i
= 0; i
< fTabManager
->CountTabs(); i
++) {
1189 BWebView
* view
= dynamic_cast<BWebView
*>(fTabManager
->ViewForTab(i
));
1195 ret
= archive
->AddString("tab", view
->MainFrameURL());
1203 BrowserWindow::QuitRequested()
1205 // TODO: Check for modified form data and ask user for confirmation, etc.
1207 BMessage
message(WINDOW_CLOSED
);
1210 // Iterate over all tabs to delete all BWebViews.
1211 // Do this here, so WebKit tear down happens earlier.
1212 SetCurrentWebView(NULL
);
1213 while (fTabManager
->CountTabs() > 0)
1216 message
.AddRect("window frame", WindowFrame());
1217 be_app
->PostMessage(&message
);
1223 BrowserWindow::MenusBeginning()
1225 _UpdateHistoryMenu();
1226 _UpdateClipboardItems();
1227 fMenusRunning
= true;
1232 BrowserWindow::MenusEnded()
1234 fMenusRunning
= false;
1239 BrowserWindow::ScreenChanged(BRect screenSize
, color_space format
)
1247 BrowserWindow::WorkspacesChanged(uint32 oldWorkspaces
, uint32 newWorkspaces
)
1255 viewIsChild(const BView
* parent
, const BView
* view
)
1260 int32 count
= parent
->CountChildren();
1261 for (int32 i
= 0; i
< count
; i
++) {
1262 BView
* child
= parent
->ChildAt(i
);
1263 if (viewIsChild(child
, view
))
1271 BrowserWindow::SetCurrentWebView(BWebView
* webView
)
1273 if (webView
== CurrentWebView())
1276 if (CurrentWebView() != NULL
) {
1277 // Remember the currently focused view before switching tabs,
1278 // so that we can revert the focus when switching back to this tab
1280 PageUserData
* userData
= static_cast<PageUserData
*>(
1281 CurrentWebView()->GetUserData());
1282 if (userData
== NULL
) {
1283 userData
= new PageUserData(CurrentFocus());
1284 CurrentWebView()->SetUserData(userData
);
1286 userData
->SetFocusedView(CurrentFocus());
1287 userData
->SetURLInputContents(fURLInputGroup
->Text());
1288 int32 selectionStart
;
1290 fURLInputGroup
->TextView()->GetSelection(&selectionStart
,
1292 userData
->SetURLInputSelection(selectionStart
, selectionEnd
);
1295 BWebWindow::SetCurrentWebView(webView
);
1297 if (webView
!= NULL
) {
1298 webView
->SetAutoHidePointer(fAutoHidePointer
);
1300 _UpdateTitle(webView
->MainFrameTitle());
1302 // Restore the previous focus or focus the web view.
1303 PageUserData
* userData
= static_cast<PageUserData
*>(
1304 webView
->GetUserData());
1305 BView
* focusedView
= NULL
;
1306 if (userData
!= NULL
)
1307 focusedView
= userData
->FocusedView();
1309 if (focusedView
!= NULL
1310 && viewIsChild(GetLayout()->View(), focusedView
)) {
1311 focusedView
->MakeFocus(true);
1313 webView
->MakeFocus(true);
1315 bool state
= fURLInputGroup
->IsURLInputLocked();
1316 fURLInputGroup
->LockURLInput(false);
1317 // Unlock it so the following code can update the URL
1319 if (userData
!= NULL
) {
1320 fURLInputGroup
->SetPageIcon(userData
->PageIcon());
1321 if (userData
->URLInputContents().Length())
1322 fURLInputGroup
->SetText(userData
->URLInputContents());
1324 fURLInputGroup
->SetText(webView
->MainFrameURL());
1325 if (userData
->URLInputSelectionStart() >= 0) {
1326 fURLInputGroup
->TextView()->Select(
1327 userData
->URLInputSelectionStart(),
1328 userData
->URLInputSelectionEnd());
1331 fURLInputGroup
->SetPageIcon(NULL
);
1332 fURLInputGroup
->SetText(webView
->MainFrameURL());
1335 fURLInputGroup
->LockURLInput(state
);
1336 // Restore the state
1338 // Trigger update of the interface to the new page, by requesting
1339 // to resend all notifications.
1340 webView
->WebPage()->ResendNotifications();
1347 BrowserWindow::IsBlankTab() const
1349 if (CurrentWebView() == NULL
)
1351 BString requestedURL
= CurrentWebView()->MainFrameRequestedURL();
1352 return requestedURL
.Length() == 0
1353 || requestedURL
== _NewTabURL(fTabManager
->CountTabs() == 1);
1358 BrowserWindow::CreateNewTab(const BString
& _url
, bool select
,
1361 bool applyNewPagePolicy
= webView
== NULL
;
1362 // Executed in app thread (new BWebPage needs to be created in app thread).
1363 if (webView
== NULL
)
1364 webView
= new BWebView("web view", fContext
);
1366 bool isNewWindow
= fTabManager
->CountTabs() == 0;
1368 fTabManager
->AddTab(webView
, B_TRANSLATE("New tab"));
1371 if (applyNewPagePolicy
&& url
.Length() == 0)
1372 url
= _NewTabURL(isNewWindow
);
1374 if (url
.Length() > 0)
1375 webView
->LoadURL(url
.String());
1378 fTabManager
->SelectTab(fTabManager
->CountTabs() - 1);
1379 SetCurrentWebView(webView
);
1380 webView
->WebPage()->ResendNotifications();
1381 fURLInputGroup
->SetPageIcon(NULL
);
1382 fURLInputGroup
->SetText(url
.String());
1383 fURLInputGroup
->MakeFocus(true);
1386 _ShowInterface(true);
1387 _UpdateTabGroupVisibility();
1392 BrowserWindow::WindowFrame() const
1395 return fNonFullscreenWindowFrame
;
1402 BrowserWindow::ToggleFullscreen()
1404 if (fIsFullscreen
) {
1405 MoveTo(fNonFullscreenWindowFrame
.LeftTop());
1406 ResizeTo(fNonFullscreenWindowFrame
.Width(),
1407 fNonFullscreenWindowFrame
.Height());
1409 SetFlags(Flags() & ~(B_NOT_RESIZABLE
| B_NOT_MOVABLE
));
1410 SetLook(B_DOCUMENT_WINDOW_LOOK
);
1412 _ShowInterface(true);
1414 fNonFullscreenWindowFrame
= Frame();
1417 SetFlags(Flags() | (B_NOT_RESIZABLE
| B_NOT_MOVABLE
));
1418 SetLook(B_TITLED_WINDOW_LOOK
);
1420 fIsFullscreen
= !fIsFullscreen
;
1421 fFullscreenItem
->SetMarked(fIsFullscreen
);
1422 fToggleFullscreenButton
->SetVisible(fIsFullscreen
);
1426 // #pragma mark - Notification API
1430 BrowserWindow::NavigationRequested(const BString
& url
, BWebView
* view
)
1436 BrowserWindow::NewWindowRequested(const BString
& url
, bool primaryAction
)
1438 // Always open new windows in the application thread, since
1439 // creating a BWebView will try to grab the application lock.
1440 // But our own WebPage may already try to lock us from within
1441 // the application thread -> dead-lock. Thus we can't wait for
1443 BMessage
message(NEW_TAB
);
1444 message
.AddPointer("window", this);
1445 message
.AddString("url", url
);
1446 message
.AddBool("select", primaryAction
);
1447 be_app
->PostMessage(&message
);
1452 BrowserWindow::NewPageCreated(BWebView
* view
, BRect windowFrame
,
1453 bool modalDialog
, bool resizable
, bool activate
)
1455 if (windowFrame
.IsValid()) {
1456 BrowserWindow
* window
= new BrowserWindow(windowFrame
, fAppSettings
,
1457 BString(), fContext
, INTERFACE_ELEMENT_STATUS
,
1461 CreateNewTab(BString(), activate
, view
);
1466 BrowserWindow::CloseWindowRequested(BWebView
* view
)
1468 int32 index
= fTabManager
->TabForView(view
);
1470 // Tab is already gone.
1473 BMessage
message(CLOSE_TAB
);
1474 message
.AddInt32("tab index", index
);
1475 PostMessage(&message
, this);
1480 BrowserWindow::LoadNegotiating(const BString
& url
, BWebView
* view
)
1482 if (view
!= CurrentWebView()) {
1483 // Update the userData contents instead so the user sees
1484 // the correct URL when they switch back to that tab.
1485 PageUserData
* userData
= static_cast<PageUserData
*>(
1486 view
->GetUserData());
1487 if (userData
!= NULL
&& userData
->URLInputContents().Length() == 0) {
1488 userData
->SetURLInputContents(url
);
1492 fURLInputGroup
->SetText(url
.String());
1494 BString
status(B_TRANSLATE("Requesting %url"));
1495 status
.ReplaceFirst("%url", url
);
1496 view
->WebPage()->SetStatusMessage(status
);
1501 BrowserWindow::LoadCommitted(const BString
& url
, BWebView
* view
)
1503 if (view
!= CurrentWebView())
1506 // This hook is invoked when the load is commited.
1507 fURLInputGroup
->SetText(url
.String());
1509 BString
status(B_TRANSLATE("Loading %url"));
1510 status
.ReplaceFirst("%url", url
);
1511 view
->WebPage()->SetStatusMessage(status
);
1516 BrowserWindow::LoadProgress(float progress
, BWebView
* view
)
1518 if (view
!= CurrentWebView())
1521 if (progress
< 100 && fLoadingProgressBar
->IsHidden())
1522 _ShowProgressBar(true);
1523 else if (progress
== 100 && !fLoadingProgressBar
->IsHidden())
1524 _ShowProgressBar(false);
1525 fLoadingProgressBar
->SetTo(progress
);
1530 BrowserWindow::LoadFailed(const BString
& url
, BWebView
* view
)
1532 if (view
!= CurrentWebView())
1535 BString
status(B_TRANSLATE_COMMENT("%url failed", "Loading URL failed. "
1536 "Don't translate variable %url."));
1537 status
.ReplaceFirst("%url", url
);
1538 view
->WebPage()->SetStatusMessage(status
);
1539 if (!fLoadingProgressBar
->IsHidden())
1540 fLoadingProgressBar
->Hide();
1545 BrowserWindow::LoadFinished(const BString
& url
, BWebView
* view
)
1547 if (view
!= CurrentWebView())
1550 fURLInputGroup
->SetText(url
.String());
1552 BString
status(B_TRANSLATE_COMMENT("%url finished", "Loading URL "
1553 "finished. Don't translate variable %url."));
1554 status
.ReplaceFirst("%url", url
);
1555 view
->WebPage()->SetStatusMessage(status
);
1556 if (!fLoadingProgressBar
->IsHidden())
1557 fLoadingProgressBar
->Hide();
1559 NavigationCapabilitiesChanged(fBackButton
->IsEnabled(),
1560 fForwardButton
->IsEnabled(), false, view
);
1562 int32 tabIndex
= fTabManager
->TabForView(view
);
1563 if (tabIndex
> 0 && strcmp(B_TRANSLATE("New tab"),
1564 fTabManager
->TabLabel(tabIndex
)) == 0)
1565 fTabManager
->SetTabLabel(tabIndex
, url
);
1570 BrowserWindow::MainDocumentError(const BString
& failingURL
,
1571 const BString
& localizedDescription
, BWebView
* view
)
1573 // Make sure we show the page that contains the view.
1574 if (!_ShowPage(view
))
1577 // Try delegating the URL to an external app instead.
1578 int32 at
= failingURL
.FindFirst(":");
1581 failingURL
.CopyInto(proto
, 0, at
);
1583 bool handled
= false;
1585 for (unsigned int i
= 0; i
< sizeof(kHandledProtocols
) / sizeof(char*);
1587 handled
= (proto
== kHandledProtocols
[i
]);
1593 _SmartURLHandler(failingURL
);
1598 BWebWindow::MainDocumentError(failingURL
, localizedDescription
, view
);
1600 // TODO: Remove the failing URL from the BrowsingHistory!
1605 BrowserWindow::TitleChanged(const BString
& title
, BWebView
* view
)
1607 int32 tabIndex
= fTabManager
->TabForView(view
);
1611 fTabManager
->SetTabLabel(tabIndex
, title
);
1613 if (view
!= CurrentWebView())
1616 _UpdateTitle(title
);
1621 BrowserWindow::IconReceived(const BBitmap
* icon
, BWebView
* view
)
1623 // The view may already be gone, since this notification arrives
1625 if (!fTabManager
->HasView(view
))
1628 _SetPageIcon(view
, icon
);
1633 BrowserWindow::ResizeRequested(float width
, float height
, BWebView
* view
)
1635 if (view
!= CurrentWebView())
1638 // Ignore request when there is more than one BWebView embedded.
1639 if (fTabManager
->CountTabs() > 1)
1642 // Make sure the new frame is not larger than the screen frame minus
1643 // window decorator border.
1644 BScreen
screen(this);
1645 BRect screenFrame
= screen
.Frame();
1646 BRect decoratorFrame
= DecoratorFrame();
1647 BRect frame
= Frame();
1649 screenFrame
.left
+= decoratorFrame
.left
- frame
.left
;
1650 screenFrame
.right
+= decoratorFrame
.right
- frame
.right
;
1651 screenFrame
.top
+= decoratorFrame
.top
- frame
.top
;
1652 screenFrame
.bottom
+= decoratorFrame
.bottom
- frame
.bottom
;
1654 width
= min_c(width
, screen
.Frame().Width());
1655 height
= min_c(height
, screen
.Frame().Height());
1657 frame
.right
= frame
.left
+ width
;
1658 frame
.bottom
= frame
.top
+ height
;
1660 // frame is now not larger than screenFrame, but may still be partly outside
1661 if (!screenFrame
.Contains(frame
)) {
1662 if (frame
.left
< screenFrame
.left
)
1663 frame
.OffsetBy(screenFrame
.left
- frame
.left
, 0);
1664 else if (frame
.right
> screenFrame
.right
)
1665 frame
.OffsetBy(screenFrame
.right
- frame
.right
, 0);
1666 if (frame
.top
< screenFrame
.top
)
1667 frame
.OffsetBy(screenFrame
.top
- frame
.top
, 0);
1668 else if (frame
.bottom
> screenFrame
.bottom
)
1669 frame
.OffsetBy(screenFrame
.bottom
- frame
.bottom
, 0);
1672 MoveTo(frame
.left
, frame
.top
);
1673 ResizeTo(width
, height
);
1678 BrowserWindow::SetToolBarsVisible(bool flag
, BWebView
* view
)
1681 // TODO: Ignore request when there is more than one BWebView embedded!
1686 BrowserWindow::SetStatusBarVisible(bool flag
, BWebView
* view
)
1689 // TODO: Ignore request when there is more than one BWebView embedded!
1694 BrowserWindow::SetMenuBarVisible(bool flag
, BWebView
* view
)
1697 // TODO: Ignore request when there is more than one BWebView embedded!
1702 BrowserWindow::SetResizable(bool flag
, BWebView
* view
)
1704 // TODO: Ignore request when there is more than one BWebView embedded!
1707 SetFlags(Flags() & ~B_NOT_RESIZABLE
);
1709 SetFlags(Flags() | B_NOT_RESIZABLE
);
1714 BrowserWindow::StatusChanged(const BString
& statusText
, BWebView
* view
)
1716 if (view
!= CurrentWebView())
1720 fStatusText
->SetText(statusText
.String());
1725 BrowserWindow::NavigationCapabilitiesChanged(bool canGoBackward
,
1726 bool canGoForward
, bool canStop
, BWebView
* view
)
1728 if (view
!= CurrentWebView())
1731 fBackButton
->SetEnabled(canGoBackward
);
1732 fForwardButton
->SetEnabled(canGoForward
);
1733 fStopButton
->SetEnabled(canStop
);
1735 fBackMenuItem
->SetEnabled(canGoBackward
);
1736 fForwardMenuItem
->SetEnabled(canGoForward
);
1741 BrowserWindow::UpdateGlobalHistory(const BString
& url
)
1743 BrowsingHistory::DefaultInstance()->AddItem(BrowsingHistoryItem(url
));
1745 fURLInputGroup
->SetText(CurrentWebView()->MainFrameURL());
1750 BrowserWindow::AuthenticationChallenge(BString message
, BString
& inOutUser
,
1751 BString
& inOutPassword
, bool& inOutRememberCredentials
,
1752 uint32 failureCount
, BWebView
* view
)
1754 CredentialsStorage
* persistentStorage
1755 = CredentialsStorage::PersistentInstance();
1756 CredentialsStorage
* sessionStorage
1757 = CredentialsStorage::SessionInstance();
1759 // TODO: Using the message as key here is not so smart.
1760 HashKeyString
key(message
);
1762 if (failureCount
== 0) {
1763 if (persistentStorage
->Contains(key
)) {
1764 Credentials credentials
= persistentStorage
->GetCredentials(key
);
1765 inOutUser
= credentials
.Username();
1766 inOutPassword
= credentials
.Password();
1768 } else if (sessionStorage
->Contains(key
)) {
1769 Credentials credentials
= sessionStorage
->GetCredentials(key
);
1770 inOutUser
= credentials
.Username();
1771 inOutPassword
= credentials
.Password();
1775 // Switch to the page for which this authentication is required.
1776 if (!_ShowPage(view
))
1779 AuthenticationPanel
* panel
= new AuthenticationPanel(Frame());
1780 // Panel auto-destructs.
1781 bool success
= panel
->getAuthentication(message
, inOutUser
, inOutPassword
,
1782 inOutRememberCredentials
, failureCount
> 0, inOutUser
, inOutPassword
,
1783 &inOutRememberCredentials
);
1785 Credentials
credentials(inOutUser
, inOutPassword
);
1786 if (inOutRememberCredentials
)
1787 persistentStorage
->PutCredentials(key
, credentials
);
1789 sessionStorage
->PutCredentials(key
, credentials
);
1795 // #pragma mark - private
1799 BrowserWindow::_UpdateTitle(const BString
& title
)
1801 BString windowTitle
;
1803 if (title
.Length() > 0)
1804 windowTitle
= title
;
1806 BWebView
* webView
= CurrentWebView();
1807 if (webView
!= NULL
) {
1808 BString url
= webView
->MainFrameURL();
1809 int32 leafPos
= url
.FindLast('/');
1810 url
.Remove(0, leafPos
+ 1);
1815 if (windowTitle
.Length() > 0)
1816 windowTitle
<< " - ";
1817 windowTitle
<< kApplicationName
;
1818 SetTitle(windowTitle
.String());
1823 BrowserWindow::_UpdateTabGroupVisibility()
1826 if (fInterfaceVisible
)
1827 fTabGroup
->SetVisible(_TabGroupShouldBeVisible());
1828 fTabManager
->SetCloseButtonsAvailable(fTabManager
->CountTabs() > 1);
1835 BrowserWindow::_TabGroupShouldBeVisible() const
1837 return (fShowTabsIfSinglePageOpen
|| fTabManager
->CountTabs() > 1)
1838 && (fVisibleInterfaceElements
& INTERFACE_ELEMENT_TABS
) != 0;
1843 BrowserWindow::_ShutdownTab(int32 index
)
1845 BView
* view
= fTabManager
->RemoveTab(index
);
1846 BWebView
* webView
= dynamic_cast<BWebView
*>(view
);
1847 if (webView
== CurrentWebView())
1848 SetCurrentWebView(NULL
);
1849 if (webView
!= NULL
)
1850 webView
->Shutdown();
1857 BrowserWindow::_TabChanged(int32 index
)
1859 SetCurrentWebView(dynamic_cast<BWebView
*>(fTabManager
->ViewForTab(index
)));
1864 BrowserWindow::_BookmarkPath(BPath
& path
) const
1866 status_t ret
= find_directory(B_USER_SETTINGS_DIRECTORY
, &path
);
1870 ret
= path
.Append(kApplicationName
);
1874 ret
= path
.Append("Bookmarks");
1878 return create_directory(path
.Path(), 0777);
1883 BrowserWindow::_CreateBookmark()
1886 status_t status
= _BookmarkPath(path
);
1887 if (status
!= B_OK
) {
1888 BString
message(B_TRANSLATE_COMMENT("There was an error retrieving "
1889 "the bookmark folder.\n\nError: %error", "Don't translate the "
1890 "variable %error"));
1891 message
.ReplaceFirst("%error", strerror(status
));
1892 BAlert
* alert
= new BAlert(B_TRANSLATE("Bookmark error"),
1893 message
.String(), B_TRANSLATE("OK"), NULL
, NULL
,
1894 B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
1895 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1899 BWebView
* webView
= CurrentWebView();
1900 BString
url(webView
->MainFrameURL());
1901 // Create a bookmark file
1903 BString
bookmarkName(webView
->MainFrameTitle());
1904 if (bookmarkName
.Length() == 0) {
1906 int32 leafPos
= bookmarkName
.FindLast('/');
1908 bookmarkName
.Remove(0, leafPos
+ 1);
1910 // Make sure the bookmark title does not contain chars that are not
1911 // allowed in file names, and is within allowed name length.
1912 bookmarkName
.ReplaceAll('/', '-');
1913 bookmarkName
.Truncate(B_FILE_NAME_LENGTH
- 1);
1915 // Check that the bookmark exists nowhere in the bookmark hierarchy,
1916 // though the intended file name must match, we don't search the stored
1917 // URLs, only for matching file names.
1918 BDirectory
directory(path
.Path());
1919 if (status
== B_OK
&& _CheckBookmarkExists(directory
, bookmarkName
, url
)) {
1920 BString
message(B_TRANSLATE_COMMENT("A bookmark for this page "
1921 "(%bookmarkName) already exists.", "Don't translate variable "
1923 message
.ReplaceFirst("%bookmarkName", bookmarkName
);
1924 BAlert
* alert
= new BAlert(B_TRANSLATE("Bookmark info"),
1925 message
.String(), B_TRANSLATE("OK"));
1926 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1931 BPath
entryPath(path
);
1932 status
= entryPath
.Append(bookmarkName
);
1935 status
= entry
.SetTo(entryPath
.Path(), true);
1936 if (status
== B_OK
) {
1938 while (entry
.Exists()) {
1939 // Find a unique name for the bookmark, there may still be a
1940 // file in the way that stores a different URL.
1941 bookmarkName
= webView
->MainFrameTitle();
1942 bookmarkName
<< " " << tries
++;
1944 status
= entryPath
.Append(bookmarkName
);
1946 status
= entry
.SetTo(entryPath
.Path(), true);
1951 if (status
== B_OK
) {
1952 status
= bookmarkFile
.SetTo(&entry
,
1953 B_CREATE_FILE
| B_ERASE_FILE
| B_WRITE_ONLY
);
1956 // Write bookmark meta data
1958 status
= bookmarkFile
.WriteAttrString("META:url", &url
);
1959 if (status
== B_OK
) {
1960 BString title
= webView
->MainFrameTitle();
1961 bookmarkFile
.WriteAttrString("META:title", &title
);
1964 BNodeInfo
nodeInfo(&bookmarkFile
);
1965 if (status
== B_OK
) {
1966 status
= nodeInfo
.SetType("application/x-vnd.Be-bookmark");
1967 if (status
== B_OK
) {
1968 PageUserData
* userData
= static_cast<PageUserData
*>(
1969 webView
->GetUserData());
1970 if (userData
!= NULL
&& userData
->PageIcon() != NULL
) {
1971 BBitmap
miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK
,
1973 status_t ret
= miniIcon
.ImportBits(userData
->PageIcon());
1975 ret
= nodeInfo
.SetIcon(&miniIcon
, B_MINI_ICON
);
1977 fprintf(stderr
, "Failed to store mini icon for bookmark: "
1978 "%s\n", strerror(ret
));
1980 BBitmap
largeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK
,
1982 // TODO: Store 32x32 favicon which is often provided by sites.
1983 const uint8
* src
= (const uint8
*)miniIcon
.Bits();
1984 uint32 srcBPR
= miniIcon
.BytesPerRow();
1985 uint8
* dst
= (uint8
*)largeIcon
.Bits();
1986 uint32 dstBPR
= largeIcon
.BytesPerRow();
1987 for (uint32 y
= 0; y
< 16; y
++) {
1988 const uint8
* s
= src
;
1990 for (uint32 x
= 0; x
< 16; x
++) {
1996 for (uint32 x
= 0; x
< 16; x
++) {
2004 ret
= nodeInfo
.SetIcon(&largeIcon
, B_LARGE_ICON
);
2006 fprintf(stderr
, "Failed to store large icon for bookmark: "
2007 "%s\n", strerror(ret
));
2013 if (status
!= B_OK
) {
2014 BString
message(B_TRANSLATE_COMMENT("There was an error creating the "
2015 "bookmark file.\n\nError: %error", "Don't translate variable "
2017 message
.ReplaceFirst("%error", strerror(status
));
2018 BAlert
* alert
= new BAlert(B_TRANSLATE("Bookmark error"),
2019 message
.String(), B_TRANSLATE("OK"), NULL
, NULL
,
2020 B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
2021 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
2029 BrowserWindow::_ShowBookmarks()
2033 status_t status
= _BookmarkPath(path
);
2035 status
= get_ref_for_path(path
.Path(), &ref
);
2037 status
= be_roster
->Launch(&ref
);
2039 if (status
!= B_OK
&& status
!= B_ALREADY_RUNNING
) {
2040 BString
message(B_TRANSLATE_COMMENT("There was an error trying to "
2041 "show the Bookmarks folder.\n\nError: %error",
2042 "Don't translate variable %error"));
2043 message
.ReplaceFirst("%error", strerror(status
));
2044 BAlert
* alert
= new BAlert(B_TRANSLATE("Bookmark error"),
2045 message
.String(), B_TRANSLATE("OK"), NULL
, NULL
,
2046 B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
2047 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
2054 bool BrowserWindow::_CheckBookmarkExists(BDirectory
& directory
,
2055 const BString
& bookmarkName
, const BString
& url
) const
2058 while (directory
.GetNextEntry(&entry
) == B_OK
) {
2059 if (entry
.IsDirectory()) {
2060 BDirectory
subBirectory(&entry
);
2061 // At least preserve the entry file handle when recursing into
2062 // sub-folders... eventually we will run out, though, with very
2065 if (_CheckBookmarkExists(subBirectory
, bookmarkName
, url
))
2068 char entryName
[B_FILE_NAME_LENGTH
];
2069 if (entry
.GetName(entryName
) != B_OK
|| bookmarkName
!= entryName
)
2072 BFile
file(&entry
, B_READ_ONLY
);
2073 if (_ReadURLAttr(file
, storedURL
)) {
2074 // Just bail if the bookmark already exists
2075 if (storedURL
== url
)
2085 BrowserWindow::_ReadURLAttr(BFile
& bookmarkFile
, BString
& url
) const
2087 return bookmarkFile
.InitCheck() == B_OK
2088 && bookmarkFile
.ReadAttrString("META:url", &url
) == B_OK
;
2093 BrowserWindow::_AddBookmarkURLsRecursively(BDirectory
& directory
,
2094 BMessage
* message
, uint32
& addedCount
) const
2097 while (directory
.GetNextEntry(&entry
) == B_OK
) {
2098 if (entry
.IsDirectory()) {
2099 BDirectory
subBirectory(&entry
);
2100 // At least preserve the entry file handle when recursing into
2101 // sub-folders... eventually we will run out, though, with very
2104 _AddBookmarkURLsRecursively(subBirectory
, message
, addedCount
);
2107 BFile
file(&entry
, B_READ_ONLY
);
2108 if (_ReadURLAttr(file
, storedURL
)) {
2109 message
->AddString("url", storedURL
.String());
2118 BrowserWindow::_SetPageIcon(BWebView
* view
, const BBitmap
* icon
)
2120 PageUserData
* userData
= static_cast<PageUserData
*>(view
->GetUserData());
2121 if (userData
== NULL
) {
2122 userData
= new(std::nothrow
) PageUserData(NULL
);
2123 if (userData
== NULL
)
2125 view
->SetUserData(userData
);
2127 // The PageUserData makes a copy of the icon, which we pass on to
2128 // the TabManager for display in the respective tab.
2129 userData
->SetPageIcon(icon
);
2130 fTabManager
->SetTabIcon(view
, userData
->PageIcon());
2131 if (view
== CurrentWebView())
2132 fURLInputGroup
->SetPageIcon(icon
);
2137 addItemToMenuOrSubmenu(BMenu
* menu
, BMenuItem
* newItem
)
2139 BString baseURLLabel
= baseURL(BString(newItem
->Label()));
2140 for (int32 i
= menu
->CountItems() - 1; i
>= 0; i
--) {
2141 BMenuItem
* item
= menu
->ItemAt(i
);
2142 BString label
= item
->Label();
2143 if (label
.FindFirst(baseURLLabel
) >= 0) {
2144 if (item
->Submenu()) {
2145 // Submenu was already added in previous iteration.
2146 item
->Submenu()->AddItem(newItem
);
2149 menu
->RemoveItem(item
);
2150 BMenu
* subMenu
= new BMenu(baseURLLabel
.String());
2151 subMenu
->AddItem(item
);
2152 subMenu
->AddItem(newItem
);
2153 // Add common submenu for this base URL, clickable.
2154 BMessage
* message
= new BMessage(GOTO_URL
);
2155 message
->AddString("url", baseURLLabel
.String());
2156 menu
->AddItem(new BMenuItem(subMenu
, message
), i
);
2161 menu
->AddItem(newItem
);
2166 addOrDeleteMenu(BMenu
* menu
, BMenu
* toMenu
)
2168 if (menu
->CountItems() > 0)
2169 toMenu
->AddItem(menu
);
2176 BrowserWindow::_UpdateHistoryMenu()
2178 BMenuItem
* menuItem
;
2179 while ((menuItem
= fHistoryMenu
->RemoveItem(fHistoryMenuFixedItemCount
)))
2182 BrowsingHistory
* history
= BrowsingHistory::DefaultInstance();
2183 if (!history
->Lock())
2186 int32 count
= history
->CountItems();
2187 BMenuItem
* clearHistoryItem
= new BMenuItem(B_TRANSLATE("Clear history"),
2188 new BMessage(CLEAR_HISTORY
));
2189 clearHistoryItem
->SetEnabled(count
> 0);
2190 fHistoryMenu
->AddItem(clearHistoryItem
);
2195 fHistoryMenu
->AddSeparatorItem();
2197 BDateTime todayStart
= BDateTime::CurrentDateTime(B_LOCAL_TIME
);
2198 todayStart
.SetTime(BTime(0, 0, 0));
2200 BDateTime oneDayAgoStart
= todayStart
;
2201 oneDayAgoStart
.Date().AddDays(-1);
2203 BDateTime twoDaysAgoStart
= oneDayAgoStart
;
2204 twoDaysAgoStart
.Date().AddDays(-1);
2206 BDateTime threeDaysAgoStart
= twoDaysAgoStart
;
2207 threeDaysAgoStart
.Date().AddDays(-1);
2209 BDateTime fourDaysAgoStart
= threeDaysAgoStart
;
2210 fourDaysAgoStart
.Date().AddDays(-1);
2212 BDateTime fiveDaysAgoStart
= fourDaysAgoStart
;
2213 fiveDaysAgoStart
.Date().AddDays(-1);
2215 BMenu
* todayMenu
= new BMenu(B_TRANSLATE("Today"));
2216 BMenu
* yesterdayMenu
= new BMenu(B_TRANSLATE("Yesterday"));
2217 BMenu
* twoDaysAgoMenu
= new BMenu(
2218 twoDaysAgoStart
.Date().LongDayName().String());
2219 BMenu
* threeDaysAgoMenu
= new BMenu(
2220 threeDaysAgoStart
.Date().LongDayName().String());
2221 BMenu
* fourDaysAgoMenu
= new BMenu(
2222 fourDaysAgoStart
.Date().LongDayName().String());
2223 BMenu
* fiveDaysAgoMenu
= new BMenu(
2224 fiveDaysAgoStart
.Date().LongDayName().String());
2225 BMenu
* earlierMenu
= new BMenu(B_TRANSLATE("Earlier"));
2227 for (int32 i
= 0; i
< count
; i
++) {
2228 BrowsingHistoryItem historyItem
= history
->HistoryItemAt(i
);
2229 BMessage
* message
= new BMessage(GOTO_URL
);
2230 message
->AddString("url", historyItem
.URL().String());
2232 BString
truncatedUrl(historyItem
.URL());
2233 be_plain_font
->TruncateString(&truncatedUrl
, B_TRUNCATE_END
, 480);
2234 menuItem
= new BMenuItem(truncatedUrl
, message
);
2236 if (historyItem
.DateTime() < fiveDaysAgoStart
)
2237 addItemToMenuOrSubmenu(earlierMenu
, menuItem
);
2238 else if (historyItem
.DateTime() < fourDaysAgoStart
)
2239 addItemToMenuOrSubmenu(fiveDaysAgoMenu
, menuItem
);
2240 else if (historyItem
.DateTime() < threeDaysAgoStart
)
2241 addItemToMenuOrSubmenu(fourDaysAgoMenu
, menuItem
);
2242 else if (historyItem
.DateTime() < twoDaysAgoStart
)
2243 addItemToMenuOrSubmenu(threeDaysAgoMenu
, menuItem
);
2244 else if (historyItem
.DateTime() < oneDayAgoStart
)
2245 addItemToMenuOrSubmenu(twoDaysAgoMenu
, menuItem
);
2246 else if (historyItem
.DateTime() < todayStart
)
2247 addItemToMenuOrSubmenu(yesterdayMenu
, menuItem
);
2249 addItemToMenuOrSubmenu(todayMenu
, menuItem
);
2253 addOrDeleteMenu(todayMenu
, fHistoryMenu
);
2254 addOrDeleteMenu(yesterdayMenu
, fHistoryMenu
);
2255 addOrDeleteMenu(twoDaysAgoMenu
, fHistoryMenu
);
2256 addOrDeleteMenu(threeDaysAgoMenu
, fHistoryMenu
);
2257 addOrDeleteMenu(fourDaysAgoMenu
, fHistoryMenu
);
2258 addOrDeleteMenu(fiveDaysAgoMenu
, fHistoryMenu
);
2259 addOrDeleteMenu(earlierMenu
, fHistoryMenu
);
2264 BrowserWindow::_UpdateClipboardItems()
2266 BTextView
* focusTextView
= dynamic_cast<BTextView
*>(CurrentFocus());
2267 if (focusTextView
!= NULL
) {
2268 int32 selectionStart
;
2270 focusTextView
->GetSelection(&selectionStart
, &selectionEnd
);
2271 bool hasSelection
= selectionStart
< selectionEnd
;
2272 bool canPaste
= false;
2273 // A BTextView has the focus.
2274 if (be_clipboard
->Lock()) {
2275 BMessage
* data
= be_clipboard
->Data();
2277 canPaste
= data
->HasData("text/plain", B_MIME_TYPE
);
2278 be_clipboard
->Unlock();
2280 fCutMenuItem
->SetEnabled(hasSelection
);
2281 fCopyMenuItem
->SetEnabled(hasSelection
);
2282 fPasteMenuItem
->SetEnabled(canPaste
);
2283 } else if (CurrentWebView() != NULL
) {
2284 // Trigger update of the clipboard items, even if the
2285 // BWebView doesn't have focus, we'll dispatch these message
2286 // there anyway. This works so fast that the user can never see
2287 // the wrong enabled state when the menu opens until the result
2288 // message arrives. The initial state needs to be enabled, since
2289 // standard shortcut handling is always wrapped inside MenusBeginning()
2290 // and MenusEnded(), and since we update items asynchronously, we need
2291 // to have them enabled to begin with.
2292 fCutMenuItem
->SetEnabled(true);
2293 fCopyMenuItem
->SetEnabled(true);
2294 fPasteMenuItem
->SetEnabled(true);
2296 CurrentWebView()->WebPage()->SendEditingCapabilities();
2302 BrowserWindow::_ShowPage(BWebView
* view
)
2304 if (view
!= CurrentWebView()) {
2305 int32 tabIndex
= fTabManager
->TabForView(view
);
2307 // Page seems to be gone already?
2310 fTabManager
->SelectTab(tabIndex
);
2311 _TabChanged(tabIndex
);
2319 BrowserWindow::_ResizeToScreen()
2321 BScreen
screen(this);
2323 ResizeTo(screen
.Frame().Width(), screen
.Frame().Height());
2328 BrowserWindow::_SetAutoHideInterfaceInFullscreen(bool doIt
)
2330 if (fAutoHideInterfaceInFullscreenMode
== doIt
)
2333 fAutoHideInterfaceInFullscreenMode
= doIt
;
2334 if (fAppSettings
->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode
,
2336 fAppSettings
->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode
,
2340 if (fAutoHideInterfaceInFullscreenMode
) {
2341 BMessage
message(CHECK_AUTO_HIDE_INTERFACE
);
2342 fPulseRunner
= new BMessageRunner(BMessenger(this), &message
, 300000);
2344 delete fPulseRunner
;
2345 fPulseRunner
= NULL
;
2346 _ShowInterface(true);
2352 BrowserWindow::_CheckAutoHideInterface()
2354 if (!fIsFullscreen
|| !fAutoHideInterfaceInFullscreenMode
2355 || (CurrentWebView() != NULL
&& !CurrentWebView()->IsFocus())) {
2359 if (fLastMousePos
.y
== 0)
2360 _ShowInterface(true);
2361 else if (fNavigationGroup
->IsVisible()
2362 && fLastMousePos
.y
> fNavigationGroup
->Frame().bottom
2363 && system_time() - fLastMouseMovedTime
> 1000000) {
2364 // NOTE: Do not re-use navigationGroupBottom in the above
2365 // check, since we only want to hide the interface when it is visible.
2366 _ShowInterface(false);
2372 BrowserWindow::_ShowInterface(bool show
)
2374 if (fInterfaceVisible
== show
)
2377 fInterfaceVisible
= show
;
2380 #if !INTEGRATE_MENU_INTO_TAB_BAR
2381 fMenuGroup
->SetVisible(
2382 (fVisibleInterfaceElements
& INTERFACE_ELEMENT_MENU
) != 0);
2384 fTabGroup
->SetVisible(_TabGroupShouldBeVisible());
2385 fNavigationGroup
->SetVisible(
2386 (fVisibleInterfaceElements
& INTERFACE_ELEMENT_NAVIGATION
) != 0);
2387 fStatusGroup
->SetVisible(
2388 (fVisibleInterfaceElements
& INTERFACE_ELEMENT_STATUS
) != 0);
2390 fMenuGroup
->SetVisible(false);
2391 fTabGroup
->SetVisible(false);
2392 fNavigationGroup
->SetVisible(false);
2393 fStatusGroup
->SetVisible(false);
2395 // TODO: Setting the group visible seems to unhide the status bar.
2397 while (!fLoadingProgressBar
->IsHidden())
2398 fLoadingProgressBar
->Hide();
2403 BrowserWindow::_ShowProgressBar(bool show
)
2406 if (!fStatusGroup
->IsVisible() && (fVisibleInterfaceElements
2407 & INTERFACE_ELEMENT_STATUS
) != 0)
2408 fStatusGroup
->SetVisible(true);
2409 fLoadingProgressBar
->Show();
2411 if (!fInterfaceVisible
)
2412 fStatusGroup
->SetVisible(false);
2413 // TODO: This is also used in _ShowInterface. Without it the status bar
2414 // doesn't always hide again. It may be an Interface Kit bug.
2415 while (!fLoadingProgressBar
->IsHidden())
2416 fLoadingProgressBar
->Hide();
2422 BrowserWindow::_InvokeButtonVisibly(BButton
* button
)
2424 button
->SetValue(B_CONTROL_ON
);
2428 button
->SetValue(B_CONTROL_OFF
);
2433 BrowserWindow::_NewTabURL(bool isNewWindow
) const
2436 uint32 policy
= isNewWindow
? fNewWindowPolicy
: fNewTabPolicy
;
2437 // Implement new page policy
2440 url
= fStartPageURL
;
2442 case OpenSearchPage
:
2443 url
.SetTo(fSearchPageURL
);
2444 url
.ReplaceAll("%s", "");
2446 case CloneCurrentPage
:
2447 if (CurrentWebView() != NULL
)
2448 url
= CurrentWebView()->MainFrameURL();
2459 BrowserWindow::_EncodeURIComponent(const BString
& search
)
2461 // We have to take care of some of the escaping before we hand over the
2462 // search string to WebKit, if we want queries like "4+3" to not be
2463 // searched as "4 3".
2464 const BString escCharList
= " $&`:<>[]{}\"+#%@/;=?\\^|~\',";
2465 BString result
= search
;
2468 for (int32 i
= 0; i
< result
.Length(); i
++) {
2469 if (escCharList
.FindFirst(result
[i
]) != B_ERROR
) {
2470 sprintf(hexcode
, "%02X", (unsigned int)result
[i
]);
2471 result
.SetByteAt(i
, '%');
2472 result
.Insert(hexcode
, i
+ 1);
2482 BrowserWindow::_VisitURL(const BString
& url
)
2484 // fURLInputGroup->TextView()->SetText(url);
2485 CurrentWebView()->LoadURL(url
.String());
2490 BrowserWindow::_VisitSearchEngine(const BString
& search
)
2492 BString
engine(fSearchPageURL
);
2493 engine
.ReplaceAll("%s", _EncodeURIComponent(search
).String());
2500 BrowserWindow::_IsValidDomainChar(char ch
)
2502 // TODO: Currenlty, only a whitespace character breaks a domain name. It
2503 // might be a good idea (or a bad one) to make character filtering based on
2504 // the IDNA 2008 standard.
2510 /*! \brief "smart" parser for user-entered URLs
2512 We try to be flexible in what we accept as a valid URL. The protocol may
2513 be missing, or something we can't handle (in that case we run the matching
2514 app). If all attempts to make sense of the input fail, we make a search
2515 engine query for it.
2518 BrowserWindow::_SmartURLHandler(const BString
& url
)
2520 // First test if the URL has a protocol field
2521 int32 at
= url
.FindFirst(":");
2523 if (at
!= B_ERROR
) {
2524 // There is a protocol, let's see if we can handle it
2526 url
.CopyInto(proto
, 0, at
);
2528 bool handled
= false;
2530 // First try the built-in supported ones
2531 for (unsigned int i
= 0; i
< sizeof(kHandledProtocols
) / sizeof(char*);
2533 handled
= (proto
== kHandledProtocols
[i
]);
2539 // This is the easy case, a complete and well-formed URL, we can
2540 // navigate to it without further efforts.
2544 // There is what looks like a protocol, but one we don't know.
2545 // Ask the BRoster if there is a matching filetype and app which
2548 temp
= "application/x-vnd.Be.URL.";
2551 const char* argv
[] = { url
.String(), NULL
};
2553 if (be_roster
->Launch(temp
.String(), 1, argv
) == B_OK
)
2558 // There is no protocol or only an unsupported one. So let's try harder to
2559 // guess what the request is.
2561 // "localhost" is a special case, it is a valid domain name but has no dots.
2562 // Handle it separately.
2563 if (url
== "localhost")
2564 _VisitURL("http://localhost/");
2566 // Also handle URLs starting with "localhost" followed by a path.
2567 const char* localhostPrefix
= "localhost/";
2569 if (url
.Compare(localhostPrefix
, strlen(localhostPrefix
)) == 0)
2572 // In all other cases we try to detect a valid domain name. There
2573 // must be at least one dot and no spaces until the first / in the
2577 for (int32 i
= 0; i
< url
.CountChars(); i
++) {
2580 else if (url
[i
] == '/')
2582 else if (!_IsValidDomainChar(url
[i
])) {
2590 // This is apparently an URL missing the protocol part. In that
2591 // case we default to http.
2592 BString prefixed
= "http://";
2594 _VisitURL(prefixed
);
2597 // We couldn't find anything that looks like an URL. Let's
2598 // assume what we have is a search request and go to the search
2600 _VisitSearchEngine(url
);
2608 BrowserWindow::_HandlePageSourceResult(const BMessage
* message
)
2610 // TODO: This should be done in an extra thread perhaps. Doing it in
2611 // the application thread is not much better, since it actually draws
2614 BPath pathToPageSource
;
2617 status_t ret
= message
->FindString("url", &url
);
2618 if (ret
== B_OK
&& url
.FindFirst("file://") == 0) {
2620 url
.Remove(0, strlen("file://"));
2621 pathToPageSource
.SetTo(url
.String());
2623 // Something else, store it.
2624 // TODO: What if it isn't HTML, but for example SVG?
2626 ret
= message
->FindString("source", &source
);
2629 ret
= find_directory(B_SYSTEM_TEMP_DIRECTORY
, &pathToPageSource
);
2631 BString
tmpFileName("PageSource_");
2632 tmpFileName
<< system_time() << ".html";
2634 ret
= pathToPageSource
.Append(tmpFileName
.String());
2636 BFile
pageSourceFile(pathToPageSource
.Path(),
2637 B_CREATE_FILE
| B_ERASE_FILE
| B_WRITE_ONLY
);
2639 ret
= pageSourceFile
.InitCheck();
2642 ssize_t written
= pageSourceFile
.Write(source
.String(),
2644 if (written
!= source
.Length())
2645 ret
= (status_t
)written
;
2649 const char* type
= "text/html";
2650 size_t size
= strlen(type
);
2651 pageSourceFile
.WriteAttr("BEOS:TYPE", B_STRING_TYPE
, 0, type
, size
);
2652 // If it fails we don't care.
2658 ret
= get_ref_for_path(pathToPageSource
.Path(), &ref
);
2661 BMessage
refsMessage(B_REFS_RECEIVED
);
2662 ret
= refsMessage
.AddRef("refs", &ref
);
2664 ret
= be_roster
->Launch("text/x-source-code", &refsMessage
);
2665 if (ret
== B_ALREADY_RUNNING
)
2672 snprintf(buffer
, sizeof(buffer
), "Failed to show the "
2673 "page source: %s\n", strerror(ret
));
2674 BAlert
* alert
= new BAlert(B_TRANSLATE("Page source error"), buffer
,
2676 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
2683 BrowserWindow::_ShowBookmarkBar(bool show
)
2685 // It is not allowed to show the bookmark bar when it is empty
2686 if (show
&& (fBookmarkBar
== NULL
|| fBookmarkBar
->CountItems() <= 1))
2688 fBookmarkBarMenuItem
->SetMarked(false);
2692 fBookmarkBarMenuItem
->SetMarked(show
);
2694 if (fBookmarkBar
== NULL
|| fBookmarkBar
->IsHidden() != show
)
2697 fAppSettings
->SetValue(kSettingsShowBookmarkBar
, show
);
2700 fBookmarkBar
->Show();
2702 fBookmarkBar
->Hide();