diff options
Diffstat (limited to 'chrome/browser/tab_contents/infobar_container.cc')
-rw-r--r-- | chrome/browser/tab_contents/infobar_container.cc | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/chrome/browser/tab_contents/infobar_container.cc b/chrome/browser/tab_contents/infobar_container.cc new file mode 100644 index 0000000..ef2aac5 --- /dev/null +++ b/chrome/browser/tab_contents/infobar_container.cc @@ -0,0 +1,205 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if defined(TOOLKIT_VIEWS) // TODO(pkasting): Port non-views to use this. + +#include "chrome/browser/tab_contents/infobar_container.h" + +#include "chrome/browser/tab_contents/infobar.h" +#include "chrome/browser/tab_contents/infobar_delegate.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "content/common/notification_details.h" +#include "content/common/notification_source.h" +#include "ui/base/animation/slide_animation.h" + +InfoBarContainer::Delegate::~Delegate() { +} + +InfoBarContainer::InfoBarContainer(Delegate* delegate) + : delegate_(delegate), + tab_contents_(NULL), + top_arrow_target_height_(InfoBar::kDefaultArrowTargetHeight) { +} + +InfoBarContainer::~InfoBarContainer() { + // RemoveAllInfoBarsForDestruction() should have already cleared our infobars. + DCHECK(infobars_.empty()); +} + +void InfoBarContainer::ChangeTabContents(TabContentsWrapper* contents) { + registrar_.RemoveAll(); + + while (!infobars_.empty()) { + InfoBar* infobar = infobars_.front(); + // NULL the container pointer first so that if the infobar is currently + // animating, OnInfoBarStateChanged() won't get called; we'll manually + // trigger this once for the whole set of changes below. This also prevents + // InfoBar::MaybeDelete() from calling RemoveInfoBar() a second time if the + // infobar happens to be at zero height (dunno if this can actually happen). + infobar->set_container(NULL); + RemoveInfoBar(infobar); + } + + tab_contents_ = contents; + if (tab_contents_) { + Source<TabContents> tc_source(tab_contents_->tab_contents()); + registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED, + tc_source); + registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, + tc_source); + registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REPLACED, + tc_source); + + for (size_t i = 0; i < tab_contents_->infobar_count(); ++i) { + // As when we removed the infobars above, we prevent callbacks to + // OnInfoBarAnimated() for each infobar. + AddInfoBar( + tab_contents_->GetInfoBarDelegateAt(i)->CreateInfoBar(tab_contents_), + false, NO_CALLBACK); + } + } + + // Now that everything is up to date, signal the delegate to re-layout. + OnInfoBarStateChanged(false); +} + +int InfoBarContainer::GetVerticalOverlap(int* total_height) { + // Our |total_height| is the sum of the preferred heights of the InfoBars + // contained within us plus the |vertical_overlap|. + int vertical_overlap = 0; + int next_infobar_y = 0; + + for (InfoBars::iterator i(infobars_.begin()); i != infobars_.end(); ++i) { + InfoBar* infobar = *i; + next_infobar_y -= infobar->arrow_height(); + vertical_overlap = std::max(vertical_overlap, -next_infobar_y); + next_infobar_y += infobar->total_height(); + } + + if (total_height) + *total_height = next_infobar_y + vertical_overlap; + return vertical_overlap; +} + +void InfoBarContainer::SetMaxTopArrowHeight(int height) { + // Decrease the height by the arrow stroke thickness, which is the separator + // line height, because the infobar arrow target heights are without-stroke. + top_arrow_target_height_ = std::min( + std::max(height - InfoBar::kSeparatorLineHeight, 0), + InfoBar::kMaximumArrowTargetHeight); + UpdateInfoBarArrowTargetHeights(); +} + +void InfoBarContainer::OnInfoBarStateChanged(bool is_animating) { + if (delegate_) + delegate_->InfoBarContainerStateChanged(is_animating); +} + +void InfoBarContainer::RemoveDelegate(InfoBarDelegate* delegate) { + tab_contents_->RemoveInfoBar(delegate); +} + +void InfoBarContainer::RemoveInfoBar(InfoBar* infobar) { + InfoBars::iterator infobar_iterator(std::find(infobars_.begin(), + infobars_.end(), infobar)); + DCHECK(infobar_iterator != infobars_.end()); + PlatformSpecificRemoveInfoBar(infobar); + infobars_.erase(infobar_iterator); +} + +void InfoBarContainer::RemoveAllInfoBarsForDestruction() { + // Before we remove any children, we reset |delegate_|, so that no removals + // will result in us trying to call + // delegate_->InfoBarContainerStateChanged(). This is important because at + // this point |delegate_| may be shutting down, and it's at best unimportant + // and at worst disastrous to call that. + delegate_ = NULL; + ChangeTabContents(NULL); +} + +void InfoBarContainer::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::TAB_CONTENTS_INFOBAR_ADDED: + AddInfoBar( + Details<InfoBarDelegate>(details)->CreateInfoBar(tab_contents_), true, + WANT_CALLBACK); + break; + + case NotificationType::TAB_CONTENTS_INFOBAR_REMOVED: + RemoveInfoBar(Details<InfoBarDelegate>(details).ptr(), true); + break; + + case NotificationType::TAB_CONTENTS_INFOBAR_REPLACED: { + typedef std::pair<InfoBarDelegate*, InfoBarDelegate*> InfoBarPair; + InfoBarPair* infobar_pair = Details<InfoBarPair>(details).ptr(); + RemoveInfoBar(infobar_pair->first, false); + AddInfoBar(infobar_pair->second->CreateInfoBar(tab_contents_), false, + WANT_CALLBACK); + break; + } + + default: + NOTREACHED(); + break; + } +} + +void InfoBarContainer::RemoveInfoBar(InfoBarDelegate* delegate, + bool use_animation) { + // Search for the infobar associated with |delegate|. We cannot search for + // |delegate| in |tab_contents_|, because an InfoBar remains alive until its + // close animation completes, while the delegate is removed from the tab + // immediately. + for (InfoBars::iterator i(infobars_.begin()); i != infobars_.end(); ++i) { + InfoBar* infobar = *i; + if (infobar->delegate() == delegate) { + // We merely need hide the infobar; it will call back to RemoveInfoBar() + // itself once it's hidden. + infobar->Hide(use_animation); + UpdateInfoBarArrowTargetHeights(); + break; + } + } +} + +void InfoBarContainer::AddInfoBar(InfoBar* infobar, + bool animate, + CallbackStatus callback_status) { + DCHECK(std::find(infobars_.begin(), infobars_.end(), infobar) == + infobars_.end()); + infobars_.push_back(infobar); + UpdateInfoBarArrowTargetHeights(); + PlatformSpecificAddInfoBar(infobar); + if (callback_status == WANT_CALLBACK) + infobar->set_container(this); + infobar->Show(animate); + if (callback_status == NO_CALLBACK) + infobar->set_container(this); +} + +void InfoBarContainer::UpdateInfoBarArrowTargetHeights() { + for (size_t i = 0; i < infobars_.size(); ++i) + infobars_[i]->SetArrowTargetHeight(ArrowTargetHeightForInfoBar(i)); +} + +int InfoBarContainer::ArrowTargetHeightForInfoBar(size_t infobar_index) const { + if (!delegate_->DrawInfoBarArrows(NULL)) + return 0; + if (infobar_index == 0) + return top_arrow_target_height_; + const ui::SlideAnimation* first_infobar_animation = + const_cast<const InfoBar*>(infobars_.front())->animation(); + if ((infobar_index > 1) || first_infobar_animation->IsShowing()) + return InfoBar::kDefaultArrowTargetHeight; + // When the first infobar is animating closed, we animate the second infobar's + // arrow target height from the default to the top target height. Note that + // the animation values here are going from 1.0 -> 0.0 as the top bar closes. + return top_arrow_target_height_ + static_cast<int>( + (InfoBar::kDefaultArrowTargetHeight - top_arrow_target_height_) * + first_infobar_animation->GetCurrentValue()); +} + +#endif // TOOLKIT_VIEWS |