1// Copyright (c) 2012 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#include "pdf/fading_controls.h"
6
7#include "base/logging.h"
8#include "base/stl_util.h"
9#include "pdf/draw_utils.h"
10#include "pdf/resource_consts.h"
11#include "ppapi/cpp/input_event.h"
12
13namespace chrome_pdf {
14
15const uint32 kFadingAlphaShift = 64;
16const uint32 kSplashFadingAlphaShift = 16;
17
18FadingControls::FadingControls()
19    : state_(NONE), current_transparency_(kOpaqueAlpha), fading_timer_id_(0),
20      current_capture_control_(kInvalidControlId),
21      fading_timeout_(kFadingTimeoutMs), alpha_shift_(kFadingAlphaShift),
22      splash_(false), splash_timeout_(0) {
23}
24
25FadingControls::~FadingControls() {
26  STLDeleteElements(&controls_);
27}
28
29bool FadingControls::CreateFadingControls(
30    uint32 id, const pp::Rect& rc, bool visible,
31    Control::Owner* owner, uint8 transparency) {
32  current_transparency_ = transparency;
33  return Control::Create(id, rc, visible, owner);
34}
35
36void FadingControls::Paint(pp::ImageData* image_data, const pp::Rect& rc) {
37  // When this control is set to invisible the individual controls are not.
38  // So we need to check for visible() here.
39  if (!visible())
40    return;
41
42  std::list<Control*>::iterator iter;
43  for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
44    (*iter)->Paint(image_data, rc);
45  }
46}
47
48bool FadingControls::HandleEvent(const pp::InputEvent& event) {
49  if (!visible())
50    return false;
51
52  pp::MouseInputEvent mouse_event(event);
53  if (mouse_event.is_null())
54    return NotifyControls(event);
55
56  pp::Point pt = mouse_event.GetPosition();
57
58  bool is_mouse_click =
59      mouse_event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN ||
60      mouse_event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP;
61
62  if (rect().Contains(pt)) {
63    CancelSplashMode();
64    FadeIn();
65
66    // Eat mouse click if are invisible or just fading in.
67    // That prevents accidental clicks on the controls for touch devices.
68    bool eat_mouse_click =
69        (state_ == FADING_IN || current_transparency_ == kTransparentAlpha);
70    if (eat_mouse_click && is_mouse_click &&
71        mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT)
72      return true;  // Eat this event here.
73  }
74
75  if ((!rect().Contains(pt)) ||
76      event.GetType() == PP_INPUTEVENT_TYPE_MOUSELEAVE) {
77    if (!splash_)
78      FadeOut();
79    pp::MouseInputEvent event_leave(pp::MouseInputEvent(
80        owner()->GetInstance(),
81        PP_INPUTEVENT_TYPE_MOUSELEAVE,
82        event.GetTimeStamp(),
83        event.GetModifiers(),
84        mouse_event.GetButton(),
85        mouse_event.GetPosition(),
86        mouse_event.GetClickCount(),
87        mouse_event.GetMovement()));
88    return NotifyControls(event_leave);
89  }
90
91  return NotifyControls(event);
92}
93
94void FadingControls::OnTimerFired(uint32 timer_id) {
95  if (timer_id == fading_timer_id_) {
96    int32 current_alpha = static_cast<int32>(current_transparency_);
97    if (state_ == FADING_IN)
98      current_alpha += alpha_shift_;
99    else if (state_ == FADING_OUT)
100      current_alpha -= alpha_shift_;
101
102    if (current_alpha >= kOpaqueAlpha) {
103      state_ = NONE;
104      current_alpha = kOpaqueAlpha;
105    } else if (current_alpha <= kTransparentAlpha) {
106      state_ = NONE;
107      current_alpha = kTransparentAlpha;
108    }
109    current_transparency_ = static_cast<uint8>(current_alpha);
110
111    // Invalidate controls with new alpha transparency.
112    std::list<Control*>::iterator iter;
113    for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
114      // We are going to invalidate the whole FadingControls area, to
115      // allow simultaneous drawing.
116      (*iter)->AdjustTransparency(current_transparency_, false);
117    }
118    owner()->Invalidate(id(), GetControlsRect());
119
120    if (state_ != NONE)  // Fading still in progress.
121      fading_timer_id_ = owner()->ScheduleTimer(id(), fading_timeout_);
122    else
123      OnFadingComplete();
124  } else {
125    // Dispatch timer to controls.
126    std::list<Control*>::iterator iter;
127    for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
128      (*iter)->OnTimerFired(timer_id);
129    }
130  }
131}
132
133void FadingControls::EventCaptureReleased() {
134  if (current_capture_control_ != kInvalidControlId) {
135    // Remove previous catpure.
136    Control* ctrl = GetControl(current_capture_control_);
137    if (ctrl)
138      ctrl->EventCaptureReleased();
139  }
140}
141
142void FadingControls::MoveBy(const pp::Point& offset, bool invalidate) {
143  std::list<Control*>::iterator iter;
144  for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
145    // We invalidate entire FadingControl later if needed.
146    (*iter)->MoveBy(offset, false);
147  }
148  Control::MoveBy(offset, invalidate);
149}
150
151void FadingControls::OnEvent(uint32 control_id, uint32 event_id, void* data) {
152  owner()->OnEvent(control_id, event_id, data);
153}
154
155void FadingControls::Invalidate(uint32 control_id, const pp::Rect& rc) {
156  owner()->Invalidate(control_id, rc);
157}
158
159uint32 FadingControls::ScheduleTimer(uint32 control_id, uint32 timeout_ms) {
160  // TODO(gene): implement timer routine properly.
161  NOTIMPLEMENTED();
162  //owner()->ScheduleTimer(control_id);
163  return 0;
164}
165
166void FadingControls::SetEventCapture(uint32 control_id, bool set_capture) {
167  if (control_id == current_capture_control_) {
168    if (!set_capture)  // Remove event capture.
169      current_capture_control_ = kInvalidControlId;
170  } else {
171    EventCaptureReleased();
172    current_capture_control_ = control_id;
173  }
174}
175
176void FadingControls::SetCursor(uint32 control_id,
177                               PP_CursorType_Dev cursor_type) {
178  owner()->SetCursor(control_id, cursor_type);
179}
180
181pp::Instance* FadingControls::GetInstance() {
182  return owner()->GetInstance();
183}
184
185bool FadingControls::AddControl(Control* control) {
186  DCHECK(control);
187  if (control->owner() != this)
188    return false;
189  if (!rect().Contains(control->rect()))
190    return false;
191
192  control->AdjustTransparency(current_transparency_, false);
193  controls_.push_back(control);
194  return true;
195}
196
197void FadingControls::RemoveControl(uint32 control_id) {
198  if (current_capture_control_ == control_id) {
199    current_capture_control_ = kInvalidControlId;
200  }
201  std::list<Control*>::iterator iter;
202  for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
203    if ((*iter)->id() == control_id) {
204      delete (*iter);
205      controls_.erase(iter);
206      break;
207    }
208  }
209}
210
211Control* FadingControls::GetControl(uint32 id) {
212  std::list<Control*>::iterator iter;
213  for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
214    if ((*iter)->id() == id)
215      return *iter;
216  }
217  return NULL;
218}
219
220pp::Rect FadingControls::GetControlsRect() {
221  pp::Rect rc;
222  std::list<Control*>::iterator iter;
223  for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
224    rc = rc.Union((*iter)->rect());
225  }
226  return rc;
227}
228
229bool FadingControls::ExpandLeft(int offset) {
230  pp::Rect rc = rect();
231  rc.set_width(rc.width() + offset);
232  rc.set_x(rc.x() - offset);
233  if (!rc.Contains(GetControlsRect()))
234    return false;
235  // No need to invalidate since we are expanding triggering area only.
236  SetRect(rc, false);
237  return true;
238}
239
240void FadingControls::Splash(uint32 time_ms) {
241  splash_ = true;
242  splash_timeout_ = time_ms;
243  alpha_shift_ = kSplashFadingAlphaShift;
244  FadeIn();
245}
246
247bool FadingControls::NotifyControls(const pp::InputEvent& event) {
248  // First pass event to a control that current capture is set to.
249  Control* ctrl = GetControl(current_capture_control_);
250  if (ctrl) {
251    if (ctrl->HandleEvent(event))
252      return true;
253  }
254
255  std::list<Control*>::iterator iter;
256  for (iter = controls_.begin(); iter != controls_.end(); ++iter) {
257    // Now pass event to all control except control with capture,
258    // since we already passed to it above.
259    if ((*iter) != ctrl && (*iter)->HandleEvent(event))
260      return true;
261  }
262  return false;
263}
264
265void FadingControls::FadeIn() {
266  bool already_visible =
267      (state_ == NONE && current_transparency_ == kOpaqueAlpha);
268  if (state_ != FADING_IN && !already_visible) {
269    state_ = FADING_IN;
270    fading_timer_id_ = owner()->ScheduleTimer(id(), fading_timeout_);
271  }
272  if (already_visible)
273    OnFadingComplete();
274}
275
276void FadingControls::FadeOut() {
277  bool already_invisible =
278      (state_ == NONE && current_transparency_ == kTransparentAlpha);
279  if (state_ != FADING_OUT && !already_invisible) {
280    state_ = FADING_OUT;
281    fading_timer_id_ = owner()->ScheduleTimer(id(), fading_timeout_);
282  }
283  if (already_invisible)
284    OnFadingComplete();
285}
286
287void FadingControls::OnFadingComplete() {
288  DCHECK(current_transparency_ == kOpaqueAlpha ||
289      current_transparency_ == kTransparentAlpha);
290  // In the splash mode following states are possible:
291  // Fade-in complete: splash_==true, splash_timeout_ != 0
292  //   We need to schedule timer for splash_timeout_.
293  // Splash timeout complete: splash_==true, splash_timeout_ == 0
294  //   We need to fade out still using splash settings.
295  // Fade-out complete: current_transparency_ == kTransparentAlpha
296  //   We need to cancel splash mode and go back to normal settings.
297  if (splash_) {
298    if (current_transparency_ == kOpaqueAlpha) {
299      if (splash_timeout_) {
300        fading_timer_id_ = owner()->ScheduleTimer(id(), splash_timeout_);
301        splash_timeout_ = 0;
302      } else {
303        FadeOut();
304      }
305    } else {
306      CancelSplashMode();
307    }
308  }
309}
310
311void FadingControls::CancelSplashMode() {
312  splash_ = false;
313  alpha_shift_ = kFadingAlphaShift;
314}
315
316}  // namespace chrome_pdf
317