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#include "chrome/browser/ui/touch/tabs/touch_tab.h"
6
7#include "base/logging.h"
8#include "chrome/browser/themes/theme_service.h"
9#include "grit/app_resources.h"
10#include "grit/theme_resources.h"
11#include "ui/base/resource/resource_bundle.h"
12#include "ui/gfx/canvas_skia.h"
13#include "ui/gfx/favicon_size.h"
14#include "ui/gfx/path.h"
15#include "ui/gfx/skbitmap_operations.h"
16
17static const int kLeftPadding = 16;
18static const int kRightPadding = 15;
19static const int kDropShadowHeight = 2;
20
21// The size of the favicon touch area. This generally would be the same as
22// kFaviconSize in ui/gfx/favicon_size.h
23static const int kTouchTabIconSize = 32;
24
25TouchTab::TouchTabImage TouchTab::tab_alpha = {0};
26TouchTab::TouchTabImage TouchTab::tab_active = {0};
27TouchTab::TouchTabImage TouchTab::tab_inactive = {0};
28
29////////////////////////////////////////////////////////////////////////////////
30// TouchTab, public:
31
32TouchTab::TouchTab(TabController* controller)
33    : BaseTab(controller) {
34  InitTabResources();
35}
36
37TouchTab::~TouchTab() {
38}
39
40// static
41gfx::Size TouchTab::GetMinimumUnselectedSize() {
42  InitTabResources();
43
44  gfx::Size minimum_size;
45  minimum_size.set_width(kLeftPadding + kRightPadding);
46  minimum_size.set_height(32);
47  return minimum_size;
48}
49
50////////////////////////////////////////////////////////////////////////////////
51// TouchTab, protected:
52const gfx::Rect& TouchTab::GetTitleBounds() const {
53  return title_bounds_;
54}
55
56const gfx::Rect& TouchTab::GetIconBounds() const {
57  return favicon_bounds_;
58}
59
60////////////////////////////////////////////////////////////////////////////////
61// TouchTab, views::View overrides:
62
63// We'll get selected via the mouse interactions with the TouchTabStrip.  There
64// is no need to directly handle the mouse movements in the TouchTab.
65
66bool TouchTab::OnMousePressed(const views::MouseEvent& event) {
67  return false;
68}
69
70bool TouchTab::OnMouseDragged(const views::MouseEvent& event) {
71  return false;
72}
73
74void TouchTab::OnMouseReleased(const views::MouseEvent& event) {
75}
76
77void TouchTab::OnPaint(gfx::Canvas* canvas) {
78  // Don't paint if we're narrower than we can render correctly. (This should
79  // only happen during animations).
80  if (width() < GetMinimumUnselectedSize().width() && !data().mini)
81    return;
82
83  PaintTabBackground(canvas);
84
85  SkColor title_color = GetThemeProvider()->
86      GetColor(IsSelected() ?
87          ThemeService::COLOR_TAB_TEXT :
88          ThemeService::COLOR_BACKGROUND_TAB_TEXT);
89
90  PaintTitle(canvas, title_color);
91  PaintIcon(canvas);
92}
93
94void TouchTab::Layout() {
95  gfx::Rect local_bounds = GetContentsBounds();
96  TouchTabImage* tab_image = &tab_active;
97  TouchTabImage* alpha = &tab_alpha;
98  int image_height = alpha->image_l->height();
99  int x_base = tab_image->image_l->width();
100  int y_base = height() - image_height;
101  int center_width = width() - tab_image->l_width - tab_image->r_width;
102  if (center_width < 0)
103    center_width = 0;
104  title_bounds_ = gfx::Rect(x_base, y_base, center_width, image_height);
105  favicon_bounds_ = local_bounds;
106}
107
108bool TouchTab::HasHitTestMask() const {
109  return true;
110}
111
112void TouchTab::GetHitTestMask(gfx::Path* path) const {
113  DCHECK(path);
114
115  SkScalar h = SkIntToScalar(height());
116  SkScalar w = SkIntToScalar(width());
117
118  path->moveTo(0, h);
119  path->lineTo(0, 0);
120  path->lineTo(w, 0);
121  path->lineTo(w, h);
122  path->lineTo(0, h);
123  path->close();
124}
125
126////////////////////////////////////////////////////////////////////////////////
127// TouchTab, private:
128
129void TouchTab::PaintTabBackground(gfx::Canvas* canvas) {
130  if (IsSelected()) {
131    PaintActiveTabBackground(canvas);
132  }
133}
134
135void TouchTab::PaintActiveTabBackground(gfx::Canvas* canvas) {
136  int offset = GetMirroredX() + background_offset_.x();
137  ThemeProvider* tp = GetThemeProvider();
138  if (!tp)
139    NOTREACHED() << "Unable to get theme provider";
140
141  SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(IDR_THEME_TOOLBAR);
142
143  TouchTabImage* tab_image = &tab_active;
144  TouchTabImage* alpha = &tab_alpha;
145
146  // Draw left edge.
147  int image_height = alpha->image_l->height();
148  int y_base = height() - image_height;
149  SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap(
150      *tab_bg, offset, 0, tab_image->l_width, image_height);
151  SkBitmap theme_l =
152      SkBitmapOperations::CreateMaskedBitmap(tab_l, *alpha->image_l);
153  canvas->DrawBitmapInt(theme_l, 0, y_base);
154
155  // Draw right edge.
156  SkBitmap tab_r = SkBitmapOperations::CreateTiledBitmap(*tab_bg,
157      offset + width() - tab_image->r_width, 0,
158      tab_image->r_width, image_height);
159  SkBitmap theme_r =
160      SkBitmapOperations::CreateMaskedBitmap(tab_r, *alpha->image_r);
161  canvas->DrawBitmapInt(theme_r, width() - tab_image->r_width, y_base);
162
163  // Draw center.  Instead of masking out the top portion we simply skip over it
164  // by incrementing by kDropShadowHeight, since it's a simple rectangle.
165  canvas->TileImageInt(*tab_bg,
166     offset + tab_image->l_width,
167     kDropShadowHeight + tab_image->y_offset,
168     tab_image->l_width,
169     y_base + kDropShadowHeight + tab_image->y_offset,
170     width() - tab_image->l_width - tab_image->r_width,
171     height() - kDropShadowHeight - tab_image->y_offset);
172
173  // Now draw the highlights/shadows around the tab edge.
174  canvas->DrawBitmapInt(*tab_image->image_l, 0, y_base);
175  canvas->TileImageInt(*tab_image->image_c, tab_image->l_width, y_base,
176      width() - tab_image->l_width - tab_image->r_width, image_height);
177  canvas->DrawBitmapInt(*tab_image->image_r, width() - tab_image->r_width,
178                        y_base);
179}
180
181void TouchTab::PaintIcon(gfx::Canvas* canvas) {
182  // TODO(wyck): use thumbnailer to get better page images
183  int x = favicon_bounds_.x();
184  int y = favicon_bounds_.y();
185
186  TouchTabImage* tab_image = &tab_active;
187  int x_base = tab_image->image_l->width();
188
189  x += x_base;
190
191  if (base::i18n::IsRTL()) {
192    x = width() - x -
193        (data().favicon.isNull() ? kFaviconSize : data().favicon.width());
194  }
195
196  int favicon_x = x;
197  if (!data().favicon.isNull() && data().favicon.width() != kFaviconSize)
198    favicon_x += (data().favicon.width() - kFaviconSize) / 2;
199
200  if (data().network_state != TabRendererData::NETWORK_STATE_NONE) {
201    ThemeProvider* tp = GetThemeProvider();
202    SkBitmap frames(*tp->GetBitmapNamed(
203        (data().network_state == TabRendererData::NETWORK_STATE_WAITING) ?
204        IDR_THROBBER_WAITING : IDR_THROBBER));
205    int image_size = frames.height();
206    int image_offset = loading_animation_frame() * image_size;
207    canvas->DrawBitmapInt(frames, image_offset, 0, image_size, image_size, x, y,
208                          kTouchTabIconSize, kTouchTabIconSize, false);
209  } else {
210    canvas->Save();
211    canvas->ClipRectInt(0, 0, width(), height());
212    if (should_display_crashed_favicon()) {
213      ResourceBundle& rb = ResourceBundle::GetSharedInstance();
214      SkBitmap crashed_favicon(*rb.GetBitmapNamed(IDR_SAD_FAVICON));
215      canvas->DrawBitmapInt(crashed_favicon, 0, 0, crashed_favicon.width(),
216          crashed_favicon.height(), x, y + favicon_hiding_offset(),
217          kTouchTabIconSize, kTouchTabIconSize, true);
218    } else {
219      if (!data().favicon.isNull()) {
220
221        if ((data().favicon.width() == kTouchTabIconSize) &&
222            (data().favicon.height() == kTouchTabIconSize)) {
223          canvas->DrawBitmapInt(data().favicon, 0, 0,
224                                data().favicon.width(), data().favicon.height(),
225                                x, y + favicon_hiding_offset(),
226                                kTouchTabIconSize, kTouchTabIconSize, true);
227        } else {
228          // Draw a background around target touch area in case the favicon
229          // is smaller than touch area (e.g www.google.com is 16x16 now)
230          canvas->DrawRectInt(
231              GetThemeProvider()->GetColor(
232                  ThemeService::COLOR_BUTTON_BACKGROUND),
233              x, y, kTouchTabIconSize, kTouchTabIconSize);
234
235          // We center the image
236          // TODO(saintlou): later request larger image from HistoryService
237          canvas->DrawBitmapInt(data().favicon, 0, 0, data().favicon.width(),
238              data().favicon.height(),
239              x + ((kTouchTabIconSize - data().favicon.width()) / 2),
240              y + ((kTouchTabIconSize - data().favicon.height()) / 2) +
241                                                     favicon_hiding_offset(),
242              data().favicon.width(), data().favicon.height(), true);
243        }
244      }
245    }
246    canvas->Restore();
247  }
248}
249
250// static
251void TouchTab::InitTabResources() {
252  static bool initialized = false;
253  if (initialized)
254    return;
255
256  initialized = true;
257
258  // Load the tab images once now, and maybe again later if the theme changes.
259  LoadTabImages();
260}
261
262
263// static
264void TouchTab::LoadTabImages() {
265  // We're not letting people override tab images just yet.
266  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
267
268  tab_alpha.image_l = rb.GetBitmapNamed(IDR_TAB_ALPHA_LEFT);
269  tab_alpha.image_r = rb.GetBitmapNamed(IDR_TAB_ALPHA_RIGHT);
270
271  tab_active.image_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_LEFT);
272  tab_active.image_c = rb.GetBitmapNamed(IDR_TAB_ACTIVE_CENTER);
273  tab_active.image_r = rb.GetBitmapNamed(IDR_TAB_ACTIVE_RIGHT);
274  tab_active.l_width = tab_active.image_l->width();
275  tab_active.r_width = tab_active.image_r->width();
276
277  tab_inactive.image_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT);
278  tab_inactive.image_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER);
279  tab_inactive.image_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT);
280  tab_inactive.l_width = tab_inactive.image_l->width();
281  tab_inactive.r_width = tab_inactive.image_r->width();
282}
283