| // 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. |
| // |
| // Implementation of the manager for infobar windows. |
| |
| #include "chrome_frame/infobars/internal/infobar_window.h" |
| |
| #include <algorithm> |
| |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "chrome_frame/function_stub.h" |
| |
| namespace { |
| |
| // length of each step when opening or closing |
| const UINT kInfobarSlidingTimerIntervalMs = 50U; |
| // pixels per step, when opening or closing |
| const int kInfobarSlideOpenStep = 2; |
| const int kInfobarSlideCloseStep = 6; |
| |
| } // namespace |
| |
| VOID CALLBACK OnSliderTimer(InfobarWindow::Host* host, |
| HWND /*hwnd*/, UINT /*uMsg*/, |
| UINT_PTR /*idEvent*/, DWORD /*dwTime*/) { |
| if (host) |
| host->UpdateLayout(); |
| } |
| |
| InfobarWindow::InfobarWindow(InfobarType type) |
| : type_(type), |
| host_(NULL), |
| target_height_(0), |
| initial_height_(0), |
| current_height_(0), |
| current_width_(0), |
| timer_id_(0), |
| timer_stub_(NULL), |
| frame_impl_(this) { |
| DCHECK(type_ >= FIRST_INFOBAR_TYPE); |
| DCHECK(type_ < END_OF_INFOBAR_TYPE); |
| } |
| |
| InfobarWindow::~InfobarWindow() { |
| if (StopTimer() && timer_stub_ != NULL) |
| FunctionStub::Destroy(timer_stub_); |
| else if (timer_stub_ != NULL) |
| timer_stub_->set_argument(NULL); // Couldn't stop it, so orphan and disable |
| } |
| |
| void InfobarWindow::SetHost(Host* host) { |
| DCHECK(host_ == NULL); |
| DCHECK(timer_stub_ == NULL); |
| DCHECK(host != NULL); |
| host_ = host; |
| timer_stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(host), |
| OnSliderTimer); |
| } |
| |
| bool InfobarWindow::Show(InfobarContent* content) { |
| DCHECK(host_ != NULL); |
| if (host_ == NULL) |
| return false; |
| |
| scoped_ptr<InfobarContent> new_content(content); |
| content_.reset(); |
| |
| if (!new_content->InstallInFrame(&frame_impl_)) |
| return false; |
| |
| // Force a call to ReserveSpace, which will capture the width of the displaced |
| // window. |
| if (current_width_ == 0) |
| host_->UpdateLayout(); |
| if (current_width_ == 0) |
| return false; // Might not be any displaced window.. then we can't display. |
| |
| content_.swap(new_content); |
| StartSlidingTowards(content_->GetDesiredSize(current_width_, 0)); |
| |
| return true; |
| } |
| |
| void InfobarWindow::Hide() { |
| DCHECK(host_ != NULL); |
| if (host_ == NULL) |
| return; |
| |
| StartSlidingTowards(0); |
| } |
| |
| void InfobarWindow::ReserveSpace(RECT* rect) { |
| DCHECK(rect); |
| DCHECK(host_ != NULL); |
| if (rect == NULL || host_ == NULL) |
| return; |
| |
| current_width_ = rect->right - rect->left; |
| current_height_ = CalculateHeight(); |
| |
| RECT infobar_rect = *rect; |
| |
| switch (type_) { |
| case TOP_INFOBAR: |
| infobar_rect.bottom = rect->top + current_height_; |
| rect->top = std::min(rect->bottom, infobar_rect.bottom); |
| break; |
| case BOTTOM_INFOBAR: |
| infobar_rect.top = rect->bottom - current_height_; |
| rect->bottom = std::max(rect->top, infobar_rect.top); |
| break; |
| default: |
| NOTREACHED() << "Unknown InfobarType value."; |
| break; |
| } |
| |
| if (content_ != NULL) |
| content_->SetDimensions(infobar_rect); |
| |
| // Done sliding? |
| if (current_height_ == target_height_) { |
| StopTimer(); |
| if (current_height_ == 0) |
| content_.reset(); |
| } |
| } |
| |
| void InfobarWindow::StartSlidingTowards(int target_height) { |
| initial_height_ = current_height_; |
| target_height_ = target_height; |
| |
| if (StartTimer()) |
| slide_start_ = base::Time::Now(); |
| else |
| slide_start_ = base::Time(); // NULL time means don't slide, resize now |
| |
| // Trigger an immediate re-laying out. The timer will handle remaining steps. |
| host_->UpdateLayout(); |
| } |
| |
| bool InfobarWindow::StartTimer() { |
| if (timer_id_ != 0) |
| return true; |
| |
| DCHECK(timer_stub_ != NULL); |
| if (timer_stub_ == NULL) |
| return false; |
| |
| timer_id_ = ::SetTimer(NULL, |
| timer_id_, |
| kInfobarSlidingTimerIntervalMs, |
| reinterpret_cast<TIMERPROC>(timer_stub_->code())); |
| |
| DPLOG_IF(ERROR, timer_id_ == 0) << "Failure in SetTimer."; |
| |
| return timer_id_ != 0; |
| } |
| |
| bool InfobarWindow::StopTimer() { |
| if (timer_id_ == 0) |
| return true; |
| |
| if (::KillTimer(NULL, timer_id_)) { |
| timer_id_ = 0; |
| return true; |
| } |
| |
| DPLOG(ERROR) << "Failure in KillTimer."; |
| return false; |
| } |
| |
| int InfobarWindow::CalculateHeight() { |
| if (slide_start_.is_null()) |
| return target_height_; |
| |
| base::TimeDelta elapsed = base::Time::Now() - slide_start_; |
| int elapsed_steps = static_cast<int>(elapsed.InMilliseconds()) / |
| kInfobarSlidingTimerIntervalMs; |
| |
| if (initial_height_ < target_height_) { |
| return std::min(initial_height_ + elapsed_steps * kInfobarSlideOpenStep, |
| target_height_); |
| } else if (initial_height_ > target_height_) { |
| return std::max(initial_height_ - elapsed_steps * kInfobarSlideCloseStep, |
| target_height_); |
| } else { |
| return target_height_; |
| } |
| } |
| |
| InfobarWindow::FrameImpl::FrameImpl(InfobarWindow* infobar_window) |
| : infobar_window_(infobar_window) { |
| } |
| |
| HWND InfobarWindow::FrameImpl::GetFrameWindow() { |
| return infobar_window_->host_->GetContainerWindow(); |
| } |
| |
| void InfobarWindow::FrameImpl::CloseInfobar() { |
| infobar_window_->Hide(); |
| } |