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 "ui/base/models/list_selection_model.h"
6
7#include <algorithm>
8#include <valarray>
9
10#include "base/logging.h"
11
12namespace ui {
13
14// static
15const int ListSelectionModel::kUnselectedIndex = -1;
16
17static void IncrementFromImpl(int index, int* value) {
18  if (*value >= index)
19    (*value)++;
20}
21
22static bool DecrementFromImpl(int index, int* value) {
23  if (*value == index) {
24    *value = ListSelectionModel::kUnselectedIndex;
25    return true;
26  }
27  if (*value > index)
28    (*value)--;
29  return false;
30}
31
32ListSelectionModel::ListSelectionModel()
33    : active_(kUnselectedIndex),
34      anchor_(kUnselectedIndex) {
35}
36
37ListSelectionModel::~ListSelectionModel() {
38}
39
40void ListSelectionModel::IncrementFrom(int index) {
41  // Shift the selection to account for the newly inserted tab.
42  for (SelectedIndices::iterator i = selected_indices_.begin();
43       i != selected_indices_.end(); ++i) {
44    IncrementFromImpl(index, &(*i));
45  }
46  IncrementFromImpl(index, &anchor_);
47  IncrementFromImpl(index, &active_);
48}
49
50void ListSelectionModel::DecrementFrom(int index) {
51  for (SelectedIndices::iterator i = selected_indices_.begin();
52       i != selected_indices_.end(); ) {
53    if (DecrementFromImpl(index, &(*i)))
54      i = selected_indices_.erase(i);
55    else
56      ++i;
57  }
58  DecrementFromImpl(index, &anchor_);
59  DecrementFromImpl(index, &active_);
60}
61
62void ListSelectionModel::SetSelectedIndex(int index) {
63  anchor_ = active_ = index;
64  selected_indices_.clear();
65  if (index != kUnselectedIndex)
66    selected_indices_.push_back(index);
67}
68
69bool ListSelectionModel::IsSelected(int index) const {
70  return std::find(selected_indices_.begin(), selected_indices_.end(), index) !=
71      selected_indices_.end();
72}
73
74void ListSelectionModel::AddIndexToSelection(int index) {
75  if (!IsSelected(index)) {
76    selected_indices_.push_back(index);
77    std::sort(selected_indices_.begin(), selected_indices_.end());
78  }
79}
80
81void ListSelectionModel::RemoveIndexFromSelection(int index) {
82  SelectedIndices::iterator i = std::find(selected_indices_.begin(),
83                                          selected_indices_.end(), index);
84  if (i != selected_indices_.end())
85    selected_indices_.erase(i);
86}
87
88void ListSelectionModel::SetSelectionFromAnchorTo(int index) {
89  if (anchor_ == kUnselectedIndex) {
90    SetSelectedIndex(index);
91  } else {
92    int delta = std::abs(index - anchor_);
93    SelectedIndices new_selection(delta + 1, 0);
94    for (int i = 0, min = std::min(index, anchor_); i <= delta; ++i)
95      new_selection[i] = i + min;
96    selected_indices_.swap(new_selection);
97    active_ = index;
98  }
99}
100
101void ListSelectionModel::AddSelectionFromAnchorTo(int index) {
102  if (anchor_ == kUnselectedIndex) {
103    SetSelectedIndex(index);
104  } else {
105    for (int i = std::min(index, anchor_), end = std::max(index, anchor_);
106         i <= end; ++i) {
107      if (!IsSelected(i))
108        selected_indices_.push_back(i);
109    }
110    std::sort(selected_indices_.begin(), selected_indices_.end());
111    active_ = index;
112  }
113}
114
115void ListSelectionModel::Move(int from, int to) {
116  DCHECK_NE(to, from);
117  bool was_anchor = from == anchor_;
118  bool was_active = from == active_;
119  bool was_selected = IsSelected(from);
120  if (to < from) {
121    IncrementFrom(to);
122    DecrementFrom(from + 1);
123  } else {
124    DecrementFrom(from);
125    IncrementFrom(to);
126  }
127  if (was_active)
128    active_ = to;
129  if (was_anchor)
130    anchor_ = to;
131  if (was_selected)
132    AddIndexToSelection(to);
133}
134
135void ListSelectionModel::Clear() {
136  anchor_ = active_ = kUnselectedIndex;
137  SelectedIndices empty_selection;
138  selected_indices_.swap(empty_selection);
139}
140
141void ListSelectionModel::Copy(const ListSelectionModel& source) {
142  selected_indices_ = source.selected_indices_;
143  active_ = source.active_;
144  anchor_ = source.anchor_;
145}
146
147bool ListSelectionModel::Equals(const ListSelectionModel& rhs) const {
148  return active_ == rhs.active() &&
149      anchor_ == rhs.anchor() &&
150      selected_indices() == rhs.selected_indices();
151}
152
153}  // namespace ui
154