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