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 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