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/views/controls/table/table_view.h"
6
7#include "base/strings/string_number_conversions.h"
8#include "base/strings/utf_string_conversions.h"
9#include "testing/gtest/include/gtest/gtest.h"
10#include "ui/views/controls/table/table_grouper.h"
11#include "ui/views/controls/table/table_header.h"
12#include "ui/views/controls/table/table_view_observer.h"
13
14// Put the tests in the views namespace to make it easier to declare them as
15// friend classes.
16namespace views {
17
18class TableViewTestHelper {
19 public:
20  explicit TableViewTestHelper(TableView* table) : table_(table) {}
21
22  std::string GetPaintRegion(const gfx::Rect& bounds) {
23    TableView::PaintRegion region(table_->GetPaintRegion(bounds));
24    return "rows=" + base::IntToString(region.min_row) + " " +
25        base::IntToString(region.max_row) + " cols=" +
26        base::IntToString(region.min_column) + " " +
27        base::IntToString(region.max_column);
28  }
29
30  size_t visible_col_count() {
31    return table_->visible_columns().size();
32  }
33
34  TableHeader* header() { return table_->header_; }
35
36 private:
37  TableView* table_;
38
39  DISALLOW_COPY_AND_ASSIGN(TableViewTestHelper);
40};
41
42namespace {
43
44// TestTableModel2 -------------------------------------------------------------
45
46// Trivial TableModel implementation that is backed by a vector of vectors.
47// Provides methods for adding/removing/changing the contents that notify the
48// observer appropriately.
49//
50// Initial contents are:
51// 0, 1
52// 1, 1
53// 2, 2
54// 3, 0
55class TestTableModel2 : public ui::TableModel {
56 public:
57  TestTableModel2();
58
59  // Adds a new row at index |row| with values |c1_value| and |c2_value|.
60  void AddRow(int row, int c1_value, int c2_value);
61
62  // Removes the row at index |row|.
63  void RemoveRow(int row);
64
65  // Changes the values of the row at |row|.
66  void ChangeRow(int row, int c1_value, int c2_value);
67
68  // ui::TableModel:
69  virtual int RowCount() OVERRIDE;
70  virtual base::string16 GetText(int row, int column_id) OVERRIDE;
71  virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE;
72  virtual int CompareValues(int row1, int row2, int column_id) OVERRIDE;
73
74 private:
75  ui::TableModelObserver* observer_;
76
77  // The data.
78  std::vector<std::vector<int> > rows_;
79
80  DISALLOW_COPY_AND_ASSIGN(TestTableModel2);
81};
82
83TestTableModel2::TestTableModel2() : observer_(NULL) {
84  AddRow(0, 0, 1);
85  AddRow(1, 1, 1);
86  AddRow(2, 2, 2);
87  AddRow(3, 3, 0);
88}
89
90void TestTableModel2::AddRow(int row, int c1_value, int c2_value) {
91  DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
92  std::vector<int> new_row;
93  new_row.push_back(c1_value);
94  new_row.push_back(c2_value);
95  rows_.insert(rows_.begin() + row, new_row);
96  if (observer_)
97    observer_->OnItemsAdded(row, 1);
98}
99void TestTableModel2::RemoveRow(int row) {
100  DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
101  rows_.erase(rows_.begin() + row);
102  if (observer_)
103    observer_->OnItemsRemoved(row, 1);
104}
105
106void TestTableModel2::ChangeRow(int row, int c1_value, int c2_value) {
107  DCHECK(row >= 0 && row < static_cast<int>(rows_.size()));
108  rows_[row][0] = c1_value;
109  rows_[row][1] = c2_value;
110  if (observer_)
111    observer_->OnItemsChanged(row, 1);
112}
113
114int TestTableModel2::RowCount() {
115  return static_cast<int>(rows_.size());
116}
117
118base::string16 TestTableModel2::GetText(int row, int column_id) {
119  return base::IntToString16(rows_[row][column_id]);
120}
121
122void TestTableModel2::SetObserver(ui::TableModelObserver* observer) {
123  observer_ = observer;
124}
125
126int TestTableModel2::CompareValues(int row1, int row2, int column_id) {
127  return rows_[row1][column_id] - rows_[row2][column_id];
128}
129
130// Returns the view to model mapping as a string.
131std::string GetViewToModelAsString(TableView* table) {
132  std::string result;
133  for (int i = 0; i < table->RowCount(); ++i) {
134    if (i != 0)
135      result += " ";
136    result += base::IntToString(table->ViewToModel(i));
137  }
138  return result;
139}
140
141// Returns the model to view mapping as a string.
142std::string GetModelToViewAsString(TableView* table) {
143  std::string result;
144  for (int i = 0; i < table->RowCount(); ++i) {
145    if (i != 0)
146      result += " ";
147    result += base::IntToString(table->ModelToView(i));
148  }
149  return result;
150}
151
152class TestTableView : public TableView {
153 public:
154  TestTableView(ui::TableModel* model,
155                const std::vector<ui::TableColumn>& columns)
156      : TableView(model, columns, TEXT_ONLY, false) {
157  }
158
159  // View overrides:
160  virtual bool HasFocus() const OVERRIDE {
161    // Overriden so key processing works.
162    return true;
163  }
164
165 private:
166  DISALLOW_COPY_AND_ASSIGN(TestTableView);
167};
168
169}  // namespace
170
171class TableViewTest : public testing::Test {
172 public:
173  TableViewTest() : table_(NULL) {}
174
175  virtual void SetUp() OVERRIDE {
176    model_.reset(new TestTableModel2);
177    std::vector<ui::TableColumn> columns(2);
178    columns[0].title = base::ASCIIToUTF16("Title Column 0");
179    columns[0].sortable = true;
180    columns[1].title = base::ASCIIToUTF16("Title Column 1");
181    columns[1].id = 1;
182    columns[1].sortable = true;
183    table_ = new TestTableView(model_.get(), columns);
184    parent_.reset(table_->CreateParentIfNecessary());
185    parent_->SetBounds(0, 0, 10000, 10000);
186    parent_->Layout();
187    helper_.reset(new TableViewTestHelper(table_));
188  }
189
190  void ClickOnRow(int row, int flags) {
191    const int y = row * table_->row_height();
192    const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, y),
193                                 gfx::Point(0, y),
194                                 ui::EF_LEFT_MOUSE_BUTTON | flags,
195                                 ui::EF_LEFT_MOUSE_BUTTON);
196    table_->OnMousePressed(pressed);
197  }
198
199  void TapOnRow(int row) {
200    const int y = row * table_->row_height();
201    const ui::GestureEventDetails event_details(ui::ET_GESTURE_TAP);
202    ui::GestureEvent tap(0, y, 0, base::TimeDelta(), event_details);
203    table_->OnGestureEvent(&tap);
204  }
205
206  // Returns the state of the selection model as a string. The format is:
207  // 'active=X anchor=X selection=X X X...'.
208  std::string SelectionStateAsString() const {
209    const ui::ListSelectionModel& model(table_->selection_model());
210    std::string result = "active=" + base::IntToString(model.active()) +
211        " anchor=" + base::IntToString(model.anchor()) +
212        " selection=";
213    const ui::ListSelectionModel::SelectedIndices& selection(
214        model.selected_indices());
215    for (size_t i = 0; i < selection.size(); ++i) {
216      if (i != 0)
217        result += " ";
218      result += base::IntToString(selection[i]);
219    }
220    return result;
221  }
222
223  void PressKey(ui::KeyboardCode code) {
224    ui::KeyEvent event(ui::ET_KEY_PRESSED, code, ui::EF_NONE);
225    table_->OnKeyPressed(event);
226  }
227
228 protected:
229  scoped_ptr<TestTableModel2> model_;
230
231  // Owned by |parent_|.
232  TableView* table_;
233
234  scoped_ptr<TableViewTestHelper> helper_;
235
236 private:
237  scoped_ptr<View> parent_;
238
239  DISALLOW_COPY_AND_ASSIGN(TableViewTest);
240};
241
242// Verifies GetPaintRegion.
243TEST_F(TableViewTest, GetPaintRegion) {
244  // Two columns should be visible.
245  EXPECT_EQ(2u, helper_->visible_col_count());
246
247  EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
248  EXPECT_EQ("rows=0 4 cols=0 1",
249            helper_->GetPaintRegion(gfx::Rect(0, 0, 1, table_->height())));
250}
251
252// Verifies SetColumnVisibility().
253TEST_F(TableViewTest, ColumnVisibility) {
254  // Two columns should be visible.
255  EXPECT_EQ(2u, helper_->visible_col_count());
256
257  // Should do nothing (column already visible).
258  table_->SetColumnVisibility(0, true);
259  EXPECT_EQ(2u, helper_->visible_col_count());
260
261  // Hide the first column.
262  table_->SetColumnVisibility(0, false);
263  ASSERT_EQ(1u, helper_->visible_col_count());
264  EXPECT_EQ(1, table_->visible_columns()[0].column.id);
265  EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
266
267  // Hide the second column.
268  table_->SetColumnVisibility(1, false);
269  EXPECT_EQ(0u, helper_->visible_col_count());
270
271  // Show the second column.
272  table_->SetColumnVisibility(1, true);
273  ASSERT_EQ(1u, helper_->visible_col_count());
274  EXPECT_EQ(1, table_->visible_columns()[0].column.id);
275  EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
276
277  // Show the first column.
278  table_->SetColumnVisibility(0, true);
279  ASSERT_EQ(2u, helper_->visible_col_count());
280  EXPECT_EQ(1, table_->visible_columns()[0].column.id);
281  EXPECT_EQ(0, table_->visible_columns()[1].column.id);
282  EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
283}
284
285// Verifies resizing a column works.
286TEST_F(TableViewTest, Resize) {
287  const int x = table_->visible_columns()[0].width;
288  EXPECT_NE(0, x);
289  // Drag the mouse 1 pixel to the left.
290  const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
291                               gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON,
292                               ui::EF_LEFT_MOUSE_BUTTON);
293  helper_->header()->OnMousePressed(pressed);
294  const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, gfx::Point(x - 1, 0),
295                               gfx::Point(x - 1, 0), ui::EF_LEFT_MOUSE_BUTTON,
296                               0);
297  helper_->header()->OnMouseDragged(dragged);
298
299  // This should shrink the first column and pull the second column in.
300  EXPECT_EQ(x - 1, table_->visible_columns()[0].width);
301  EXPECT_EQ(x - 1, table_->visible_columns()[1].x);
302}
303
304// Verifies resizing a column works with a gesture.
305TEST_F(TableViewTest, ResizeViaGesture) {
306  const int x = table_->visible_columns()[0].width;
307  EXPECT_NE(0, x);
308  // Drag the mouse 1 pixel to the left.
309  ui::GestureEvent scroll_begin(
310      x,
311      0,
312      0,
313      base::TimeDelta(),
314      ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
315  helper_->header()->OnGestureEvent(&scroll_begin);
316  ui::GestureEvent scroll_update(
317      x - 1,
318      0,
319      0,
320      base::TimeDelta(),
321      ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE));
322  helper_->header()->OnGestureEvent(&scroll_update);
323
324  // This should shrink the first column and pull the second column in.
325  EXPECT_EQ(x - 1, table_->visible_columns()[0].width);
326  EXPECT_EQ(x - 1, table_->visible_columns()[1].x);
327}
328
329// Assertions for table sorting.
330TEST_F(TableViewTest, Sort) {
331  // Toggle the sort order of the first column, shouldn't change anything.
332  table_->ToggleSortOrder(0);
333  ASSERT_EQ(1u, table_->sort_descriptors().size());
334  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
335  EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
336  EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
337  EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
338
339  // Invert the sort (first column descending).
340  table_->ToggleSortOrder(0);
341  ASSERT_EQ(1u, table_->sort_descriptors().size());
342  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
343  EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
344  EXPECT_EQ("3 2 1 0", GetViewToModelAsString(table_));
345  EXPECT_EQ("3 2 1 0", GetModelToViewAsString(table_));
346
347  // Change cell 0x3 to -1, meaning we have 0, 1, 2, -1 (in the first column).
348  model_->ChangeRow(3, -1, 0);
349  ASSERT_EQ(1u, table_->sort_descriptors().size());
350  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
351  EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
352  EXPECT_EQ("2 1 0 3", GetViewToModelAsString(table_));
353  EXPECT_EQ("2 1 0 3", GetModelToViewAsString(table_));
354
355  // Invert sort again (first column ascending).
356  table_->ToggleSortOrder(0);
357  ASSERT_EQ(1u, table_->sort_descriptors().size());
358  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
359  EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
360  EXPECT_EQ("3 0 1 2", GetViewToModelAsString(table_));
361  EXPECT_EQ("1 2 3 0", GetModelToViewAsString(table_));
362
363  // Add a row so that model has 0, 3, 1, 2, -1.
364  model_->AddRow(1, 3, 4);
365  ASSERT_EQ(1u, table_->sort_descriptors().size());
366  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
367  EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
368  EXPECT_EQ("4 0 2 3 1", GetViewToModelAsString(table_));
369  EXPECT_EQ("1 4 2 3 0", GetModelToViewAsString(table_));
370
371  // Delete the first row, ending up with 3, 1, 2, -1.
372  model_->RemoveRow(0);
373  ASSERT_EQ(1u, table_->sort_descriptors().size());
374  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
375  EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
376  EXPECT_EQ("3 1 2 0", GetViewToModelAsString(table_));
377  EXPECT_EQ("3 1 2 0", GetModelToViewAsString(table_));
378}
379
380// Verfies clicking on the header sorts.
381TEST_F(TableViewTest, SortOnMouse) {
382  EXPECT_TRUE(table_->sort_descriptors().empty());
383
384  const int x = table_->visible_columns()[0].width / 2;
385  EXPECT_NE(0, x);
386  // Press and release the mouse.
387  const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
388                               gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON,
389                               ui::EF_LEFT_MOUSE_BUTTON);
390  // The header must return true, else it won't normally get the release.
391  EXPECT_TRUE(helper_->header()->OnMousePressed(pressed));
392  const ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(x, 0),
393                               gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON,
394                               ui::EF_LEFT_MOUSE_BUTTON);
395  helper_->header()->OnMouseReleased(release);
396
397  ASSERT_EQ(1u, table_->sort_descriptors().size());
398  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
399  EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
400}
401
402namespace {
403
404class TableGrouperImpl : public TableGrouper {
405 public:
406  TableGrouperImpl() {}
407
408  void SetRanges(const std::vector<int>& ranges) {
409    ranges_ = ranges;
410  }
411
412  // TableGrouper overrides:
413  virtual void GetGroupRange(int model_index, GroupRange* range) OVERRIDE {
414    int offset = 0;
415    size_t range_index = 0;
416    for (; range_index < ranges_.size() && offset < model_index; ++range_index)
417      offset += ranges_[range_index];
418
419    if (offset == model_index) {
420      range->start = model_index;
421      range->length = ranges_[range_index];
422    } else {
423      range->start = offset - ranges_[range_index - 1];
424      range->length = ranges_[range_index - 1];
425    }
426  }
427
428 private:
429  std::vector<int> ranges_;
430
431  DISALLOW_COPY_AND_ASSIGN(TableGrouperImpl);
432};
433
434}  // namespace
435
436// Assertions around grouping.
437TEST_F(TableViewTest, Grouping) {
438  // Configure the grouper so that there are two groups:
439  // A 0
440  //   1
441  // B 2
442  //   3
443  TableGrouperImpl grouper;
444  std::vector<int> ranges;
445  ranges.push_back(2);
446  ranges.push_back(2);
447  grouper.SetRanges(ranges);
448  table_->SetGrouper(&grouper);
449
450  // Toggle the sort order of the first column, shouldn't change anything.
451  table_->ToggleSortOrder(0);
452  ASSERT_EQ(1u, table_->sort_descriptors().size());
453  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
454  EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
455  EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
456  EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
457
458  // Sort descending, resulting:
459  // B 2
460  //   3
461  // A 0
462  //   1
463  table_->ToggleSortOrder(0);
464  ASSERT_EQ(1u, table_->sort_descriptors().size());
465  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
466  EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
467  EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
468  EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
469
470  // Change the entry in the 4th row to -1. The model now becomes:
471  // A 0
472  //   1
473  // B 2
474  //   -1
475  // Since the first entry in the range didn't change the sort isn't impacted.
476  model_->ChangeRow(3, -1, 0);
477  ASSERT_EQ(1u, table_->sort_descriptors().size());
478  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
479  EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
480  EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
481  EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
482
483  // Change the entry in the 3rd row to -1. The model now becomes:
484  // A 0
485  //   1
486  // B -1
487  //   -1
488  model_->ChangeRow(2, -1, 0);
489  ASSERT_EQ(1u, table_->sort_descriptors().size());
490  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
491  EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
492  EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
493  EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
494
495  // Toggle to ascending sort.
496  table_->ToggleSortOrder(0);
497  ASSERT_EQ(1u, table_->sort_descriptors().size());
498  EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
499  EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
500  EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
501  EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
502}
503
504namespace {
505
506class TableViewObserverImpl : public TableViewObserver {
507 public:
508  TableViewObserverImpl() : selection_changed_count_(0) {}
509
510  int GetChangedCountAndClear() {
511    const int count = selection_changed_count_;
512    selection_changed_count_ = 0;
513    return count;
514  }
515
516  // TableViewObserver overrides:
517  virtual void OnSelectionChanged() OVERRIDE {
518    selection_changed_count_++;
519  }
520
521 private:
522  int selection_changed_count_;
523
524  DISALLOW_COPY_AND_ASSIGN(TableViewObserverImpl);
525};
526
527}  // namespace
528
529// Assertions around changing the selection.
530TEST_F(TableViewTest, Selection) {
531  TableViewObserverImpl observer;
532  table_->SetObserver(&observer);
533
534  // Initially no selection.
535  EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
536
537  // Select the last row.
538  table_->Select(3);
539  EXPECT_EQ(1, observer.GetChangedCountAndClear());
540  EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
541
542  // Change sort, shouldn't notify of change (toggle twice so that order
543  // actually changes).
544  table_->ToggleSortOrder(0);
545  table_->ToggleSortOrder(0);
546  EXPECT_EQ(0, observer.GetChangedCountAndClear());
547  EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
548
549  // Remove the selected row, this should notify of a change and update the
550  // selection.
551  model_->RemoveRow(3);
552  EXPECT_EQ(1, observer.GetChangedCountAndClear());
553  EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
554
555  // Insert a row, since the selection in terms of the original model hasn't
556  // changed the observer is not notified.
557  model_->AddRow(0, 1, 2);
558  EXPECT_EQ(0, observer.GetChangedCountAndClear());
559  EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
560
561  table_->SetObserver(NULL);
562}
563
564// Verifies selection works by way of a gesture.
565TEST_F(TableViewTest, SelectOnTap) {
566  // Initially no selection.
567  EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
568
569  TableViewObserverImpl observer;
570  table_->SetObserver(&observer);
571
572  // Click on the first row, should select it.
573  TapOnRow(0);
574  EXPECT_EQ(1, observer.GetChangedCountAndClear());
575  EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString());
576
577  table_->SetObserver(NULL);
578}
579
580// Verifies up/down correctly navigates through groups.
581TEST_F(TableViewTest, KeyUpDown) {
582  // Configure the grouper so that there are three groups:
583  // A 0
584  //   1
585  // B 5
586  // C 2
587  //   3
588  model_->AddRow(2, 5, 0);
589  TableGrouperImpl grouper;
590  std::vector<int> ranges;
591  ranges.push_back(2);
592  ranges.push_back(1);
593  ranges.push_back(2);
594  grouper.SetRanges(ranges);
595  table_->SetGrouper(&grouper);
596
597  TableViewObserverImpl observer;
598  table_->SetObserver(&observer);
599
600  // Initially no selection.
601  EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
602
603  PressKey(ui::VKEY_DOWN);
604  EXPECT_EQ(1, observer.GetChangedCountAndClear());
605  EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
606
607  PressKey(ui::VKEY_DOWN);
608  EXPECT_EQ(1, observer.GetChangedCountAndClear());
609  EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
610
611  PressKey(ui::VKEY_DOWN);
612  EXPECT_EQ(1, observer.GetChangedCountAndClear());
613  EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
614
615  PressKey(ui::VKEY_DOWN);
616  EXPECT_EQ(1, observer.GetChangedCountAndClear());
617  EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
618
619  PressKey(ui::VKEY_DOWN);
620  EXPECT_EQ(1, observer.GetChangedCountAndClear());
621  EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
622
623  PressKey(ui::VKEY_DOWN);
624  EXPECT_EQ(0, observer.GetChangedCountAndClear());
625  EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
626
627  PressKey(ui::VKEY_UP);
628  EXPECT_EQ(1, observer.GetChangedCountAndClear());
629  EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
630
631  PressKey(ui::VKEY_UP);
632  EXPECT_EQ(1, observer.GetChangedCountAndClear());
633  EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
634
635  PressKey(ui::VKEY_UP);
636  EXPECT_EQ(1, observer.GetChangedCountAndClear());
637  EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
638
639  PressKey(ui::VKEY_UP);
640  EXPECT_EQ(1, observer.GetChangedCountAndClear());
641  EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
642
643  PressKey(ui::VKEY_UP);
644  EXPECT_EQ(0, observer.GetChangedCountAndClear());
645  EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
646
647  // Sort the table descending by column 1, view now looks like:
648  // B 5   model: 2
649  // C 2          3
650  //   3          4
651  // A 0          0
652  //   1          1
653  table_->ToggleSortOrder(0);
654  table_->ToggleSortOrder(0);
655
656  EXPECT_EQ("2 3 4 0 1", GetViewToModelAsString(table_));
657
658  table_->Select(-1);
659  EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
660
661  observer.GetChangedCountAndClear();
662  // Up with nothing selected selects the first row.
663  PressKey(ui::VKEY_UP);
664  EXPECT_EQ(1, observer.GetChangedCountAndClear());
665  EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
666
667  PressKey(ui::VKEY_DOWN);
668  EXPECT_EQ(1, observer.GetChangedCountAndClear());
669  EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
670
671  PressKey(ui::VKEY_DOWN);
672  EXPECT_EQ(1, observer.GetChangedCountAndClear());
673  EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
674
675  PressKey(ui::VKEY_DOWN);
676  EXPECT_EQ(1, observer.GetChangedCountAndClear());
677  EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
678
679  PressKey(ui::VKEY_DOWN);
680  EXPECT_EQ(1, observer.GetChangedCountAndClear());
681  EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
682
683  PressKey(ui::VKEY_DOWN);
684  EXPECT_EQ(0, observer.GetChangedCountAndClear());
685  EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
686
687  PressKey(ui::VKEY_UP);
688  EXPECT_EQ(1, observer.GetChangedCountAndClear());
689  EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
690
691  PressKey(ui::VKEY_UP);
692  EXPECT_EQ(1, observer.GetChangedCountAndClear());
693  EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
694
695  PressKey(ui::VKEY_UP);
696  EXPECT_EQ(1, observer.GetChangedCountAndClear());
697  EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
698
699  PressKey(ui::VKEY_UP);
700  EXPECT_EQ(1, observer.GetChangedCountAndClear());
701  EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
702
703  PressKey(ui::VKEY_UP);
704  EXPECT_EQ(0, observer.GetChangedCountAndClear());
705  EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
706
707  table_->SetObserver(NULL);
708}
709
710// Verifies home/end do the right thing.
711TEST_F(TableViewTest, HomeEnd) {
712  // Configure the grouper so that there are three groups:
713  // A 0
714  //   1
715  // B 5
716  // C 2
717  //   3
718  model_->AddRow(2, 5, 0);
719  TableGrouperImpl grouper;
720  std::vector<int> ranges;
721  ranges.push_back(2);
722  ranges.push_back(1);
723  ranges.push_back(2);
724  grouper.SetRanges(ranges);
725  table_->SetGrouper(&grouper);
726
727  TableViewObserverImpl observer;
728  table_->SetObserver(&observer);
729
730  // Initially no selection.
731  EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
732
733  PressKey(ui::VKEY_HOME);
734  EXPECT_EQ(1, observer.GetChangedCountAndClear());
735  EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
736
737  PressKey(ui::VKEY_END);
738  EXPECT_EQ(1, observer.GetChangedCountAndClear());
739  EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
740
741  table_->SetObserver(NULL);
742}
743
744// Verifies multiple selection gestures work (control-click, shift-click ...).
745TEST_F(TableViewTest, Multiselection) {
746  // Configure the grouper so that there are three groups:
747  // A 0
748  //   1
749  // B 5
750  // C 2
751  //   3
752  model_->AddRow(2, 5, 0);
753  TableGrouperImpl grouper;
754  std::vector<int> ranges;
755  ranges.push_back(2);
756  ranges.push_back(1);
757  ranges.push_back(2);
758  grouper.SetRanges(ranges);
759  table_->SetGrouper(&grouper);
760
761  // Initially no selection.
762  EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
763
764  TableViewObserverImpl observer;
765  table_->SetObserver(&observer);
766
767  // Click on the first row, should select it and the second row.
768  ClickOnRow(0, 0);
769  EXPECT_EQ(1, observer.GetChangedCountAndClear());
770  EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
771
772  // Click on the last row, should select it and the row before it.
773  ClickOnRow(4, 0);
774  EXPECT_EQ(1, observer.GetChangedCountAndClear());
775  EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
776
777  // Shift click on the third row, should extend selection to it.
778  ClickOnRow(2, ui::EF_SHIFT_DOWN);
779  EXPECT_EQ(1, observer.GetChangedCountAndClear());
780  EXPECT_EQ("active=2 anchor=4 selection=2 3 4", SelectionStateAsString());
781
782  // Control click on third row, should toggle it.
783  ClickOnRow(2, ui::EF_CONTROL_DOWN);
784  EXPECT_EQ(1, observer.GetChangedCountAndClear());
785  EXPECT_EQ("active=2 anchor=2 selection=3 4", SelectionStateAsString());
786
787  // Control-shift click on second row, should extend selection to it.
788  ClickOnRow(1, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
789  EXPECT_EQ(1, observer.GetChangedCountAndClear());
790  EXPECT_EQ("active=1 anchor=2 selection=0 1 2 3 4", SelectionStateAsString());
791
792  // Click on last row again.
793  ClickOnRow(4, 0);
794  EXPECT_EQ(1, observer.GetChangedCountAndClear());
795  EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
796
797  table_->SetObserver(NULL);
798}
799
800// Verifies multiple selection gestures work when sorted.
801TEST_F(TableViewTest, MultiselectionWithSort) {
802  // Configure the grouper so that there are three groups:
803  // A 0
804  //   1
805  // B 5
806  // C 2
807  //   3
808  model_->AddRow(2, 5, 0);
809  TableGrouperImpl grouper;
810  std::vector<int> ranges;
811  ranges.push_back(2);
812  ranges.push_back(1);
813  ranges.push_back(2);
814  grouper.SetRanges(ranges);
815  table_->SetGrouper(&grouper);
816
817  // Sort the table descending by column 1, view now looks like:
818  // B 5   model: 2
819  // C 2          3
820  //   3          4
821  // A 0          0
822  //   1          1
823  table_->ToggleSortOrder(0);
824  table_->ToggleSortOrder(0);
825
826  // Initially no selection.
827  EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
828
829  TableViewObserverImpl observer;
830  table_->SetObserver(&observer);
831
832  // Click on the third row, should select it and the second row.
833  ClickOnRow(2, 0);
834  EXPECT_EQ(1, observer.GetChangedCountAndClear());
835  EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
836
837  // Extend selection to first row.
838  ClickOnRow(0, ui::EF_SHIFT_DOWN);
839  EXPECT_EQ(1, observer.GetChangedCountAndClear());
840  EXPECT_EQ("active=2 anchor=4 selection=2 3 4", SelectionStateAsString());
841
842  table_->SetObserver(NULL);
843}
844
845}  // namespace views
846