// 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/app/theme/theme_resources.h" #include "chrome/browser/standard_layout.h" #include "chrome/browser/views/info_bar_item_view.h" #include "chrome/common/l10n_util.h" #include "chrome/common/resource_bundle.h" #include "chrome/views/external_focus_tracker.h" #include "chrome/views/image_view.h" #include "chrome/views/root_view.h" #include "chrome/views/view_container.h" #include "generated_resources.h" namespace { class HorizontalSpacer : public ChromeViews::View { public: explicit HorizontalSpacer(int width) : width_(width) {} void GetPreferredSize(CSize* out) { out->cx = width_; out->cy = 0; } private: int width_; }; const int kInfoBarVerticalSpacing = 3; const int kInfoBarLeftMargin = 3; const double kInfoBarHeight = 37.0; } // namespace InfoBarItemView::InfoBarItemView() : insert_index_(0), close_button_(NULL), icon_(NULL) { Init(); } InfoBarItemView::~InfoBarItemView() { } // static int InfoBarItemView::CenterPosition(int size, int target_size) { return (target_size - size) / 2; } void InfoBarItemView::GetPreferredSize(CSize* out) { out->cx = GetParent()->GetWidth(); out->cy = static_cast(kInfoBarHeight * animation_->GetCurrentValue()); } // The following is an overall note on the underlying implementation. You don't // need this in order to use this view. Ignore unless you're editing // implementation: // Layout() lays out all of its child views, but it uses insert_index_ to // decide whether to lay out on the left or right. Whenever a view is added or // removed the insert_index_ is updated accordingly to make sure it is directly // between left aligned views and right aligned views. Whenever a view is added, // a spacer view provides padding to the right of the view if the view is // left aligned, or to the left of the view if the view is right aligned. // Removing assumes this spacer view exists. // // For example, below M stands for built in margins, I stands for the icon // which is optional and includes padding of its own. L stands for a left // aligned view, and R for a right aligned view. P is padding, which can be // zero. The insert index is currently 4, separating the right of left views. // The numbers represent what index the child views P, R, and L occupy. // // M I L P L P P R P R M // 0 1 2 3 ^4 5 6 7 // Say we call AddChildViewTrailing(right_view, 10). We end up with: // M I L P L P P R P R P R M // 0 1 2 3 ^4 5 6 7 8 9 // First the right view was added, then its padding was added, the insert index // did not need to change because it still separates the right and left views. // Note that the padding showed up at the lower index, or to the left of the // right aligned view. // Then we call AddChildViewLeading(left_view, 0). We end up with: // M I L P L P L P P R P R P R M // 0 1 2 3 4 5 ^6 7 8 9 10 11 // First the left view was added, then the insert_index_ was incremented, then // the padding is added, even though it is zero (It has no effect on layout) // and insert_index_ is incremented again to keep it between the right and // left views. Note in this case, the padding appears to the right of the view // left aligned view. Removing works the same, but in reverse. void InfoBarItemView::Layout() { const int width = GetWidth(); const int height = GetHeight(); int next_x = width - kButtonHEdgeMargin; int height_diff = static_cast(kInfoBarHeight) - height; const int child_count = GetChildViewCount(); // Anything greater than or equal to insert_index_ is laid out on the right, // with the greatest index (the first one added to the right) being laid out // rightmost. for (int i = child_count - 1; i >= insert_index_ ; i--) { View* v = GetChildViewAt(i); if (v->IsVisible()) { CSize view_size; v->GetPreferredSize(&view_size); next_x = next_x - view_size.cx; v->SetBounds(next_x, CenterPosition(view_size.cy, static_cast(kInfoBarHeight)) - height_diff, view_size.cx, view_size.cy); } } int left_most_x = next_x; next_x = kInfoBarLeftMargin; // Anything less than insert_index_ is laid out on the left, with the // smallest index (the first one added to the left) being laid out leftmost. for (int i = 0; i < insert_index_ ; i++) { View* v = GetChildViewAt(i); if (v->IsVisible()) { CSize view_size; v->GetPreferredSize(&view_size); int remaining_space = std::max(0, left_most_x - next_x); if (view_size.cx > remaining_space) { view_size.cx = remaining_space; } v->SetBounds(next_x, CenterPosition(view_size.cy, static_cast(kInfoBarHeight)) - height_diff, view_size.cx, view_size.cy); next_x = next_x + view_size.cx; } } } void InfoBarItemView::DidChangeBounds(const CRect& previous, const CRect& current) { if (GetParent() != NULL) Layout(); } void InfoBarItemView::BeginClose() { animation_->Hide(); } void InfoBarItemView::Close() { ChromeViews::View* parent = GetParent(); parent->RemoveChildView(this); if (focus_tracker_.get() != NULL) focus_tracker_->FocusLastFocusedExternalView(); delete this; } void InfoBarItemView::CloseButtonPressed() { // Close this view by default. BeginClose(); } void InfoBarItemView::AddChildViewTrailing(ChromeViews::View* view, int leading_padding) { ChromeViews::View::AddChildView(insert_index_, view); View* padding = new HorizontalSpacer(leading_padding); ChromeViews::View::AddChildView(insert_index_, padding); } void InfoBarItemView::AddChildViewTrailing(ChromeViews::View* view) { AddChildViewTrailing(view, kUnrelatedControlHorizontalSpacing); } void InfoBarItemView::AddChildViewLeading(ChromeViews::View* view, int trailing_padding) { ChromeViews::View::AddChildView(insert_index_, view); insert_index_++; View* padding = new HorizontalSpacer(trailing_padding); ChromeViews::View::AddChildView(insert_index_, padding); insert_index_++; } void InfoBarItemView::AddChildViewLeading(ChromeViews::View* view) { AddChildViewLeading(view, kRelatedControlSmallHorizontalSpacing); } void InfoBarItemView::SetIcon(const SkBitmap& icon) { if (icon_ == NULL) { // Add the icon and its padding to the far left of the info bar, and adjust // the insert index accordingly. icon_ = new ChromeViews::ImageView(); View* padding = new HorizontalSpacer(kRelatedControlHorizontalSpacing); ChromeViews::View::AddChildView(0, padding); ChromeViews::View::AddChildView(0, icon_); insert_index_ += 2; } icon_->SetImage(icon); Layout(); } void InfoBarItemView::ViewHierarchyChanged(bool is_add, View *parent, View *child) { if (child == this) { if (is_add) { Layout(); View* root_view = GetRootView(); HWND root_hwnd = NULL; if (root_view) root_hwnd = root_view->GetViewContainer()->GetHWND(); if (root_hwnd) { focus_tracker_.reset(new ChromeViews::ExternalFocusTracker( this, ChromeViews::FocusManager::GetFocusManager(root_hwnd))); } } else { // When we're removed from the hierarchy our focus manager is no longer // valid. if (focus_tracker_.get() != NULL) focus_tracker_->SetFocusManager(NULL); } } } void InfoBarItemView::AddChildView(ChromeViews::View* view) { AddChildViewTrailing(view, kUnrelatedControlHorizontalSpacing); } void InfoBarItemView::AddChildView(int index, ChromeViews::View* view) { if (index < insert_index_) AddChildViewLeading(view); else AddChildViewTrailing(view); } void InfoBarItemView::RemoveChildView(ChromeViews::View* view) { int index = GetChildIndex(view); if (index >= 0) { if (index < insert_index_) { // We're removing a leading view. So the view at index + 1 (immediately // trailing) is the corresponding spacer view. View* spacer_view = GetChildViewAt(index + 1); ChromeViews::View::RemoveChildView(view); ChromeViews::View::RemoveChildView(spacer_view); delete spacer_view; // Need to change the insert_index_ so it is still pointing at the // "middle" index between left and right aligned views. insert_index_ -= 2; } else { // We're removing a trailing view. So the view at index - 1 (immediately // leading) is the corresponding spacer view. View* spacer_view = GetChildViewAt(index - 1); ChromeViews::View::RemoveChildView(view); ChromeViews::View::RemoveChildView(spacer_view); delete spacer_view; } } } void InfoBarItemView::ButtonPressed(ChromeViews::BaseButton* button) { if (button == close_button_) CloseButtonPressed(); } void InfoBarItemView::AnimationProgressed(const Animation* animation) { static_cast(GetParent())->ChildAnimationProgressed(); } void InfoBarItemView::AnimationEnded(const Animation* animation) { static_cast(GetParent())->ChildAnimationEnded(); if (!animation_->IsShowing()) Close(); } void InfoBarItemView::Init() { ResourceBundle &rb = ResourceBundle::GetSharedInstance(); close_button_ = new ChromeViews::Button(); close_button_->SetImage(ChromeViews::Button::BS_NORMAL, rb.GetBitmapNamed(IDR_CLOSE_BAR)); close_button_->SetImage(ChromeViews::Button::BS_HOT, rb.GetBitmapNamed(IDR_CLOSE_BAR_H)); close_button_->SetImage(ChromeViews::Button::BS_PUSHED, rb.GetBitmapNamed(IDR_CLOSE_BAR_P)); close_button_->SetListener(this, 0); close_button_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_CLOSE)); AddChildViewTrailing(close_button_); animation_.reset(new SlideAnimation(this)); animation_->SetTweenType(SlideAnimation::NONE); animation_->Show(); }