Home | History | Annotate | Download | only in internal
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 //
      5 // Implementation of the manager for infobar windows.
      6 
      7 #include "chrome_frame/infobars/internal/infobar_window.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "base/compiler_specific.h"
     12 #include "base/logging.h"
     13 #include "chrome_frame/function_stub.h"
     14 
     15 namespace {
     16 
     17 // length of each step when opening or closing
     18 const UINT kInfobarSlidingTimerIntervalMs = 50U;
     19 // pixels per step, when opening or closing
     20 const int kInfobarSlideOpenStep = 2;
     21 const int kInfobarSlideCloseStep = 6;
     22 
     23 }  // namespace
     24 
     25 VOID CALLBACK OnSliderTimer(InfobarWindow::Host* host,
     26                             HWND /*hwnd*/, UINT /*uMsg*/,
     27                             UINT_PTR /*idEvent*/, DWORD /*dwTime*/) {
     28   if (host)
     29     host->UpdateLayout();
     30 }
     31 
     32 InfobarWindow::InfobarWindow(InfobarType type)
     33     : type_(type),
     34       host_(NULL),
     35       target_height_(0),
     36       initial_height_(0),
     37       current_height_(0),
     38       current_width_(0),
     39       timer_id_(0),
     40       timer_stub_(NULL),
     41       frame_impl_(this) {
     42   DCHECK(type_ >= FIRST_INFOBAR_TYPE);
     43   DCHECK(type_ < END_OF_INFOBAR_TYPE);
     44 }
     45 
     46 InfobarWindow::~InfobarWindow() {
     47   if (StopTimer() && timer_stub_ != NULL)
     48     FunctionStub::Destroy(timer_stub_);
     49   else if (timer_stub_ != NULL)
     50     timer_stub_->set_argument(NULL);  // Couldn't stop it, so orphan and disable
     51 }
     52 
     53 void InfobarWindow::SetHost(Host* host) {
     54   DCHECK(host_ == NULL);
     55   DCHECK(timer_stub_ == NULL);
     56   DCHECK(host != NULL);
     57   host_ = host;
     58   timer_stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(host),
     59                                      OnSliderTimer);
     60 }
     61 
     62 bool InfobarWindow::Show(InfobarContent* content) {
     63   DCHECK(host_ != NULL);
     64   if (host_ == NULL)
     65     return false;
     66 
     67   scoped_ptr<InfobarContent> new_content(content);
     68   content_.reset();
     69 
     70   if (!new_content->InstallInFrame(&frame_impl_))
     71     return false;
     72 
     73   // Force a call to ReserveSpace, which will capture the width of the displaced
     74   // window.
     75   if (current_width_ == 0)
     76     host_->UpdateLayout();
     77   if (current_width_ == 0)
     78     return false;  // Might not be any displaced window.. then we can't display.
     79 
     80   content_.swap(new_content);
     81   StartSlidingTowards(content_->GetDesiredSize(current_width_, 0));
     82 
     83   return true;
     84 }
     85 
     86 void InfobarWindow::Hide() {
     87   DCHECK(host_ != NULL);
     88   if (host_ == NULL)
     89     return;
     90 
     91   StartSlidingTowards(0);
     92 }
     93 
     94 void InfobarWindow::ReserveSpace(RECT* rect) {
     95   DCHECK(rect);
     96   DCHECK(host_ != NULL);
     97   if (rect == NULL || host_ == NULL)
     98     return;
     99 
    100   current_width_ = rect->right - rect->left;
    101   current_height_ = CalculateHeight();
    102 
    103   RECT infobar_rect = *rect;
    104 
    105   switch (type_) {
    106     case TOP_INFOBAR:
    107       infobar_rect.bottom = rect->top + current_height_;
    108       rect->top = std::min(rect->bottom, infobar_rect.bottom);
    109       break;
    110     case BOTTOM_INFOBAR:
    111       infobar_rect.top = rect->bottom - current_height_;
    112       rect->bottom = std::max(rect->top, infobar_rect.top);
    113       break;
    114     default:
    115       NOTREACHED() << "Unknown InfobarType value.";
    116       break;
    117   }
    118 
    119   if (content_ != NULL)
    120     content_->SetDimensions(infobar_rect);
    121 
    122   // Done sliding?
    123   if (current_height_ == target_height_) {
    124     StopTimer();
    125     if (current_height_ == 0)
    126       content_.reset();
    127   }
    128 }
    129 
    130 void InfobarWindow::StartSlidingTowards(int target_height) {
    131   initial_height_ = current_height_;
    132   target_height_ = target_height;
    133 
    134   if (StartTimer())
    135     slide_start_ = base::Time::Now();
    136   else
    137     slide_start_ = base::Time();  // NULL time means don't slide, resize now
    138 
    139   // Trigger an immediate re-laying out. The timer will handle remaining steps.
    140   host_->UpdateLayout();
    141 }
    142 
    143 bool InfobarWindow::StartTimer() {
    144   if (timer_id_ != 0)
    145     return true;
    146 
    147   DCHECK(timer_stub_ != NULL);
    148   if (timer_stub_ == NULL)
    149     return false;
    150 
    151   timer_id_ = ::SetTimer(NULL,
    152                          timer_id_,
    153                          kInfobarSlidingTimerIntervalMs,
    154                          reinterpret_cast<TIMERPROC>(timer_stub_->code()));
    155 
    156   DPLOG_IF(ERROR, timer_id_ == 0) << "Failure in SetTimer.";
    157 
    158   return timer_id_ != 0;
    159 }
    160 
    161 bool InfobarWindow::StopTimer() {
    162   if (timer_id_ == 0)
    163     return true;
    164 
    165   if (::KillTimer(NULL, timer_id_)) {
    166     timer_id_ = 0;
    167     return true;
    168   }
    169 
    170   DPLOG(ERROR) << "Failure in KillTimer.";
    171   return false;
    172 }
    173 
    174 int InfobarWindow::CalculateHeight() {
    175   if (slide_start_.is_null())
    176     return target_height_;
    177 
    178   base::TimeDelta elapsed = base::Time::Now() - slide_start_;
    179   int elapsed_steps = static_cast<int>(elapsed.InMilliseconds()) /
    180                       kInfobarSlidingTimerIntervalMs;
    181 
    182   if (initial_height_ < target_height_) {
    183     return std::min(initial_height_ + elapsed_steps * kInfobarSlideOpenStep,
    184                     target_height_);
    185   } else if (initial_height_ > target_height_) {
    186     return std::max(initial_height_ - elapsed_steps * kInfobarSlideCloseStep,
    187                     target_height_);
    188   } else {
    189     return target_height_;
    190   }
    191 }
    192 
    193 InfobarWindow::FrameImpl::FrameImpl(InfobarWindow* infobar_window)
    194     : infobar_window_(infobar_window) {
    195 }
    196 
    197 HWND InfobarWindow::FrameImpl::GetFrameWindow() {
    198   return infobar_window_->host_->GetContainerWindow();
    199 }
    200 
    201 void InfobarWindow::FrameImpl::CloseInfobar() {
    202   infobar_window_->Hide();
    203 }
    204