// 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();
}