Merge XFA to PDFium master at 4dc95e7 on 10/28/2014
[pdfium.git] / xfa_test / pdf / thumbnail_control.cc
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/thumbnail_control.h"
6
7 #include <algorithm>
8
9 #include "base/logging.h"
10 #include "base/strings/string_util.h"
11 #include "pdf/draw_utils.h"
12 #include "pdf/number_image_generator.h"
13
14 namespace chrome_pdf {
15
16 const int kLeftBorderSize = 52;
17 const int kBorderSize = 12;
18 const int kHighlightBorderSize = 2;
19
20 const uint32 kLeftColor = 0x003F537B;
21 const uint32 kRightColor = 0x990D1626;
22
23 const uint32 kTopHighlightColor = 0xFF426DC9;
24 const uint32 kBottomHighlightColor = 0xFF6391DE;
25 const uint32 kThumbnailBackgroundColor = 0xFF000000;
26
27 const uint32 kSlidingTimeoutMs = 50;
28 const int32 kSlidingShift = 50;
29
30 const double kNonSelectedThumbnailAlpha = 0.91;
31
32 ThumbnailControl::ThumbnailControl()
33     : engine_(NULL), sliding_width_(0), sliding_shift_(kSlidingShift),
34       sliding_timeout_(kSlidingTimeoutMs), sliding_timer_id_(0) {
35 }
36
37 ThumbnailControl::~ThumbnailControl() {
38   ClearCache();
39 }
40
41 bool ThumbnailControl::CreateThumbnailControl(
42     uint32 id, const pp::Rect& rc,
43     bool visible, Owner* owner, PDFEngine* engine,
44     NumberImageGenerator* number_image_generator) {
45   engine_ = engine;
46   number_image_generator_ = number_image_generator;
47   sliding_width_ = rc.width();
48
49   return Control::Create(id, rc, visible, owner);
50 }
51
52 void ThumbnailControl::SetPosition(int position, int total, bool invalidate) {
53   visible_rect_ = pp::Rect();
54   visible_pages_.clear();
55
56   if (rect().width() < kLeftBorderSize + kBorderSize) {
57     return;  // control is too narrow to show thumbnails.
58   }
59
60   int num_pages = engine_->GetNumberOfPages();
61
62   int max_doc_width = 0, total_doc_height = 0;
63   std::vector<pp::Rect> page_sizes(num_pages);
64   for (int i = 0; i < num_pages; ++i) {
65     page_sizes[i] = engine_->GetPageRect(i);
66     max_doc_width = std::max(max_doc_width, page_sizes[i].width());
67     total_doc_height += page_sizes[i].height();
68   }
69
70   if (!max_doc_width)
71     return;
72
73   int max_thumbnail_width = rect().width() - kLeftBorderSize - kBorderSize;
74   double thumbnail_ratio =
75       max_thumbnail_width / static_cast<double>(max_doc_width);
76
77   int total_thumbnail_height = 0;
78   for (int i = 0; i < num_pages; ++i) {
79     total_thumbnail_height += kBorderSize;
80     int thumbnail_width =
81         static_cast<int>(page_sizes[i].width() * thumbnail_ratio);
82     int thumbnail_height =
83         static_cast<int>(page_sizes[i].height() * thumbnail_ratio);
84     int x = (max_thumbnail_width - thumbnail_width) / 2;
85     page_sizes[i] =
86         pp::Rect(x, total_thumbnail_height, thumbnail_width, thumbnail_height);
87     total_thumbnail_height += thumbnail_height;
88   }
89   total_thumbnail_height += kBorderSize;
90
91   int visible_y = 0;
92   if (total > 0) {
93     double range = total_thumbnail_height - rect().height();
94     if (range < 0)
95       range = 0;
96     visible_y = static_cast<int>(range * position / total);
97   }
98   visible_rect_ = pp::Rect(0, visible_y, max_thumbnail_width, rect().height());
99
100   for (int i = 0; i < num_pages; ++i) {
101     if (page_sizes[i].Intersects(visible_rect_)) {
102       PageInfo page_info;
103       page_info.index = i;
104       page_info.rect = page_sizes[i];
105       page_info.rect.Offset(kLeftBorderSize, -visible_rect_.y());
106       visible_pages_.push_back(page_info);
107     }
108   }
109
110   if (invalidate)
111     owner()->Invalidate(id(), rect());
112 }
113
114 void ThumbnailControl::Show(bool visible, bool invalidate) {
115   if (!visible || invalidate)
116     ClearCache();
117   sliding_width_ = rect().width();
118   Control::Show(visible, invalidate);
119 }
120
121 void ThumbnailControl::SlideIn() {
122   if (visible())
123     return;
124
125   Show(true, false);
126   sliding_width_ = 0;
127   sliding_shift_ = kSlidingShift;
128
129   sliding_timer_id_ = owner()->ScheduleTimer(id(), sliding_timeout_);
130   owner()->Invalidate(id(), rect());
131 }
132
133 void ThumbnailControl::SlideOut() {
134   if (!visible())
135     return;
136   sliding_shift_ = -kSlidingShift;
137   sliding_timer_id_ = owner()->ScheduleTimer(id(), sliding_timeout_);
138 }
139
140 void ThumbnailControl::Paint(pp::ImageData* image_data, const pp::Rect& rc) {
141   if (!visible())
142     return;
143
144   pp::Rect control_rc(rect());
145   control_rc.Offset(control_rc.width() - sliding_width_, 0);
146   control_rc.set_width(sliding_width_);
147
148   pp::Rect draw_rc = rc.Intersect(control_rc);
149   if (draw_rc.IsEmpty())
150     return;
151
152   pp::Rect gradient_rc(control_rc.x(), draw_rc.y(),
153                        control_rc.width(), draw_rc.height());
154   GradientFill(owner()->GetInstance(),
155                image_data,
156                draw_rc,
157                gradient_rc,
158                kLeftColor,
159                kRightColor,
160                true,
161                transparency());
162
163   int selected_page = engine_->GetMostVisiblePage();
164   for (size_t i = 0; i < visible_pages_.size(); ++i) {
165     pp::Rect page_rc = visible_pages_[i].rect;
166     page_rc.Offset(control_rc.point());
167
168     if (visible_pages_[i].index == selected_page) {
169       pp::Rect highlight_rc = page_rc;
170       highlight_rc.Inset(-kHighlightBorderSize, -kHighlightBorderSize);
171       GradientFill(owner()->GetInstance(),
172                    image_data,
173                    draw_rc,
174                    highlight_rc,
175                    kTopHighlightColor,
176                    kBottomHighlightColor,
177                    false,
178                    transparency());
179     }
180
181     pp::Rect draw_page_rc = page_rc.Intersect(draw_rc);
182     if (draw_page_rc.IsEmpty())
183       continue;
184
185     // First search page image in the cache.
186     pp::ImageData* thumbnail = NULL;
187     std::map<int, pp::ImageData*>::iterator it =
188         image_cache_.find(visible_pages_[i].index);
189     if (it != image_cache_.end()) {
190       if (it->second->size() == page_rc.size())
191         thumbnail = image_cache_[visible_pages_[i].index];
192       else
193         image_cache_.erase(it);
194     }
195
196     // If page is not found in the cache, create new one.
197     if (thumbnail == NULL) {
198       thumbnail = new pp::ImageData(owner()->GetInstance(),
199                                     PP_IMAGEDATAFORMAT_BGRA_PREMUL,
200                                     page_rc.size(),
201                                     false);
202       engine_->PaintThumbnail(thumbnail, visible_pages_[i].index);
203
204       pp::ImageData page_number;
205       number_image_generator_->GenerateImage(
206           visible_pages_[i].index + 1, &page_number);
207       pp::Point origin(
208           (thumbnail->size().width() - page_number.size().width()) / 2,
209           (thumbnail->size().height() - page_number.size().height()) / 2);
210
211       if (origin.x() > 0 && origin.y() > 0) {
212         AlphaBlend(page_number, pp::Rect(pp::Point(), page_number.size()),
213                    thumbnail, origin, kOpaqueAlpha);
214       }
215
216       image_cache_[visible_pages_[i].index] = thumbnail;
217     }
218
219     uint8 alpha = transparency();
220     if (visible_pages_[i].index != selected_page)
221       alpha = static_cast<uint8>(alpha * kNonSelectedThumbnailAlpha);
222     FillRect(image_data, draw_page_rc, kThumbnailBackgroundColor);
223     draw_page_rc.Offset(-page_rc.x(), -page_rc.y());
224     AlphaBlend(*thumbnail, draw_page_rc, image_data,
225         draw_page_rc.point() + page_rc.point(), alpha);
226   }
227 }
228
229 bool ThumbnailControl::HandleEvent(const pp::InputEvent& event) {
230   if (!visible())
231     return false;
232
233   pp::MouseInputEvent mouse_event(event);
234   if (mouse_event.is_null())
235     return false;
236   pp::Point pt = mouse_event.GetPosition();
237   if (!rect().Contains(pt))
238     return false;
239
240   int over_page = -1;
241   for (size_t i = 0; i < visible_pages_.size(); ++i) {
242     pp::Rect page_rc = visible_pages_[i].rect;
243     page_rc.Offset(rect().point());
244     if (page_rc.Contains(pt)) {
245       over_page = i;
246       break;
247     }
248   }
249
250   bool handled = false;
251   switch (event.GetType()) {
252     case PP_INPUTEVENT_TYPE_MOUSEMOVE:
253       owner()->SetCursor(id(),
254           over_page == -1 ? PP_CURSORTYPE_POINTER : PP_CURSORTYPE_HAND);
255       break;
256     case PP_INPUTEVENT_TYPE_MOUSEDOWN:
257       if (over_page != -1) {
258         owner()->Invalidate(id(), rect());
259         owner()->OnEvent(id(), EVENT_ID_THUMBNAIL_SELECTED,
260                          &visible_pages_[over_page].index);
261       }
262       handled = true;
263       break;
264     default:
265       break;
266   }
267
268   return handled;
269 }
270
271 void ThumbnailControl::OnTimerFired(uint32 timer_id) {
272   if (timer_id == sliding_timer_id_) {
273     sliding_width_ += sliding_shift_;
274     if (sliding_width_ <= 0) {
275       // We completely slided out. Make control invisible now.
276       Show(false, false);
277     } else if (sliding_width_ >= rect().width()) {
278       // We completely slided in. Make sliding width to full control width.
279       sliding_width_ = rect().width();
280     } else {
281       // We have not completed sliding yet. Keep sliding.
282       sliding_timer_id_ = owner()->ScheduleTimer(id(), sliding_timeout_);
283     }
284     owner()->Invalidate(id(), rect());
285   }
286 }
287
288 void ThumbnailControl::ResetEngine(PDFEngine* engine) {
289   engine_ = engine;
290   ClearCache();
291 }
292
293 void ThumbnailControl::ClearCache() {
294   std::map<int, pp::ImageData*>::iterator it;
295   for (it = image_cache_.begin(); it != image_cache_.end(); ++it) {
296     delete it->second;
297   }
298   image_cache_.clear();
299 }
300
301 }  // namespace chrome_pdf