diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/views/location_bar_view.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/views/location_bar_view.cc')
-rw-r--r-- | chrome/browser/views/location_bar_view.cc | 1048 |
1 files changed, 1048 insertions, 0 deletions
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc new file mode 100644 index 0000000..515646d --- /dev/null +++ b/chrome/browser/views/location_bar_view.cc @@ -0,0 +1,1048 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/views/location_bar_view.h" + +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/theme/theme_resources.h" +#include "chrome/browser/alternate_nav_url_fetcher.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/page_info_window.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/ssl_error_info.h" +#include "chrome/browser/template_url.h" +#include "chrome/browser/template_url_model.h" +#include "chrome/browser/view_ids.h" +#include "chrome/browser/views/info_bubble.h" +#include "chrome/browser/views/first_run_bubble.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/gfx/chrome_canvas.h" +#include "chrome/common/win_util.h" +#include "chrome/views/background.h" +#include "chrome/views/border.h" +#include "chrome/views/view_container.h" +#include "googleurl/src/gurl.h" +#include "googleurl/src/url_canon.h" +#include "generated_resources.h" + +using ChromeViews::View; + +const int LocationBarView::kTextVertMargin = 2; + +const COLORREF LocationBarView::kBackgroundColorByLevel[] = { + RGB(255, 245, 195), // SecurityLevel SECURE: Yellow. + RGB(255, 255, 255), // SecurityLevel NORMAL: White. + RGB(255, 255, 255), // SecurityLevel INSECURE: White. +}; + +// The margins around the solid color we draw. +static const int kBackgroundVertMargin = 2; +static const int kBackgroundHoriMargin = 0; + +// Padding on the right and left of the entry field. +static const int kEntryPadding = 3; + +// Padding between the entry and the leading/trailing views. +static const int kInnerPadding = 3; + +static const SkBitmap* kBackground = NULL; + +static const SkBitmap* kPopupBackgroundLeft = NULL; +static const SkBitmap* kPopupBackgroundCenter = NULL; +static const SkBitmap* kPopupBackgroundRight = NULL; +static const int kPopupBackgroundVertMargin = 2; +static const int kPopupBackgroundHorzMargin = 2; + +// The delay the mouse has to be hovering over the lock/warning icon before the +// info bubble is shown. +static const int kInfoBubbleHoverDelayMs = 500; + +// The tab key image. +static const SkBitmap* kTabButtonBitmap = NULL; + +// Returns the description for a keyword. +static std::wstring GetKeywordDescription(Profile* profile, + const std::wstring& keyword) { + // Make sure the TemplateURL still exists. + // TODO(sky): Once LocationBarView adds a listener to the TemplateURLModel + // to track changes to the model, this should become a DCHECK. + const TemplateURL* template_url = + profile->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword); + return template_url ? template_url->short_name() : std::wstring(); +} + +LocationBarView::LocationBarView(Profile* profile, + CommandController* controller, + ToolbarModel* model, + Delegate* delegate, + bool popup_window_mode) + : profile_(profile), + controller_(controller), + model_(model), + delegate_(delegate), + disposition_(CURRENT_TAB), + location_entry_view_(NULL), + selected_keyword_view_(profile), + keyword_hint_view_(profile), + type_to_search_view_(l10n_util::GetString(IDS_OMNIBOX_EMPTY_TEXT)), + security_image_view_(profile, model), + popup_window_mode_(popup_window_mode), + first_run_bubble_(this) { + DCHECK(profile_); + SetID(VIEW_ID_LOCATION_BAR); + SetFocusable(true); + + if (!kBackground) { + ResourceBundle &rb = ResourceBundle::GetSharedInstance(); + kBackground = rb.GetBitmapNamed(IDR_LOCATIONBG); + kPopupBackgroundLeft = + rb.GetBitmapNamed(IDR_LOCATIONBG_POPUPMODE_LEFT); + kPopupBackgroundCenter = + rb.GetBitmapNamed(IDR_LOCATIONBG_POPUPMODE_CENTER); + kPopupBackgroundRight = + rb.GetBitmapNamed(IDR_LOCATIONBG_POPUPMODE_RIGHT); + } +} + +bool LocationBarView::IsInitialized() const { + return location_entry_view_ != NULL; +} + +void LocationBarView::Init() { + if (popup_window_mode_) { + font_ = ResourceBundle::GetSharedInstance().GetFont( + ResourceBundle::BaseFont); + } else { + // Use a larger version of the system font. + font_ = font_.DeriveFont(3); + } + + // URL edit field. + ChromeViews::ViewContainer* vc = GetViewContainer(); + DCHECK(vc) << "LocationBarView::Init - vc is NULL!"; + location_entry_.reset(new AutocompleteEdit(font_, this, model_, this, + vc->GetHWND(), + profile_, controller_, + popup_window_mode_)); + + // View container for URL edit field. + location_entry_view_ = new ChromeViews::HWNDView; + DCHECK(location_entry_view_) << "LocationBarView::Init - OOM!"; + location_entry_view_->SetID(VIEW_ID_AUTOCOMPLETE); + AddChildView(location_entry_view_); + location_entry_view_->SetAssociatedFocusView(this); + location_entry_view_->Attach(location_entry_->m_hWnd); + + AddChildView(&selected_keyword_view_); + selected_keyword_view_.SetFont(font_); + selected_keyword_view_.SetVisible(false); + selected_keyword_view_.SetParentOwned(false); + + DWORD sys_color = GetSysColor(COLOR_GRAYTEXT); + SkColor gray = SkColorSetRGB(GetRValue(sys_color), GetGValue(sys_color), + GetBValue(sys_color)); + + AddChildView(&type_to_search_view_); + type_to_search_view_.SetVisible(false); + type_to_search_view_.SetFont(font_); + type_to_search_view_.SetColor(gray); + type_to_search_view_.SetParentOwned(false); + + AddChildView(&keyword_hint_view_); + keyword_hint_view_.SetVisible(false); + keyword_hint_view_.SetFont(font_); + keyword_hint_view_.SetColor(gray); + keyword_hint_view_.SetParentOwned(false); + + AddChildView(&security_image_view_); + security_image_view_.SetVisible(false); + security_image_view_.SetParentOwned(false); + + AddChildView(&info_label_); + info_label_.SetVisible(false); + info_label_.SetParentOwned(false); + + // Notify us when any ancestor is resized. In this case we want to tell the + // AutocompleteEdit to close its popup. + SetNotifyWhenVisibleBoundsInRootChanges(true); + + // Initialize the location entry. We do this to avoid a black flash which is + // visible when the location entry has just been initialized. + Update(NULL); + + OnChanged(); +} + +void LocationBarView::Update(const TabContents* tab_for_state_restoring) { + SetSecurityIcon(model_->GetIcon()); + std::wstring info_text, info_tooltip; + SkColor text_color; + model_->GetInfoText(&info_text, &text_color, &info_tooltip); + SetInfoText(info_text, text_color, info_tooltip); + location_entry_->Update(tab_for_state_restoring); + Layout(); + SchedulePaint(); +} + +void LocationBarView::Focus() { + ::SetFocus(location_entry_->m_hWnd); +} + +void LocationBarView::SetProfile(Profile* profile) { + DCHECK(profile); + if (profile_ != profile) { + profile_ = profile; + location_entry_->SetProfile(profile); + selected_keyword_view_.set_profile(profile); + keyword_hint_view_.set_profile(profile); + security_image_view_.set_profile(profile); + } +} + +void LocationBarView::GetPreferredSize(CSize *out) { + CSize size; + security_image_view_.GetPreferredSize(&size); + out->cx = 0; + + out->cy = std::max( + (popup_window_mode_ ? kPopupBackgroundCenter : kBackground)->height(), + static_cast<int>(size.cy)); +} + +void LocationBarView::DidChangeBounds(const CRect& previous, + const CRect& current) { + Layout(); +} + +void LocationBarView::Layout() { + DoLayout(true); +} + +void LocationBarView::Paint(ChromeCanvas* canvas) { + View::Paint(canvas); + + SkColor bg = SkColorSetRGB( + GetRValue(kBackgroundColorByLevel[model_->GetSchemeSecurityLevel()]), + GetGValue(kBackgroundColorByLevel[model_->GetSchemeSecurityLevel()]), + GetBValue(kBackgroundColorByLevel[model_->GetSchemeSecurityLevel()])); + + if (popup_window_mode_ == false) { + int bh = kBackground->height(); + + canvas->TileImageInt(*kBackground, 0, (GetHeight() - bh) / 2, GetWidth(), + bh); + + canvas->FillRectInt(bg, kBackgroundHoriMargin, kBackgroundVertMargin, + GetWidth() - 2 * kBackgroundHoriMargin, + bh - kBackgroundVertMargin * 2); + } else { + canvas->TileImageInt(*kPopupBackgroundLeft, 0, 0, + kPopupBackgroundLeft->width(), + kPopupBackgroundLeft->height()); + canvas->TileImageInt(*kPopupBackgroundCenter, + kPopupBackgroundLeft->width(), 0, + GetWidth() - + kPopupBackgroundLeft->width() - + kPopupBackgroundRight->width(), + kPopupBackgroundCenter->height()); + canvas->TileImageInt(*kPopupBackgroundRight, + GetWidth() - kPopupBackgroundRight->width(), + 0, kPopupBackgroundRight->width(), + kPopupBackgroundRight->height()); + + canvas->FillRectInt(bg, kPopupBackgroundHorzMargin, + kPopupBackgroundVertMargin, + GetWidth() - kPopupBackgroundHorzMargin * 2, + kPopupBackgroundCenter->height() - + kPopupBackgroundVertMargin * 2); + } +} + +bool LocationBarView::CanProcessTabKeyEvents() { + // We want to receive tab key events when the hint is showing. + return keyword_hint_view_.IsVisible(); +} + +void LocationBarView::VisibleBoundsInRootChanged() { + location_entry_->ClosePopup(); +} + +bool LocationBarView::OnMousePressed(const ChromeViews::MouseEvent& event) { + UINT msg; + if (event.IsLeftMouseButton()) { + msg = (event.GetFlags() & ChromeViews::MouseEvent::EF_IS_DOUBLE_CLICK) ? + WM_LBUTTONDBLCLK : WM_LBUTTONDOWN; + } else if (event.IsMiddleMouseButton()) { + msg = (event.GetFlags() & ChromeViews::MouseEvent::EF_IS_DOUBLE_CLICK) ? + WM_MBUTTONDBLCLK : WM_MBUTTONDOWN; + } else if (event.IsRightMouseButton()) { + msg = (event.GetFlags() & ChromeViews::MouseEvent::EF_IS_DOUBLE_CLICK) ? + WM_RBUTTONDBLCLK : WM_RBUTTONDOWN; + } else { + NOTREACHED(); + return false; + } + OnMouseEvent(event, msg); + return true; +} + +bool LocationBarView::OnMouseDragged(const ChromeViews::MouseEvent& event) { + OnMouseEvent(event, WM_MOUSEMOVE); + return true; +} + +void LocationBarView::OnMouseReleased(const ChromeViews::MouseEvent& event, + bool canceled) { + UINT msg; + if (canceled) { + msg = WM_CAPTURECHANGED; + } else if (event.IsLeftMouseButton()) { + msg = WM_LBUTTONUP; + } else if (event.IsMiddleMouseButton()) { + msg = WM_MBUTTONUP; + } else if (event.IsRightMouseButton()) { + msg = WM_RBUTTONUP; + } else { + NOTREACHED(); + return; + } + OnMouseEvent(event, msg); +} + +void LocationBarView::OnAutocompleteAccept( + const std::wstring& url, + WindowOpenDisposition disposition, + PageTransition::Type transition, + const std::wstring& alternate_nav_url) { + if (url.empty()) + return; + + location_input_ = url; + disposition_ = disposition; + transition_ = transition; + + if (controller_) { + if (alternate_nav_url.empty()) { + controller_->ExecuteCommand(IDC_OPENURL); + return; + } + + scoped_ptr<AlternateNavURLFetcher> fetcher( + new AlternateNavURLFetcher(alternate_nav_url)); + // The AlternateNavURLFetcher will listen for the next navigation state + // update notification (expecting it to be a new page load) and hook + // itself in to that loading process. + controller_->ExecuteCommand(IDC_OPENURL); + if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) { + // I'm not sure this should be reachable, but I'm not also sure enough + // that it shouldn't to stick in a NOTREACHED(). In any case, this is + // harmless; we can simply let the fetcher get deleted here and it will + // clean itself up properly. + } else { + fetcher.release(); // The navigation controller will delete the fetcher. + } + } +} + +void LocationBarView::OnChanged() { + DoLayout(false); +} + +SkBitmap LocationBarView::GetFavIcon() const { + DCHECK(delegate_); + DCHECK(delegate_->GetTabContents()); + return delegate_->GetTabContents()->GetFavIcon(); +} + +std::wstring LocationBarView::GetTitle() const { + DCHECK(delegate_); + DCHECK(delegate_->GetTabContents()); + return delegate_->GetTabContents()->GetTitle(); +} + +void LocationBarView::DoLayout(const bool force_layout) { + if (!location_entry_.get()) + return; + + RECT formatting_rect; + location_entry_->GetRect(&formatting_rect); + RECT edit_bounds; + location_entry_->GetClientRect(&edit_bounds); + + int entry_width = GetWidth() - kEntryPadding - kEntryPadding; + CSize security_image_size; + if (security_image_view_.IsVisible()) { + security_image_view_.GetPreferredSize(&security_image_size); + entry_width -= security_image_size.cx; + } + CSize info_label_size; + if (info_label_.IsVisible()) { + info_label_.GetPreferredSize(&info_label_size); + entry_width -= (info_label_size.cx + kInnerPadding); + } + + const int max_edit_width = entry_width - formatting_rect.left - + (edit_bounds.right - formatting_rect.right); + if (max_edit_width < 0) + return; + const int text_width = TextDisplayWidth(); + bool needs_layout = force_layout; + needs_layout |= AdjustHints(text_width, max_edit_width); + + if (!needs_layout) + return; + + // TODO(sky): baseline layout. + int bh = kBackground->height(); + int location_y = ((GetHeight() - bh) / 2) + kTextVertMargin; + int location_height = bh - (2 * kTextVertMargin); + if (info_label_.IsVisible()) { + info_label_.SetBounds(GetWidth() - kEntryPadding - info_label_size.cx, + location_y, + info_label_size.cx, location_height); + } + if (security_image_view_.IsVisible()) { + const int info_label_width = info_label_size.cx ? + info_label_size.cx + kInnerPadding : 0; + security_image_view_.SetBounds(GetWidth() - kEntryPadding - + info_label_width - + security_image_size.cx, + location_y, + security_image_size.cx, location_height); + } + gfx::Rect location_bounds(kEntryPadding, location_y, entry_width, + location_height); + if (selected_keyword_view_.IsVisible()) { + LayoutView(true, &selected_keyword_view_, text_width, max_edit_width, + &location_bounds); + } else if (keyword_hint_view_.IsVisible()) { + LayoutView(false, &keyword_hint_view_, text_width, max_edit_width, + &location_bounds); + } else if (type_to_search_view_.IsVisible()) { + LayoutView(false, &type_to_search_view_, text_width, max_edit_width, + &location_bounds); + } + + location_entry_view_->SetBounds(location_bounds.x(), + location_bounds.y(), + location_bounds.width(), + location_bounds.height()); + if (!force_layout) { + // If force_layout is false and we got this far it means one of the views + // was added/removed or changed in size. We need to paint ourselves. + SchedulePaint(); + } +} + +int LocationBarView::TextDisplayWidth() { + POINT last_char_position = + location_entry_->PosFromChar(location_entry_->GetTextLength()); + POINT scroll_position; + location_entry_->GetScrollPos(&scroll_position); + const int position_x = last_char_position.x + scroll_position.x; + return UILayoutIsRightToLeft() ? GetWidth() - position_x : position_x; +} + +bool LocationBarView::UsePref(int pref_width, int text_width, int max_width) { + return (pref_width + kInnerPadding + text_width <= max_width); +} + +bool LocationBarView::NeedsResize(View* view, int text_width, int max_width) { + CSize size; + view->GetPreferredSize(&size); + if (!UsePref(size.cx, text_width, max_width)) + view->GetMinimumSize(&size); + return (view->GetWidth() != size.cx); +} + +bool LocationBarView::AdjustHints(int text_width, int max_width) { + const std::wstring keyword(location_entry_->keyword()); + const bool is_keyword_hint(location_entry_->is_keyword_hint()); + const bool show_selected_keyword = !keyword.empty() && !is_keyword_hint; + const bool show_keyword_hint = !keyword.empty() && is_keyword_hint; + bool show_search_hint(location_entry_->show_search_hint()); + DCHECK(keyword.empty() || !show_search_hint); + + if (show_search_hint) { + // Only show type to search if all the text fits. + CSize view_pref; + type_to_search_view_.GetPreferredSize(&view_pref); + show_search_hint = UsePref(view_pref.cx, text_width, max_width); + } + + // NOTE: This isn't just one big || statement as ToggleVisibility MUST be + // invoked for each view. + bool needs_layout = false; + needs_layout |= ToggleVisibility(show_selected_keyword, + &selected_keyword_view_); + needs_layout |= ToggleVisibility(show_keyword_hint, &keyword_hint_view_); + needs_layout |= ToggleVisibility(show_search_hint, &type_to_search_view_); + if (show_selected_keyword) { + if (selected_keyword_view_.keyword() != keyword) { + needs_layout = true; + selected_keyword_view_.SetKeyword(keyword); + } + needs_layout |= NeedsResize(&selected_keyword_view_, text_width, max_width); + } else if (show_keyword_hint) { + if (keyword_hint_view_.keyword() != keyword) { + needs_layout = true; + keyword_hint_view_.SetKeyword(keyword); + } + needs_layout |= NeedsResize(&keyword_hint_view_, text_width, max_width); + } + + return needs_layout; +} + +void LocationBarView::LayoutView(bool leading, ChromeViews::View* view, + int text_width, int max_width, + gfx::Rect* bounds) { + DCHECK(view && bounds); + CSize view_size(0, 0); + view->GetPreferredSize(&view_size); + if (!UsePref(view_size.cx, text_width, max_width)) + view->GetMinimumSize(&view_size); + if (view_size.cx + kInnerPadding < bounds->width()) { + view->SetVisible(true); + if (leading) { + view->SetBounds(bounds->x(), bounds->y(), view_size.cx, bounds->height()); + bounds->Offset(view_size.cx + kInnerPadding, 0); + } else { + view->SetBounds(bounds->right() - view_size.cx, bounds->y(), + view_size.cx, bounds->height()); + } + bounds->set_width(bounds->width() - view_size.cx - kInnerPadding); + } else { + view->SetVisible(false); + } +} + +void LocationBarView::SetSecurityIcon(ToolbarModel::Icon icon) { + switch (icon) { + case ToolbarModel::LOCK_ICON: + security_image_view_.SetImageShown(SecurityImageView::LOCK); + security_image_view_.SetVisible(true); + break; + case ToolbarModel::WARNING_ICON: + security_image_view_.SetImageShown(SecurityImageView::WARNING); + security_image_view_.SetVisible(true); + break; + case ToolbarModel::NO_ICON: + security_image_view_.SetVisible(false); + break; + default: + NOTREACHED(); + security_image_view_.SetVisible(false); + break; + } +} + +void LocationBarView::SetInfoText(const std::wstring& text, + SkColor text_color, + const std::wstring& tooltip_text) { + info_label_.SetVisible(!text.empty()); + info_label_.SetText(text); + info_label_.SetColor(text_color); + info_label_.SetTooltipText(tooltip_text); +} + +bool LocationBarView::ToggleVisibility(bool new_vis, View* view) { + DCHECK(view); + if (view->IsVisible() != new_vis) { + view->SetVisible(new_vis); + return true; + } + return false; +} + +void LocationBarView::OnMouseEvent(const ChromeViews::MouseEvent& event, + UINT msg) { + UINT flags = 0; + if (event.IsControlDown()) + flags |= MK_CONTROL; + if (event.IsShiftDown()) + flags |= MK_SHIFT; + if (event.IsLeftMouseButton()) + flags |= MK_LBUTTON; + if (event.IsMiddleMouseButton()) + flags |= MK_MBUTTON; + if (event.IsRightMouseButton()) + flags |= MK_RBUTTON; + + CPoint screen_point(event.GetLocation()); + ConvertPointToScreen(this, &screen_point); + + location_entry_->HandleExternalMsg(msg, flags, screen_point); +} + +bool LocationBarView::GetAccessibleRole(VARIANT* role) { + DCHECK(role); + + role->vt = VT_I4; + role->lVal = ROLE_SYSTEM_GROUPING; + return true; +} + +// SelectedKeywordView ------------------------------------------------------- + +// The background is drawn using ImagePainter3. This is the left/center/right +// image names. +static const int kBorderImages[] = { + IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_L, + IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_C, + IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_R }; + +// Insets around the label. +static const int kTopInset = 0; +static const int kBottomInset = 0; +static const int kLeftInset = 4; +static const int kRightInset = 4; + +// Offset from the top the background is drawn at. +static const int kBackgroundYOffset = 2; + +LocationBarView::SelectedKeywordView::SelectedKeywordView(Profile* profile) + : background_painter_(kBorderImages), + profile_(profile) { + AddChildView(&full_label_); + AddChildView(&partial_label_); + // Full_label and partial_label are deleted by us, make sure View doesn't + // delete them too. + full_label_.SetParentOwned(false); + partial_label_.SetParentOwned(false); + full_label_.SetVisible(false); + partial_label_.SetVisible(false); + full_label_.SetBorder( + ChromeViews::Border::CreateEmptyBorder(kTopInset, kLeftInset, + kBottomInset, kRightInset)); + partial_label_.SetBorder( + ChromeViews::Border::CreateEmptyBorder(kTopInset, kLeftInset, + kBottomInset, kRightInset)); +} + +LocationBarView::SelectedKeywordView::~SelectedKeywordView() { +} + +void LocationBarView::SelectedKeywordView::SetFont(const ChromeFont& font) { + full_label_.SetFont(font); + partial_label_.SetFont(font); +} + +void LocationBarView::SelectedKeywordView::Paint(ChromeCanvas* canvas) { + canvas->TranslateInt(0, kBackgroundYOffset); + background_painter_.Paint(GetWidth(), GetHeight() - kTopInset, canvas); + canvas->TranslateInt(0, -kBackgroundYOffset); +} + +void LocationBarView::SelectedKeywordView::GetPreferredSize(CSize* size) { + full_label_.GetPreferredSize(size); +} + +void LocationBarView::SelectedKeywordView::GetMinimumSize(CSize* size) { + partial_label_.GetMinimumSize(size); +} + +void LocationBarView::SelectedKeywordView::DidChangeBounds( + const CRect& previous, + const CRect& current) { + Layout(); +} + +void LocationBarView::SelectedKeywordView::Layout() { + CSize pref; + GetPreferredSize(&pref); + bool at_pref = (GetWidth() == pref.cx); + if (at_pref) + full_label_.SetBounds(0, 0, GetWidth(), GetHeight()); + else + partial_label_.SetBounds(0, 0, GetWidth(), GetHeight()); + full_label_.SetVisible(at_pref); + partial_label_.SetVisible(!at_pref); +} + +void LocationBarView::SelectedKeywordView::SetKeyword( + const std::wstring& keyword) { + keyword_ = keyword; + if (keyword.empty()) + return; + DCHECK(profile_); + if (!profile_->GetTemplateURLModel()) + return; + + const std::wstring description = GetKeywordDescription(profile_, keyword); + full_label_.SetText(l10n_util::GetStringF(IDS_OMNIBOX_KEYWORD_TEXT, + description)); + const std::wstring min_string = CalculateMinString(description); + if (!min_string.empty()) { + partial_label_.SetText( + l10n_util::GetStringF(IDS_OMNIBOX_KEYWORD_TEXT, min_string)); + } else { + partial_label_.SetText(full_label_.GetText()); + } +} + +std::wstring LocationBarView::SelectedKeywordView::CalculateMinString( + const std::wstring& description) { + // Chop at the first '.' or whitespace. + const size_t dot_index = description.find(L'.'); + const size_t ws_index = description.find_first_of(kWhitespaceWide); + size_t chop_index = std::min(dot_index, ws_index); + if (chop_index == std::wstring::npos) { + // No dot or whitespace, truncate to at most 3 chars. + return l10n_util::TruncateString(description, 3); + } + return description.substr(0, chop_index); +} + +// KeywordHintView ------------------------------------------------------------- + +// Amount of space to offset the tab image from the top of the view by. +static const int kTabImageYOffset = 4; + +LocationBarView::KeywordHintView::KeywordHintView(Profile* profile) + : profile_(profile) { + AddChildView(&leading_label_); + AddChildView(&trailing_label_); + + if (!kTabButtonBitmap) { + kTabButtonBitmap = ResourceBundle::GetSharedInstance(). + GetBitmapNamed(IDR_LOCATION_BAR_KEYWORD_HINT_TAB); + } +} + +LocationBarView::KeywordHintView::~KeywordHintView() { + // Labels are freed by us. Remove them so that View doesn't + // try to free them too. + RemoveChildView(&leading_label_); + RemoveChildView(&trailing_label_); +} + +void LocationBarView::KeywordHintView::SetFont(const ChromeFont& font) { + leading_label_.SetFont(font); + trailing_label_.SetFont(font); +} + +void LocationBarView::KeywordHintView::SetColor(const SkColor& color) { + leading_label_.SetColor(color); + trailing_label_.SetColor(color); +} + +void LocationBarView::KeywordHintView::SetKeyword(const std::wstring& keyword) { + keyword_ = keyword; + if (keyword_.empty()) + return; + DCHECK(profile_); + if (!profile_->GetTemplateURLModel()) + return; + + std::vector<size_t> content_param_offsets; + const std::wstring keyword_hint(l10n_util::GetStringF( + IDS_OMNIBOX_KEYWORD_HINT, std::wstring(), + GetKeywordDescription(profile_, keyword), &content_param_offsets)); + if (content_param_offsets.size() == 2) { + leading_label_.SetText(keyword_hint.substr(0, + content_param_offsets.front())); + trailing_label_.SetText(keyword_hint.substr(content_param_offsets.front())); + } else { + // See comments on an identical NOTREACHED() in search_provider.cc. + NOTREACHED(); + } +} + +void LocationBarView::KeywordHintView::Paint(ChromeCanvas* canvas) { + int image_x = leading_label_.IsVisible() ? leading_label_.GetWidth() : 0; + + // Since we paint the button image directly on the canvas (instead of using a + // child view), we must mirror the button's position manually if the locale + // is right-to-left. + gfx::Rect tab_button_bounds(image_x, + kTabImageYOffset, + kTabButtonBitmap->width(), + kTabButtonBitmap->height()); + tab_button_bounds.set_x(MirroredLeftPointForRect(tab_button_bounds)); + canvas->DrawBitmapInt(*kTabButtonBitmap, + tab_button_bounds.x(), + tab_button_bounds.y()); +} + +void LocationBarView::KeywordHintView::GetPreferredSize(CSize *out) { + // TODO(sky): currently height doesn't matter, once baseline support is + // added this should check baselines. + leading_label_.GetPreferredSize(out); + int width = out->cx; + width += kTabButtonBitmap->width(); + trailing_label_.GetPreferredSize(out); + width += out->cx; + out->cx = width; +} + +void LocationBarView::KeywordHintView::GetMinimumSize(CSize* out) { + // TODO(sky): currently height doesn't matter, once baseline support is + // added this should check baselines. + out->cx = kTabButtonBitmap->width(); +} + +void LocationBarView::KeywordHintView::Layout() { + // TODO(sky): baseline layout. + bool show_labels = (GetWidth() != kTabButtonBitmap->width()); + + leading_label_.SetVisible(show_labels); + trailing_label_.SetVisible(show_labels); + int height = GetHeight(); + int x = 0; + CSize pref; + + if (show_labels) { + leading_label_.GetPreferredSize(&pref); + leading_label_.SetBounds(x, 0, pref.cx, height); + + x += pref.cx + kTabButtonBitmap->width(); + trailing_label_.GetPreferredSize(&pref); + trailing_label_.SetBounds(x, 0, pref.cx, height); + } +} + +void LocationBarView::KeywordHintView::DidChangeBounds(const CRect& previous, + const CRect& current) { + Layout(); +} + +// We don't translate accelerators for ALT + numpad digit, they are used for +// entering special characters. +bool LocationBarView::ShouldLookupAccelerators(const ChromeViews::KeyEvent& e) { + if (!e.IsAltDown()) + return true; + + return !win_util::IsNumPadDigit(e.GetCharacter(), e.IsExtendedKey()); +} + +// ShowInfoBubbleTask----------------------------------------------------------- + +class LocationBarView::ShowInfoBubbleTask : public Task { + public: + explicit ShowInfoBubbleTask(LocationBarView::SecurityImageView* image_view); + virtual void Run(); + void Cancel(); + + private: + LocationBarView::SecurityImageView* image_view_; + bool cancelled_; + + DISALLOW_EVIL_CONSTRUCTORS(ShowInfoBubbleTask); +}; + +LocationBarView::ShowInfoBubbleTask::ShowInfoBubbleTask( + LocationBarView::SecurityImageView* image_view) + : cancelled_(false), + image_view_(image_view) { +} + +void LocationBarView::ShowInfoBubbleTask::Run() { + if (cancelled_) + return; + + if (!image_view_->GetViewContainer()->IsActive()) { + // The browser is no longer active. Let's not show the info bubble, this + // would make the browser the active window again. Also makes sure we NULL + // show_info_bubble_task_ to prevent the SecurityImageView from keeping a + // dangling pointer. + image_view_->show_info_bubble_task_ = NULL; + return; + } + + image_view_->ShowInfoBubble(); +} + +void LocationBarView::ShowInfoBubbleTask::Cancel() { + cancelled_ = true; +} + +// ----------------------------------------------------------------------------- + +void LocationBarView::ShowFirstRunBubbleInternal() { + if (!location_entry_view_) + return; + if (!location_entry_view_->GetViewContainer()->IsActive()) { + // The browser is no longer active. Let's not show the info bubble, this + // would make the browser the active window again. + return; + } + + CPoint location(0, 0); + + // If the UI layout is RTL, the coordinate system is not transformed and + // therefore we need to adjust the X coordinate so that bubble appears on the + // right hand side of the location bar. + if (UILayoutIsRightToLeft()) + location.x += GetWidth(); + ChromeViews::View::ConvertPointToScreen(this, &location); + + // We try to guess that 20 pixels offset is a good place for the first + // letter in the OmniBox. + gfx::Rect bounds(location.x, location.y, 20, GetHeight()); + + // Moving the bounds "backwards" so that it appears within the location bar + // if the UI layout is RTL. + if (UILayoutIsRightToLeft()) + bounds.set_x(location.x - 20); + + FirstRunBubble::Show( + location_entry_view_->GetRootView()->GetViewContainer()->GetHWND(), + bounds); +} + +void LocationBarView::ShowFirstRunBubble() { + // We wait 30 milliseconds to open. It allows less flicker. + Task* task = first_run_bubble_.NewRunnableMethod( + &LocationBarView::ShowFirstRunBubbleInternal); + MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 30); +} + +// SecurityImageView------------------------------------------------------------ + +// static +SkBitmap* LocationBarView::SecurityImageView::lock_icon_ = NULL; +SkBitmap* LocationBarView::SecurityImageView::warning_icon_ = NULL; + +LocationBarView::SecurityImageView::SecurityImageView(Profile* profile, + ToolbarModel* model) + : profile_(profile), + model_(model), + show_info_bubble_task_(NULL), + info_bubble_(NULL) { + if (!lock_icon_) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + lock_icon_ = rb.GetBitmapNamed(IDR_LOCK); + warning_icon_ = rb.GetBitmapNamed(IDR_WARNING); + } + SetImageShown(LOCK); +} + +LocationBarView::SecurityImageView::~SecurityImageView() { + if (show_info_bubble_task_) + show_info_bubble_task_->Cancel(); + + if (info_bubble_) { + // We are going to be invalid, make sure the InfoBubble does not keep a + // pointer to us. + info_bubble_->SetDelegate(NULL); + } +} + +void LocationBarView::SecurityImageView::SetImageShown(Image image) { + switch (image) { + case LOCK: + ImageView::SetImage(lock_icon_); + break; + case WARNING: + ImageView::SetImage(warning_icon_); + break; + default: + NOTREACHED(); + break; + } +} + +void LocationBarView::SecurityImageView::ShowInfoBubble() { + std::wstring text; + SkColor text_color; + model_->GetIconHoverText(&text, &text_color); + + CPoint location(0, 0); + ChromeViews::View::ConvertPointToScreen(this, &location); + gfx::Rect bounds(location.x, location.y, GetWidth(), GetHeight()); + + ChromeViews::Label* label = new ChromeViews::Label(text); + label->SetMultiLine(true); + label->SetColor(text_color); + label->SetFont(ResourceBundle::GetSharedInstance().GetFont( + ResourceBundle::BaseFont).DeriveFont(2)); + label->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT); + label->SizeToFit(0); + DCHECK(info_bubble_ == NULL); + info_bubble_ = InfoBubble::Show(GetRootView()->GetViewContainer()->GetHWND(), + bounds, label, this); + show_info_bubble_task_ = NULL; +} + +void LocationBarView::SecurityImageView::OnMouseMoved( + const ChromeViews::MouseEvent& event) { + if (show_info_bubble_task_) { + show_info_bubble_task_->Cancel(); + show_info_bubble_task_ = NULL; + } + + if (info_bubble_) { + // If an info bubble is currently showing, nothing to do. + return; + } + + show_info_bubble_task_ = new ShowInfoBubbleTask(this); + MessageLoop::current()->PostDelayedTask(FROM_HERE, show_info_bubble_task_, + kInfoBubbleHoverDelayMs); +} + +void LocationBarView::SecurityImageView::OnMouseExited( + const ChromeViews::MouseEvent& event) { + if (show_info_bubble_task_) { + show_info_bubble_task_->Cancel(); + show_info_bubble_task_ = NULL; + } + + if (info_bubble_) + info_bubble_->Close(); +} + +bool LocationBarView::SecurityImageView::OnMousePressed( + const ChromeViews::MouseEvent& event) { + NavigationEntry* nav_entry = + BrowserList::GetLastActive()->GetSelectedTabContents()-> + controller()->GetActiveEntry(); + PageInfoWindow::Create(profile_, + nav_entry, + GetRootView()->GetViewContainer()->GetHWND(), + PageInfoWindow::SECURITY); + return true; +} + +void LocationBarView::SecurityImageView::InfoBubbleClosing( + InfoBubble* info_bubble) { + info_bubble_ = NULL; +} + +bool LocationBarView::OverrideAccelerator( + const ChromeViews::Accelerator& accelerator) { + return location_entry_->OverrideAccelerator(accelerator); +} |