// Copyright (c) 2012 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.
#include "pdf/fading_controls.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "pdf/draw_utils.h"
#include "pdf/resource_consts.h"
#include "ppapi/cpp/input_event.h"
namespace chrome_pdf {
const uint32 kFadingAlphaShift = 64;
const uint32 kSplashFadingAlphaShift = 16;
FadingControls::FadingControls()
: state_(NONE), current_transparency_(kOpaqueAlpha), fading_timer_id_(0),
current_capture_control_(kInvalidControlId),
fading_timeout_(kFadingTimeoutMs), alpha_shift_(kFadingAlphaShift),
splash_(false), splash_timeout_(0) {
}
FadingControls::~FadingControls() {
STLDeleteElements(&controls_);
}
bool FadingControls::CreateFadingControls(
uint32 id, const pp::Rect& rc, bool visible,
Control::Owner* owner, uint8 transparency) {
current_transparency_ = transparency;
return Control::Create(id, rc, visible, owner);
}
void FadingControls::Paint(pp::ImageData* image_data, const pp::Rect& rc) {
// When this control is set to invisible the individual controls are not.
// So we need to check for visible() here.
if (!visible())
return;
std::list<Control*>::iterator iter;
for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
(*iter)->Paint(image_data, rc);
}
}
bool FadingControls::HandleEvent(const pp::InputEvent& event) {
if (!visible())
return false;
pp::MouseInputEvent mouse_event(event);
if (mouse_event.is_null())
return NotifyControls(event);
pp::Point pt = mouse_event.GetPosition();
bool is_mouse_click =
mouse_event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN ||
mouse_event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP;
if (rect().Contains(pt)) {
CancelSplashMode();
FadeIn();
// Eat mouse click if are invisible or just fading in.
// That prevents accidental clicks on the controls for touch devices.
bool eat_mouse_click =
(state_ == FADING_IN || current_transparency_ == kTransparentAlpha);
if (eat_mouse_click && is_mouse_click &&
mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT)
return true; // Eat this event here.
}
if ((!rect().Contains(pt)) ||
event.GetType() == PP_INPUTEVENT_TYPE_MOUSELEAVE) {
if (!splash_)
FadeOut();
pp::MouseInputEvent event_leave(pp::MouseInputEvent(
owner()->GetInstance(),
PP_INPUTEVENT_TYPE_MOUSELEAVE,
event.GetTimeStamp(),
event.GetModifiers(),
mouse_event.GetButton(),
mouse_event.GetPosition(),
mouse_event.GetClickCount(),
mouse_event.GetMovement()));
return NotifyControls(event_leave);
}
return NotifyControls(event);
}
void FadingControls::OnTimerFired(uint32 timer_id) {
if (timer_id == fading_timer_id_) {
int32 current_alpha = static_cast<int32>(current_transparency_);
if (state_ == FADING_IN)
current_alpha += alpha_shift_;
else if (state_ == FADING_OUT)
current_alpha -= alpha_shift_;
if (current_alpha >= kOpaqueAlpha) {
state_ = NONE;
current_alpha = kOpaqueAlpha;
} else if (current_alpha <= kTransparentAlpha) {
state_ = NONE;
current_alpha = kTransparentAlpha;
}
current_transparency_ = static_cast<uint8>(current_alpha);
// Invalidate controls with new alpha transparency.
std::list<Control*>::iterator iter;
for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
// We are going to invalidate the whole FadingControls area, to
// allow simultaneous drawing.
(*iter)->AdjustTransparency(current_transparency_, false);
}
owner()->Invalidate(id(), GetControlsRect());
if (state_ != NONE) // Fading still in progress.
fading_timer_id_ = owner()->ScheduleTimer(id(), fading_timeout_);
else
OnFadingComplete();
} else {
// Dispatch timer to controls.
std::list<Control*>::iterator iter;
for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
(*iter)->OnTimerFired(timer_id);
}
}
}
void FadingControls::EventCaptureReleased() {
if (current_capture_control_ != kInvalidControlId) {
// Remove previous catpure.
Control* ctrl = GetControl(current_capture_control_);
if (ctrl)
ctrl->EventCaptureReleased();
}
}
void FadingControls::MoveBy(const pp::Point& offset, bool invalidate) {
std::list<Control*>::iterator iter;
for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
// We invalidate entire FadingControl later if needed.
(*iter)->MoveBy(offset, false);
}
Control::MoveBy(offset, invalidate);
}
void FadingControls::OnEvent(uint32 control_id, uint32 event_id, void* data) {
owner()->OnEvent(control_id, event_id, data);
}
void FadingControls::Invalidate(uint32 control_id, const pp::Rect& rc) {
owner()->Invalidate(control_id, rc);
}
uint32 FadingControls::ScheduleTimer(uint32 control_id, uint32 timeout_ms) {
// TODO(gene): implement timer routine properly.
NOTIMPLEMENTED();
//owner()->ScheduleTimer(control_id);
return 0;
}
void FadingControls::SetEventCapture(uint32 control_id, bool set_capture) {
if (control_id == current_capture_control_) {
if (!set_capture) // Remove event capture.
current_capture_control_ = kInvalidControlId;
} else {
EventCaptureReleased();
current_capture_control_ = control_id;
}
}
void FadingControls::SetCursor(uint32 control_id,
PP_CursorType_Dev cursor_type) {
owner()->SetCursor(control_id, cursor_type);
}
pp::Instance* FadingControls::GetInstance() {
return owner()->GetInstance();
}
bool FadingControls::AddControl(Control* control) {
DCHECK(control);
if (control->owner() != this)
return false;
if (!rect().Contains(control->rect()))
return false;
control->AdjustTransparency(current_transparency_, false);
controls_.push_back(control);
return true;
}
void FadingControls::RemoveControl(uint32 control_id) {
if (current_capture_control_ == control_id) {
current_capture_control_ = kInvalidControlId;
}
std::list<Control*>::iterator iter;
for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
if ((*iter)->id() == control_id) {
delete (*iter);
controls_.erase(iter);
break;
}
}
}
Control* FadingControls::GetControl(uint32 id) {
std::list<Control*>::iterator iter;
for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
if ((*iter)->id() == id)
return *iter;
}
return NULL;
}
pp::Rect FadingControls::GetControlsRect() {
pp::Rect rc;
std::list<Control*>::iterator iter;
for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
rc = rc.Union((*iter)->rect());
}
return rc;
}
bool FadingControls::ExpandLeft(int offset) {
pp::Rect rc = rect();
rc.set_width(rc.width() + offset);
rc.set_x(rc.x() - offset);
if (!rc.Contains(GetControlsRect()))
return false;
// No need to invalidate since we are expanding triggering area only.
SetRect(rc, false);
return true;
}
void FadingControls::Splash(uint32 time_ms) {
splash_ = true;
splash_timeout_ = time_ms;
alpha_shift_ = kSplashFadingAlphaShift;
FadeIn();
}
bool FadingControls::NotifyControls(const pp::InputEvent& event) {
// First pass event to a control that current capture is set to.
Control* ctrl = GetControl(current_capture_control_);
if (ctrl) {
if (ctrl->HandleEvent(event))
return true;
}
std::list<Control*>::iterator iter;
for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
// Now pass event to all control except control with capture,
// since we already passed to it above.
if ((*iter) != ctrl && (*iter)->HandleEvent(event))
return true;
}
return false;
}
void FadingControls::FadeIn() {
bool already_visible =
(state_ == NONE && current_transparency_ == kOpaqueAlpha);
if (state_ != FADING_IN && !already_visible) {
state_ = FADING_IN;
fading_timer_id_ = owner()->ScheduleTimer(id(), fading_timeout_);
}
if (already_visible)
OnFadingComplete();
}
void FadingControls::FadeOut() {
bool already_invisible =
(state_ == NONE && current_transparency_ == kTransparentAlpha);
if (state_ != FADING_OUT && !already_invisible) {
state_ = FADING_OUT;
fading_timer_id_ = owner()->ScheduleTimer(id(), fading_timeout_);
}
if (already_invisible)
OnFadingComplete();
}
void FadingControls::OnFadingComplete() {
DCHECK(current_transparency_ == kOpaqueAlpha ||
current_transparency_ == kTransparentAlpha);
// In the splash mode following states are possible:
// Fade-in complete: splash_==true, splash_timeout_ != 0
// We need to schedule timer for splash_timeout_.
// Splash timeout complete: splash_==true, splash_timeout_ == 0
// We need to fade out still using splash settings.
// Fade-out complete: current_transparency_ == kTransparentAlpha
// We need to cancel splash mode and go back to normal settings.
if (splash_) {
if (current_transparency_ == kOpaqueAlpha) {
if (splash_timeout_) {
fading_timer_id_ = owner()->ScheduleTimer(id(), splash_timeout_);
splash_timeout_ = 0;
} else {
FadeOut();
}
} else {
CancelSplashMode();
}
}
}
void FadingControls::CancelSplashMode() {
splash_ = false;
alpha_shift_ = kFadingAlphaShift;
}
} // namespace chrome_pdf