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 "ash/launcher/launcher_model.h" 6 7 #include <algorithm> 8 9 #include "ash/ash_switches.h" 10 #include "ash/launcher/launcher_model_observer.h" 11 12 namespace ash { 13 14 namespace { 15 16 int LauncherItemTypeToWeight(LauncherItemType type) { 17 if (ash::switches::UseAlternateShelfLayout()) { 18 switch (type) { 19 case TYPE_APP_LIST: 20 return 0; 21 case TYPE_BROWSER_SHORTCUT: 22 case TYPE_APP_SHORTCUT: 23 case TYPE_WINDOWED_APP: 24 return 1; 25 case TYPE_TABBED: 26 case TYPE_PLATFORM_APP: 27 return 2; 28 case TYPE_APP_PANEL: 29 return 3; 30 } 31 } else { 32 switch (type) { 33 case TYPE_BROWSER_SHORTCUT: 34 case TYPE_APP_SHORTCUT: 35 case TYPE_WINDOWED_APP: 36 return 0; 37 case TYPE_TABBED: 38 case TYPE_PLATFORM_APP: 39 return 1; 40 case TYPE_APP_LIST: 41 return 2; 42 case TYPE_APP_PANEL: 43 return 3; 44 } 45 } 46 47 NOTREACHED() << "Invalid type " << type; 48 return 1; 49 } 50 51 bool CompareByWeight(const LauncherItem& a, const LauncherItem& b) { 52 return LauncherItemTypeToWeight(a.type) < LauncherItemTypeToWeight(b.type); 53 } 54 55 } // namespace 56 57 LauncherModel::LauncherModel() : next_id_(1), status_(STATUS_NORMAL) { 58 LauncherItem app_list; 59 app_list.type = TYPE_APP_LIST; 60 app_list.is_incognito = false; 61 AddAt(0, app_list); 62 } 63 64 LauncherModel::~LauncherModel() { 65 } 66 67 int LauncherModel::Add(const LauncherItem& item) { 68 return AddAt(items_.size(), item); 69 } 70 71 int LauncherModel::AddAt(int index, const LauncherItem& item) { 72 index = ValidateInsertionIndex(item.type, index); 73 items_.insert(items_.begin() + index, item); 74 items_[index].id = next_id_++; 75 FOR_EACH_OBSERVER(LauncherModelObserver, observers_, 76 LauncherItemAdded(index)); 77 return index; 78 } 79 80 void LauncherModel::RemoveItemAt(int index) { 81 DCHECK(index >= 0 && index < item_count()); 82 // The app list and browser shortcut can't be removed. 83 DCHECK(items_[index].type != TYPE_APP_LIST && 84 items_[index].type != TYPE_BROWSER_SHORTCUT); 85 LauncherID id = items_[index].id; 86 items_.erase(items_.begin() + index); 87 FOR_EACH_OBSERVER(LauncherModelObserver, observers_, 88 LauncherItemRemoved(index, id)); 89 } 90 91 void LauncherModel::Move(int index, int target_index) { 92 if (index == target_index) 93 return; 94 // TODO: this needs to enforce valid ranges. 95 LauncherItem item(items_[index]); 96 items_.erase(items_.begin() + index); 97 items_.insert(items_.begin() + target_index, item); 98 FOR_EACH_OBSERVER(LauncherModelObserver, observers_, 99 LauncherItemMoved(index, target_index)); 100 } 101 102 void LauncherModel::Set(int index, const LauncherItem& item) { 103 DCHECK(index >= 0 && index < item_count()); 104 int new_index = item.type == items_[index].type ? 105 index : ValidateInsertionIndex(item.type, index); 106 107 LauncherItem old_item(items_[index]); 108 items_[index] = item; 109 items_[index].id = old_item.id; 110 FOR_EACH_OBSERVER(LauncherModelObserver, observers_, 111 LauncherItemChanged(index, old_item)); 112 113 // If the type changes confirm that the item is still in the right order. 114 if (new_index != index) { 115 // The move function works by removing one item and then inserting it at the 116 // new location. However - by removing the item first the order will change 117 // so that our target index needs to be corrected. 118 // TODO(skuhne): Moving this into the Move function breaks lots of unit 119 // tests. So several functions were already using this incorrectly. 120 // That needs to be cleaned up. 121 if (index < new_index) 122 new_index--; 123 124 Move(index, new_index); 125 } 126 } 127 128 int LauncherModel::ItemIndexByID(LauncherID id) const { 129 LauncherItems::const_iterator i = ItemByID(id); 130 return i == items_.end() ? -1 : static_cast<int>(i - items_.begin()); 131 } 132 133 LauncherItems::const_iterator LauncherModel::ItemByID(int id) const { 134 for (LauncherItems::const_iterator i = items_.begin(); 135 i != items_.end(); ++i) { 136 if (i->id == id) 137 return i; 138 } 139 return items_.end(); 140 } 141 142 int LauncherModel::FirstPanelIndex() const { 143 LauncherItem weight_dummy; 144 weight_dummy.type = TYPE_APP_PANEL; 145 return std::lower_bound(items_.begin(), items_.end(), weight_dummy, 146 CompareByWeight) - items_.begin(); 147 } 148 149 void LauncherModel::SetStatus(Status status) { 150 if (status_ == status) 151 return; 152 153 status_ = status; 154 FOR_EACH_OBSERVER(LauncherModelObserver, observers_, 155 LauncherStatusChanged()); 156 } 157 158 void LauncherModel::AddObserver(LauncherModelObserver* observer) { 159 observers_.AddObserver(observer); 160 } 161 162 void LauncherModel::RemoveObserver(LauncherModelObserver* observer) { 163 observers_.RemoveObserver(observer); 164 } 165 166 int LauncherModel::ValidateInsertionIndex(LauncherItemType type, 167 int index) const { 168 DCHECK(index >= 0 && index <= item_count() + 169 (ash::switches::UseAlternateShelfLayout() ? 1 : 0)); 170 171 // Clamp |index| to the allowed range for the type as determined by |weight|. 172 LauncherItem weight_dummy; 173 weight_dummy.type = type; 174 index = std::max(std::lower_bound(items_.begin(), items_.end(), weight_dummy, 175 CompareByWeight) - items_.begin(), 176 static_cast<LauncherItems::difference_type>(index)); 177 index = std::min(std::upper_bound(items_.begin(), items_.end(), weight_dummy, 178 CompareByWeight) - items_.begin(), 179 static_cast<LauncherItems::difference_type>(index)); 180 181 return index; 182 } 183 184 } // namespace ash 185