// 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::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(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(current_alpha); // Invalidate controls with new alpha transparency. std::list::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::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::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::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::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::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::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