Home | History | Annotate | Download | only in table
      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 <map>
      8 
      9 #include "base/auto_reset.h"
     10 #include "base/i18n/rtl.h"
     11 #include "ui/events/event.h"
     12 #include "ui/gfx/canvas.h"
     13 #include "ui/gfx/image/image_skia.h"
     14 #include "ui/gfx/rect_conversions.h"
     15 #include "ui/gfx/skia_util.h"
     16 #include "ui/gfx/text_utils.h"
     17 #include "ui/native_theme/native_theme.h"
     18 #include "ui/views/controls/scroll_view.h"
     19 #include "ui/views/controls/table/table_grouper.h"
     20 #include "ui/views/controls/table/table_header.h"
     21 #include "ui/views/controls/table/table_utils.h"
     22 #include "ui/views/controls/table/table_view_observer.h"
     23 #include "ui/views/controls/table/table_view_row_background_painter.h"
     24 
     25 // Padding around the text (on each side).
     26 static const int kTextVerticalPadding = 3;
     27 static const int kTextHorizontalPadding = 6;
     28 
     29 // Size of images.
     30 static const int kImageSize = 16;
     31 
     32 static const int kGroupingIndicatorSize = 6;
     33 
     34 namespace views {
     35 
     36 namespace {
     37 
     38 // Returns result, unless ascending is false in which case -result is returned.
     39 int SwapCompareResult(int result, bool ascending) {
     40   return ascending ? result : -result;
     41 }
     42 
     43 // Populates |model_index_to_range_start| based on the |grouper|.
     44 void GetModelIndexToRangeStart(TableGrouper* grouper,
     45                                int row_count,
     46                                std::map<int, int>* model_index_to_range_start) {
     47   for (int model_index = 0; model_index < row_count;) {
     48     GroupRange range;
     49     grouper->GetGroupRange(model_index, &range);
     50     DCHECK_GT(range.length, 0);
     51     for (int range_counter = 0; range_counter < range.length; range_counter++)
     52       (*model_index_to_range_start)[range_counter + model_index] = model_index;
     53     model_index += range.length;
     54   }
     55 }
     56 
     57 // Returns the color id for the background of selected text. |has_focus|
     58 // indicates if the table has focus.
     59 ui::NativeTheme::ColorId text_background_color_id(bool has_focus) {
     60   return has_focus ?
     61       ui::NativeTheme::kColorId_TableSelectionBackgroundFocused :
     62       ui::NativeTheme::kColorId_TableSelectionBackgroundUnfocused;
     63 }
     64 
     65 // Returns the color id for text. |has_focus| indicates if the table has focus.
     66 ui::NativeTheme::ColorId selected_text_color_id(bool has_focus) {
     67   return has_focus ? ui::NativeTheme::kColorId_TableSelectedText :
     68       ui::NativeTheme::kColorId_TableSelectedTextUnfocused;
     69 }
     70 
     71 } // namespace
     72 
     73 // Used as the comparator to sort the contents of the table.
     74 struct TableView::SortHelper {
     75   explicit SortHelper(TableView* table) : table(table) {}
     76 
     77   bool operator()(int model_index1, int model_index2) {
     78     return table->CompareRows(model_index1, model_index2) < 0;
     79   }
     80 
     81   TableView* table;
     82 };
     83 
     84 // Used as the comparator to sort the contents of the table when a TableGrouper
     85 // is present. When groups are present we sort the groups based on the first row
     86 // in the group and within the groups we keep the same order as the model.
     87 struct TableView::GroupSortHelper {
     88   explicit GroupSortHelper(TableView* table) : table(table) {}
     89 
     90   bool operator()(int model_index1, int model_index2) {
     91     const int range1 = model_index_to_range_start[model_index1];
     92     const int range2 = model_index_to_range_start[model_index2];
     93     if (range1 == range2) {
     94       // The two rows are in the same group, sort so that items in the same
     95       // group always appear in the same order.
     96       return model_index1 < model_index2;
     97     }
     98     return table->CompareRows(range1, range2) < 0;
     99   }
    100 
    101   TableView* table;
    102   std::map<int, int> model_index_to_range_start;
    103 };
    104 
    105 TableView::VisibleColumn::VisibleColumn() : x(0), width(0) {}
    106 
    107 TableView::VisibleColumn::~VisibleColumn() {}
    108 
    109 TableView::PaintRegion::PaintRegion()
    110     : min_row(0),
    111       max_row(0),
    112       min_column(0),
    113       max_column(0) {
    114 }
    115 
    116 TableView::PaintRegion::~PaintRegion() {}
    117 
    118 TableView::TableView(ui::TableModel* model,
    119                      const std::vector<ui::TableColumn>& columns,
    120                      TableTypes table_type,
    121                      bool single_selection)
    122     : model_(NULL),
    123       columns_(columns),
    124       header_(NULL),
    125       table_type_(table_type),
    126       single_selection_(single_selection),
    127       table_view_observer_(NULL),
    128       row_height_(font_list_.GetHeight() + kTextVerticalPadding * 2),
    129       last_parent_width_(0),
    130       layout_width_(0),
    131       grouper_(NULL),
    132       in_set_visible_column_width_(false) {
    133   for (size_t i = 0; i < columns.size(); ++i) {
    134     VisibleColumn visible_column;
    135     visible_column.column = columns[i];
    136     visible_columns_.push_back(visible_column);
    137   }
    138   SetFocusable(true);
    139   SetModel(model);
    140 }
    141 
    142 TableView::~TableView() {
    143   if (model_)
    144     model_->SetObserver(NULL);
    145 }
    146 
    147 // TODO: this doesn't support arbitrarily changing the model, rename this to
    148 // ClearModel() or something.
    149 void TableView::SetModel(ui::TableModel* model) {
    150   if (model == model_)
    151     return;
    152 
    153   if (model_)
    154     model_->SetObserver(NULL);
    155   model_ = model;
    156   selection_model_.Clear();
    157   if (model_)
    158     model_->SetObserver(this);
    159 }
    160 
    161 View* TableView::CreateParentIfNecessary() {
    162   ScrollView* scroll_view = ScrollView::CreateScrollViewWithBorder();
    163   scroll_view->SetContents(this);
    164   CreateHeaderIfNecessary();
    165   if (header_)
    166     scroll_view->SetHeader(header_);
    167   return scroll_view;
    168 }
    169 
    170 void TableView::SetRowBackgroundPainter(
    171     scoped_ptr<TableViewRowBackgroundPainter> painter) {
    172   row_background_painter_ = painter.Pass();
    173 }
    174 
    175 void TableView::SetGrouper(TableGrouper* grouper) {
    176   grouper_ = grouper;
    177   SortItemsAndUpdateMapping();
    178 }
    179 
    180 int TableView::RowCount() const {
    181   return model_ ? model_->RowCount() : 0;
    182 }
    183 
    184 int TableView::SelectedRowCount() {
    185   return static_cast<int>(selection_model_.size());
    186 }
    187 
    188 void TableView::Select(int model_row) {
    189   if (!model_)
    190     return;
    191 
    192   SelectByViewIndex(model_row == -1 ? -1 : ModelToView(model_row));
    193 }
    194 
    195 int TableView::FirstSelectedRow() {
    196   return SelectedRowCount() == 0 ? -1 : selection_model_.selected_indices()[0];
    197 }
    198 
    199 void TableView::SetColumnVisibility(int id, bool is_visible) {
    200   if (is_visible == IsColumnVisible(id))
    201     return;
    202 
    203   if (is_visible) {
    204     VisibleColumn visible_column;
    205     visible_column.column = FindColumnByID(id);
    206     visible_columns_.push_back(visible_column);
    207   } else {
    208     for (size_t i = 0; i < visible_columns_.size(); ++i) {
    209       if (visible_columns_[i].column.id == id) {
    210         visible_columns_.erase(visible_columns_.begin() + i);
    211         break;
    212       }
    213     }
    214   }
    215   UpdateVisibleColumnSizes();
    216   PreferredSizeChanged();
    217   SchedulePaint();
    218   if (header_)
    219     header_->SchedulePaint();
    220 }
    221 
    222 void TableView::ToggleSortOrder(int visible_column_index) {
    223   DCHECK(visible_column_index >= 0 &&
    224          visible_column_index < static_cast<int>(visible_columns_.size()));
    225   if (!visible_columns_[visible_column_index].column.sortable)
    226     return;
    227   const int column_id = visible_columns_[visible_column_index].column.id;
    228   SortDescriptors sort(sort_descriptors_);
    229   if (!sort.empty() && sort[0].column_id == column_id) {
    230     sort[0].ascending = !sort[0].ascending;
    231   } else {
    232     SortDescriptor descriptor(column_id, true);
    233     sort.insert(sort.begin(), descriptor);
    234     // Only persist two sort descriptors.
    235     if (sort.size() > 2)
    236       sort.resize(2);
    237   }
    238   SetSortDescriptors(sort);
    239 }
    240 
    241 bool TableView::IsColumnVisible(int id) const {
    242   for (size_t i = 0; i < visible_columns_.size(); ++i) {
    243     if (visible_columns_[i].column.id == id)
    244       return true;
    245   }
    246   return false;
    247 }
    248 
    249 void TableView::AddColumn(const ui::TableColumn& col) {
    250   DCHECK(!HasColumn(col.id));
    251   columns_.push_back(col);
    252 }
    253 
    254 bool TableView::HasColumn(int id) const {
    255   for (size_t i = 0; i < columns_.size(); ++i) {
    256     if (columns_[i].id == id)
    257       return true;
    258   }
    259   return false;
    260 }
    261 
    262 void TableView::SetVisibleColumnWidth(int index, int width) {
    263   DCHECK(index >= 0 && index < static_cast<int>(visible_columns_.size()));
    264   if (visible_columns_[index].width == width)
    265     return;
    266   base::AutoReset<bool> reseter(&in_set_visible_column_width_, true);
    267   visible_columns_[index].width = width;
    268   for (size_t i = index + 1; i < visible_columns_.size(); ++i) {
    269     visible_columns_[i].x =
    270         visible_columns_[i - 1].x + visible_columns_[i - 1].width;
    271   }
    272   PreferredSizeChanged();
    273   SchedulePaint();
    274 }
    275 
    276 int TableView::ModelToView(int model_index) const {
    277   if (!is_sorted())
    278     return model_index;
    279   DCHECK_GE(model_index, 0) << " negative model_index " << model_index;
    280   DCHECK_LT(model_index, RowCount()) << " out of bounds model_index " <<
    281       model_index;
    282   return model_to_view_[model_index];
    283 }
    284 
    285 int TableView::ViewToModel(int view_index) const {
    286   if (!is_sorted())
    287     return view_index;
    288   DCHECK_GE(view_index, 0) << " negative view_index " << view_index;
    289   DCHECK_LT(view_index, RowCount()) << " out of bounds view_index " <<
    290       view_index;
    291   return view_to_model_[view_index];
    292 }
    293 
    294 void TableView::Layout() {
    295   // parent()->parent() is the scrollview. When its width changes we force
    296   // recalculating column sizes.
    297   View* scroll_view = parent() ? parent()->parent() : NULL;
    298   if (scroll_view) {
    299     const int scroll_view_width = scroll_view->GetContentsBounds().width();
    300     if (scroll_view_width != last_parent_width_) {
    301       last_parent_width_ = scroll_view_width;
    302       if (!in_set_visible_column_width_) {
    303         // Layout to the parent (the Viewport), which differs from
    304         // |scroll_view_width| when scrollbars are present.
    305         layout_width_ = parent()->width();
    306         UpdateVisibleColumnSizes();
    307       }
    308     }
    309   }
    310   // We have to override Layout like this since we're contained in a ScrollView.
    311   gfx::Size pref = GetPreferredSize();
    312   int width = pref.width();
    313   int height = pref.height();
    314   if (parent()) {
    315     width = std::max(parent()->width(), width);
    316     height = std::max(parent()->height(), height);
    317   }
    318   SetBounds(x(), y(), width, height);
    319 }
    320 
    321 gfx::Size TableView::GetPreferredSize() const {
    322   int width = 50;
    323   if (header_ && !visible_columns_.empty())
    324     width = visible_columns_.back().x + visible_columns_.back().width;
    325   return gfx::Size(width, RowCount() * row_height_);
    326 }
    327 
    328 bool TableView::OnKeyPressed(const ui::KeyEvent& event) {
    329   if (!HasFocus())
    330     return false;
    331 
    332   switch (event.key_code()) {
    333     case ui::VKEY_A:
    334       // control-a selects all.
    335       if (event.IsControlDown() && !single_selection_ && RowCount()) {
    336         ui::ListSelectionModel selection_model;
    337         selection_model.SetSelectedIndex(selection_model_.active());
    338         for (int i = 0; i < RowCount(); ++i)
    339           selection_model.AddIndexToSelection(i);
    340         SetSelectionModel(selection_model);
    341         return true;
    342       }
    343       break;
    344 
    345     case ui::VKEY_HOME:
    346       if (RowCount())
    347         SelectByViewIndex(0);
    348       return true;
    349 
    350     case ui::VKEY_END:
    351       if (RowCount())
    352         SelectByViewIndex(RowCount() - 1);
    353       return true;
    354 
    355     case ui::VKEY_UP:
    356       AdvanceSelection(ADVANCE_DECREMENT);
    357       return true;
    358 
    359     case ui::VKEY_DOWN:
    360       AdvanceSelection(ADVANCE_INCREMENT);
    361       return true;
    362 
    363     default:
    364       break;
    365   }
    366   if (table_view_observer_)
    367     table_view_observer_->OnKeyDown(event.key_code());
    368   return false;
    369 }
    370 
    371 bool TableView::OnMousePressed(const ui::MouseEvent& event) {
    372   RequestFocus();
    373   if (!event.IsOnlyLeftMouseButton())
    374     return true;
    375 
    376   const int row = event.y() / row_height_;
    377   if (row < 0 || row >= RowCount())
    378     return true;
    379 
    380   if (event.GetClickCount() == 2) {
    381     SelectByViewIndex(row);
    382     if (table_view_observer_)
    383       table_view_observer_->OnDoubleClick();
    384   } else if (event.GetClickCount() == 1) {
    385     ui::ListSelectionModel new_model;
    386     ConfigureSelectionModelForEvent(event, &new_model);
    387     SetSelectionModel(new_model);
    388   }
    389 
    390   return true;
    391 }
    392 
    393 void TableView::OnGestureEvent(ui::GestureEvent* event) {
    394   if (event->type() != ui::ET_GESTURE_TAP)
    395     return;
    396 
    397   const int row = event->y() / row_height_;
    398   if (row < 0 || row >= RowCount())
    399     return;
    400 
    401   event->StopPropagation();
    402   ui::ListSelectionModel new_model;
    403   ConfigureSelectionModelForEvent(*event, &new_model);
    404   SetSelectionModel(new_model);
    405 }
    406 
    407 bool TableView::GetTooltipText(const gfx::Point& p,
    408                                base::string16* tooltip) const {
    409   return GetTooltipImpl(p, tooltip, NULL);
    410 }
    411 
    412 bool TableView::GetTooltipTextOrigin(const gfx::Point& p,
    413                                      gfx::Point* loc) const {
    414   return GetTooltipImpl(p, NULL, loc);
    415 }
    416 
    417 void TableView::OnModelChanged() {
    418   selection_model_.Clear();
    419   NumRowsChanged();
    420 }
    421 
    422 void TableView::OnItemsChanged(int start, int length) {
    423   SortItemsAndUpdateMapping();
    424 }
    425 
    426 void TableView::OnItemsAdded(int start, int length) {
    427   for (int i = 0; i < length; ++i)
    428     selection_model_.IncrementFrom(start);
    429   NumRowsChanged();
    430 }
    431 
    432 void TableView::OnItemsRemoved(int start, int length) {
    433   // Determine the currently selected index in terms of the view. We inline the
    434   // implementation here since ViewToModel() has DCHECKs that fail since the
    435   // model has changed but |model_to_view_| has not been updated yet.
    436   const int previously_selected_model_index = FirstSelectedRow();
    437   int previously_selected_view_index = previously_selected_model_index;
    438   if (previously_selected_model_index != -1 && is_sorted())
    439     previously_selected_view_index =
    440         model_to_view_[previously_selected_model_index];
    441   for (int i = 0; i < length; ++i)
    442     selection_model_.DecrementFrom(start);
    443   NumRowsChanged();
    444   // If the selection was empty and is no longer empty select the same visual
    445   // index.
    446   if (selection_model_.empty() && previously_selected_view_index != -1 &&
    447       RowCount()) {
    448     selection_model_.SetSelectedIndex(
    449         ViewToModel(std::min(RowCount() - 1, previously_selected_view_index)));
    450   }
    451   if (table_view_observer_)
    452     table_view_observer_->OnSelectionChanged();
    453 }
    454 
    455 gfx::Point TableView::GetKeyboardContextMenuLocation() {
    456   int first_selected = FirstSelectedRow();
    457   gfx::Rect vis_bounds(GetVisibleBounds());
    458   int y = vis_bounds.height() / 2;
    459   if (first_selected != -1) {
    460     gfx::Rect cell_bounds(GetRowBounds(first_selected));
    461     if (cell_bounds.bottom() >= vis_bounds.y() &&
    462         cell_bounds.bottom() < vis_bounds.bottom()) {
    463       y = cell_bounds.bottom();
    464     }
    465   }
    466   gfx::Point screen_loc(0, y);
    467   if (base::i18n::IsRTL())
    468     screen_loc.set_x(width());
    469   ConvertPointToScreen(this, &screen_loc);
    470   return screen_loc;
    471 }
    472 
    473 void TableView::OnPaint(gfx::Canvas* canvas) {
    474   // Don't invoke View::OnPaint so that we can render our own focus border.
    475 
    476   canvas->DrawColor(GetNativeTheme()->GetSystemColor(
    477                         ui::NativeTheme::kColorId_TableBackground));
    478 
    479   if (!RowCount() || visible_columns_.empty())
    480     return;
    481 
    482   const PaintRegion region(GetPaintRegion(GetPaintBounds(canvas)));
    483   if (region.min_column == -1)
    484     return;  // No need to paint anything.
    485 
    486   const SkColor selected_bg_color = GetNativeTheme()->GetSystemColor(
    487       text_background_color_id(HasFocus()));
    488   const SkColor fg_color = GetNativeTheme()->GetSystemColor(
    489       ui::NativeTheme::kColorId_TableText);
    490   const SkColor selected_fg_color = GetNativeTheme()->GetSystemColor(
    491       selected_text_color_id(HasFocus()));
    492   for (int i = region.min_row; i < region.max_row; ++i) {
    493     const int model_index = ViewToModel(i);
    494     const bool is_selected = selection_model_.IsSelected(model_index);
    495     if (is_selected) {
    496       canvas->FillRect(GetRowBounds(i), selected_bg_color);
    497     } else if (row_background_painter_) {
    498       row_background_painter_->PaintRowBackground(model_index,
    499                                                   GetRowBounds(i),
    500                                                   canvas);
    501     }
    502     if (selection_model_.active() == i && HasFocus())
    503       canvas->DrawFocusRect(GetRowBounds(i));
    504     for (int j = region.min_column; j < region.max_column; ++j) {
    505       const gfx::Rect cell_bounds(GetCellBounds(i, j));
    506       int text_x = kTextHorizontalPadding + cell_bounds.x();
    507 
    508       // Provide space for the grouping indicator, but draw it separately.
    509       if (j == 0 && grouper_)
    510         text_x += kGroupingIndicatorSize + kTextHorizontalPadding;
    511 
    512       // Always paint the icon in the first visible column.
    513       if (j == 0 && table_type_ == ICON_AND_TEXT) {
    514         gfx::ImageSkia image = model_->GetIcon(model_index);
    515         if (!image.isNull()) {
    516           int image_x = GetMirroredXWithWidthInView(text_x, kImageSize);
    517           canvas->DrawImageInt(
    518               image, 0, 0, image.width(), image.height(),
    519               image_x,
    520               cell_bounds.y() + (cell_bounds.height() - kImageSize) / 2,
    521               kImageSize, kImageSize, true);
    522         }
    523         text_x += kImageSize + kTextHorizontalPadding;
    524       }
    525       if (text_x < cell_bounds.right() - kTextHorizontalPadding) {
    526         canvas->DrawStringRectWithFlags(
    527             model_->GetText(model_index, visible_columns_[j].column.id),
    528             font_list_, is_selected ? selected_fg_color : fg_color,
    529             gfx::Rect(GetMirroredXWithWidthInView(
    530                 text_x, cell_bounds.right() - text_x - kTextHorizontalPadding),
    531                       cell_bounds.y() + kTextVerticalPadding,
    532                       cell_bounds.right() - text_x,
    533                       cell_bounds.height() - kTextVerticalPadding * 2),
    534             TableColumnAlignmentToCanvasAlignment(
    535                 visible_columns_[j].column.alignment));
    536       }
    537     }
    538   }
    539 
    540   if (!grouper_ || region.min_column > 0)
    541     return;
    542 
    543   const SkColor grouping_color = GetNativeTheme()->GetSystemColor(
    544       ui::NativeTheme::kColorId_TableGroupingIndicatorColor);
    545   SkPaint grouping_paint;
    546   grouping_paint.setColor(grouping_color);
    547   grouping_paint.setStyle(SkPaint::kFill_Style);
    548   grouping_paint.setAntiAlias(true);
    549   const int group_indicator_x = GetMirroredXInView(GetCellBounds(0, 0).x() +
    550       kTextHorizontalPadding + kGroupingIndicatorSize / 2);
    551   for (int i = region.min_row; i < region.max_row; ) {
    552     const int model_index = ViewToModel(i);
    553     GroupRange range;
    554     grouper_->GetGroupRange(model_index, &range);
    555     DCHECK_GT(range.length, 0);
    556     // The order of rows in a group is consistent regardless of sort, so it's ok
    557     // to do this calculation.
    558     const int start = i - (model_index - range.start);
    559     const int last = start + range.length - 1;
    560     const gfx::Rect start_cell_bounds(GetCellBounds(start, 0));
    561     if (start != last) {
    562       const gfx::Rect last_cell_bounds(GetCellBounds(last, 0));
    563       canvas->FillRect(gfx::Rect(
    564                            group_indicator_x - kGroupingIndicatorSize / 2,
    565                            start_cell_bounds.CenterPoint().y(),
    566                            kGroupingIndicatorSize,
    567                            last_cell_bounds.y() - start_cell_bounds.y()),
    568                        grouping_color);
    569       canvas->DrawCircle(gfx::Point(group_indicator_x,
    570                                     last_cell_bounds.CenterPoint().y()),
    571                          kGroupingIndicatorSize / 2, grouping_paint);
    572     }
    573     canvas->DrawCircle(gfx::Point(group_indicator_x,
    574                                   start_cell_bounds.CenterPoint().y()),
    575                        kGroupingIndicatorSize / 2, grouping_paint);
    576     i = last + 1;
    577   }
    578 }
    579 
    580 void TableView::OnFocus() {
    581   SchedulePaintForSelection();
    582 }
    583 
    584 void TableView::OnBlur() {
    585   SchedulePaintForSelection();
    586 }
    587 
    588 void TableView::NumRowsChanged() {
    589   SortItemsAndUpdateMapping();
    590   PreferredSizeChanged();
    591   SchedulePaint();
    592 }
    593 
    594 void TableView::SetSortDescriptors(const SortDescriptors& sort_descriptors) {
    595   sort_descriptors_ = sort_descriptors;
    596   SortItemsAndUpdateMapping();
    597   if (header_)
    598     header_->SchedulePaint();
    599 }
    600 
    601 void TableView::SortItemsAndUpdateMapping() {
    602   if (!is_sorted()) {
    603     view_to_model_.clear();
    604     model_to_view_.clear();
    605   } else {
    606     const int row_count = RowCount();
    607     view_to_model_.resize(row_count);
    608     model_to_view_.resize(row_count);
    609     for (int i = 0; i < row_count; ++i)
    610       view_to_model_[i] = i;
    611     if (grouper_) {
    612       GroupSortHelper sort_helper(this);
    613       GetModelIndexToRangeStart(grouper_, RowCount(),
    614                                 &sort_helper.model_index_to_range_start);
    615       std::sort(view_to_model_.begin(), view_to_model_.end(), sort_helper);
    616     } else {
    617       std::sort(view_to_model_.begin(), view_to_model_.end(), SortHelper(this));
    618     }
    619     for (int i = 0; i < row_count; ++i)
    620       model_to_view_[view_to_model_[i]] = i;
    621     model_->ClearCollator();
    622   }
    623   SchedulePaint();
    624 }
    625 
    626 int TableView::CompareRows(int model_row1, int model_row2) {
    627   const int sort_result = model_->CompareValues(
    628       model_row1, model_row2, sort_descriptors_[0].column_id);
    629   if (sort_result == 0 && sort_descriptors_.size() > 1) {
    630     // Try the secondary sort.
    631     return SwapCompareResult(
    632         model_->CompareValues(model_row1, model_row2,
    633                               sort_descriptors_[1].column_id),
    634         sort_descriptors_[1].ascending);
    635   }
    636   return SwapCompareResult(sort_result, sort_descriptors_[0].ascending);
    637 }
    638 
    639 gfx::Rect TableView::GetRowBounds(int row) const {
    640   return gfx::Rect(0, row * row_height_, width(), row_height_);
    641 }
    642 
    643 gfx::Rect TableView::GetCellBounds(int row, int visible_column_index) const {
    644   if (!header_)
    645     return GetRowBounds(row);
    646   const VisibleColumn& vis_col(visible_columns_[visible_column_index]);
    647   return gfx::Rect(vis_col.x, row * row_height_, vis_col.width, row_height_);
    648 }
    649 
    650 void TableView::AdjustCellBoundsForText(int visible_column_index,
    651                                         gfx::Rect* bounds) const {
    652   int text_x = kTextHorizontalPadding + bounds->x();
    653   if (visible_column_index == 0) {
    654     if (grouper_)
    655       text_x += kGroupingIndicatorSize + kTextHorizontalPadding;
    656     if (table_type_ == ICON_AND_TEXT)
    657       text_x += kImageSize + kTextHorizontalPadding;
    658   }
    659   bounds->set_x(text_x);
    660   bounds->set_width(
    661       std::max(0, bounds->right() - kTextHorizontalPadding - text_x));
    662 }
    663 
    664 void TableView::CreateHeaderIfNecessary() {
    665   // Only create a header if there is more than one column or the title of the
    666   // only column is not empty.
    667   if (header_ || (columns_.size() == 1 && columns_[0].title.empty()))
    668     return;
    669 
    670   header_ = new TableHeader(this);
    671 }
    672 
    673 void TableView::UpdateVisibleColumnSizes() {
    674   if (!header_)
    675     return;
    676 
    677   std::vector<ui::TableColumn> columns;
    678   for (size_t i = 0; i < visible_columns_.size(); ++i)
    679     columns.push_back(visible_columns_[i].column);
    680 
    681   int first_column_padding = 0;
    682   if (table_type_ == ICON_AND_TEXT && header_)
    683     first_column_padding += kImageSize + kTextHorizontalPadding;
    684   if (grouper_)
    685     first_column_padding += kGroupingIndicatorSize + kTextHorizontalPadding;
    686 
    687   std::vector<int> sizes = views::CalculateTableColumnSizes(
    688       layout_width_, first_column_padding, header_->font_list(), font_list_,
    689       std::max(kTextHorizontalPadding, TableHeader::kHorizontalPadding) * 2,
    690       TableHeader::kSortIndicatorWidth, columns, model_);
    691   DCHECK_EQ(visible_columns_.size(), sizes.size());
    692   int x = 0;
    693   for (size_t i = 0; i < visible_columns_.size(); ++i) {
    694     visible_columns_[i].x = x;
    695     visible_columns_[i].width = sizes[i];
    696     x += sizes[i];
    697   }
    698 }
    699 
    700 TableView::PaintRegion TableView::GetPaintRegion(
    701     const gfx::Rect& bounds) const {
    702   DCHECK(!visible_columns_.empty());
    703   DCHECK(RowCount());
    704 
    705   PaintRegion region;
    706   region.min_row = std::min(RowCount() - 1,
    707                             std::max(0, bounds.y() / row_height_));
    708   region.max_row = bounds.bottom() / row_height_;
    709   if (bounds.bottom() % row_height_ != 0)
    710     region.max_row++;
    711   region.max_row = std::min(region.max_row, RowCount());
    712 
    713   if (!header_) {
    714     region.max_column = 1;
    715     return region;
    716   }
    717 
    718   const int paint_x = GetMirroredXForRect(bounds);
    719   const int paint_max_x = paint_x + bounds.width();
    720   region.min_column = -1;
    721   region.max_column = visible_columns_.size();
    722   for (size_t i = 0; i < visible_columns_.size(); ++i) {
    723     int max_x = visible_columns_[i].x + visible_columns_[i].width;
    724     if (region.min_column == -1 && max_x >= paint_x)
    725       region.min_column = static_cast<int>(i);
    726     if (region.min_column != -1 && visible_columns_[i].x >= paint_max_x) {
    727       region.max_column = i;
    728       break;
    729     }
    730   }
    731   return region;
    732 }
    733 
    734 gfx::Rect TableView::GetPaintBounds(gfx::Canvas* canvas) const {
    735   SkRect sk_clip_rect;
    736   if (canvas->sk_canvas()->getClipBounds(&sk_clip_rect))
    737     return gfx::ToEnclosingRect(gfx::SkRectToRectF(sk_clip_rect));
    738   return GetVisibleBounds();
    739 }
    740 
    741 void TableView::SchedulePaintForSelection() {
    742   if (selection_model_.size() == 1) {
    743     const int first_model_row = FirstSelectedRow();
    744     SchedulePaintInRect(GetRowBounds(ModelToView(first_model_row)));
    745     if (first_model_row != selection_model_.active())
    746       SchedulePaintInRect(GetRowBounds(ModelToView(selection_model_.active())));
    747   } else if (selection_model_.size() > 1) {
    748     SchedulePaint();
    749   }
    750 }
    751 
    752 ui::TableColumn TableView::FindColumnByID(int id) const {
    753   for (size_t i = 0; i < columns_.size(); ++i) {
    754     if (columns_[i].id == id)
    755       return columns_[i];
    756   }
    757   NOTREACHED();
    758   return ui::TableColumn();
    759 }
    760 
    761 void TableView::SelectByViewIndex(int view_index) {
    762   ui::ListSelectionModel new_selection;
    763   if (view_index != -1) {
    764     SelectRowsInRangeFrom(view_index, true, &new_selection);
    765     new_selection.set_anchor(ViewToModel(view_index));
    766     new_selection.set_active(ViewToModel(view_index));
    767   }
    768 
    769   SetSelectionModel(new_selection);
    770 }
    771 
    772 void TableView::SetSelectionModel(const ui::ListSelectionModel& new_selection) {
    773   if (new_selection.Equals(selection_model_))
    774     return;
    775 
    776   SchedulePaintForSelection();
    777   selection_model_.Copy(new_selection);
    778   SchedulePaintForSelection();
    779 
    780   // Scroll the group for the active item to visible.
    781   if (selection_model_.active() != -1) {
    782     gfx::Rect vis_rect(GetVisibleBounds());
    783     const GroupRange range(GetGroupRange(selection_model_.active()));
    784     const int start_y = GetRowBounds(ModelToView(range.start)).y();
    785     const int end_y =
    786         GetRowBounds(ModelToView(range.start + range.length - 1)).bottom();
    787     vis_rect.set_y(start_y);
    788     vis_rect.set_height(end_y - start_y);
    789     ScrollRectToVisible(vis_rect);
    790   }
    791 
    792   if (table_view_observer_)
    793     table_view_observer_->OnSelectionChanged();
    794 }
    795 
    796 void TableView::AdvanceSelection(AdvanceDirection direction) {
    797   if (selection_model_.active() == -1) {
    798     SelectByViewIndex(0);
    799     return;
    800   }
    801   int view_index = ModelToView(selection_model_.active());
    802   if (direction == ADVANCE_DECREMENT)
    803     view_index = std::max(0, view_index - 1);
    804   else
    805     view_index = std::min(RowCount() - 1, view_index + 1);
    806   SelectByViewIndex(view_index);
    807 }
    808 
    809 void TableView::ConfigureSelectionModelForEvent(
    810     const ui::LocatedEvent& event,
    811     ui::ListSelectionModel* model) const {
    812   const int view_index = event.y() / row_height_;
    813   DCHECK(view_index >= 0 && view_index < RowCount());
    814 
    815   if (selection_model_.anchor() == -1 ||
    816       single_selection_ ||
    817       (!event.IsControlDown() && !event.IsShiftDown())) {
    818     SelectRowsInRangeFrom(view_index, true, model);
    819     model->set_anchor(ViewToModel(view_index));
    820     model->set_active(ViewToModel(view_index));
    821     return;
    822   }
    823   if ((event.IsControlDown() && event.IsShiftDown()) || event.IsShiftDown()) {
    824     // control-shift: copy existing model and make sure rows between anchor and
    825     // |view_index| are selected.
    826     // shift: reset selection so that only rows between anchor and |view_index|
    827     // are selected.
    828     if (event.IsControlDown() && event.IsShiftDown())
    829       model->Copy(selection_model_);
    830     else
    831       model->set_anchor(selection_model_.anchor());
    832     for (int i = std::min(view_index, ModelToView(model->anchor())),
    833              end = std::max(view_index, ModelToView(model->anchor()));
    834          i <= end; ++i) {
    835       SelectRowsInRangeFrom(i, true, model);
    836     }
    837     model->set_active(ViewToModel(view_index));
    838   } else {
    839     DCHECK(event.IsControlDown());
    840     // Toggle the selection state of |view_index| and set the anchor/active to
    841     // it and don't change the state of any other rows.
    842     model->Copy(selection_model_);
    843     model->set_anchor(ViewToModel(view_index));
    844     model->set_active(ViewToModel(view_index));
    845     SelectRowsInRangeFrom(view_index,
    846                           !model->IsSelected(ViewToModel(view_index)),
    847                           model);
    848   }
    849 }
    850 
    851 void TableView::SelectRowsInRangeFrom(int view_index,
    852                                       bool select,
    853                                       ui::ListSelectionModel* model) const {
    854   const GroupRange range(GetGroupRange(ViewToModel(view_index)));
    855   for (int i = 0; i < range.length; ++i) {
    856     if (select)
    857       model->AddIndexToSelection(range.start + i);
    858     else
    859       model->RemoveIndexFromSelection(range.start + i);
    860   }
    861 }
    862 
    863 GroupRange TableView::GetGroupRange(int model_index) const {
    864   GroupRange range;
    865   if (grouper_) {
    866     grouper_->GetGroupRange(model_index, &range);
    867   } else {
    868     range.start = model_index;
    869     range.length = 1;
    870   }
    871   return range;
    872 }
    873 
    874 bool TableView::GetTooltipImpl(const gfx::Point& location,
    875                                base::string16* tooltip,
    876                                gfx::Point* tooltip_origin) const {
    877   const int row = location.y() / row_height_;
    878   if (row < 0 || row >= RowCount() || visible_columns_.empty())
    879     return false;
    880 
    881   const int x = GetMirroredXInView(location.x());
    882   const int column = GetClosestVisibleColumnIndex(this, x);
    883   if (x < visible_columns_[column].x ||
    884       x > (visible_columns_[column].x + visible_columns_[column].width))
    885     return false;
    886 
    887   const base::string16 text(model_->GetText(ViewToModel(row),
    888                                       visible_columns_[column].column.id));
    889   if (text.empty())
    890     return false;
    891 
    892   gfx::Rect cell_bounds(GetCellBounds(row, column));
    893   AdjustCellBoundsForText(column, &cell_bounds);
    894   const int right = std::min(GetVisibleBounds().right(), cell_bounds.right());
    895   if (right > cell_bounds.x() &&
    896       gfx::GetStringWidth(text, font_list_) <= (right - cell_bounds.x()))
    897     return false;
    898 
    899   if (tooltip)
    900     *tooltip = text;
    901   if (tooltip_origin) {
    902     tooltip_origin->SetPoint(cell_bounds.x(),
    903                              cell_bounds.y() + kTextVerticalPadding);
    904   }
    905   return true;
    906 }
    907 
    908 }  // namespace views
    909