12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/table/table_view.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <map>
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/auto_reset.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/i18n/rtl.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/events/event.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/canvas.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/image/image_skia.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/rect_conversions.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/skia_util.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/native_theme/native_theme.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/scroll_view.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/table/table_grouper.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/table/table_header.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/table/table_utils.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/table/table_view_observer.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/table/table_view_row_background_painter.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Padding around the text (on each side).
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const int kTextVerticalPadding = 3;
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const int kTextHorizontalPadding = 6;
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Size of images.
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const int kImageSize = 16;
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const int kGroupingIndicatorSize = 6;
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace views {
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns result, unless ascending is false in which case -result is returned.
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int SwapCompareResult(int result, bool ascending) {
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ascending ? result : -result;
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Populates |model_index_to_range_start| based on the |grouper|.
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void GetModelIndexToRangeStart(TableGrouper* grouper,
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               int row_count,
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               std::map<int, int>* model_index_to_range_start) {
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int model_index = 0; model_index < row_count;) {
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GroupRange range;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    grouper->GetGroupRange(model_index, &range);
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_GT(range.length, 0);
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int range_counter = 0; range_counter < range.length; range_counter++)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (*model_index_to_range_start)[range_counter + model_index] = model_index;
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model_index += range.length;
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns the color id for the background of selected text. |has_focus|
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// indicates if the table has focus.
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ui::NativeTheme::ColorId text_background_color_id(bool has_focus) {
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return has_focus ?
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ui::NativeTheme::kColorId_TableSelectionBackgroundFocused :
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ui::NativeTheme::kColorId_TableSelectionBackgroundUnfocused;
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns the color id for text. |has_focus| indicates if the table has focus.
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ui::NativeTheme::ColorId selected_text_color_id(bool has_focus) {
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return has_focus ? ui::NativeTheme::kColorId_TableSelectedText :
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ui::NativeTheme::kColorId_TableSelectedTextUnfocused;
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} // namespace
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Used as the comparator to sort the contents of the table.
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)struct TableView::SortHelper {
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  explicit SortHelper(TableView* table) : table(table) {}
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool operator()(int model_index1, int model_index2) {
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return table->CompareRows(model_index1, model_index2) < 0;
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TableView* table;
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Used as the comparator to sort the contents of the table when a TableGrouper
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// is present. When groups are present we sort the groups based on the first row
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// in the group and within the groups we keep the same order as the model.
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)struct TableView::GroupSortHelper {
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  explicit GroupSortHelper(TableView* table) : table(table) {}
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool operator()(int model_index1, int model_index2) {
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int range1 = model_index_to_range_start[model_index1];
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int range2 = model_index_to_range_start[model_index2];
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (range1 == range2) {
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // The two rows are in the same group, sort so that items in the same
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // group always appear in the same order.
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return model_index1 < model_index2;
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return table->CompareRows(range1, range2) < 0;
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TableView* table;
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::map<int, int> model_index_to_range_start;
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TableView::VisibleColumn::VisibleColumn() : x(0), width(0) {}
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TableView::VisibleColumn::~VisibleColumn() {}
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TableView::PaintRegion::PaintRegion()
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : min_row(0),
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      max_row(0),
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      min_column(0),
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      max_column(0) {
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TableView::PaintRegion::~PaintRegion() {}
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TableView::TableView(ui::TableModel* model,
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     const std::vector<ui::TableColumn>& columns,
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     TableTypes table_type,
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                     bool single_selection)
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : model_(NULL),
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      columns_(columns),
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      header_(NULL),
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      table_type_(table_type),
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      single_selection_(single_selection),
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      table_view_observer_(NULL),
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      row_height_(font_.GetHeight() + kTextVerticalPadding * 2),
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      last_parent_width_(0),
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      layout_width_(0),
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      grouper_(NULL),
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      in_set_visible_column_width_(false) {
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < columns.size(); ++i) {
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    VisibleColumn visible_column;
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    visible_column.column = columns[i];
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    visible_columns_.push_back(visible_column);
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  set_focusable(true);
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetModel(model);
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TableView::~TableView() {
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (model_)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model_->SetObserver(NULL);
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// TODO: this doesn't support arbitrarily changing the model, rename this to
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// ClearModel() or something.
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SetModel(ui::TableModel* model) {
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (model == model_)
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (model_)
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model_->SetObserver(NULL);
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  model_ = model;
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  selection_model_.Clear();
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (model_)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model_->SetObserver(this);
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)View* TableView::CreateParentIfNecessary() {
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScrollView* scroll_view = ScrollView::CreateScrollViewWithBorder();
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scroll_view->SetContents(this);
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CreateHeaderIfNecessary();
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (header_)
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scroll_view->SetHeader(header_);
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return scroll_view;
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SetRowBackgroundPainter(
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<TableViewRowBackgroundPainter> painter) {
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  row_background_painter_ = painter.Pass();
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SetGrouper(TableGrouper* grouper) {
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  grouper_ = grouper;
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SortItemsAndUpdateMapping();
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int TableView::RowCount() const {
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return model_ ? model_->RowCount() : 0;
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int TableView::SelectedRowCount() {
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return static_cast<int>(selection_model_.size());
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::Select(int model_row) {
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!model_)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SelectByViewIndex(model_row == -1 ? -1 : ModelToView(model_row));
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int TableView::FirstSelectedRow() {
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return SelectedRowCount() == 0 ? -1 : selection_model_.selected_indices()[0];
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SetColumnVisibility(int id, bool is_visible) {
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (is_visible == IsColumnVisible(id))
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (is_visible) {
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    VisibleColumn visible_column;
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    visible_column.column = FindColumnByID(id);
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    visible_columns_.push_back(visible_column);
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (size_t i = 0; i < visible_columns_.size(); ++i) {
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (visible_columns_[i].column.id == id) {
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        visible_columns_.erase(visible_columns_.begin() + i);
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UpdateVisibleColumnSizes();
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PreferredSizeChanged();
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SchedulePaint();
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (header_)
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    header_->SchedulePaint();
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::ToggleSortOrder(int visible_column_index) {
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(visible_column_index >= 0 &&
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         visible_column_index < static_cast<int>(visible_columns_.size()));
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!visible_columns_[visible_column_index].column.sortable)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int column_id = visible_columns_[visible_column_index].column.id;
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SortDescriptors sort(sort_descriptors_);
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!sort.empty() && sort[0].column_id == column_id) {
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sort[0].ascending = !sort[0].ascending;
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SortDescriptor descriptor(column_id, true);
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sort.insert(sort.begin(), descriptor);
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Only persist two sort descriptors.
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (sort.size() > 2)
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      sort.resize(2);
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetSortDescriptors(sort);
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TableView::IsColumnVisible(int id) const {
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < visible_columns_.size(); ++i) {
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (visible_columns_[i].column.id == id)
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::AddColumn(const ui::TableColumn& col) {
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(!HasColumn(col.id));
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  columns_.push_back(col);
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TableView::HasColumn(int id) const {
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < columns_.size(); ++i) {
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (columns_[i].id == id)
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SetVisibleColumnWidth(int index, int width) {
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(index >= 0 && index < static_cast<int>(visible_columns_.size()));
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (visible_columns_[index].width == width)
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::AutoReset<bool> reseter(&in_set_visible_column_width_, true);
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  visible_columns_[index].width = width;
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = index + 1; i < visible_columns_.size(); ++i) {
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    visible_columns_[i].x =
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        visible_columns_[i - 1].x + visible_columns_[i - 1].width;
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PreferredSizeChanged();
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SchedulePaint();
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int TableView::ModelToView(int model_index) const {
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!is_sorted())
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return model_index;
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_GE(model_index, 0) << " negative model_index " << model_index;
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_LT(model_index, RowCount()) << " out of bounds model_index " <<
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      model_index;
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return model_to_view_[model_index];
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int TableView::ViewToModel(int view_index) const {
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!is_sorted())
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return view_index;
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_GE(view_index, 0) << " negative view_index " << view_index;
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_LT(view_index, RowCount()) << " out of bounds view_index " <<
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      view_index;
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return view_to_model_[view_index];
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::Layout() {
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // parent()->parent() is the scrollview. When its width changes we force
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // recalculating column sizes.
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  View* scroll_view = parent() ? parent()->parent() : NULL;
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (scroll_view) {
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int scroll_view_width = scroll_view->GetContentsBounds().width();
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (scroll_view_width != last_parent_width_) {
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      last_parent_width_ = scroll_view_width;
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!in_set_visible_column_width_) {
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Layout to the parent (the Viewport), which differs from
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // |scroll_view_width| when scrollbars are present.
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        layout_width_ = parent()->width();
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        UpdateVisibleColumnSizes();
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We have to override Layout like this since we're contained in a ScrollView.
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Size pref = GetPreferredSize();
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int width = pref.width();
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int height = pref.height();
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (parent()) {
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    width = std::max(parent()->width(), width);
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    height = std::max(parent()->height(), height);
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetBounds(x(), y(), width, height);
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Size TableView::GetPreferredSize() {
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int width = 50;
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (header_ && !visible_columns_.empty())
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    width = visible_columns_.back().x + visible_columns_.back().width;
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gfx::Size(width, RowCount() * row_height_);
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TableView::OnKeyPressed(const ui::KeyEvent& event) {
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!HasFocus())
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (event.key_code()) {
332c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case ui::VKEY_A:
333c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // control-a selects all.
334c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (event.IsControlDown() && !single_selection_ && RowCount()) {
335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ui::ListSelectionModel selection_model;
336c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        selection_model.SetSelectedIndex(selection_model_.active());
337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        for (int i = 0; i < RowCount(); ++i)
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          selection_model.AddIndexToSelection(i);
339c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        SetSelectionModel(selection_model);
340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return true;
341c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
342c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
343c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case ui::VKEY_HOME:
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (RowCount())
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        SelectByViewIndex(0);
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case ui::VKEY_END:
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (RowCount())
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        SelectByViewIndex(RowCount() - 1);
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case ui::VKEY_UP:
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AdvanceSelection(ADVANCE_DECREMENT);
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case ui::VKEY_DOWN:
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AdvanceSelection(ADVANCE_INCREMENT);
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (table_view_observer_)
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    table_view_observer_->OnKeyDown(event.key_code());
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TableView::OnMousePressed(const ui::MouseEvent& event) {
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  RequestFocus();
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!event.IsOnlyLeftMouseButton())
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int row = event.y() / row_height_;
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (row < 0 || row >= RowCount())
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (event.GetClickCount() == 2) {
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SelectByViewIndex(row);
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (table_view_observer_)
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      table_view_observer_->OnDoubleClick();
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (event.GetClickCount() == 1) {
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ui::ListSelectionModel new_model;
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ConfigureSelectionModelForEvent(event, &new_model);
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SetSelectionModel(new_model);
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TableView::GetTooltipText(const gfx::Point& p,
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               string16* tooltip) const {
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetTooltipImpl(p, tooltip, NULL);
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TableView::GetTooltipTextOrigin(const gfx::Point& p,
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     gfx::Point* loc) const {
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetTooltipImpl(p, NULL, loc);
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::OnModelChanged() {
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  selection_model_.Clear();
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NumRowsChanged();
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::OnItemsChanged(int start, int length) {
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SortItemsAndUpdateMapping();
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::OnItemsAdded(int start, int length) {
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = 0; i < length; ++i)
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    selection_model_.IncrementFrom(start);
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NumRowsChanged();
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::OnItemsRemoved(int start, int length) {
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Determine the currently selected index in terms of the view. We inline the
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // implementation here since ViewToModel() has DCHECKs that fail since the
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // model has changed but |model_to_view_| has not been updated yet.
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int previously_selected_model_index = FirstSelectedRow();
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int previously_selected_view_index = previously_selected_model_index;
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (previously_selected_model_index != -1 && is_sorted())
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    previously_selected_view_index =
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        model_to_view_[previously_selected_model_index];
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = 0; i < length; ++i)
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    selection_model_.DecrementFrom(start);
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NumRowsChanged();
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If the selection was empty and is no longer empty select the same visual
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // index.
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (selection_model_.empty() && previously_selected_view_index != -1 &&
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      RowCount()) {
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    selection_model_.SetSelectedIndex(
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ViewToModel(std::min(RowCount() - 1, previously_selected_view_index)));
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (table_view_observer_)
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    table_view_observer_->OnSelectionChanged();
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Point TableView::GetKeyboardContextMenuLocation() {
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int first_selected = FirstSelectedRow();
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Rect vis_bounds(GetVisibleBounds());
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int y = vis_bounds.height() / 2;
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (first_selected != -1) {
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::Rect cell_bounds(GetRowBounds(first_selected));
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (cell_bounds.bottom() >= vis_bounds.y() &&
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        cell_bounds.bottom() < vis_bounds.bottom()) {
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      y = cell_bounds.bottom();
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Point screen_loc(0, y);
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (base::i18n::IsRTL())
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    screen_loc.set_x(width());
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ConvertPointToScreen(this, &screen_loc);
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return screen_loc;
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::OnPaint(gfx::Canvas* canvas) {
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Don't invoke View::OnPaint so that we can render our own focus border.
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas->DrawColor(GetNativeTheme()->GetSystemColor(
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        ui::NativeTheme::kColorId_TableBackground));
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!RowCount() || visible_columns_.empty())
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const PaintRegion region(GetPaintRegion(GetPaintBounds(canvas)));
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (region.min_column == -1)
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;  // No need to paint anything.
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const SkColor selected_bg_color = GetNativeTheme()->GetSystemColor(
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      text_background_color_id(HasFocus()));
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const SkColor fg_color = GetNativeTheme()->GetSystemColor(
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ui::NativeTheme::kColorId_TableText);
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const SkColor selected_fg_color = GetNativeTheme()->GetSystemColor(
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      selected_text_color_id(HasFocus()));
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = region.min_row; i < region.max_row; ++i) {
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int model_index = ViewToModel(i);
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const bool is_selected = selection_model_.IsSelected(model_index);
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (is_selected) {
481c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      canvas->FillRect(GetRowBounds(i), selected_bg_color);
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (row_background_painter_) {
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      row_background_painter_->PaintRowBackground(model_index,
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                  GetRowBounds(i),
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                  canvas);
4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
487c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (selection_model_.active() == i && HasFocus())
488c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      canvas->DrawFocusRect(GetRowBounds(i));
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int j = region.min_column; j < region.max_column; ++j) {
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const gfx::Rect cell_bounds(GetCellBounds(i, j));
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int text_x = kTextHorizontalPadding + cell_bounds.x();
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Provide space for the grouping indicator, but draw it separately.
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (j == 0 && grouper_)
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        text_x += kGroupingIndicatorSize + kTextHorizontalPadding;
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Always paint the icon in the first visible column.
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (j == 0 && table_type_ == ICON_AND_TEXT) {
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        gfx::ImageSkia image = model_->GetIcon(model_index);
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!image.isNull()) {
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          int image_x = GetMirroredXWithWidthInView(text_x, kImageSize);
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          canvas->DrawImageInt(
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              image, 0, 0, image.width(), image.height(),
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              image_x,
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              cell_bounds.y() + (cell_bounds.height() - kImageSize) / 2,
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              kImageSize, kImageSize, true);
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        text_x += kImageSize + kTextHorizontalPadding;
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (text_x < cell_bounds.right() - kTextHorizontalPadding) {
5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        canvas->DrawStringInt(
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            model_->GetText(model_index, visible_columns_[j].column.id), font_,
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            is_selected ? selected_fg_color : fg_color,
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            GetMirroredXWithWidthInView(text_x, cell_bounds.right() - text_x -
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        kTextHorizontalPadding),
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            cell_bounds.y() + kTextVerticalPadding,
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            cell_bounds.right() - text_x,
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            cell_bounds.height() - kTextVerticalPadding * 2,
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            TableColumnAlignmentToCanvasAlignment(
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                visible_columns_[j].column.alignment));
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!grouper_ || region.min_column > 0)
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const SkColor grouping_color = GetNativeTheme()->GetSystemColor(
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ui::NativeTheme::kColorId_TableGroupingIndicatorColor);
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkPaint grouping_paint;
5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  grouping_paint.setColor(grouping_color);
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  grouping_paint.setStyle(SkPaint::kFill_Style);
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  grouping_paint.setAntiAlias(true);
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int group_indicator_x = GetMirroredXInView(GetCellBounds(0, 0).x() +
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      kTextHorizontalPadding + kGroupingIndicatorSize / 2);
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = region.min_row; i < region.max_row; ) {
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int model_index = ViewToModel(i);
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GroupRange range;
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    grouper_->GetGroupRange(model_index, &range);
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_GT(range.length, 0);
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The order of rows in a group is consistent regardless of sort, so it's ok
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // to do this calculation.
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int start = i - (model_index - range.start);
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int last = start + range.length - 1;
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const gfx::Rect start_cell_bounds(GetCellBounds(start, 0));
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (start != last) {
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const gfx::Rect last_cell_bounds(GetCellBounds(last, 0));
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      canvas->FillRect(gfx::Rect(
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           group_indicator_x - kGroupingIndicatorSize / 2,
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           start_cell_bounds.CenterPoint().y(),
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           kGroupingIndicatorSize,
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           last_cell_bounds.y() - start_cell_bounds.y()),
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       grouping_color);
5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      canvas->DrawCircle(gfx::Point(group_indicator_x,
5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    last_cell_bounds.CenterPoint().y()),
5562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         kGroupingIndicatorSize / 2, grouping_paint);
5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    canvas->DrawCircle(gfx::Point(group_indicator_x,
5592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  start_cell_bounds.CenterPoint().y()),
5602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       kGroupingIndicatorSize / 2, grouping_paint);
5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    i = last + 1;
5622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::OnFocus() {
5662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SchedulePaintForSelection();
5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::OnBlur() {
5702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SchedulePaintForSelection();
5712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::NumRowsChanged() {
5742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SortItemsAndUpdateMapping();
5752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PreferredSizeChanged();
5762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SchedulePaint();
5772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SetSortDescriptors(const SortDescriptors& sort_descriptors) {
5802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sort_descriptors_ = sort_descriptors;
5812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SortItemsAndUpdateMapping();
5822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (header_)
5832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    header_->SchedulePaint();
5842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SortItemsAndUpdateMapping() {
5872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!is_sorted()) {
5882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    view_to_model_.clear();
5892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model_to_view_.clear();
5902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
5912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int row_count = RowCount();
5922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    view_to_model_.resize(row_count);
5932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model_to_view_.resize(row_count);
5942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int i = 0; i < row_count; ++i)
5952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      view_to_model_[i] = i;
5962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (grouper_) {
5972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GroupSortHelper sort_helper(this);
5982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetModelIndexToRangeStart(grouper_, RowCount(),
5992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                &sort_helper.model_index_to_range_start);
6002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::sort(view_to_model_.begin(), view_to_model_.end(), sort_helper);
6012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
6022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::sort(view_to_model_.begin(), view_to_model_.end(), SortHelper(this));
6032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int i = 0; i < row_count; ++i)
6052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      model_to_view_[view_to_model_[i]] = i;
6062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model_->ClearCollator();
6072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SchedulePaint();
6092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int TableView::CompareRows(int model_row1, int model_row2) {
6122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int sort_result = model_->CompareValues(
6132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      model_row1, model_row2, sort_descriptors_[0].column_id);
6142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (sort_result == 0 && sort_descriptors_.size() > 1) {
6152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Try the secondary sort.
6162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return SwapCompareResult(
6172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        model_->CompareValues(model_row1, model_row2,
6182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              sort_descriptors_[1].column_id),
6192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        sort_descriptors_[1].ascending);
6202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return SwapCompareResult(sort_result, sort_descriptors_[0].ascending);
6222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Rect TableView::GetRowBounds(int row) const {
6252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gfx::Rect(0, row * row_height_, width(), row_height_);
6262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Rect TableView::GetCellBounds(int row, int visible_column_index) const {
6292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!header_)
6302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return GetRowBounds(row);
6312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const VisibleColumn& vis_col(visible_columns_[visible_column_index]);
6322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gfx::Rect(vis_col.x, row * row_height_, vis_col.width, row_height_);
6332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::AdjustCellBoundsForText(int visible_column_index,
6362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        gfx::Rect* bounds) const {
6372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int text_x = kTextHorizontalPadding + bounds->x();
6382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (visible_column_index == 0) {
6392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (grouper_)
6402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      text_x += kGroupingIndicatorSize + kTextHorizontalPadding;
6412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (table_type_ == ICON_AND_TEXT)
6422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      text_x += kImageSize + kTextHorizontalPadding;
6432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bounds->set_x(text_x);
6452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bounds->set_width(
6462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::max(0, bounds->right() - kTextHorizontalPadding - text_x));
6472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::CreateHeaderIfNecessary() {
6502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Only create a header if there is more than one column or the title of the
6512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // only column is not empty.
6522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (header_ || (columns_.size() == 1 && columns_[0].title.empty()))
6532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
6542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  header_ = new TableHeader(this);
6562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::UpdateVisibleColumnSizes() {
6592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!header_)
6602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
6612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<ui::TableColumn> columns;
6632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < visible_columns_.size(); ++i)
6642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    columns.push_back(visible_columns_[i].column);
6652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int first_column_padding = 0;
6672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (table_type_ == ICON_AND_TEXT && header_)
6682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    first_column_padding += kImageSize + kTextHorizontalPadding;
6692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (grouper_)
6702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    first_column_padding += kGroupingIndicatorSize + kTextHorizontalPadding;
6712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<int> sizes = views::CalculateTableColumnSizes(
6732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      layout_width_, first_column_padding, header_->font(), font_,
6742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::max(kTextHorizontalPadding, TableHeader::kHorizontalPadding) * 2,
6752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      TableHeader::kSortIndicatorWidth, columns, model_);
6762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(visible_columns_.size(), sizes.size());
6772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int x = 0;
6782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < visible_columns_.size(); ++i) {
6792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    visible_columns_[i].x = x;
6802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    visible_columns_[i].width = sizes[i];
6812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    x += sizes[i];
6822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TableView::PaintRegion TableView::GetPaintRegion(
6862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const gfx::Rect& bounds) const {
6872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(!visible_columns_.empty());
6882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(RowCount());
6892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PaintRegion region;
6912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  region.min_row = std::min(RowCount() - 1,
6922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            std::max(0, bounds.y() / row_height_));
6932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  region.max_row = bounds.bottom() / row_height_;
6942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (bounds.bottom() % row_height_ != 0)
6952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    region.max_row++;
6962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  region.max_row = std::min(region.max_row, RowCount());
6972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!header_) {
6992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    region.max_column = 1;
7002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return region;
7012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int paint_x = GetMirroredXForRect(bounds);
7042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int paint_max_x = paint_x + bounds.width();
7052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  region.min_column = -1;
7062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  region.max_column = visible_columns_.size();
7072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < visible_columns_.size(); ++i) {
7082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int max_x = visible_columns_[i].x + visible_columns_[i].width;
7092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (region.min_column == -1 && max_x >= paint_x)
7102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      region.min_column = static_cast<int>(i);
7112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (region.min_column != -1 && visible_columns_[i].x >= paint_max_x) {
7122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      region.max_column = i;
7132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
7142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return region;
7172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Rect TableView::GetPaintBounds(gfx::Canvas* canvas) const {
7202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkRect sk_clip_rect;
7212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (canvas->sk_canvas()->getClipBounds(&sk_clip_rect))
7222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return gfx::ToEnclosingRect(gfx::SkRectToRectF(sk_clip_rect));
7232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetVisibleBounds();
7242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SchedulePaintForSelection() {
727c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (selection_model_.size() == 1) {
728c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const int first_model_row = FirstSelectedRow();
729c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    SchedulePaintInRect(GetRowBounds(ModelToView(first_model_row)));
730c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (first_model_row != selection_model_.active())
731c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      SchedulePaintInRect(GetRowBounds(ModelToView(selection_model_.active())));
732c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else if (selection_model_.size() > 1) {
7332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SchedulePaint();
734c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
7352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ui::TableColumn TableView::FindColumnByID(int id) const {
7382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < columns_.size(); ++i) {
7392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (columns_[i].id == id)
7402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return columns_[i];
7412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NOTREACHED();
7432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ui::TableColumn();
7442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SelectByViewIndex(int view_index) {
7472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ui::ListSelectionModel new_selection;
7482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (view_index != -1) {
7492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SelectRowsInRangeFrom(view_index, true, &new_selection);
750c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    new_selection.set_anchor(ViewToModel(view_index));
751c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    new_selection.set_active(ViewToModel(view_index));
7522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetSelectionModel(new_selection);
7552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SetSelectionModel(const ui::ListSelectionModel& new_selection) {
7582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (new_selection.Equals(selection_model_))
7592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
7602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SchedulePaintForSelection();
7622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  selection_model_.Copy(new_selection);
7632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SchedulePaintForSelection();
7642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Scroll the group for the active item to visible.
7662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (selection_model_.active() != -1) {
7672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::Rect vis_rect(GetVisibleBounds());
7682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GroupRange range(GetGroupRange(selection_model_.active()));
7692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int start_y = GetRowBounds(ModelToView(range.start)).y();
7702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int end_y =
7712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        GetRowBounds(ModelToView(range.start + range.length - 1)).bottom();
7722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    vis_rect.set_y(start_y);
7732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    vis_rect.set_height(end_y - start_y);
7742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ScrollRectToVisible(vis_rect);
7752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (table_view_observer_)
7782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    table_view_observer_->OnSelectionChanged();
7792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::AdvanceSelection(AdvanceDirection direction) {
7822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (selection_model_.active() == -1) {
7832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SelectByViewIndex(0);
7842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
7852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int view_index = ModelToView(selection_model_.active());
787c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (direction == ADVANCE_DECREMENT)
788c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    view_index = std::max(0, view_index - 1);
789c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  else
790c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    view_index = std::min(RowCount() - 1, view_index + 1);
7912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SelectByViewIndex(view_index);
7922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::ConfigureSelectionModelForEvent(
7952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ui::MouseEvent& event,
7962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ui::ListSelectionModel* model) const {
797c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const int view_index = event.y() / row_height_;
7982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(view_index >= 0 && view_index < RowCount());
7992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (selection_model_.anchor() == -1 ||
8012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      single_selection_ ||
8022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (!event.IsControlDown() && !event.IsShiftDown())) {
8032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SelectRowsInRangeFrom(view_index, true, model);
804c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    model->set_anchor(ViewToModel(view_index));
805c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    model->set_active(ViewToModel(view_index));
8062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
8072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if ((event.IsControlDown() && event.IsShiftDown()) || event.IsShiftDown()) {
8092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // control-shift: copy existing model and make sure rows between anchor and
8102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // |view_index| are selected.
8112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // shift: reset selection so that only rows between anchor and |view_index|
8122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // are selected.
8132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (event.IsControlDown() && event.IsShiftDown())
8142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      model->Copy(selection_model_);
8152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else
8162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      model->set_anchor(selection_model_.anchor());
8172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int i = std::min(view_index, ModelToView(model->anchor())),
8182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             end = std::max(view_index, ModelToView(model->anchor()));
8192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         i <= end; ++i) {
8202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SelectRowsInRangeFrom(i, true, model);
8212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model->set_active(ViewToModel(view_index));
8232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
8242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(event.IsControlDown());
8252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Toggle the selection state of |view_index| and set the anchor/active to
8262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // it and don't change the state of any other rows.
8272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model->Copy(selection_model_);
8282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model->set_anchor(ViewToModel(view_index));
8292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    model->set_active(ViewToModel(view_index));
8302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SelectRowsInRangeFrom(view_index,
8312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          !model->IsSelected(ViewToModel(view_index)),
8322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          model);
8332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TableView::SelectRowsInRangeFrom(int view_index,
8372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      bool select,
8382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      ui::ListSelectionModel* model) const {
8392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const GroupRange range(GetGroupRange(ViewToModel(view_index)));
8402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = 0; i < range.length; ++i) {
8412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (select)
8422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      model->AddIndexToSelection(range.start + i);
8432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else
8442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      model->RemoveIndexFromSelection(range.start + i);
8452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)GroupRange TableView::GetGroupRange(int model_index) const {
8492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GroupRange range;
8502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (grouper_) {
8512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    grouper_->GetGroupRange(model_index, &range);
8522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
8532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    range.start = model_index;
8542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    range.length = 1;
8552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return range;
8572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TableView::GetTooltipImpl(const gfx::Point& location,
8602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               string16* tooltip,
8612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               gfx::Point* tooltip_origin) const {
8622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int row = location.y() / row_height_;
8632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (row < 0 || row >= RowCount() || visible_columns_.empty())
8642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
8652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int x = GetMirroredXInView(location.x());
8672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int column = GetClosestVisibleColumnIndex(this, x);
8682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (x < visible_columns_[column].x ||
8692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      x > (visible_columns_[column].x + visible_columns_[column].width))
8702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
8712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const string16 text(model_->GetText(ViewToModel(row),
8732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      visible_columns_[column].column.id));
8742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (text.empty())
8752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
8762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Rect cell_bounds(GetCellBounds(row, column));
8782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AdjustCellBoundsForText(column, &cell_bounds);
8792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int right = std::min(GetVisibleBounds().right(), cell_bounds.right());
8802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (right > cell_bounds.x() &&
8812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      font_.GetStringWidth(text) <= (right - cell_bounds.x()))
8822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
8832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (tooltip)
8852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *tooltip = text;
8862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (tooltip_origin) {
8872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tooltip_origin->SetPoint(cell_bounds.x(),
8882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             cell_bounds.y() + kTextVerticalPadding);
8892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
8912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace views
894