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 <string>
6#include <vector>
7
8#include "ash/session/session_state_delegate.h"
9#include "ash/shell.h"
10#include "ash/shell/example_factory.h"
11#include "ash/shell/toplevel_window.h"
12#include "ash/shell_delegate.h"
13#include "base/basictypes.h"
14#include "base/callback.h"
15#include "base/files/file_path.h"
16#include "base/i18n/case_conversion.h"
17#include "base/i18n/string_search.h"
18#include "base/strings/string_util.h"
19#include "base/strings/stringprintf.h"
20#include "base/strings/utf_string_conversions.h"
21#include "ui/app_list/app_list_item.h"
22#include "ui/app_list/app_list_item_list.h"
23#include "ui/app_list/app_list_model.h"
24#include "ui/app_list/app_list_view_delegate.h"
25#include "ui/app_list/search_box_model.h"
26#include "ui/app_list/search_result.h"
27#include "ui/app_list/speech_ui_model.h"
28#include "ui/gfx/canvas.h"
29#include "ui/gfx/font_list.h"
30#include "ui/gfx/geometry/rect.h"
31#include "ui/gfx/image/image_skia.h"
32#include "ui/views/examples/examples_window_with_content.h"
33
34namespace ash {
35namespace shell {
36
37namespace {
38
39// WindowTypeShelfItem is an app item of app list. It carries a window
40// launch type and launches corresponding example window when activated.
41class WindowTypeShelfItem : public app_list::AppListItem {
42 public:
43  enum Type {
44    TOPLEVEL_WINDOW = 0,
45    NON_RESIZABLE_WINDOW,
46    LOCK_SCREEN,
47    WIDGETS_WINDOW,
48    EXAMPLES_WINDOW,
49    LAST_TYPE,
50  };
51
52  explicit WindowTypeShelfItem(const std::string& id, Type type)
53      : app_list::AppListItem(id),
54        type_(type) {
55    std::string title(GetTitle(type));
56    SetIcon(GetIcon(type), false);
57    SetName(title);
58  }
59
60  static gfx::ImageSkia GetIcon(Type type) {
61    static const SkColor kColors[] = {
62        SK_ColorRED,
63        SK_ColorGREEN,
64        SK_ColorBLUE,
65        SK_ColorYELLOW,
66        SK_ColorCYAN,
67    };
68
69    const int kIconSize = 128;
70    SkBitmap icon;
71    icon.allocN32Pixels(kIconSize, kIconSize);
72    icon.eraseColor(kColors[static_cast<int>(type) % arraysize(kColors)]);
73    return gfx::ImageSkia::CreateFrom1xBitmap(icon);
74  }
75
76  // The text below is not localized as this is an example code.
77  static std::string GetTitle(Type type) {
78    switch (type) {
79      case TOPLEVEL_WINDOW:
80        return "Create Window";
81      case NON_RESIZABLE_WINDOW:
82        return "Create Non-Resizable Window";
83      case LOCK_SCREEN:
84        return "Lock Screen";
85      case WIDGETS_WINDOW:
86        return "Show Example Widgets";
87      case EXAMPLES_WINDOW:
88        return "Open Views Examples Window";
89      default:
90        return "Unknown window type.";
91    }
92  }
93
94  // The text below is not localized as this is an example code.
95  static std::string GetDetails(Type type) {
96    // Assigns details only to some types so that we see both one-line
97    // and two-line results.
98    switch (type) {
99      case WIDGETS_WINDOW:
100        return "Creates a window to show example widgets";
101      case EXAMPLES_WINDOW:
102        return "Creates a window to show views example.";
103      default:
104        return std::string();
105    }
106  }
107
108  static void ActivateItem(Type type, int event_flags) {
109     switch (type) {
110      case TOPLEVEL_WINDOW: {
111        ToplevelWindow::CreateParams params;
112        params.can_resize = true;
113        ToplevelWindow::CreateToplevelWindow(params);
114        break;
115      }
116      case NON_RESIZABLE_WINDOW: {
117        ToplevelWindow::CreateToplevelWindow(ToplevelWindow::CreateParams());
118        break;
119      }
120      case LOCK_SCREEN: {
121        Shell::GetInstance()->session_state_delegate()->LockScreen();
122        break;
123      }
124      case WIDGETS_WINDOW: {
125        CreateWidgetsWindow();
126        break;
127      }
128      case EXAMPLES_WINDOW: {
129        views::examples::ShowExamplesWindowWithContent(
130            views::examples::DO_NOTHING_ON_CLOSE,
131            Shell::GetInstance()->delegate()->GetActiveBrowserContext(),
132            NULL);
133        break;
134      }
135      default:
136        break;
137    }
138  }
139
140  // AppListItem
141  virtual void Activate(int event_flags) OVERRIDE {
142    ActivateItem(type_, event_flags);
143  }
144
145 private:
146  Type type_;
147
148  DISALLOW_COPY_AND_ASSIGN(WindowTypeShelfItem);
149};
150
151// ExampleSearchResult is an app list search result. It provides what icon to
152// show, what should title and details text look like. It also carries the
153// matching window launch type so that AppListViewDelegate knows how to open
154// it.
155class ExampleSearchResult : public app_list::SearchResult {
156 public:
157  ExampleSearchResult(WindowTypeShelfItem::Type type,
158                      const base::string16& query)
159      : type_(type) {
160    SetIcon(WindowTypeShelfItem::GetIcon(type_));
161
162    base::string16 title =
163        base::UTF8ToUTF16(WindowTypeShelfItem::GetTitle(type_));
164    set_title(title);
165
166    Tags title_tags;
167    const size_t match_len = query.length();
168
169    // Highlight matching parts in title with bold.
170    // Note the following is not a proper way to handle i18n string.
171    title = base::i18n::ToLower(title);
172    size_t match_start = title.find(query);
173    while (match_start != base::string16::npos) {
174      title_tags.push_back(Tag(Tag::MATCH,
175                               match_start,
176                               match_start + match_len));
177      match_start = title.find(query, match_start + match_len);
178    }
179    set_title_tags(title_tags);
180
181    base::string16 details =
182        base::UTF8ToUTF16(WindowTypeShelfItem::GetDetails(type_));
183    set_details(details);
184    Tags details_tags;
185    details_tags.push_back(Tag(Tag::DIM, 0, details.length()));
186    set_details_tags(details_tags);
187  }
188
189  WindowTypeShelfItem::Type type() const { return type_; }
190
191 private:
192  WindowTypeShelfItem::Type type_;
193
194  DISALLOW_COPY_AND_ASSIGN(ExampleSearchResult);
195};
196
197class ExampleAppListViewDelegate : public app_list::AppListViewDelegate {
198 public:
199  ExampleAppListViewDelegate()
200      : model_(new app_list::AppListModel) {
201    PopulateApps();
202    DecorateSearchBox(model_->search_box());
203  }
204
205 private:
206  void PopulateApps() {
207    for (int i = 0; i < static_cast<int>(WindowTypeShelfItem::LAST_TYPE); ++i) {
208      WindowTypeShelfItem::Type type =
209          static_cast<WindowTypeShelfItem::Type>(i);
210      std::string id = base::StringPrintf("%d", i);
211      scoped_ptr<WindowTypeShelfItem> shelf_item(
212          new WindowTypeShelfItem(id, type));
213      model_->AddItem(shelf_item.PassAs<app_list::AppListItem>());
214    }
215  }
216
217  gfx::ImageSkia CreateSearchBoxIcon() {
218    const base::string16 icon_text = base::ASCIIToUTF16("ash");
219    const gfx::Size icon_size(32, 32);
220
221    gfx::Canvas canvas(icon_size, 1.0f, false /* is_opaque */);
222    canvas.DrawStringRectWithFlags(
223        icon_text,
224        gfx::FontList(),
225        SK_ColorBLACK,
226        gfx::Rect(icon_size),
227        gfx::Canvas::TEXT_ALIGN_CENTER | gfx::Canvas::NO_SUBPIXEL_RENDERING);
228
229    return gfx::ImageSkia(canvas.ExtractImageRep());
230  }
231
232  void DecorateSearchBox(app_list::SearchBoxModel* search_box_model) {
233    search_box_model->SetIcon(CreateSearchBoxIcon());
234    search_box_model->SetHintText(base::ASCIIToUTF16("Type to search..."));
235  }
236
237  // Overridden from app_list::AppListViewDelegate:
238  virtual bool ForceNativeDesktop() const OVERRIDE {
239    return false;
240  }
241
242  virtual void SetProfileByPath(const base::FilePath& profile_path) OVERRIDE {
243    // Nothing needs to be done.
244  }
245
246  virtual const Users& GetUsers() const OVERRIDE {
247    return users_;
248  }
249
250  virtual bool ShouldCenterWindow() const OVERRIDE {
251    return false;
252  }
253
254  virtual app_list::AppListModel* GetModel() OVERRIDE { return model_.get(); }
255
256  virtual app_list::SpeechUIModel* GetSpeechUI() OVERRIDE {
257    return &speech_ui_;
258  }
259
260  virtual void GetShortcutPathForApp(
261      const std::string& app_id,
262      const base::Callback<void(const base::FilePath&)>& callback) OVERRIDE {
263    callback.Run(base::FilePath());
264  }
265
266  virtual void OpenSearchResult(app_list::SearchResult* result,
267                                bool auto_launch,
268                                int event_flags) OVERRIDE {
269    const ExampleSearchResult* example_result =
270        static_cast<const ExampleSearchResult*>(result);
271    WindowTypeShelfItem::ActivateItem(example_result->type(), event_flags);
272  }
273
274  virtual void InvokeSearchResultAction(app_list::SearchResult* result,
275                                        int action_index,
276                                        int event_flags) OVERRIDE {
277    NOTIMPLEMENTED();
278  }
279
280  virtual base::TimeDelta GetAutoLaunchTimeout() OVERRIDE {
281    return base::TimeDelta();
282  }
283
284  virtual void AutoLaunchCanceled() OVERRIDE {
285  }
286
287  virtual void StartSearch() OVERRIDE {
288    base::string16 query;
289    base::TrimWhitespace(model_->search_box()->text(), base::TRIM_ALL, &query);
290    query = base::i18n::ToLower(query);
291
292    model_->results()->DeleteAll();
293    if (query.empty())
294      return;
295
296    for (int i = 0; i < static_cast<int>(WindowTypeShelfItem::LAST_TYPE); ++i) {
297      WindowTypeShelfItem::Type type =
298          static_cast<WindowTypeShelfItem::Type>(i);
299
300      base::string16 title =
301          base::UTF8ToUTF16(WindowTypeShelfItem::GetTitle(type));
302      if (base::i18n::StringSearchIgnoringCaseAndAccents(
303              query, title, NULL, NULL)) {
304        model_->results()->Add(new ExampleSearchResult(type, query));
305      }
306    }
307  }
308
309  virtual void StopSearch() OVERRIDE {
310    // Nothing needs to be done.
311  }
312
313  virtual void ViewInitialized() OVERRIDE {
314    // Nothing needs to be done.
315  }
316
317  virtual void Dismiss() OVERRIDE {
318    DCHECK(ash::Shell::HasInstance());
319    Shell::GetInstance()->DismissAppList();
320  }
321
322  virtual void ViewClosing() OVERRIDE {
323    // Nothing needs to be done.
324  }
325
326  virtual gfx::ImageSkia GetWindowIcon() OVERRIDE {
327    return gfx::ImageSkia();
328  }
329
330  virtual void OpenSettings() OVERRIDE {
331    // Nothing needs to be done.
332  }
333
334  virtual void OpenHelp() OVERRIDE {
335    // Nothing needs to be done.
336  }
337
338  virtual void OpenFeedback() OVERRIDE {
339    // Nothing needs to be done.
340  }
341
342  virtual void ToggleSpeechRecognition() OVERRIDE {
343    NOTIMPLEMENTED();
344  }
345
346  virtual void ShowForProfileByPath(
347      const base::FilePath& profile_path) OVERRIDE {
348    // Nothing needs to be done.
349  }
350
351  virtual views::View* CreateStartPageWebView(const gfx::Size& size) OVERRIDE {
352    return NULL;
353  }
354
355  virtual std::vector<views::View*> CreateCustomPageWebViews(
356      const gfx::Size& size) OVERRIDE {
357    return std::vector<views::View*>();
358  }
359
360  virtual bool IsSpeechRecognitionEnabled() OVERRIDE {
361    return false;
362  }
363
364  scoped_ptr<app_list::AppListModel> model_;
365  app_list::SpeechUIModel speech_ui_;
366  Users users_;
367
368  DISALLOW_COPY_AND_ASSIGN(ExampleAppListViewDelegate);
369};
370
371}  // namespace
372
373app_list::AppListViewDelegate* CreateAppListViewDelegate() {
374  return new ExampleAppListViewDelegate;
375}
376
377}  // namespace shell
378}  // namespace ash
379