1// Copyright 2013 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 "ui/app_list/app_list_folder_item.h"
6
7#include "base/guid.h"
8#include "ui/app_list/app_list_constants.h"
9#include "ui/app_list/app_list_item_list.h"
10#include "ui/gfx/canvas.h"
11#include "ui/gfx/image/canvas_image_source.h"
12#include "ui/gfx/image/image_skia_operations.h"
13
14namespace app_list {
15
16namespace {
17
18const int kItemIconDimension = 16;
19
20// Generates the folder icon with the top 4 child item icons laid in 2x2 tile.
21class FolderImageSource : public gfx::CanvasImageSource {
22 public:
23  typedef std::vector<gfx::ImageSkia> Icons;
24
25  FolderImageSource(const Icons& icons, const gfx::Size& size)
26      : gfx::CanvasImageSource(size, false),
27        icons_(icons),
28        size_(size) {
29    DCHECK(icons.size() <= kNumFolderTopItems);
30  }
31
32  virtual ~FolderImageSource() {}
33
34 private:
35  void DrawIcon(gfx::Canvas* canvas,
36                const gfx::ImageSkia& icon,
37                const gfx::Size icon_size,
38                int x, int y) {
39    if (icon.isNull())
40      return;
41
42    gfx::ImageSkia resized(
43        gfx::ImageSkiaOperations::CreateResizedImage(
44            icon, skia::ImageOperations::RESIZE_BEST, icon_size));
45    canvas->DrawImageInt(resized, 0, 0, resized.width(), resized.height(),
46        x, y, resized.width(), resized.height(), true);
47  }
48
49  // gfx::CanvasImageSource overrides:
50  virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
51    // Draw folder circle.
52    gfx::Point center = gfx::Point(size().width() / 2 , size().height() / 2);
53    SkPaint paint;
54    paint.setStyle(SkPaint::kFill_Style);
55    paint.setAntiAlias(true);
56    paint.setColor(kFolderBubbleColor);
57    canvas->DrawCircle(center, size().width() / 2, paint);
58
59    if (icons_.size() == 0)
60      return;
61
62    // Draw top items' icons.
63    const gfx::Size item_icon_size =
64        gfx::Size(kItemIconDimension, kItemIconDimension);
65    Rects top_icon_bounds =
66        AppListFolderItem::GetTopIconsBounds(gfx::Rect(size()));
67
68    for (size_t i= 0; i < kNumFolderTopItems && i < icons_.size(); ++i) {
69      DrawIcon(canvas, icons_[i], item_icon_size,
70               top_icon_bounds[i].x(), top_icon_bounds[i].y());
71    }
72  }
73
74  Icons icons_;
75  gfx::Size size_;
76
77  DISALLOW_COPY_AND_ASSIGN(FolderImageSource);
78};
79
80}  // namespace
81
82AppListFolderItem::AppListFolderItem(const std::string& id,
83                                     FolderType folder_type)
84    : AppListItem(id),
85      folder_type_(folder_type),
86      item_list_(new AppListItemList) {
87  item_list_->AddObserver(this);
88}
89
90AppListFolderItem::~AppListFolderItem() {
91  for (size_t i = 0; i < top_items_.size(); ++i)
92    top_items_[i]->RemoveObserver(this);
93  item_list_->RemoveObserver(this);
94}
95
96void AppListFolderItem::UpdateIcon() {
97  FolderImageSource::Icons top_icons;
98  for (size_t i = 0; i < top_items_.size(); ++i)
99    top_icons.push_back(top_items_[i]->icon());
100
101  const gfx::Size icon_size = gfx::Size(kGridIconDimension, kGridIconDimension);
102  gfx::ImageSkia icon = gfx::ImageSkia(
103      new FolderImageSource(top_icons, icon_size),
104      icon_size);
105  SetIcon(icon, false);
106}
107
108const gfx::ImageSkia& AppListFolderItem::GetTopIcon(size_t item_index) {
109  DCHECK(item_index <= top_items_.size());
110  return top_items_[item_index]->icon();
111}
112
113gfx::Rect AppListFolderItem::GetTargetIconRectInFolderForItem(
114    AppListItem* item,
115    const gfx::Rect& folder_icon_bounds) {
116  for (size_t i = 0; i < top_items_.size(); ++i) {
117    if (item->id() == top_items_[i]->id()) {
118      Rects rects = AppListFolderItem::GetTopIconsBounds(folder_icon_bounds);
119      return rects[i];
120    }
121  }
122
123  gfx::Rect target_rect(folder_icon_bounds);
124  target_rect.ClampToCenteredSize(
125      gfx::Size(kItemIconDimension, kItemIconDimension));
126  return target_rect;
127}
128
129void AppListFolderItem::Activate(int event_flags) {
130  // Folder handling is implemented by the View, so do nothing.
131}
132
133// static
134const char AppListFolderItem::kItemType[] = "FolderItem";
135
136// static
137Rects AppListFolderItem::GetTopIconsBounds(
138    const gfx::Rect& folder_icon_bounds) {
139  const int delta_to_center = 1;
140  gfx::Point icon_center = folder_icon_bounds.CenterPoint();
141  Rects top_icon_bounds;
142
143  // Get the top left icon bounds.
144  int left_x = icon_center.x() - kItemIconDimension - delta_to_center;
145  int top_y = icon_center.y() - kItemIconDimension - delta_to_center;
146  gfx::Rect top_left(left_x, top_y, kItemIconDimension, kItemIconDimension);
147  top_icon_bounds.push_back(top_left);
148
149  // Get the top right icon bounds.
150  int right_x = icon_center.x() + delta_to_center;
151  gfx::Rect top_right(right_x, top_y, kItemIconDimension, kItemIconDimension);
152  top_icon_bounds.push_back(top_right);
153
154  // Get the bottom left icon bounds.
155  int bottom_y = icon_center.y() + delta_to_center;
156  gfx::Rect bottom_left(
157      left_x, bottom_y, kItemIconDimension, kItemIconDimension);
158  top_icon_bounds.push_back(bottom_left);
159
160  // Get the bottom right icon bounds.
161  gfx::Rect bottom_right(
162      right_x, bottom_y, kItemIconDimension, kItemIconDimension);
163  top_icon_bounds.push_back(bottom_right);
164
165  return top_icon_bounds;
166}
167
168const char* AppListFolderItem::GetItemType() const {
169  return AppListFolderItem::kItemType;
170}
171
172ui::MenuModel* AppListFolderItem::GetContextMenuModel() {
173  // TODO(stevenjb/jennyz): Implement.
174  return NULL;
175}
176
177AppListItem* AppListFolderItem::FindChildItem(const std::string& id) {
178  return item_list_->FindItem(id);
179}
180
181size_t AppListFolderItem::ChildItemCount() const {
182  return item_list_->item_count();
183}
184
185void AppListFolderItem::OnExtensionPreferenceChanged() {
186  for (size_t i = 0; i < item_list_->item_count(); ++i)
187    item_list_->item_at(i)->OnExtensionPreferenceChanged();
188}
189
190bool AppListFolderItem::CompareForTest(const AppListItem* other) const {
191  if (!AppListItem::CompareForTest(other))
192    return false;
193  const AppListFolderItem* other_folder =
194      static_cast<const AppListFolderItem*>(other);
195  if (other_folder->item_list()->item_count() != item_list_->item_count())
196    return false;
197  for (size_t i = 0; i < item_list_->item_count(); ++i) {
198    if (!item_list()->item_at(i)->CompareForTest(
199            other_folder->item_list()->item_at(i)))
200      return false;
201  }
202  return true;
203}
204
205std::string AppListFolderItem::GenerateId() {
206  return base::GenerateGUID();
207}
208
209void AppListFolderItem::ItemIconChanged() {
210  UpdateIcon();
211}
212
213void AppListFolderItem::OnListItemAdded(size_t index,
214                                        AppListItem* item) {
215  if (index <= kNumFolderTopItems)
216    UpdateTopItems();
217}
218
219void AppListFolderItem::OnListItemRemoved(size_t index,
220                                          AppListItem* item) {
221  if (index <= kNumFolderTopItems)
222    UpdateTopItems();
223}
224
225void AppListFolderItem::OnListItemMoved(size_t from_index,
226                                        size_t to_index,
227                                        AppListItem* item) {
228  if (from_index <= kNumFolderTopItems || to_index <= kNumFolderTopItems)
229    UpdateTopItems();
230}
231
232void AppListFolderItem::UpdateTopItems() {
233  for (size_t i = 0; i < top_items_.size(); ++i)
234    top_items_[i]->RemoveObserver(this);
235  top_items_.clear();
236
237  for (size_t i = 0;
238       i < kNumFolderTopItems && i < item_list_->item_count(); ++i) {
239    AppListItem* item = item_list_->item_at(i);
240    item->AddObserver(this);
241    top_items_.push_back(item);
242  }
243  UpdateIcon();
244}
245
246}  // namespace app_list
247