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