Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: ui/views/controls/table/table_view.cc

Issue 851853002: It is time. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Trying to reup because the last upload failed. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/views/controls/table/table_view.h ('k') | ui/views/controls/table/table_view_observer.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
OLDNEW
« no previous file with comments | « ui/views/controls/table/table_view.h ('k') | ui/views/controls/table/table_view_observer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698