Home | History | Annotate | Download | only in shelf
      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