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 "ui/app_list/views/search_result_list_view.h"
6
7#include <algorithm>
8
9#include "base/bind.h"
10#include "base/message_loop/message_loop.h"
11#include "ui/app_list/search_result_list_view_delegate.h"
12#include "ui/app_list/views/search_result_view.h"
13#include "ui/base/events/event.h"
14#include "ui/views/layout/box_layout.h"
15
16namespace {
17
18const int kMaxResults = 6;
19
20}  // namespace
21
22namespace app_list {
23
24SearchResultListView::SearchResultListView(
25    SearchResultListViewDelegate* delegate)
26    : delegate_(delegate),
27      results_(NULL),
28      last_visible_index_(0),
29      selected_index_(-1),
30      update_factory_(this) {
31  SetLayoutManager(
32      new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
33
34  for (int i = 0; i < kMaxResults; ++i)
35    AddChildView(new SearchResultView(this, this));
36}
37
38SearchResultListView::~SearchResultListView() {
39  if (results_)
40    results_->RemoveObserver(this);
41}
42
43void SearchResultListView::SetResults(AppListModel::SearchResults* results) {
44  if (results_)
45    results_->RemoveObserver(this);
46
47  results_ = results;
48  if (results_)
49    results_->AddObserver(this);
50
51  Update();
52}
53
54void SearchResultListView::SetSelectedIndex(int selected_index) {
55  if (selected_index_ == selected_index)
56    return;
57
58  if (selected_index_ >= 0)
59    GetResultViewAt(selected_index_)->SchedulePaint();
60
61  selected_index_ = selected_index;
62
63  if (selected_index_ >= 0)
64    GetResultViewAt(selected_index_)->SchedulePaint();
65}
66
67bool SearchResultListView::IsResultViewSelected(
68    const SearchResultView* result_view) const {
69  if (selected_index_ < 0)
70    return false;
71
72  return static_cast<const SearchResultView*>(child_at(selected_index_)) ==
73      result_view;
74}
75
76bool SearchResultListView::OnKeyPressed(const ui::KeyEvent& event) {
77  switch (event.key_code()) {
78    case ui::VKEY_TAB:
79      if (event.IsShiftDown())
80        SetSelectedIndex(std::max(selected_index_ - 1, 0));
81      else
82        SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
83      return true;
84    case ui::VKEY_UP:
85      SetSelectedIndex(std::max(selected_index_ - 1, 0));
86      return true;
87    case ui::VKEY_DOWN:
88      SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
89      return true;
90    case ui::VKEY_RETURN:
91      if (selected_index_ >= 0)
92        SearchResultActivated(GetResultViewAt(selected_index_), event.flags());
93      return true;
94    default:
95      break;
96  }
97  return false;
98}
99
100SearchResultView* SearchResultListView::GetResultViewAt(int index) {
101  DCHECK(index >= 0 && index < child_count());
102  return static_cast<SearchResultView*>(child_at(index));
103}
104
105void SearchResultListView::Update() {
106  last_visible_index_ = 0;
107  for (size_t i = 0; i < static_cast<size_t>(child_count()); ++i) {
108    SearchResultView* result_view = GetResultViewAt(i);
109    if (i < results_->item_count()) {
110      result_view->SetResult(results_->GetItemAt(i));
111      result_view->SetVisible(true);
112      last_visible_index_ = i;
113    } else {
114      result_view->SetResult(NULL);
115      result_view->SetVisible(false);
116    }
117  }
118  if (selected_index_ > last_visible_index_)
119    SetSelectedIndex(last_visible_index_);
120
121  Layout();
122  update_factory_.InvalidateWeakPtrs();
123}
124
125void SearchResultListView::ScheduleUpdate() {
126  // When search results are added one by one, each addition generates an update
127  // request. Consolidates those update requests into one Update call.
128  if (!update_factory_.HasWeakPtrs()) {
129    base::MessageLoop::current()->PostTask(
130        FROM_HERE,
131        base::Bind(&SearchResultListView::Update,
132                   update_factory_.GetWeakPtr()));
133  }
134}
135
136void SearchResultListView::ListItemsAdded(size_t start, size_t count) {
137  ScheduleUpdate();
138}
139
140void SearchResultListView::ListItemsRemoved(size_t start, size_t count) {
141  size_t last = std::min(start + count, static_cast<size_t>(child_count()));
142  for (size_t i = start; i < last; ++i)
143    GetResultViewAt(i)->ClearResultNoRepaint();
144
145  ScheduleUpdate();
146}
147
148void SearchResultListView::ListItemMoved(size_t index, size_t target_index) {
149  NOTREACHED();
150}
151
152void SearchResultListView::ListItemsChanged(size_t start, size_t count) {
153  ScheduleUpdate();
154}
155
156void SearchResultListView::SearchResultActivated(SearchResultView* view,
157                                                 int event_flags) {
158  if (delegate_ && view->result())
159    delegate_->OpenResult(view->result(), event_flags);
160}
161
162void SearchResultListView::SearchResultActionActivated(SearchResultView* view,
163                                                       size_t action_index,
164                                                       int event_flags) {
165  if (delegate_ && view->result()) {
166    delegate_->InvokeResultAction(
167        view->result(), action_index, event_flags);
168  }
169}
170
171void SearchResultListView::OnSearchResultInstalled(SearchResultView* view) {
172  if (delegate_ && view->result())
173    delegate_->OnResultInstalled(view->result());
174}
175
176void SearchResultListView::OnSearchResultUninstalled(SearchResultView* view) {
177  if (delegate_ && view->result())
178    delegate_->OnResultUninstalled(view->result());
179}
180
181}  // namespace app_list
182