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#import "ui/app_list/cocoa/apps_search_results_model_bridge.h"
6
7#import <Cocoa/Cocoa.h>
8
9#include "base/strings/sys_string_conversions.h"
10#include "ui/app_list/app_list_model.h"
11#import "ui/app_list/cocoa/apps_search_results_controller.h"
12#include "ui/app_list/search_result.h"
13#include "ui/app_list/search_result_observer.h"
14#import "ui/base/cocoa/menu_controller.h"
15
16namespace app_list {
17
18class AppsSearchResultsModelBridge::ItemObserver : public SearchResultObserver {
19 public:
20  ItemObserver(AppsSearchResultsModelBridge* bridge, size_t index)
21      : bridge_(bridge), row_in_view_(index) {
22    // Cache the result, because the results array is updated before notifying
23    // observers (which happens before deleting the SearchResult).
24    result_ = [bridge_->parent_ results]->GetItemAt(index);
25    result_->AddObserver(this);
26  }
27
28  virtual ~ItemObserver() {
29    result_->RemoveObserver(this);
30  }
31
32  NSMenu* GetContextMenu() {
33    if (!context_menu_controller_) {
34      ui::MenuModel* menu_model = result_->GetContextMenuModel();
35      if (!menu_model)
36        return nil;
37
38      context_menu_controller_.reset(
39          [[MenuController alloc] initWithModel:menu_model
40                         useWithPopUpButtonCell:NO]);
41    }
42    return [context_menu_controller_ menu];
43  }
44
45  // SearchResultObserver overrides:
46  virtual void OnIconChanged() OVERRIDE {
47    bridge_->ReloadDataForItems(row_in_view_, 1);
48  }
49  virtual void OnActionsChanged() OVERRIDE {}
50  virtual void OnIsInstallingChanged() OVERRIDE {}
51  virtual void OnPercentDownloadedChanged() OVERRIDE {}
52  virtual void OnItemInstalled() OVERRIDE {}
53  virtual void OnItemUninstalled() OVERRIDE;
54
55 private:
56  AppsSearchResultsModelBridge* bridge_;  // Weak. Owns us.
57  SearchResult* result_;  // Weak. Owned by AppListModel::SearchResults.
58  size_t row_in_view_;
59  base::scoped_nsobject<MenuController> context_menu_controller_;
60
61  DISALLOW_COPY_AND_ASSIGN(ItemObserver);
62};
63
64void AppsSearchResultsModelBridge::ItemObserver::OnItemUninstalled() {
65  // Performing the search again will destroy |this|, so post a task. This also
66  // ensures that the AppSearchProvider has observed the uninstall before
67  // performing the search again, otherwise it will provide a NULL result.
68  [[bridge_->parent_ delegate] performSelector:@selector(redoSearch)
69                                    withObject:nil
70                                    afterDelay:0];
71}
72
73AppsSearchResultsModelBridge::AppsSearchResultsModelBridge(
74    AppsSearchResultsController* results_controller)
75    : parent_(results_controller) {
76  UpdateItemObservers();
77  [parent_ results]->AddObserver(this);
78}
79
80AppsSearchResultsModelBridge::~AppsSearchResultsModelBridge() {
81  [parent_ results]->RemoveObserver(this);
82}
83
84NSMenu* AppsSearchResultsModelBridge::MenuForItem(size_t index) {
85  DCHECK_LT(index, item_observers_.size());
86  return item_observers_[index]->GetContextMenu();
87}
88
89void AppsSearchResultsModelBridge::UpdateItemObservers() {
90  DCHECK(item_observers_.empty());
91  const size_t itemCount = [parent_ results]->item_count();
92  for (size_t i = 0 ; i < itemCount; ++i)
93    item_observers_.push_back(new ItemObserver(this, i));
94}
95
96void AppsSearchResultsModelBridge::ReloadDataForItems(
97    size_t start, size_t count) const {
98  NSIndexSet* column = [NSIndexSet indexSetWithIndex:0];
99  NSIndexSet* rows =
100      [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(start, count)];
101  [[parent_ tableView] reloadDataForRowIndexes:rows
102                                 columnIndexes:column];
103}
104
105void AppsSearchResultsModelBridge::ListItemsAdded(
106    size_t start, size_t count) {
107  item_observers_.clear();
108  if (start == static_cast<size_t>([[parent_ tableView] numberOfRows]))
109    [[parent_ tableView] noteNumberOfRowsChanged];
110  else
111    [[parent_ tableView] reloadData];
112  UpdateItemObservers();
113}
114
115void AppsSearchResultsModelBridge::ListItemsRemoved(
116    size_t start, size_t count) {
117  item_observers_.clear();
118  if (start == [parent_ results]->item_count())
119    [[parent_ tableView] noteNumberOfRowsChanged];
120  else
121    [[parent_ tableView] reloadData];
122  UpdateItemObservers();
123}
124
125void AppsSearchResultsModelBridge::ListItemMoved(
126    size_t index, size_t target_index) {
127  NOTREACHED();
128}
129
130void AppsSearchResultsModelBridge::ListItemsChanged(
131    size_t start, size_t count) {
132  item_observers_.clear();
133  ReloadDataForItems(start, count);
134  UpdateItemObservers();
135}
136
137}  // namespace app_list
138