summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-05 00:42:17 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-05 00:42:17 +0000
commit9c2fd4b882fd801e607d48befc8b4cac50865393 (patch)
tree35df102517da24d1e3e1c81e908b2193affbc2f6 /ui
parent4ea6de7e64205a2d897bc28aa2a05504174956b7 (diff)
downloadchromium_src-9c2fd4b882fd801e607d48befc8b4cac50865393.zip
chromium_src-9c2fd4b882fd801e607d48befc8b4cac50865393.tar.gz
chromium_src-9c2fd4b882fd801e607d48befc8b4cac50865393.tar.bz2
Nukes TableViewWin and related classes. TableViewViews has been the
default for a couple of weeks and I'm not aware of any major issues. BUG=none TEST=none R=ben@chromium.org Review URL: https://chromiumcodereview.appspot.com/12183021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@180584 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/views/controls/table/group_table_model.h28
-rw-r--r--ui/views/controls/table/group_table_view.h11
-rw-r--r--ui/views/controls/table/group_table_view_views.cc31
-rw-r--r--ui/views/controls/table/group_table_view_views.h37
-rw-r--r--ui/views/controls/table/group_table_view_win.cc202
-rw-r--r--ui/views/controls/table/group_table_view_win.h75
-rw-r--r--ui/views/controls/table/table_grouper.h5
-rw-r--r--ui/views/controls/table/table_view.cc (renamed from ui/views/controls/table/table_view_views.cc)3
-rw-r--r--ui/views/controls/table/table_view.h360
-rw-r--r--ui/views/controls/table/table_view_unittest.cc928
-rw-r--r--ui/views/controls/table/table_view_views.h361
-rw-r--r--ui/views/controls/table/table_view_views_unittest.cc741
-rw-r--r--ui/views/controls/table/table_view_win.cc1646
-rw-r--r--ui/views/controls/table/table_view_win.h506
-rw-r--r--ui/views/examples/table_example.cc1
-rw-r--r--ui/views/views.gyp12
16 files changed, 963 insertions, 3984 deletions
diff --git a/ui/views/controls/table/group_table_model.h b/ui/views/controls/table/group_table_model.h
deleted file mode 100644
index 15358ff..0000000
--- a/ui/views/controls/table/group_table_model.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_MODEL_H_
-#define UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_MODEL_H_
-
-#include "ui/base/models/table_model.h"
-
-namespace views {
-
-// TODO(sky): move GroupRange into TableGrouper and remove this file.
-struct GroupRange {
- int start;
- int length;
-};
-
-// The model driving the GroupTableView.
-class GroupTableModel : public ui::TableModel {
- public:
- // Populates the passed range with the first row/last row (included)
- // that this item belongs to.
- virtual void GetGroupRangeForItem(int item, GroupRange* range) = 0;
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_MODEL_H_
diff --git a/ui/views/controls/table/group_table_view.h b/ui/views/controls/table/group_table_view.h
deleted file mode 100644
index b0f6a15..0000000
--- a/ui/views/controls/table/group_table_view.h
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_VIEW_H_
-#define UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_VIEW_H_
-
-// TODO(sky): clean this up once TableViewViews stabilizes.
-#include "ui/views/controls/table/group_table_view_views.h"
-
-#endif // UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_VIEW_H_
diff --git a/ui/views/controls/table/group_table_view_views.cc b/ui/views/controls/table/group_table_view_views.cc
deleted file mode 100644
index b3fb06a..0000000
--- a/ui/views/controls/table/group_table_view_views.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/controls/table/group_table_view_views.h"
-
-#include "ui/views/controls/table/group_table_model.h"
-
-namespace views {
-
-const char GroupTableView::kViewClassName[] = "views/GroupTableView";
-
-GroupTableView::GroupTableView(GroupTableModel* model,
- const std::vector<ui::TableColumn>& columns,
- TableTypes table_type,
- bool single_selection,
- bool resizable_columns,
- bool autosize_columns,
- bool draw_group_separators)
- : TableView(model, columns, table_type, false, resizable_columns,
- autosize_columns) {
-}
-
-GroupTableView::~GroupTableView() {
-}
-
-std::string GroupTableView::GetClassName() const {
- return kViewClassName;
-}
-
-} // namespace views
diff --git a/ui/views/controls/table/group_table_view_views.h b/ui/views/controls/table/group_table_view_views.h
deleted file mode 100644
index 71d491f..0000000
--- a/ui/views/controls/table/group_table_view_views.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_VIEW_VIEWS_H_
-#define UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_VIEW_VIEWS_H_
-
-#include "ui/views/controls/table/table_view.h"
-
-namespace views {
-
-class GroupTableModel;
-
-// GroupTableView adds grouping to the TableView class.
-// It allows to have groups of rows that act as a single row from the selection
-// perspective. Groups are visually separated by a horizontal line.
-class VIEWS_EXPORT GroupTableView : public TableView {
- public:
- // The view class name.
- static const char kViewClassName[];
-
- GroupTableView(GroupTableModel* model,
- const std::vector<ui::TableColumn>& columns,
- TableTypes table_type, bool single_selection,
- bool resizable_columns, bool autosize_columns,
- bool draw_group_separators);
- virtual ~GroupTableView();
-
- // View overrides:
- virtual std::string GetClassName() const OVERRIDE;
-
- DISALLOW_COPY_AND_ASSIGN(GroupTableView);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_VIEW_VIEWS_H_
diff --git a/ui/views/controls/table/group_table_view_win.cc b/ui/views/controls/table/group_table_view_win.cc
deleted file mode 100644
index b056bd2..0000000
--- a/ui/views/controls/table/group_table_view_win.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/controls/table/group_table_view_win.h"
-
-#include "base/bind.h"
-#include "base/compiler_specific.h"
-#include "base/message_loop.h"
-#include "ui/gfx/canvas.h"
-#include "ui/views/controls/table/group_table_model.h"
-
-namespace views {
-
-const COLORREF kSeparatorLineColor = RGB(208, 208, 208);
-const int kSeparatorLineThickness = 1;
-
-const char GroupTableView::kViewClassName[] = "views/GroupTableView";
-
-GroupTableView::GroupTableView(GroupTableModel* model,
- const std::vector<ui::TableColumn>& columns,
- TableTypes table_type,
- bool single_selection,
- bool resizable_columns,
- bool autosize_columns,
- bool draw_group_separators)
- : TableView(model, columns, table_type, false, resizable_columns,
- autosize_columns),
- model_(model),
- draw_group_separators_(draw_group_separators),
- ALLOW_THIS_IN_INITIALIZER_LIST(sync_selection_factory_(this)) {
-}
-
-GroupTableView::~GroupTableView() {
-}
-
-void GroupTableView::SyncSelection() {
- int index = 0;
- int row_count = model_->RowCount();
- GroupRange group_range;
- while (index < row_count) {
- model_->GetGroupRangeForItem(index, &group_range);
- if (group_range.length == 1) {
- // No synching required for single items.
- index++;
- } else {
- // We need to select the group if at least one item is selected.
- bool should_select = false;
- for (int i = group_range.start;
- i < group_range.start + group_range.length; ++i) {
- if (IsItemSelected(i)) {
- should_select = true;
- break;
- }
- }
- if (should_select) {
- for (int i = group_range.start;
- i < group_range.start + group_range.length; ++i) {
- SetSelectedState(i, true);
- }
- }
- index += group_range.length;
- }
- }
-}
-
-bool GroupTableView::OnKeyDown(ui::KeyboardCode virtual_keycode) {
- // In a list view, multiple items can be selected but only one item has the
- // focus. This creates a problem when the arrow keys are used for navigating
- // between items in the list view. An example will make this more clear:
- //
- // Suppose we have 5 items in the list view, and three of these items are
- // part of one group:
- //
- // Index0: ItemA (No Group)
- // Index1: ItemB (GroupX)
- // Index2: ItemC (GroupX)
- // Index3: ItemD (GroupX)
- // Index4: ItemE (No Group)
- //
- // When GroupX is selected (say, by clicking on ItemD with the mouse),
- // GroupTableView::SyncSelection() will make sure ItemB, ItemC and ItemD are
- // selected. Also, the item with the focus will be ItemD (simply because
- // this is the item the user happened to click on). If then the UP arrow is
- // pressed once, the focus will be switched to ItemC and not to ItemA and the
- // end result is that we are stuck in GroupX even though the intention was to
- // switch to ItemA.
- //
- // For that exact reason, we need to set the focus appropriately when we
- // detect that one of the arrow keys is pressed. Thus, when it comes time
- // for the list view control to actually switch the focus, the right item
- // will be selected.
- if ((virtual_keycode != ui::VKEY_UP) &&
- (virtual_keycode != ui::VKEY_DOWN)) {
- return TableView::OnKeyDown(virtual_keycode);
- }
-
- // We start by finding the index of the item with the focus. If no item
- // currently has the focus, then this routine doesn't do anything.
- int focused_index;
- int row_count = model_->RowCount();
- for (focused_index = 0; focused_index < row_count; focused_index++) {
- if (ItemHasTheFocus(focused_index)) {
- break;
- }
- }
-
- if (focused_index == row_count)
- return false;
-
- DCHECK_LT(focused_index, row_count);
-
- // Nothing to do if the item which has the focus is not part of a group.
- GroupRange group_range;
- model_->GetGroupRangeForItem(focused_index, &group_range);
- if (group_range.length == 1)
- return false;
-
- // If the user pressed the UP key, then the focus should be set to the
- // topmost element in the group. If the user pressed the DOWN key, the focus
- // should be set to the bottommost element.
- if (virtual_keycode == ui::VKEY_UP) {
- SetFocusOnItem(group_range.start);
- } else {
- DCHECK_EQ(virtual_keycode, ui::VKEY_DOWN);
- SetFocusOnItem(group_range.start + group_range.length - 1);
- }
-
- return false;
-}
-
-void GroupTableView::PrepareForSort() {
- GroupRange range;
- int row_count = RowCount();
- model_index_to_range_start_map_.clear();
- for (int model_row = 0; model_row < row_count;) {
- model_->GetGroupRangeForItem(model_row, &range);
- for (int range_counter = 0; range_counter < range.length; range_counter++)
- model_index_to_range_start_map_[range_counter + model_row] = model_row;
- model_row += range.length;
- }
-}
-
-int GroupTableView::CompareRows(int model_row1, int model_row2) {
- int range1 = model_index_to_range_start_map_[model_row1];
- int range2 = model_index_to_range_start_map_[model_row2];
- if (range1 == range2) {
- // The two rows are in the same group, sort so that items in the same group
- // always appear in the same order.
- return model_row1 - model_row2;
- }
- // Sort by the first entry of each of the groups.
- return TableView::CompareRows(range1, range2);
-}
-
-void GroupTableView::OnSelectedStateChanged() {
- // The goal is to make sure all items for a same group are in a consistent
- // state in term of selection. When a user clicks an item, several selection
- // messages are sent, possibly including unselecting all currently selected
- // items. For that reason, we post a task to be performed later, after all
- // selection messages have been processed. In the meantime we just ignore all
- // selection notifications.
- if (!sync_selection_factory_.HasWeakPtrs()) {
- MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&GroupTableView::SyncSelection,
- sync_selection_factory_.GetWeakPtr()));
- }
- TableView::OnSelectedStateChanged();
-}
-
-// Draws the line separator betweens the groups.
-void GroupTableView::PostPaint(int model_row, int column, bool selected,
- const gfx::Rect& bounds, HDC hdc) {
- if (!draw_group_separators_)
- return;
-
- GroupRange group_range;
- model_->GetGroupRangeForItem(model_row, &group_range);
-
- // We always paint a vertical line at the end of the last cell.
- HPEN hPen = CreatePen(PS_SOLID, kSeparatorLineThickness, kSeparatorLineColor);
- HPEN hPenOld = (HPEN) SelectObject(hdc, hPen);
- int x = bounds.right() - kSeparatorLineThickness;
- MoveToEx(hdc, x, bounds.y(), NULL);
- LineTo(hdc, x, bounds.bottom());
-
- // We paint a separator line after the last item of a group.
- if (model_row == (group_range.start + group_range.length - 1)) {
- int y = bounds.bottom() - kSeparatorLineThickness;
- MoveToEx(hdc, 0, y, NULL);
- LineTo(hdc, bounds.width(), y);
- }
- SelectObject(hdc, hPenOld);
- DeleteObject(hPen);
-}
-
-std::string GroupTableView::GetClassName() const {
- return kViewClassName;
-}
-
-} // namespace views
diff --git a/ui/views/controls/table/group_table_view_win.h b/ui/views/controls/table/group_table_view_win.h
deleted file mode 100644
index eb104fa..0000000
--- a/ui/views/controls/table/group_table_view_win.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_VIEW_WIN_H_
-#define UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_VIEW_WIN_H_
-
-#include "base/memory/weak_ptr.h"
-#include "ui/base/models/table_model.h"
-#include "ui/views/controls/table/table_view.h"
-
-namespace views {
-
-class GroupTableModel;
-
-// GroupTableView adds grouping to the TableView class.
-// It allows to have groups of rows that act as a single row from the selection
-// perspective. Groups are visually separated by a horizontal line.
-class VIEWS_EXPORT GroupTableView : public TableView {
- public:
- // The view class name.
- static const char kViewClassName[];
-
- GroupTableView(GroupTableModel* model,
- const std::vector<ui::TableColumn>& columns,
- TableTypes table_type, bool single_selection,
- bool resizable_columns, bool autosize_columns,
- bool draw_group_separators);
- virtual ~GroupTableView();
-
- virtual std::string GetClassName() const;
-
- protected:
- // Notification from the ListView that the selected state of an item has
- // changed.
- void OnSelectedStateChanged();
-
- // Extra-painting required to draw the separator line between groups.
- virtual bool ImplementPostPaint() { return true; }
- virtual void PostPaint(int model_row, int column, bool selected,
- const gfx::Rect& bounds, HDC device_context);
-
- // In order to make keyboard navigation possible (using the Up and Down
- // keys), we must take action when an arrow key is pressed. The reason we
- // need to process this message has to do with the manner in which the focus
- // needs to be set on a group item when a group is selected.
- virtual bool OnKeyDown(ui::KeyboardCode virtual_keycode);
-
- // Overriden to make sure rows in the same group stay grouped together.
- virtual int CompareRows(int model_row1, int model_row2);
-
- // Updates model_index_to_range_start_map_ from the model.
- virtual void PrepareForSort();
-
- private:
- // Make the selection of group consistent.
- void SyncSelection();
-
- GroupTableModel* model_;
-
- // If true, draw separators between groups.
- bool draw_group_separators_;
-
- // A factory to make the selection consistent among groups.
- base::WeakPtrFactory<GroupTableView> sync_selection_factory_;
-
- // Maps from model row to start of group.
- std::map<int,int> model_index_to_range_start_map_;
-
- DISALLOW_COPY_AND_ASSIGN(GroupTableView);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_CONTROLS_TABLE_GROUP_TABLE_VIEW_WIN_H_
diff --git a/ui/views/controls/table/table_grouper.h b/ui/views/controls/table/table_grouper.h
index 54ef21d..d6659ac 100644
--- a/ui/views/controls/table/table_grouper.h
+++ b/ui/views/controls/table/table_grouper.h
@@ -7,7 +7,10 @@
namespace views {
-struct GroupRange;
+struct GroupRange {
+ int start;
+ int length;
+};
// TableGrouper is used by TableView to group a set of rows and treat them
// as one. Rows that fall in the same group are selected together and sorted
diff --git a/ui/views/controls/table/table_view_views.cc b/ui/views/controls/table/table_view.cc
index 5e3000c..9772488 100644
--- a/ui/views/controls/table/table_view_views.cc
+++ b/ui/views/controls/table/table_view.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/views/controls/table/table_view_views.h"
+#include "ui/views/controls/table/table_view.h"
#include <map>
@@ -15,7 +15,6 @@
#include "ui/gfx/skia_util.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/controls/scroll_view.h"
-#include "ui/views/controls/table/group_table_model.h"
#include "ui/views/controls/table/table_grouper.h"
#include "ui/views/controls/table/table_header.h"
#include "ui/views/controls/table/table_utils.h"
diff --git a/ui/views/controls/table/table_view.h b/ui/views/controls/table/table_view.h
index 2547235..5ce25f0 100644
--- a/ui/views/controls/table/table_view.h
+++ b/ui/views/controls/table/table_view.h
@@ -2,10 +2,360 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_H_
-#define UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_H_
+#ifndef UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_VIEWS_H_
+#define UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_VIEWS_H_
-// TODO(sky): clean this up once TableViewViews stabilizes.
-#include "ui/views/controls/table/table_view_views.h"
+#include <vector>
-#endif // UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_H_
+#include "base/memory/scoped_ptr.h"
+#include "ui/base/models/list_selection_model.h"
+#include "ui/base/models/table_model.h"
+#include "ui/base/models/table_model_observer.h"
+#include "ui/gfx/font.h"
+#include "ui/views/view.h"
+#include "ui/views/views_export.h"
+
+// A TableView is a view that displays multiple rows with any number of columns.
+// TableView is driven by a TableModel. The model returns the contents
+// to display. TableModel also has an Observer which is used to notify
+// TableView of changes to the model so that the display may be updated
+// appropriately.
+//
+// TableView itself has an observer that is notified when the selection
+// changes.
+//
+// When a table is sorted the model coordinates do not necessarily match the
+// view coordinates. All table methods are in terms of the model. If you need to
+// convert to view coordinates use ModelToView().
+//
+// Sorting is done by a locale sensitive string sort. You can customize the
+// sort by way of overriding TableModel::CompareValues().
+namespace views {
+
+struct GroupRange;
+class TableGrouper;
+class TableHeader;
+class TableViewObserver;
+class TableViewRowBackgroundPainter;
+class TableViewTestHelper;
+
+// The cells in the first column of a table can contain:
+// - only text
+// - a small icon (16x16) and some text
+// - a check box and some text
+enum TableTypes {
+ TEXT_ONLY = 0,
+ ICON_AND_TEXT,
+};
+
+class VIEWS_EXPORT TableView
+ : public views::View,
+ public ui::TableModelObserver {
+ public:
+ // Used to track a visible column. Useful only for the header.
+ struct VIEWS_EXPORT VisibleColumn {
+ VisibleColumn();
+ ~VisibleColumn();
+
+ // The column.
+ ui::TableColumn column;
+
+ // Starting x-coordinate of the column.
+ int x;
+
+ // Width of the column.
+ int width;
+ };
+
+ // Describes a sorted column.
+ struct VIEWS_EXPORT SortDescriptor {
+ SortDescriptor() : column_id(-1), ascending(true) {}
+ SortDescriptor(int column_id, bool ascending)
+ : column_id(column_id),
+ ascending(ascending) {}
+
+ // ID of the sorted column.
+ int column_id;
+
+ // Is the sort ascending?
+ bool ascending;
+ };
+
+ typedef std::vector<SortDescriptor> SortDescriptors;
+
+ // Creates a new table using the model and columns specified.
+ // The table type applies to the content of the first column (text, icon and
+ // text, checkbox and text).
+ // When autosize_columns is true, columns always fill the available width. If
+ // false, columns are not resized when the table is resized. An extra empty
+ // column at the right fills the remaining space.
+ // When resizable_columns is true, users can resize columns by dragging the
+ // separator on the column header. NOTE: Right now this is always true. The
+ // code to set it false is still in place to be a base for future, better
+ // resizing behavior (see http://b/issue?id=874646 ), but no one uses or
+ // tests the case where this flag is false.
+ // Note that setting both resizable_columns and autosize_columns to false is
+ // probably not a good idea, as there is no way for the user to increase a
+ // column's size in that case.
+ TableView(ui::TableModel* model,
+ const std::vector<ui::TableColumn>& columns,
+ TableTypes table_type,
+ bool single_selection,
+ bool resizable_columns,
+ bool autosize_columns);
+ virtual ~TableView();
+
+ // Assigns a new model to the table view, detaching the old one if present.
+ // If |model| is NULL, the table view cannot be used after this call. This
+ // should be called in the containing view's destructor to avoid destruction
+ // issues when the model needs to be deleted before the table.
+ void SetModel(ui::TableModel* model);
+ ui::TableModel* model() const { return model_; }
+
+ // Returns a new ScrollView that contains the receiver.
+ View* CreateParentIfNecessary();
+
+ void SetRowBackgroundPainter(
+ scoped_ptr<TableViewRowBackgroundPainter> painter);
+
+ // Sets the TableGrouper. TableView does not own |grouper| (common use case is
+ // to have TableModel implement TableGrouper).
+ void SetGrouper(TableGrouper* grouper);
+
+ // Returns the number of rows in the TableView.
+ int RowCount() const;
+
+ // Returns the number of selected rows.
+ // TODO(sky): remove this and force callers to use selection_model().
+ int SelectedRowCount();
+
+ // Selects the specified item, making sure it's visible.
+ void Select(int model_row);
+
+ // Returns the first selected row in terms of the model.
+ int FirstSelectedRow();
+
+ const ui::ListSelectionModel& selection_model() const {
+ return selection_model_;
+ }
+
+ // Changes the visibility of the specified column (by id).
+ void SetColumnVisibility(int id, bool is_visible);
+ bool IsColumnVisible(int id) const;
+
+ // Adds the specified column. |col| is not made visible.
+ void AddColumn(const ui::TableColumn& col);
+
+ // Returns true if the column with the specified id is known (either visible
+ // or not).
+ bool HasColumn(int id) const;
+
+ // TODO(sky): rename to set_observer().
+ void SetObserver(TableViewObserver* observer) {
+ table_view_observer_ = observer;
+ }
+ TableViewObserver* observer() const { return table_view_observer_; }
+
+ const std::vector<VisibleColumn>& visible_columns() const {
+ return visible_columns_;
+ }
+
+ // Sets the width of the column. |index| is in terms of |visible_columns_|.
+ void SetVisibleColumnWidth(int index, int width);
+
+ // Toggles the sort order of the specified visible column index.
+ void ToggleSortOrder(int visible_column_index);
+
+ const SortDescriptors& sort_descriptors() const { return sort_descriptors_; }
+ bool is_sorted() const { return !sort_descriptors_.empty(); }
+
+ // Maps from the index in terms of the model to that of the view.
+ int ModelToView(int model_index) const;
+
+ // Maps from the index in terms of the view to that of the model.
+ int ViewToModel(int view_index) const;
+
+ int row_height() const { return row_height_; }
+
+ // View overrides:
+ virtual void Layout() OVERRIDE;
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
+ virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
+ virtual bool GetTooltipText(const gfx::Point& p,
+ string16* tooltip) const OVERRIDE;
+ virtual bool GetTooltipTextOrigin(const gfx::Point& p,
+ gfx::Point* loc) const OVERRIDE;
+
+ // ui::TableModelObserver overrides:
+ virtual void OnModelChanged() OVERRIDE;
+ virtual void OnItemsChanged(int start, int length) OVERRIDE;
+ virtual void OnItemsAdded(int start, int length) OVERRIDE;
+ virtual void OnItemsRemoved(int start, int length) OVERRIDE;
+
+ protected:
+ // View overrides:
+ virtual gfx::Point GetKeyboardContextMenuLocation() OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual void OnFocus() OVERRIDE;
+ virtual void OnBlur() OVERRIDE;
+
+ private:
+ friend class TableViewTestHelper;
+ struct GroupSortHelper;
+ struct SortHelper;
+
+ // Used during painting to determine the range of cells that need to be
+ // painted.
+ // NOTE: the row indices returned by this are in terms of the view and column
+ // indices in terms of |visible_columns_|.
+ struct VIEWS_EXPORT PaintRegion {
+ PaintRegion();
+ ~PaintRegion();
+
+ int min_row;
+ int max_row;
+ int min_column;
+ int max_column;
+ };
+
+ // Used by AdvanceSelection() to determine the direction to change the
+ // selection.
+ enum AdvanceDirection {
+ ADVANCE_DECREMENT,
+ ADVANCE_INCREMENT,
+ };
+
+ // Invoked when the number of rows changes in some way.
+ void NumRowsChanged();
+
+ // Resets the sort descriptions.
+ void SetSortDescriptors(const SortDescriptors& sort_descriptors);
+
+ // Does the actual sort and updates the mappings (|view_to_model_| and
+ // |model_to_view_|) appropriately.
+ void SortItemsAndUpdateMapping();
+
+ // Used to sort the two rows. Returns a value < 0, == 0 or > 0 indicating
+ // whether the row2 comes before row1, row2 is the same as row1 or row1 comes
+ // after row2. This invokes CompareValues on the model with the sorted column.
+ int CompareRows(int model_row1, int model_row2);
+
+ // Returns the bounds of the specified row.
+ gfx::Rect GetRowBounds(int row) const;
+
+ // Returns the bounds of the specified cell. |visible_column_index| indexes
+ // into |visible_columns_|.
+ gfx::Rect GetCellBounds(int row, int visible_column_index) const;
+
+ // Adjusts |bounds| based on where the text should be painted. |bounds| comes
+ // from GetCellBounds() and |visible_column_index| is the corresponding column
+ // (in terms of |visible_columns_|).
+ void AdjustCellBoundsForText(int visible_column_index,
+ gfx::Rect* bounds) const;
+
+ // Creates |header_| if necessary.
+ void CreateHeaderIfNecessary();
+
+ // Updates the |x| and |width| of each of the columns in |visible_columns_|.
+ void UpdateVisibleColumnSizes();
+
+ // Returns the cells that need to be painted for the specified region.
+ // |bounds| is in terms of |this|.
+ PaintRegion GetPaintRegion(const gfx::Rect& bounds) const;
+
+ // Returns the bounds that need to be painted based on the clip set on
+ // |canvas|.
+ gfx::Rect GetPaintBounds(gfx::Canvas* canvas) const;
+
+ // Invokes SchedulePaint() for the selected rows.
+ void SchedulePaintForSelection();
+
+ // Returns the TableColumn matching the specified id.
+ ui::TableColumn FindColumnByID(int id) const;
+
+ // Sets the selection to the specified index (in terms of the view).
+ void SelectByViewIndex(int view_index);
+
+ // Sets the selection model to |new_selection|.
+ void SetSelectionModel(const ui::ListSelectionModel& new_selection);
+
+ // Advances the selection (from the active index) in the specified direction.
+ void AdvanceSelection(AdvanceDirection direction);
+
+ // Sets |model| appropriately based on a mouse event.
+ void ConfigureSelectionModelForEvent(const ui::MouseEvent& event,
+ ui::ListSelectionModel* model) const;
+
+ // Set the selection state of row at |view_index| to |select|, additionally
+ // any other rows in the GroupRange containing |view_index| are updated as
+ // well. This does not change the anchor or active index of |model|.
+ void SelectRowsInRangeFrom(int view_index,
+ bool select,
+ ui::ListSelectionModel* model) const;
+
+ // Returns the range of the specified model index. If a TableGrouper has not
+ // been set this returns a group with a start of |model_index| and length of
+ // 1.
+ GroupRange GetGroupRange(int model_index) const;
+
+ // Used by both GetTooltipText methods. Returns true if there is a tooltip and
+ // sets |tooltip| and/or |tooltip_origin| as appropriate, each of which may be
+ // NULL.
+ bool GetTooltipImpl(const gfx::Point& location,
+ string16* tooltip,
+ gfx::Point* tooltip_origin) const;
+
+ ui::TableModel* model_;
+
+ std::vector<ui::TableColumn> columns_;
+
+ // The set of visible columns. The values of these point to |columns_|. This
+ // may contain a subset of |columns_|.
+ std::vector<VisibleColumn> visible_columns_;
+
+ // The header. This is only created if more than one column is specified or
+ // the first column has a non-empty title.
+ TableHeader* header_;
+
+ const TableTypes table_type_;
+
+ const bool single_selection_;
+
+ // TODO(sky): rename to observer_.
+ TableViewObserver* table_view_observer_;
+
+ // The selection, in terms of the model.
+ ui::ListSelectionModel selection_model_;
+
+ gfx::Font font_;
+
+ int row_height_;
+
+ // Width of the ScrollView last time Layout() was invoked. Used to determine
+ // when we should invoke UpdateVisibleColumnSizes().
+ int last_parent_width_;
+
+ // The width we layout to. This may differ from |last_parent_width_|.
+ int layout_width_;
+
+ // Current sort.
+ SortDescriptors sort_descriptors_;
+
+ // Mappings used when sorted.
+ std::vector<int> view_to_model_;
+ std::vector<int> model_to_view_;
+
+ scoped_ptr<TableViewRowBackgroundPainter> row_background_painter_;
+
+ TableGrouper* grouper_;
+
+ // True if in SetVisibleColumnWidth().
+ bool in_set_visible_column_width_;
+
+ DISALLOW_COPY_AND_ASSIGN(TableView);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_VIEWS_H_
diff --git a/ui/views/controls/table/table_view_unittest.cc b/ui/views/controls/table/table_view_unittest.cc
index 81e782e..2fe709c 100644
--- a/ui/views/controls/table/table_view_unittest.cc
+++ b/ui/views/controls/table/table_view_unittest.cc
@@ -2,33 +2,46 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// For WinDDK ATL compatibility, these ATL headers must come first.
-
-// TODO(sky): nuke this once TableViewViews stabilizes.
-
-#include "build/build_config.h"
-
-#include <atlbase.h> // NOLINT
-#include <atlwin.h> // NOLINT
-#include <vector> // NOLINT
+#include "ui/views/controls/table/table_view.h"
-#include "base/compiler_specific.h"
-#include "base/message_loop.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/models/table_model.h"
-#include "ui/base/models/table_model_observer.h"
-#include "ui/views/controls/table/table_view.h"
-#include "ui/views/test/views_test_base.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/controls/table/table_grouper.h"
+#include "ui/views/controls/table/table_header.h"
+#include "ui/views/controls/table/table_view_observer.h"
// Put the tests in the views namespace to make it easier to declare them as
// friend classes.
namespace views {
-// TestTableModel --------------------------------------------------------------
+class TableViewTestHelper {
+ public:
+ explicit TableViewTestHelper(TableView* table) : table_(table) {}
+
+ std::string GetPaintRegion(const gfx::Rect& bounds) {
+ TableView::PaintRegion region(table_->GetPaintRegion(bounds));
+ return "rows=" + base::IntToString(region.min_row) + " " +
+ base::IntToString(region.max_row) + " cols=" +
+ base::IntToString(region.min_column) + " " +
+ base::IntToString(region.max_column);
+ }
+
+ size_t visible_col_count() {
+ return table_->visible_columns().size();
+ }
+
+ TableHeader* header() { return table_->header_; }
+
+ private:
+ TableView* table_;
+
+ DISALLOW_COPY_AND_ASSIGN(TableViewTestHelper);
+};
+
+namespace {
+
+// TestTableModel2 -------------------------------------------------------------
// Trivial TableModel implementation that is backed by a vector of vectors.
// Provides methods for adding/removing/changing the contents that notify the
@@ -38,9 +51,10 @@ namespace views {
// 0, 1
// 1, 1
// 2, 2
-class TestTableModel : public ui::TableModel {
+// 3, 0
+class TestTableModel2 : public ui::TableModel {
public:
- TestTableModel();
+ TestTableModel2();
// Adds a new row at index |row| with values |c1_value| and |c2_value|.
void AddRow(int row, int c1_value, int c2_value);
@@ -63,37 +77,17 @@ class TestTableModel : public ui::TableModel {
// The data.
std::vector<std::vector<int> > rows_;
- DISALLOW_COPY_AND_ASSIGN(TestTableModel);
+ DISALLOW_COPY_AND_ASSIGN(TestTableModel2);
};
-// Same behavior as TestTableModel, except even items are in one group, while
-// odd items are put in a different group.
-class GroupTestTableModel : public TestTableModel {
- virtual bool HasGroups() { return true; }
-
- virtual Groups GetGroups() {
- Groups groups;
- Group group1, group2;
- group1.title = ASCIIToUTF16("Group 1");
- group1.id = 0;
- group2.title = ASCIIToUTF16("Group 2");
- group2.id = 0;
- groups.push_back(group1);
- groups.push_back(group2);
- return groups;
- }
-
- // Return group = 0 if row is even, otherwise group = 1.
- virtual int GetGroupID(int row) { return row % 2; }
-};
-
-TestTableModel::TestTableModel() : observer_(NULL) {
+TestTableModel2::TestTableModel2() : observer_(NULL) {
AddRow(0, 0, 1);
AddRow(1, 1, 1);
AddRow(2, 2, 2);
+ AddRow(3, 3, 0);
}
-void TestTableModel::AddRow(int row, int c1_value, int c2_value) {
+void TestTableModel2::AddRow(int row, int c1_value, int c2_value) {
DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
std::vector<int> new_row;
new_row.push_back(c1_value);
@@ -102,14 +96,14 @@ void TestTableModel::AddRow(int row, int c1_value, int c2_value) {
if (observer_)
observer_->OnItemsAdded(row, 1);
}
-void TestTableModel::RemoveRow(int row) {
+void TestTableModel2::RemoveRow(int row) {
DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
rows_.erase(rows_.begin() + row);
if (observer_)
observer_->OnItemsRemoved(row, 1);
}
-void TestTableModel::ChangeRow(int row, int c1_value, int c2_value) {
+void TestTableModel2::ChangeRow(int row, int c1_value, int c2_value) {
DCHECK(row >= 0 && row < static_cast<int>(rows_.size()));
rows_[row][0] = c1_value;
rows_[row][1] = c2_value;
@@ -117,350 +111,630 @@ void TestTableModel::ChangeRow(int row, int c1_value, int c2_value) {
observer_->OnItemsChanged(row, 1);
}
-int TestTableModel::RowCount() {
+int TestTableModel2::RowCount() {
return static_cast<int>(rows_.size());
}
-string16 TestTableModel::GetText(int row, int column_id) {
- return UTF8ToUTF16(base::IntToString(rows_[row][column_id]));
+string16 TestTableModel2::GetText(int row, int column_id) {
+ return base::IntToString16(rows_[row][column_id]);
}
-void TestTableModel::SetObserver(ui::TableModelObserver* observer) {
+void TestTableModel2::SetObserver(ui::TableModelObserver* observer) {
observer_ = observer;
}
-int TestTableModel::CompareValues(int row1, int row2, int column_id) {
+int TestTableModel2::CompareValues(int row1, int row2, int column_id) {
return rows_[row1][column_id] - rows_[row2][column_id];
}
-// TableViewTest ---------------------------------------------------------------
-
-class TableViewTest : public ViewsTestBase, views::WidgetDelegate {
- public:
- virtual void SetUp() OVERRIDE;
- virtual void TearDown() OVERRIDE;
-
- virtual views::View* GetContentsView() OVERRIDE {
- return table_;
- }
- virtual views::Widget* GetWidget() OVERRIDE {
- return table_->GetWidget();
+// Returns the view to model mapping as a string.
+std::string GetViewToModelAsString(TableView* table) {
+ std::string result;
+ for (int i = 0; i < table->RowCount(); ++i) {
+ if (i != 0)
+ result += " ";
+ result += base::IntToString(table->ViewToModel(i));
}
- virtual const views::Widget* GetWidget() const OVERRIDE {
- return table_->GetWidget();
- }
-
- protected:
- // Creates the model.
- virtual TestTableModel* CreateModel();
-
- // Verifies the view order matches that of the supplied arguments. The
- // arguments are in terms of the model. For example, values of '1, 0' indicate
- // the model index at row 0 is 1 and the model index at row 1 is 0.
- void VerifyViewOrder(int first, ...);
-
- // Verifies the selection matches the supplied arguments. The supplied
- // arguments are in terms of this model. This uses the iterator returned by
- // SelectionBegin.
- void VerifySelectedRows(int first, ...);
+ return result;
+}
- // Configures the state for the various multi-selection tests.
- // This selects model rows 0 and 1, and if |sort| is true the first column
- // is sorted in descending order.
- void SetUpMultiSelectTestState(bool sort);
+// Returns the model to view mapping as a string.
+std::string GetModelToViewAsString(TableView* table) {
+ std::string result;
+ for (int i = 0; i < table->RowCount(); ++i) {
+ if (i != 0)
+ result += " ";
+ result += base::IntToString(table->ModelToView(i));
+ }
+ return result;
+}
- scoped_ptr<TestTableModel> model_;
+class TestTableView : public TableView {
+ public:
+ TestTableView(ui::TableModel* model,
+ const std::vector<ui::TableColumn>& columns)
+ : TableView(model, columns, TEXT_ONLY, false, true, true) {
+ }
- // The table. This is owned by the window.
- TableView* table_;
+ // View overrides:
+ virtual bool HasFocus() const {
+ // Overriden so key processing works.
+ return true;
+ }
private:
- views::Widget* window_;
+ DISALLOW_COPY_AND_ASSIGN(TestTableView);
};
-void TableViewTest::SetUp() {
- ViewsTestBase::SetUp();
-
- model_.reset(CreateModel());
- std::vector<ui::TableColumn> columns;
- columns.resize(2);
- columns[0].id = 0;
- columns[1].id = 1;
-
- // TODO(erg): This crashes on windows. Try making this derive from ViewsTests.
- table_ = new TableView(model_.get(), columns, views::ICON_AND_TEXT,
- false, false, false);
- window_ = views::Widget::CreateWindowWithContextAndBounds(
- this, GetContext(), gfx::Rect(100, 100, 512, 512));
-}
+} // namespace
-void TableViewTest::TearDown() {
- window_->Close();
+class TableViewTest : public testing::Test {
+ public:
+ TableViewTest() : table_(NULL) {}
+
+ virtual void SetUp() OVERRIDE {
+ model_.reset(new TestTableModel2);
+ std::vector<ui::TableColumn> columns(2);
+ columns[0].title = ASCIIToUTF16("Title Column 0");
+ columns[0].sortable = true;
+ columns[1].title = ASCIIToUTF16("Title Column 1");
+ columns[1].id = 1;
+ columns[1].sortable = true;
+ table_ = new TestTableView(model_.get(), columns);
+ parent_.reset(table_->CreateParentIfNecessary());
+ parent_->SetBounds(0, 0, 10000, 10000);
+ parent_->Layout();
+ helper_.reset(new TableViewTestHelper(table_));
+ }
- ViewsTestBase::TearDown();
-}
+ void ClickOnRow(int row, int flags) {
+ const int y = row * table_->row_height();
+ const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, y),
+ gfx::Point(0, y),
+ ui::EF_LEFT_MOUSE_BUTTON | flags);
+ table_->OnMousePressed(pressed);
+ }
-void TableViewTest::VerifyViewOrder(int first, ...) {
- va_list marker;
- va_start(marker, first);
- int value = first;
- int index = 0;
- for (int value = first, index = 0; value != -1; index++) {
- ASSERT_EQ(value, table_->ViewToModel(index));
- value = va_arg(marker, int);
+ // Returns the state of the selection model as a string. The format is:
+ // 'active=X anchor=X selection=X X X...'.
+ std::string SelectionStateAsString() const {
+ const ui::ListSelectionModel& model(table_->selection_model());
+ std::string result = "active=" + base::IntToString(model.active()) +
+ " anchor=" + base::IntToString(model.anchor()) +
+ " selection=";
+ const ui::ListSelectionModel::SelectedIndices& selection(
+ model.selected_indices());
+ for (size_t i = 0; i < selection.size(); ++i) {
+ if (i != 0)
+ result += " ";
+ result += base::IntToString(selection[i]);
+ }
+ return result;
}
- va_end(marker);
-}
-void TableViewTest::VerifySelectedRows(int first, ...) {
- va_list marker;
- va_start(marker, first);
- int value = first;
- int index = 0;
- TableView::iterator selection_iterator = table_->SelectionBegin();
- for (int value = first, index = 0; value != -1; index++) {
- ASSERT_TRUE(selection_iterator != table_->SelectionEnd());
- ASSERT_EQ(value, *selection_iterator);
- value = va_arg(marker, int);
- ++selection_iterator;
+ void PressKey(ui::KeyboardCode code) {
+ ui::KeyEvent event(ui::ET_KEY_PRESSED, code, 0, false);
+ table_->OnKeyPressed(event);
}
- ASSERT_TRUE(selection_iterator == table_->SelectionEnd());
- va_end(marker);
-}
-void TableViewTest::SetUpMultiSelectTestState(bool sort) {
- // Select two rows.
- table_->SetSelectedState(0, true);
- table_->SetSelectedState(1, true);
-
- VerifySelectedRows(1, 0, -1);
- if (!sort || HasFatalFailure())
- return;
-
- // Sort by first column descending.
- TableView::SortDescriptors sd;
- sd.push_back(TableView::SortDescriptor(0, false));
- table_->SetSortDescriptors(sd);
- VerifyViewOrder(2, 1, 0, -1);
- if (HasFatalFailure())
- return;
-
- // Make sure the two rows are sorted.
- // NOTE: the order changed because iteration happens over view indices.
- VerifySelectedRows(0, 1, -1);
-}
+ protected:
+ scoped_ptr<TestTableModel2> model_;
-TestTableModel* TableViewTest::CreateModel() {
- return new TestTableModel();
-}
+ // Owned by |parent_|.
+ TableView* table_;
-// NullModelTableViewTest ------------------------------------------------------
+ scoped_ptr<TableViewTestHelper> helper_;
-class NullModelTableViewTest : public TableViewTest {
- protected:
- // Creates the model.
- TestTableModel* CreateModel() {
- return NULL;
- }
-};
+ private:
+ scoped_ptr<View> parent_;
-// GroupModelTableViewTest -----------------------------------------------------
-class GroupModelTableViewTest : public TableViewTest {
- protected:
- TestTableModel* CreateModel() {
- return new GroupTestTableModel();
- }
+ DISALLOW_COPY_AND_ASSIGN(TableViewTest);
};
-// Tests -----------------------------------------------------------------------
-
-// Failing: http://crbug.com/45015
-// Tests various sorting permutations.
-TEST_F(TableViewTest, DISABLED_Sort) {
- // Sort by first column descending.
- TableView::SortDescriptors sort;
- sort.push_back(TableView::SortDescriptor(0, false));
- table_->SetSortDescriptors(sort);
- VerifyViewOrder(2, 1, 0, -1);
- if (HasFatalFailure())
- return;
-
- // Sort by second column ascending, first column descending.
- sort.clear();
- sort.push_back(TableView::SortDescriptor(1, true));
- sort.push_back(TableView::SortDescriptor(0, false));
- sort[1].ascending = false;
- table_->SetSortDescriptors(sort);
- VerifyViewOrder(1, 0, 2, -1);
- if (HasFatalFailure())
- return;
-
- // Clear the sort.
- table_->SetSortDescriptors(TableView::SortDescriptors());
- VerifyViewOrder(0, 1, 2, -1);
- if (HasFatalFailure())
- return;
-}
+// Verifies GetPaintRegion.
+TEST_F(TableViewTest, GetPaintRegion) {
+ // Two columns should be visible.
+ EXPECT_EQ(2u, helper_->visible_col_count());
-// Failing: http://crbug.com/45015
-// Tests changing the model while sorted.
-TEST_F(TableViewTest, DISABLED_SortThenChange) {
- // Sort by first column descending.
- TableView::SortDescriptors sort;
- sort.push_back(TableView::SortDescriptor(0, false));
- table_->SetSortDescriptors(sort);
- VerifyViewOrder(2, 1, 0, -1);
- if (HasFatalFailure())
- return;
-
- model_->ChangeRow(0, 3, 1);
- VerifyViewOrder(0, 2, 1, -1);
+ EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
+ EXPECT_EQ("rows=0 4 cols=0 1",
+ helper_->GetPaintRegion(gfx::Rect(0, 0, 1, table_->height())));
}
-// Failing: http://crbug.com/45015
-// Tests adding to the model while sorted.
-TEST_F(TableViewTest, DISABLED_AddToSorted) {
- // Sort by first column descending.
- TableView::SortDescriptors sort;
- sort.push_back(TableView::SortDescriptor(0, false));
- table_->SetSortDescriptors(sort);
- VerifyViewOrder(2, 1, 0, -1);
- if (HasFatalFailure())
- return;
-
- // Add row so that it occurs first.
- model_->AddRow(0, 5, -1);
- VerifyViewOrder(0, 3, 2, 1, -1);
- if (HasFatalFailure())
- return;
-
- // Add row so that it occurs last.
- model_->AddRow(0, -1, -1);
- VerifyViewOrder(1, 4, 3, 2, 0, -1);
+// Verifies SetColumnVisibility().
+TEST_F(TableViewTest, ColumnVisibility) {
+ // Two columns should be visible.
+ EXPECT_EQ(2u, helper_->visible_col_count());
+
+ // Should do nothing (column already visible).
+ table_->SetColumnVisibility(0, true);
+ EXPECT_EQ(2u, helper_->visible_col_count());
+
+ // Hide the first column.
+ table_->SetColumnVisibility(0, false);
+ ASSERT_EQ(1u, helper_->visible_col_count());
+ EXPECT_EQ(1, table_->visible_columns()[0].column.id);
+ EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
+
+ // Hide the second column.
+ table_->SetColumnVisibility(1, false);
+ EXPECT_EQ(0u, helper_->visible_col_count());
+
+ // Show the second column.
+ table_->SetColumnVisibility(1, true);
+ ASSERT_EQ(1u, helper_->visible_col_count());
+ EXPECT_EQ(1, table_->visible_columns()[0].column.id);
+ EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
+
+ // Show the first column.
+ table_->SetColumnVisibility(0, true);
+ ASSERT_EQ(2u, helper_->visible_col_count());
+ EXPECT_EQ(1, table_->visible_columns()[0].column.id);
+ EXPECT_EQ(0, table_->visible_columns()[1].column.id);
+ EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
}
-// Failing: http://crbug.com/45015
-// Tests selection on sort.
-TEST_F(TableViewTest, DISABLED_PersistSelectionOnSort) {
- // Select row 0.
- table_->Select(0);
-
- // Sort by first column descending.
- TableView::SortDescriptors sort;
- sort.push_back(TableView::SortDescriptor(0, false));
- table_->SetSortDescriptors(sort);
- VerifyViewOrder(2, 1, 0, -1);
- if (HasFatalFailure())
- return;
-
- // Make sure 0 is still selected.
- EXPECT_EQ(0, table_->FirstSelectedRow());
+// Verifies resizing a column works.
+TEST_F(TableViewTest, Resize) {
+ const int x = table_->visible_columns()[0].width;
+ EXPECT_NE(0, x);
+ // Drag the mouse 1 pixel to the left.
+ const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
+ gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON);
+ helper_->header()->OnMousePressed(pressed);
+ const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, gfx::Point(x - 1, 0),
+ gfx::Point(x - 1, 0), ui::EF_LEFT_MOUSE_BUTTON);
+ helper_->header()->OnMouseDragged(dragged);
+
+ // This should shrink the first column and pull the second column in.
+ EXPECT_EQ(x - 1, table_->visible_columns()[0].width);
+ EXPECT_EQ(x - 1, table_->visible_columns()[1].x);
}
-// Failing: http://crbug.com/45015
-// Tests selection iterator with sort.
-TEST_F(TableViewTest, DISABLED_PersistMultiSelectionOnSort) {
- SetUpMultiSelectTestState(true);
+// Assertions for table sorting.
+TEST_F(TableViewTest, Sort) {
+ // Toggle the sort order of the first column, shouldn't change anything.
+ table_->ToggleSortOrder(0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
+ EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
+
+ // Invert the sort (first column descending).
+ table_->ToggleSortOrder(0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("3 2 1 0", GetViewToModelAsString(table_));
+ EXPECT_EQ("3 2 1 0", GetModelToViewAsString(table_));
+
+ // Change cell 0x3 to -1, meaning we have 0, 1, 2, -1 (in the first column).
+ model_->ChangeRow(3, -1, 0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("2 1 0 3", GetViewToModelAsString(table_));
+ EXPECT_EQ("2 1 0 3", GetModelToViewAsString(table_));
+
+ // Invert sort again (first column ascending).
+ table_->ToggleSortOrder(0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("3 0 1 2", GetViewToModelAsString(table_));
+ EXPECT_EQ("1 2 3 0", GetModelToViewAsString(table_));
+
+ // Add a row so that model has 0, 3, 1, 2, -1.
+ model_->AddRow(1, 3, 4);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("4 0 2 3 1", GetViewToModelAsString(table_));
+ EXPECT_EQ("1 4 2 3 0", GetModelToViewAsString(table_));
+
+ // Delete the first row, ending up with 3, 1, 2, -1.
+ model_->RemoveRow(0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("3 1 2 0", GetViewToModelAsString(table_));
+ EXPECT_EQ("3 1 2 0", GetModelToViewAsString(table_));
}
-// Failing: http://crbug.com/45015
-// Tests selection persists after a change when sorted with iterator.
-TEST_F(TableViewTest, DISABLED_PersistMultiSelectionOnChangeWithSort) {
- SetUpMultiSelectTestState(true);
- if (HasFatalFailure())
- return;
-
- model_->ChangeRow(0, 3, 1);
+namespace {
- VerifySelectedRows(1, 0, -1);
-}
-
-// Failing: http://crbug.com/45015
-// Tests selection persists after a remove when sorted with iterator.
-TEST_F(TableViewTest, DISABLED_PersistMultiSelectionOnRemoveWithSort) {
- SetUpMultiSelectTestState(true);
- if (HasFatalFailure())
- return;
+class TableGrouperImpl : public TableGrouper {
+ public:
+ TableGrouperImpl() {}
- model_->RemoveRow(0);
+ void SetRanges(const std::vector<int>& ranges) {
+ ranges_ = ranges;
+ }
- VerifySelectedRows(0, -1);
-}
+ // TableGrouper overrides:
+ virtual void GetGroupRange(int model_index, GroupRange* range) OVERRIDE {
+ int offset = 0;
+ size_t range_index = 0;
+ for (; range_index < ranges_.size() && offset < model_index; ++range_index)
+ offset += ranges_[range_index];
+
+ if (offset == model_index) {
+ range->start = model_index;
+ range->length = ranges_[range_index];
+ } else {
+ range->start = offset - ranges_[range_index - 1];
+ range->length = ranges_[range_index - 1];
+ }
+ }
-// Failing: http://crbug.com/45015
-// Tests selection persists after a add when sorted with iterator.
-TEST_F(TableViewTest, DISABLED_PersistMultiSelectionOnAddWithSort) {
- SetUpMultiSelectTestState(true);
- if (HasFatalFailure())
- return;
+ private:
+ std::vector<int> ranges_;
- model_->AddRow(3, 4, 4);
+ DISALLOW_COPY_AND_ASSIGN(TableGrouperImpl);
+};
- VerifySelectedRows(0, 1, -1);
+} // namespace
+
+// Assertions around grouping.
+TEST_F(TableViewTest, Grouping) {
+ // Configure the grouper so that there are two groups:
+ // A 0
+ // 1
+ // B 2
+ // 3
+ TableGrouperImpl grouper;
+ std::vector<int> ranges;
+ ranges.push_back(2);
+ ranges.push_back(2);
+ grouper.SetRanges(ranges);
+ table_->SetGrouper(&grouper);
+
+ // Toggle the sort order of the first column, shouldn't change anything.
+ table_->ToggleSortOrder(0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
+ EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
+
+ // Sort descending, resulting:
+ // B 2
+ // 3
+ // A 0
+ // 1
+ table_->ToggleSortOrder(0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
+ EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
+
+ // Change the entry in the 4th row to -1. The model now becomes:
+ // A 0
+ // 1
+ // B 2
+ // -1
+ // Since the first entry in the range didn't change the sort isn't impacted.
+ model_->ChangeRow(3, -1, 0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
+ EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
+
+ // Change the entry in the 3rd row to -1. The model now becomes:
+ // A 0
+ // 1
+ // B -1
+ // -1
+ model_->ChangeRow(2, -1, 0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
+ EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
+
+ // Toggle to ascending sort.
+ table_->ToggleSortOrder(0);
+ ASSERT_EQ(1u, table_->sort_descriptors().size());
+ EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
+ EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
+ EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
+ EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
}
-// Failing: http://crbug.com/45015
-// Tests selection persists after a change with iterator.
-TEST_F(TableViewTest, DISABLED_PersistMultiSelectionOnChange) {
- SetUpMultiSelectTestState(false);
- if (HasFatalFailure())
- return;
-
- model_->ChangeRow(0, 3, 1);
+namespace {
- VerifySelectedRows(1, 0, -1);
-}
-
-// Failing: http://crbug.com/45015
-// Tests selection persists after a remove with iterator.
-TEST_F(TableViewTest, DISABLED_PersistMultiSelectionOnRemove) {
- SetUpMultiSelectTestState(false);
- if (HasFatalFailure())
- return;
+class TableViewObserverImpl : public TableViewObserver {
+ public:
+ TableViewObserverImpl() : selection_changed_count_(0) {}
- model_->RemoveRow(0);
+ int GetChangedCountAndClear() {
+ const int count = selection_changed_count_;
+ selection_changed_count_ = 0;
+ return count;
+ }
- VerifySelectedRows(0, -1);
-}
+ // TableViewObserver overrides:
+ virtual void OnSelectionChanged() OVERRIDE {
+ selection_changed_count_++;
+ }
-// Failing: http://crbug.com/45015
-// Tests selection persists after a add with iterator.
-TEST_F(TableViewTest, DISABLED_PersistMultiSelectionOnAdd) {
- SetUpMultiSelectTestState(false);
- if (HasFatalFailure())
- return;
+ private:
+ int selection_changed_count_;
- model_->AddRow(3, 4, 4);
+ DISALLOW_COPY_AND_ASSIGN(TableViewObserverImpl);
+};
- VerifySelectedRows(1, 0, -1);
+} // namespace
+
+// Assertions around changing the selection.
+TEST_F(TableViewTest, Selection) {
+ TableViewObserverImpl observer;
+ table_->SetObserver(&observer);
+
+ // Initially no selection.
+ EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+
+ // Select the last row.
+ table_->Select(3);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
+
+ // Change sort, shouldn't notify of change (toggle twice so that order
+ // actually changes).
+ table_->ToggleSortOrder(0);
+ table_->ToggleSortOrder(0);
+ EXPECT_EQ(0, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
+
+ // Remove the selected row, this should notify of a change and update the
+ // selection.
+ model_->RemoveRow(3);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
+
+ // Insert a row, since the selection in terms of the original model hasn't
+ // changed the observer is not notified.
+ model_->AddRow(0, 1, 2);
+ EXPECT_EQ(0, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
+
+ table_->SetObserver(NULL);
}
-TEST_F(GroupModelTableViewTest, IndividualSelectAcrossGroups) {
- table_->SetSelectedState(0, true);
- table_->SetSelectedState(1, true);
- table_->SetSelectedState(2, true);
- VerifySelectedRows(2, 1, 0, -1);
+// Verifies up/down correctly navigates through groups.
+TEST_F(TableViewTest, KeyUpDown) {
+ // Configure the grouper so that there are three groups:
+ // A 0
+ // 1
+ // B 5
+ // C 2
+ // 3
+ model_->AddRow(2, 5, 0);
+ TableGrouperImpl grouper;
+ std::vector<int> ranges;
+ ranges.push_back(2);
+ ranges.push_back(1);
+ ranges.push_back(2);
+ grouper.SetRanges(ranges);
+ table_->SetGrouper(&grouper);
+
+ TableViewObserverImpl observer;
+ table_->SetObserver(&observer);
+
+ // Initially no selection.
+ EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+
+ PressKey(ui::VKEY_DOWN);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
+
+ PressKey(ui::VKEY_DOWN);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
+
+ PressKey(ui::VKEY_DOWN);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
+
+ PressKey(ui::VKEY_DOWN);
+ EXPECT_EQ(0, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
+
+ PressKey(ui::VKEY_UP);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
+
+ PressKey(ui::VKEY_UP);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
+
+ PressKey(ui::VKEY_UP);
+ EXPECT_EQ(0, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
+
+ // Sort the table descending by column 1, view now looks like:
+ // B 5 model: 2
+ // C 2 3
+ // 3 4
+ // A 0 0
+ // 1 1
+ table_->ToggleSortOrder(0);
+ table_->ToggleSortOrder(0);
+
+ EXPECT_EQ("2 3 4 0 1", GetViewToModelAsString(table_));
+
+ table_->Select(-1);
+ EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+
+ observer.GetChangedCountAndClear();
+ // Up with nothing selected selects the first row.
+ PressKey(ui::VKEY_UP);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
+
+ PressKey(ui::VKEY_DOWN);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
+
+ PressKey(ui::VKEY_DOWN);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
+
+ PressKey(ui::VKEY_DOWN);
+ EXPECT_EQ(0, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
+
+ PressKey(ui::VKEY_UP);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
+
+ PressKey(ui::VKEY_UP);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
+
+ PressKey(ui::VKEY_UP);
+ EXPECT_EQ(0, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
+
+ table_->SetObserver(NULL);
}
-TEST_F(GroupModelTableViewTest, ShiftSelectAcrossGroups) {
- table_->SetSelectedState(0, true);
- // Try to select across groups - this should fail.
- ASSERT_FALSE(table_->SelectMultiple(1, 0));
- VerifySelectedRows(0, -1);
+// Verifies home/end do the right thing.
+TEST_F(TableViewTest, HomeEnd) {
+ // Configure the grouper so that there are three groups:
+ // A 0
+ // 1
+ // B 5
+ // C 2
+ // 3
+ model_->AddRow(2, 5, 0);
+ TableGrouperImpl grouper;
+ std::vector<int> ranges;
+ ranges.push_back(2);
+ ranges.push_back(1);
+ ranges.push_back(2);
+ grouper.SetRanges(ranges);
+ table_->SetGrouper(&grouper);
+
+ TableViewObserverImpl observer;
+ table_->SetObserver(&observer);
+
+ // Initially no selection.
+ EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+
+ PressKey(ui::VKEY_HOME);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
+
+ PressKey(ui::VKEY_END);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
+
+ table_->SetObserver(NULL);
}
-TEST_F(GroupModelTableViewTest, ShiftSelectSameGroup) {
- table_->SetSelectedState(0, true);
- // Try to select in the same group - this should work but should only select
- // items in the "even" group.
- ASSERT_TRUE(table_->SelectMultiple(2, 0));
- VerifySelectedRows(2, 0, -1);
+// Verifies multiple selection gestures work (control-click, shift-click ...).
+TEST_F(TableViewTest, Multiselection) {
+ // Configure the grouper so that there are three groups:
+ // A 0
+ // 1
+ // B 5
+ // C 2
+ // 3
+ model_->AddRow(2, 5, 0);
+ TableGrouperImpl grouper;
+ std::vector<int> ranges;
+ ranges.push_back(2);
+ ranges.push_back(1);
+ ranges.push_back(2);
+ grouper.SetRanges(ranges);
+ table_->SetGrouper(&grouper);
+
+ // Initially no selection.
+ EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+
+ TableViewObserverImpl observer;
+ table_->SetObserver(&observer);
+
+ // Click on the first row, should select it and the second row.
+ ClickOnRow(0, 0);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
+
+ // Click on the last row, should select it and the row before it.
+ ClickOnRow(4, 0);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
+
+ // Shift click on the third row, should extend selection to it.
+ ClickOnRow(2, ui::EF_SHIFT_DOWN);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=2 anchor=3 selection=2 3 4", SelectionStateAsString());
+
+ // Control click on third row, should toggle it.
+ ClickOnRow(2, ui::EF_CONTROL_DOWN);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=2 anchor=2 selection=3 4", SelectionStateAsString());
+
+ // Control-shift click on second row, should extend selection to it.
+ ClickOnRow(1, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=1 anchor=2 selection=0 1 2 3 4", SelectionStateAsString());
+
+ // Click on last row again.
+ ClickOnRow(4, 0);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
+
+ table_->SetObserver(NULL);
}
-// Crashing: http://crbug.com/45015
-TEST_F(NullModelTableViewTest, DISABLED_NullModel) {
- // There's nothing explicit to test. If there is a bug in TableView relating
- // to a NULL model we'll crash.
+// Verifies multiple selection gestures work when sorted.
+TEST_F(TableViewTest, MultiselectionWithSort) {
+ // Configure the grouper so that there are three groups:
+ // A 0
+ // 1
+ // B 5
+ // C 2
+ // 3
+ model_->AddRow(2, 5, 0);
+ TableGrouperImpl grouper;
+ std::vector<int> ranges;
+ ranges.push_back(2);
+ ranges.push_back(1);
+ ranges.push_back(2);
+ grouper.SetRanges(ranges);
+ table_->SetGrouper(&grouper);
+
+ // Sort the table descending by column 1, view now looks like:
+ // B 5 model: 2
+ // C 2 3
+ // 3 4
+ // A 0 0
+ // 1 1
+ table_->ToggleSortOrder(0);
+ table_->ToggleSortOrder(0);
+
+ // Initially no selection.
+ EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+
+ TableViewObserverImpl observer;
+ table_->SetObserver(&observer);
+
+ // Click on the third row, should select it and the second row.
+ ClickOnRow(2, 0);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
+
+ // Extend selection to first row.
+ ClickOnRow(0, ui::EF_SHIFT_DOWN);
+ EXPECT_EQ(1, observer.GetChangedCountAndClear());
+ EXPECT_EQ("active=2 anchor=3 selection=2 3 4", SelectionStateAsString());
+
+ table_->SetObserver(NULL);
}
} // namespace views
diff --git a/ui/views/controls/table/table_view_views.h b/ui/views/controls/table/table_view_views.h
deleted file mode 100644
index 5ce25f0..0000000
--- a/ui/views/controls/table/table_view_views.h
+++ /dev/null
@@ -1,361 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_VIEWS_H_
-#define UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_VIEWS_H_
-
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "ui/base/models/list_selection_model.h"
-#include "ui/base/models/table_model.h"
-#include "ui/base/models/table_model_observer.h"
-#include "ui/gfx/font.h"
-#include "ui/views/view.h"
-#include "ui/views/views_export.h"
-
-// A TableView is a view that displays multiple rows with any number of columns.
-// TableView is driven by a TableModel. The model returns the contents
-// to display. TableModel also has an Observer which is used to notify
-// TableView of changes to the model so that the display may be updated
-// appropriately.
-//
-// TableView itself has an observer that is notified when the selection
-// changes.
-//
-// When a table is sorted the model coordinates do not necessarily match the
-// view coordinates. All table methods are in terms of the model. If you need to
-// convert to view coordinates use ModelToView().
-//
-// Sorting is done by a locale sensitive string sort. You can customize the
-// sort by way of overriding TableModel::CompareValues().
-namespace views {
-
-struct GroupRange;
-class TableGrouper;
-class TableHeader;
-class TableViewObserver;
-class TableViewRowBackgroundPainter;
-class TableViewTestHelper;
-
-// The cells in the first column of a table can contain:
-// - only text
-// - a small icon (16x16) and some text
-// - a check box and some text
-enum TableTypes {
- TEXT_ONLY = 0,
- ICON_AND_TEXT,
-};
-
-class VIEWS_EXPORT TableView
- : public views::View,
- public ui::TableModelObserver {
- public:
- // Used to track a visible column. Useful only for the header.
- struct VIEWS_EXPORT VisibleColumn {
- VisibleColumn();
- ~VisibleColumn();
-
- // The column.
- ui::TableColumn column;
-
- // Starting x-coordinate of the column.
- int x;
-
- // Width of the column.
- int width;
- };
-
- // Describes a sorted column.
- struct VIEWS_EXPORT SortDescriptor {
- SortDescriptor() : column_id(-1), ascending(true) {}
- SortDescriptor(int column_id, bool ascending)
- : column_id(column_id),
- ascending(ascending) {}
-
- // ID of the sorted column.
- int column_id;
-
- // Is the sort ascending?
- bool ascending;
- };
-
- typedef std::vector<SortDescriptor> SortDescriptors;
-
- // Creates a new table using the model and columns specified.
- // The table type applies to the content of the first column (text, icon and
- // text, checkbox and text).
- // When autosize_columns is true, columns always fill the available width. If
- // false, columns are not resized when the table is resized. An extra empty
- // column at the right fills the remaining space.
- // When resizable_columns is true, users can resize columns by dragging the
- // separator on the column header. NOTE: Right now this is always true. The
- // code to set it false is still in place to be a base for future, better
- // resizing behavior (see http://b/issue?id=874646 ), but no one uses or
- // tests the case where this flag is false.
- // Note that setting both resizable_columns and autosize_columns to false is
- // probably not a good idea, as there is no way for the user to increase a
- // column's size in that case.
- TableView(ui::TableModel* model,
- const std::vector<ui::TableColumn>& columns,
- TableTypes table_type,
- bool single_selection,
- bool resizable_columns,
- bool autosize_columns);
- virtual ~TableView();
-
- // Assigns a new model to the table view, detaching the old one if present.
- // If |model| is NULL, the table view cannot be used after this call. This
- // should be called in the containing view's destructor to avoid destruction
- // issues when the model needs to be deleted before the table.
- void SetModel(ui::TableModel* model);
- ui::TableModel* model() const { return model_; }
-
- // Returns a new ScrollView that contains the receiver.
- View* CreateParentIfNecessary();
-
- void SetRowBackgroundPainter(
- scoped_ptr<TableViewRowBackgroundPainter> painter);
-
- // Sets the TableGrouper. TableView does not own |grouper| (common use case is
- // to have TableModel implement TableGrouper).
- void SetGrouper(TableGrouper* grouper);
-
- // Returns the number of rows in the TableView.
- int RowCount() const;
-
- // Returns the number of selected rows.
- // TODO(sky): remove this and force callers to use selection_model().
- int SelectedRowCount();
-
- // Selects the specified item, making sure it's visible.
- void Select(int model_row);
-
- // Returns the first selected row in terms of the model.
- int FirstSelectedRow();
-
- const ui::ListSelectionModel& selection_model() const {
- return selection_model_;
- }
-
- // Changes the visibility of the specified column (by id).
- void SetColumnVisibility(int id, bool is_visible);
- bool IsColumnVisible(int id) const;
-
- // Adds the specified column. |col| is not made visible.
- void AddColumn(const ui::TableColumn& col);
-
- // Returns true if the column with the specified id is known (either visible
- // or not).
- bool HasColumn(int id) const;
-
- // TODO(sky): rename to set_observer().
- void SetObserver(TableViewObserver* observer) {
- table_view_observer_ = observer;
- }
- TableViewObserver* observer() const { return table_view_observer_; }
-
- const std::vector<VisibleColumn>& visible_columns() const {
- return visible_columns_;
- }
-
- // Sets the width of the column. |index| is in terms of |visible_columns_|.
- void SetVisibleColumnWidth(int index, int width);
-
- // Toggles the sort order of the specified visible column index.
- void ToggleSortOrder(int visible_column_index);
-
- const SortDescriptors& sort_descriptors() const { return sort_descriptors_; }
- bool is_sorted() const { return !sort_descriptors_.empty(); }
-
- // Maps from the index in terms of the model to that of the view.
- int ModelToView(int model_index) const;
-
- // Maps from the index in terms of the view to that of the model.
- int ViewToModel(int view_index) const;
-
- int row_height() const { return row_height_; }
-
- // View overrides:
- virtual void Layout() OVERRIDE;
- virtual gfx::Size GetPreferredSize() OVERRIDE;
- virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
- virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
- virtual bool GetTooltipText(const gfx::Point& p,
- string16* tooltip) const OVERRIDE;
- virtual bool GetTooltipTextOrigin(const gfx::Point& p,
- gfx::Point* loc) const OVERRIDE;
-
- // ui::TableModelObserver overrides:
- virtual void OnModelChanged() OVERRIDE;
- virtual void OnItemsChanged(int start, int length) OVERRIDE;
- virtual void OnItemsAdded(int start, int length) OVERRIDE;
- virtual void OnItemsRemoved(int start, int length) OVERRIDE;
-
- protected:
- // View overrides:
- virtual gfx::Point GetKeyboardContextMenuLocation() OVERRIDE;
- virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
- virtual void OnFocus() OVERRIDE;
- virtual void OnBlur() OVERRIDE;
-
- private:
- friend class TableViewTestHelper;
- struct GroupSortHelper;
- struct SortHelper;
-
- // Used during painting to determine the range of cells that need to be
- // painted.
- // NOTE: the row indices returned by this are in terms of the view and column
- // indices in terms of |visible_columns_|.
- struct VIEWS_EXPORT PaintRegion {
- PaintRegion();
- ~PaintRegion();
-
- int min_row;
- int max_row;
- int min_column;
- int max_column;
- };
-
- // Used by AdvanceSelection() to determine the direction to change the
- // selection.
- enum AdvanceDirection {
- ADVANCE_DECREMENT,
- ADVANCE_INCREMENT,
- };
-
- // Invoked when the number of rows changes in some way.
- void NumRowsChanged();
-
- // Resets the sort descriptions.
- void SetSortDescriptors(const SortDescriptors& sort_descriptors);
-
- // Does the actual sort and updates the mappings (|view_to_model_| and
- // |model_to_view_|) appropriately.
- void SortItemsAndUpdateMapping();
-
- // Used to sort the two rows. Returns a value < 0, == 0 or > 0 indicating
- // whether the row2 comes before row1, row2 is the same as row1 or row1 comes
- // after row2. This invokes CompareValues on the model with the sorted column.
- int CompareRows(int model_row1, int model_row2);
-
- // Returns the bounds of the specified row.
- gfx::Rect GetRowBounds(int row) const;
-
- // Returns the bounds of the specified cell. |visible_column_index| indexes
- // into |visible_columns_|.
- gfx::Rect GetCellBounds(int row, int visible_column_index) const;
-
- // Adjusts |bounds| based on where the text should be painted. |bounds| comes
- // from GetCellBounds() and |visible_column_index| is the corresponding column
- // (in terms of |visible_columns_|).
- void AdjustCellBoundsForText(int visible_column_index,
- gfx::Rect* bounds) const;
-
- // Creates |header_| if necessary.
- void CreateHeaderIfNecessary();
-
- // Updates the |x| and |width| of each of the columns in |visible_columns_|.
- void UpdateVisibleColumnSizes();
-
- // Returns the cells that need to be painted for the specified region.
- // |bounds| is in terms of |this|.
- PaintRegion GetPaintRegion(const gfx::Rect& bounds) const;
-
- // Returns the bounds that need to be painted based on the clip set on
- // |canvas|.
- gfx::Rect GetPaintBounds(gfx::Canvas* canvas) const;
-
- // Invokes SchedulePaint() for the selected rows.
- void SchedulePaintForSelection();
-
- // Returns the TableColumn matching the specified id.
- ui::TableColumn FindColumnByID(int id) const;
-
- // Sets the selection to the specified index (in terms of the view).
- void SelectByViewIndex(int view_index);
-
- // Sets the selection model to |new_selection|.
- void SetSelectionModel(const ui::ListSelectionModel& new_selection);
-
- // Advances the selection (from the active index) in the specified direction.
- void AdvanceSelection(AdvanceDirection direction);
-
- // Sets |model| appropriately based on a mouse event.
- void ConfigureSelectionModelForEvent(const ui::MouseEvent& event,
- ui::ListSelectionModel* model) const;
-
- // Set the selection state of row at |view_index| to |select|, additionally
- // any other rows in the GroupRange containing |view_index| are updated as
- // well. This does not change the anchor or active index of |model|.
- void SelectRowsInRangeFrom(int view_index,
- bool select,
- ui::ListSelectionModel* model) const;
-
- // Returns the range of the specified model index. If a TableGrouper has not
- // been set this returns a group with a start of |model_index| and length of
- // 1.
- GroupRange GetGroupRange(int model_index) const;
-
- // Used by both GetTooltipText methods. Returns true if there is a tooltip and
- // sets |tooltip| and/or |tooltip_origin| as appropriate, each of which may be
- // NULL.
- bool GetTooltipImpl(const gfx::Point& location,
- string16* tooltip,
- gfx::Point* tooltip_origin) const;
-
- ui::TableModel* model_;
-
- std::vector<ui::TableColumn> columns_;
-
- // The set of visible columns. The values of these point to |columns_|. This
- // may contain a subset of |columns_|.
- std::vector<VisibleColumn> visible_columns_;
-
- // The header. This is only created if more than one column is specified or
- // the first column has a non-empty title.
- TableHeader* header_;
-
- const TableTypes table_type_;
-
- const bool single_selection_;
-
- // TODO(sky): rename to observer_.
- TableViewObserver* table_view_observer_;
-
- // The selection, in terms of the model.
- ui::ListSelectionModel selection_model_;
-
- gfx::Font font_;
-
- int row_height_;
-
- // Width of the ScrollView last time Layout() was invoked. Used to determine
- // when we should invoke UpdateVisibleColumnSizes().
- int last_parent_width_;
-
- // The width we layout to. This may differ from |last_parent_width_|.
- int layout_width_;
-
- // Current sort.
- SortDescriptors sort_descriptors_;
-
- // Mappings used when sorted.
- std::vector<int> view_to_model_;
- std::vector<int> model_to_view_;
-
- scoped_ptr<TableViewRowBackgroundPainter> row_background_painter_;
-
- TableGrouper* grouper_;
-
- // True if in SetVisibleColumnWidth().
- bool in_set_visible_column_width_;
-
- DISALLOW_COPY_AND_ASSIGN(TableView);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_VIEWS_H_
diff --git a/ui/views/controls/table/table_view_views_unittest.cc b/ui/views/controls/table/table_view_views_unittest.cc
deleted file mode 100644
index 6e545a3..0000000
--- a/ui/views/controls/table/table_view_views_unittest.cc
+++ /dev/null
@@ -1,741 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/controls/table/table_view_views.h"
-
-#include "base/string_number_conversions.h"
-#include "base/utf_string_conversions.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/views/controls/table/group_table_model.h"
-#include "ui/views/controls/table/table_grouper.h"
-#include "ui/views/controls/table/table_header.h"
-#include "ui/views/controls/table/table_view_observer.h"
-
-// Put the tests in the views namespace to make it easier to declare them as
-// friend classes.
-namespace views {
-
-class TableViewTestHelper {
- public:
- explicit TableViewTestHelper(TableView* table) : table_(table) {}
-
- std::string GetPaintRegion(const gfx::Rect& bounds) {
- TableView::PaintRegion region(table_->GetPaintRegion(bounds));
- return "rows=" + base::IntToString(region.min_row) + " " +
- base::IntToString(region.max_row) + " cols=" +
- base::IntToString(region.min_column) + " " +
- base::IntToString(region.max_column);
- }
-
- size_t visible_col_count() {
- return table_->visible_columns().size();
- }
-
- TableHeader* header() { return table_->header_; }
-
- private:
- TableView* table_;
-
- DISALLOW_COPY_AND_ASSIGN(TableViewTestHelper);
-};
-
-namespace {
-
-// TestTableModel2 -------------------------------------------------------------
-
-// Trivial TableModel implementation that is backed by a vector of vectors.
-// Provides methods for adding/removing/changing the contents that notify the
-// observer appropriately.
-//
-// Initial contents are:
-// 0, 1
-// 1, 1
-// 2, 2
-// 3, 0
-class TestTableModel2 : public ui::TableModel {
- public:
- TestTableModel2();
-
- // Adds a new row at index |row| with values |c1_value| and |c2_value|.
- void AddRow(int row, int c1_value, int c2_value);
-
- // Removes the row at index |row|.
- void RemoveRow(int row);
-
- // Changes the values of the row at |row|.
- void ChangeRow(int row, int c1_value, int c2_value);
-
- // ui::TableModel:
- virtual int RowCount() OVERRIDE;
- virtual string16 GetText(int row, int column_id) OVERRIDE;
- virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE;
- virtual int CompareValues(int row1, int row2, int column_id) OVERRIDE;
-
- private:
- ui::TableModelObserver* observer_;
-
- // The data.
- std::vector<std::vector<int> > rows_;
-
- DISALLOW_COPY_AND_ASSIGN(TestTableModel2);
-};
-
-TestTableModel2::TestTableModel2() : observer_(NULL) {
- AddRow(0, 0, 1);
- AddRow(1, 1, 1);
- AddRow(2, 2, 2);
- AddRow(3, 3, 0);
-}
-
-void TestTableModel2::AddRow(int row, int c1_value, int c2_value) {
- DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
- std::vector<int> new_row;
- new_row.push_back(c1_value);
- new_row.push_back(c2_value);
- rows_.insert(rows_.begin() + row, new_row);
- if (observer_)
- observer_->OnItemsAdded(row, 1);
-}
-void TestTableModel2::RemoveRow(int row) {
- DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
- rows_.erase(rows_.begin() + row);
- if (observer_)
- observer_->OnItemsRemoved(row, 1);
-}
-
-void TestTableModel2::ChangeRow(int row, int c1_value, int c2_value) {
- DCHECK(row >= 0 && row < static_cast<int>(rows_.size()));
- rows_[row][0] = c1_value;
- rows_[row][1] = c2_value;
- if (observer_)
- observer_->OnItemsChanged(row, 1);
-}
-
-int TestTableModel2::RowCount() {
- return static_cast<int>(rows_.size());
-}
-
-string16 TestTableModel2::GetText(int row, int column_id) {
- return base::IntToString16(rows_[row][column_id]);
-}
-
-void TestTableModel2::SetObserver(ui::TableModelObserver* observer) {
- observer_ = observer;
-}
-
-int TestTableModel2::CompareValues(int row1, int row2, int column_id) {
- return rows_[row1][column_id] - rows_[row2][column_id];
-}
-
-// Returns the view to model mapping as a string.
-std::string GetViewToModelAsString(TableView* table) {
- std::string result;
- for (int i = 0; i < table->RowCount(); ++i) {
- if (i != 0)
- result += " ";
- result += base::IntToString(table->ViewToModel(i));
- }
- return result;
-}
-
-// Returns the model to view mapping as a string.
-std::string GetModelToViewAsString(TableView* table) {
- std::string result;
- for (int i = 0; i < table->RowCount(); ++i) {
- if (i != 0)
- result += " ";
- result += base::IntToString(table->ModelToView(i));
- }
- return result;
-}
-
-class TestTableView : public TableView {
- public:
- TestTableView(ui::TableModel* model,
- const std::vector<ui::TableColumn>& columns)
- : TableView(model, columns, TEXT_ONLY, false, true, true) {
- }
-
- // View overrides:
- virtual bool HasFocus() const {
- // Overriden so key processing works.
- return true;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestTableView);
-};
-
-} // namespace
-
-class TableViewTest : public testing::Test {
- public:
- TableViewTest() : table_(NULL) {}
-
- virtual void SetUp() OVERRIDE {
- model_.reset(new TestTableModel2);
- std::vector<ui::TableColumn> columns(2);
- columns[0].title = ASCIIToUTF16("Title Column 0");
- columns[0].sortable = true;
- columns[1].title = ASCIIToUTF16("Title Column 1");
- columns[1].id = 1;
- columns[1].sortable = true;
- table_ = new TestTableView(model_.get(), columns);
- parent_.reset(table_->CreateParentIfNecessary());
- parent_->SetBounds(0, 0, 10000, 10000);
- parent_->Layout();
- helper_.reset(new TableViewTestHelper(table_));
- }
-
- void ClickOnRow(int row, int flags) {
- const int y = row * table_->row_height();
- const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, y),
- gfx::Point(0, y),
- ui::EF_LEFT_MOUSE_BUTTON | flags);
- table_->OnMousePressed(pressed);
- }
-
- // Returns the state of the selection model as a string. The format is:
- // 'active=X anchor=X selection=X X X...'.
- std::string SelectionStateAsString() const {
- const ui::ListSelectionModel& model(table_->selection_model());
- std::string result = "active=" + base::IntToString(model.active()) +
- " anchor=" + base::IntToString(model.anchor()) +
- " selection=";
- const ui::ListSelectionModel::SelectedIndices& selection(
- model.selected_indices());
- for (size_t i = 0; i < selection.size(); ++i) {
- if (i != 0)
- result += " ";
- result += base::IntToString(selection[i]);
- }
- return result;
- }
-
- void PressKey(ui::KeyboardCode code) {
- ui::KeyEvent event(ui::ET_KEY_PRESSED, code, 0, false);
- table_->OnKeyPressed(event);
- }
-
- protected:
- scoped_ptr<TestTableModel2> model_;
-
- // Owned by |parent_|.
- TableView* table_;
-
- scoped_ptr<TableViewTestHelper> helper_;
-
- private:
- scoped_ptr<View> parent_;
-
- DISALLOW_COPY_AND_ASSIGN(TableViewTest);
-};
-
-// Verifies GetPaintRegion.
-TEST_F(TableViewTest, GetPaintRegion) {
- // Two columns should be visible.
- EXPECT_EQ(2u, helper_->visible_col_count());
-
- EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
- EXPECT_EQ("rows=0 4 cols=0 1",
- helper_->GetPaintRegion(gfx::Rect(0, 0, 1, table_->height())));
-}
-
-// Verifies SetColumnVisibility().
-TEST_F(TableViewTest, ColumnVisibility) {
- // Two columns should be visible.
- EXPECT_EQ(2u, helper_->visible_col_count());
-
- // Should do nothing (column already visible).
- table_->SetColumnVisibility(0, true);
- EXPECT_EQ(2u, helper_->visible_col_count());
-
- // Hide the first column.
- table_->SetColumnVisibility(0, false);
- ASSERT_EQ(1u, helper_->visible_col_count());
- EXPECT_EQ(1, table_->visible_columns()[0].column.id);
- EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
-
- // Hide the second column.
- table_->SetColumnVisibility(1, false);
- EXPECT_EQ(0u, helper_->visible_col_count());
-
- // Show the second column.
- table_->SetColumnVisibility(1, true);
- ASSERT_EQ(1u, helper_->visible_col_count());
- EXPECT_EQ(1, table_->visible_columns()[0].column.id);
- EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
-
- // Show the first column.
- table_->SetColumnVisibility(0, true);
- ASSERT_EQ(2u, helper_->visible_col_count());
- EXPECT_EQ(1, table_->visible_columns()[0].column.id);
- EXPECT_EQ(0, table_->visible_columns()[1].column.id);
- EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
-}
-
-// Verifies resizing a column works.
-TEST_F(TableViewTest, Resize) {
- const int x = table_->visible_columns()[0].width;
- EXPECT_NE(0, x);
- // Drag the mouse 1 pixel to the left.
- const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
- gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON);
- helper_->header()->OnMousePressed(pressed);
- const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, gfx::Point(x - 1, 0),
- gfx::Point(x - 1, 0), ui::EF_LEFT_MOUSE_BUTTON);
- helper_->header()->OnMouseDragged(dragged);
-
- // This should shrink the first column and pull the second column in.
- EXPECT_EQ(x - 1, table_->visible_columns()[0].width);
- EXPECT_EQ(x - 1, table_->visible_columns()[1].x);
-}
-
-// Assertions for table sorting.
-TEST_F(TableViewTest, Sort) {
- // Toggle the sort order of the first column, shouldn't change anything.
- table_->ToggleSortOrder(0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
- EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
-
- // Invert the sort (first column descending).
- table_->ToggleSortOrder(0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("3 2 1 0", GetViewToModelAsString(table_));
- EXPECT_EQ("3 2 1 0", GetModelToViewAsString(table_));
-
- // Change cell 0x3 to -1, meaning we have 0, 1, 2, -1 (in the first column).
- model_->ChangeRow(3, -1, 0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("2 1 0 3", GetViewToModelAsString(table_));
- EXPECT_EQ("2 1 0 3", GetModelToViewAsString(table_));
-
- // Invert sort again (first column ascending).
- table_->ToggleSortOrder(0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("3 0 1 2", GetViewToModelAsString(table_));
- EXPECT_EQ("1 2 3 0", GetModelToViewAsString(table_));
-
- // Add a row so that model has 0, 3, 1, 2, -1.
- model_->AddRow(1, 3, 4);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("4 0 2 3 1", GetViewToModelAsString(table_));
- EXPECT_EQ("1 4 2 3 0", GetModelToViewAsString(table_));
-
- // Delete the first row, ending up with 3, 1, 2, -1.
- model_->RemoveRow(0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("3 1 2 0", GetViewToModelAsString(table_));
- EXPECT_EQ("3 1 2 0", GetModelToViewAsString(table_));
-}
-
-namespace {
-
-class TableGrouperImpl : public TableGrouper {
- public:
- TableGrouperImpl() {}
-
- void SetRanges(const std::vector<int>& ranges) {
- ranges_ = ranges;
- }
-
- // TableGrouper overrides:
- virtual void GetGroupRange(int model_index, GroupRange* range) OVERRIDE {
- int offset = 0;
- size_t range_index = 0;
- for (; range_index < ranges_.size() && offset < model_index; ++range_index)
- offset += ranges_[range_index];
-
- if (offset == model_index) {
- range->start = model_index;
- range->length = ranges_[range_index];
- } else {
- range->start = offset - ranges_[range_index - 1];
- range->length = ranges_[range_index - 1];
- }
- }
-
- private:
- std::vector<int> ranges_;
-
- DISALLOW_COPY_AND_ASSIGN(TableGrouperImpl);
-};
-
-} // namespace
-
-// Assertions around grouping.
-TEST_F(TableViewTest, Grouping) {
- // Configure the grouper so that there are two groups:
- // A 0
- // 1
- // B 2
- // 3
- TableGrouperImpl grouper;
- std::vector<int> ranges;
- ranges.push_back(2);
- ranges.push_back(2);
- grouper.SetRanges(ranges);
- table_->SetGrouper(&grouper);
-
- // Toggle the sort order of the first column, shouldn't change anything.
- table_->ToggleSortOrder(0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
- EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
-
- // Sort descending, resulting:
- // B 2
- // 3
- // A 0
- // 1
- table_->ToggleSortOrder(0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
- EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
-
- // Change the entry in the 4th row to -1. The model now becomes:
- // A 0
- // 1
- // B 2
- // -1
- // Since the first entry in the range didn't change the sort isn't impacted.
- model_->ChangeRow(3, -1, 0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
- EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
-
- // Change the entry in the 3rd row to -1. The model now becomes:
- // A 0
- // 1
- // B -1
- // -1
- model_->ChangeRow(2, -1, 0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
- EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
-
- // Toggle to ascending sort.
- table_->ToggleSortOrder(0);
- ASSERT_EQ(1u, table_->sort_descriptors().size());
- EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
- EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
- EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
- EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
-}
-
-namespace {
-
-class TableViewObserverImpl : public TableViewObserver {
- public:
- TableViewObserverImpl() : selection_changed_count_(0) {}
-
- int GetChangedCountAndClear() {
- const int count = selection_changed_count_;
- selection_changed_count_ = 0;
- return count;
- }
-
- // TableViewObserver overrides:
- virtual void OnSelectionChanged() OVERRIDE {
- selection_changed_count_++;
- }
-
- private:
- int selection_changed_count_;
-
- DISALLOW_COPY_AND_ASSIGN(TableViewObserverImpl);
-};
-
-} // namespace
-
-// Assertions around changing the selection.
-TEST_F(TableViewTest, Selection) {
- TableViewObserverImpl observer;
- table_->SetObserver(&observer);
-
- // Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
-
- // Select the last row.
- table_->Select(3);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
-
- // Change sort, shouldn't notify of change (toggle twice so that order
- // actually changes).
- table_->ToggleSortOrder(0);
- table_->ToggleSortOrder(0);
- EXPECT_EQ(0, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
-
- // Remove the selected row, this should notify of a change and update the
- // selection.
- model_->RemoveRow(3);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
-
- // Insert a row, since the selection in terms of the original model hasn't
- // changed the observer is not notified.
- model_->AddRow(0, 1, 2);
- EXPECT_EQ(0, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
-
- table_->SetObserver(NULL);
-}
-
-// Verifies up/down correctly navigates through groups.
-TEST_F(TableViewTest, KeyUpDown) {
- // Configure the grouper so that there are three groups:
- // A 0
- // 1
- // B 5
- // C 2
- // 3
- model_->AddRow(2, 5, 0);
- TableGrouperImpl grouper;
- std::vector<int> ranges;
- ranges.push_back(2);
- ranges.push_back(1);
- ranges.push_back(2);
- grouper.SetRanges(ranges);
- table_->SetGrouper(&grouper);
-
- TableViewObserverImpl observer;
- table_->SetObserver(&observer);
-
- // Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
-
- PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
-
- PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
-
- PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
-
- PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(0, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
-
- PressKey(ui::VKEY_UP);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
-
- PressKey(ui::VKEY_UP);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
-
- PressKey(ui::VKEY_UP);
- EXPECT_EQ(0, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
-
- // Sort the table descending by column 1, view now looks like:
- // B 5 model: 2
- // C 2 3
- // 3 4
- // A 0 0
- // 1 1
- table_->ToggleSortOrder(0);
- table_->ToggleSortOrder(0);
-
- EXPECT_EQ("2 3 4 0 1", GetViewToModelAsString(table_));
-
- table_->Select(-1);
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
-
- observer.GetChangedCountAndClear();
- // Up with nothing selected selects the first row.
- PressKey(ui::VKEY_UP);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
-
- PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
-
- PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
-
- PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(0, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
-
- PressKey(ui::VKEY_UP);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
-
- PressKey(ui::VKEY_UP);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
-
- PressKey(ui::VKEY_UP);
- EXPECT_EQ(0, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
-
- table_->SetObserver(NULL);
-}
-
-// Verifies home/end do the right thing.
-TEST_F(TableViewTest, HomeEnd) {
- // Configure the grouper so that there are three groups:
- // A 0
- // 1
- // B 5
- // C 2
- // 3
- model_->AddRow(2, 5, 0);
- TableGrouperImpl grouper;
- std::vector<int> ranges;
- ranges.push_back(2);
- ranges.push_back(1);
- ranges.push_back(2);
- grouper.SetRanges(ranges);
- table_->SetGrouper(&grouper);
-
- TableViewObserverImpl observer;
- table_->SetObserver(&observer);
-
- // Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
-
- PressKey(ui::VKEY_HOME);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
-
- PressKey(ui::VKEY_END);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
-
- table_->SetObserver(NULL);
-}
-
-// Verifies multiple selection gestures work (control-click, shift-click ...).
-TEST_F(TableViewTest, Multiselection) {
- // Configure the grouper so that there are three groups:
- // A 0
- // 1
- // B 5
- // C 2
- // 3
- model_->AddRow(2, 5, 0);
- TableGrouperImpl grouper;
- std::vector<int> ranges;
- ranges.push_back(2);
- ranges.push_back(1);
- ranges.push_back(2);
- grouper.SetRanges(ranges);
- table_->SetGrouper(&grouper);
-
- // Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
-
- TableViewObserverImpl observer;
- table_->SetObserver(&observer);
-
- // Click on the first row, should select it and the second row.
- ClickOnRow(0, 0);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
-
- // Click on the last row, should select it and the row before it.
- ClickOnRow(4, 0);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
-
- // Shift click on the third row, should extend selection to it.
- ClickOnRow(2, ui::EF_SHIFT_DOWN);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=2 anchor=3 selection=2 3 4", SelectionStateAsString());
-
- // Control click on third row, should toggle it.
- ClickOnRow(2, ui::EF_CONTROL_DOWN);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=2 anchor=2 selection=3 4", SelectionStateAsString());
-
- // Control-shift click on second row, should extend selection to it.
- ClickOnRow(1, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=1 anchor=2 selection=0 1 2 3 4", SelectionStateAsString());
-
- // Click on last row again.
- ClickOnRow(4, 0);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
-
- table_->SetObserver(NULL);
-}
-
-// Verifies multiple selection gestures work when sorted.
-TEST_F(TableViewTest, MultiselectionWithSort) {
- // Configure the grouper so that there are three groups:
- // A 0
- // 1
- // B 5
- // C 2
- // 3
- model_->AddRow(2, 5, 0);
- TableGrouperImpl grouper;
- std::vector<int> ranges;
- ranges.push_back(2);
- ranges.push_back(1);
- ranges.push_back(2);
- grouper.SetRanges(ranges);
- table_->SetGrouper(&grouper);
-
- // Sort the table descending by column 1, view now looks like:
- // B 5 model: 2
- // C 2 3
- // 3 4
- // A 0 0
- // 1 1
- table_->ToggleSortOrder(0);
- table_->ToggleSortOrder(0);
-
- // Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
-
- TableViewObserverImpl observer;
- table_->SetObserver(&observer);
-
- // Click on the third row, should select it and the second row.
- ClickOnRow(2, 0);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
-
- // Extend selection to first row.
- ClickOnRow(0, ui::EF_SHIFT_DOWN);
- EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=2 anchor=3 selection=2 3 4", SelectionStateAsString());
-
- table_->SetObserver(NULL);
-}
-
-} // namespace views
diff --git a/ui/views/controls/table/table_view_win.cc b/ui/views/controls/table/table_view_win.cc
deleted file mode 100644
index a7331f1..0000000
--- a/ui/views/controls/table/table_view_win.cc
+++ /dev/null
@@ -1,1646 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/controls/table/table_view_win.h"
-
-#include <commctrl.h>
-#include <windowsx.h>
-
-#include <algorithm>
-
-#include "base/i18n/rtl.h"
-#include "base/string_util.h"
-#include "base/win/scoped_gdi_object.h"
-#include "skia/ext/skia_utils_win.h"
-#include "third_party/skia/include/core/SkColorFilter.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/l10n/l10n_util_win.h"
-#include "ui/base/models/table_model.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/base/win/hwnd_util.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/favicon_size.h"
-#include "ui/gfx/font.h"
-#include "ui/gfx/icon_util.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/views/controls/native/native_view_host.h"
-#include "ui/views/controls/table/table_view_observer.h"
-
-namespace {
-
-int GetViewIndexFromPoint(HWND window, const gfx::Point& p) {
- LVHITTESTINFO hit_info = {0};
- hit_info.pt.x = p.x();
- hit_info.pt.y = p.y();
- return ListView_HitTest(window, &hit_info);
-}
-
-} // namespace
-
-namespace views {
-
-// Added to column width to prevent truncation.
-const int kListViewTextPadding = 15;
-// Additional column width necessary if column has icons.
-const int kListViewIconWidthAndPadding = 18;
-
-// TableView ------------------------------------------------------------------
-
-// static
-const int TableView::kImageSize = 18;
-
-TableView::TableView(ui::TableModel* model,
- const std::vector<ui::TableColumn>& columns,
- TableTypes table_type,
- bool single_selection,
- bool resizable_columns,
- bool autosize_columns)
- : model_(model),
- table_view_observer_(NULL),
- visible_columns_(),
- all_columns_(),
- column_count_(static_cast<int>(columns.size())),
- table_type_(table_type),
- single_selection_(single_selection),
- ignore_listview_change_(false),
- custom_colors_enabled_(false),
- autosize_columns_(autosize_columns),
- resizable_columns_(resizable_columns),
- list_view_(NULL),
- header_original_handler_(NULL),
- original_handler_(NULL),
- ALLOW_THIS_IN_INITIALIZER_LIST(table_view_wrapper_(this)),
- custom_cell_font_(NULL),
- content_offset_(0) {
- for (std::vector<ui::TableColumn>::const_iterator i = columns.begin();
- i != columns.end(); ++i) {
- AddColumn(*i);
- visible_columns_.push_back(i->id);
- }
-}
-
-TableView::~TableView() {
- if (list_view_) {
- if (model_)
- model_->SetObserver(NULL);
- }
- if (custom_cell_font_)
- DeleteObject(custom_cell_font_);
-}
-
-void TableView::SetModel(ui::TableModel* model) {
- if (model == model_)
- return;
-
- if (list_view_ && model_)
- model_->SetObserver(NULL);
- model_ = model;
- if (list_view_ && model_)
- model_->SetObserver(this);
- if (list_view_)
- OnModelChanged();
-}
-
-View* TableView::CreateParentIfNecessary() {
- return this;
-}
-
-void TableView::SetSortDescriptors(const SortDescriptors& sort_descriptors) {
- if (!sort_descriptors_.empty()) {
- ResetColumnSortImage(sort_descriptors_[0].column_id,
- NO_SORT);
- }
- sort_descriptors_ = sort_descriptors;
- if (!sort_descriptors_.empty()) {
- ResetColumnSortImage(
- sort_descriptors_[0].column_id,
- sort_descriptors_[0].ascending ? ASCENDING_SORT : DESCENDING_SORT);
- }
- if (!list_view_)
- return;
-
- // For some reason we have to turn off/on redraw, otherwise the display
- // isn't updated when done.
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
-
- UpdateItemsLParams(0, 0);
-
- SortItemsAndUpdateMapping();
-
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
-}
-
-int TableView::RowCount() const {
- if (!list_view_)
- return 0;
- return ListView_GetItemCount(list_view_);
-}
-
-int TableView::SelectedRowCount() {
- if (!list_view_)
- return 0;
- return ListView_GetSelectedCount(list_view_);
-}
-
-void TableView::Select(int model_row) {
- if (!list_view_)
- return;
-
- DCHECK(model_row >= 0 && model_row < RowCount());
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
- ignore_listview_change_ = true;
-
- // Unselect everything.
- ListView_SetItemState(list_view_, -1, 0, LVIS_SELECTED);
-
- // Select the specified item.
- int view_row = ModelToView(model_row);
- ListView_SetItemState(list_view_, view_row, LVIS_SELECTED | LVIS_FOCUSED,
- LVIS_SELECTED | LVIS_FOCUSED);
-
- // Make it visible.
- ListView_EnsureVisible(list_view_, view_row, FALSE);
- ignore_listview_change_ = false;
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
- if (table_view_observer_)
- table_view_observer_->OnSelectionChanged();
-}
-
-void TableView::SetSelectedState(int model_row, bool state) {
- if (!list_view_)
- return;
-
- DCHECK(model_row >= 0 && model_row < RowCount());
-
- ignore_listview_change_ = true;
-
- // Select the specified item.
- ListView_SetItemState(list_view_, ModelToView(model_row),
- state ? LVIS_SELECTED : 0, LVIS_SELECTED);
-
- ignore_listview_change_ = false;
-}
-
-void TableView::SetFocusOnItem(int model_row) {
- if (!list_view_)
- return;
-
- DCHECK(model_row >= 0 && model_row < RowCount());
-
- ignore_listview_change_ = true;
-
- // Set the focus to the given item.
- ListView_SetItemState(list_view_, ModelToView(model_row), LVIS_FOCUSED,
- LVIS_FOCUSED);
-
- ignore_listview_change_ = false;
-}
-
-int TableView::FirstSelectedRow() {
- if (!list_view_)
- return -1;
-
- int view_row = ListView_GetNextItem(list_view_, -1, LVNI_ALL | LVIS_SELECTED);
- return view_row == -1 ? -1 : ViewToModel(view_row);
-}
-
-bool TableView::IsItemSelected(int model_row) {
- if (!list_view_)
- return false;
-
- DCHECK(model_row >= 0 && model_row < RowCount());
- return (ListView_GetItemState(list_view_, ModelToView(model_row),
- LVIS_SELECTED) == LVIS_SELECTED);
-}
-
-bool TableView::ItemHasTheFocus(int model_row) {
- if (!list_view_)
- return false;
-
- DCHECK(model_row >= 0 && model_row < RowCount());
- return (ListView_GetItemState(list_view_, ModelToView(model_row),
- LVIS_FOCUSED) == LVIS_FOCUSED);
-}
-
-TableView::iterator TableView::SelectionBegin() {
- return TableView::iterator(this, LastSelectedViewIndex());
-}
-
-TableView::iterator TableView::SelectionEnd() {
- return TableView::iterator(this, -1);
-}
-
-void TableView::OnItemsChanged(int start, int length) {
- if (!list_view_)
- return;
-
- if (length == -1) {
- DCHECK_GE(start, 0);
- length = model_->RowCount() - start;
- }
- int row_count = RowCount();
- DCHECK(start >= 0 && length > 0 && start + length <= row_count);
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
- if (table_type_ == ICON_AND_TEXT) {
- // The redraw event does not include the icon in the clip rect, preventing
- // our icon from being repainted. So far the only way I could find around
- // this is to change the image for the item. Even if the image does not
- // exist, it causes the clip rect to include the icon's bounds so we can
- // paint it in the post paint event.
- LVITEM lv_item;
- memset(&lv_item, 0, sizeof(LVITEM));
- lv_item.mask = LVIF_IMAGE;
- for (int i = start; i < start + length; ++i) {
- // Retrieve the current icon index.
- lv_item.iItem = ModelToView(i);
- BOOL r = ListView_GetItem(list_view_, &lv_item);
- DCHECK(r);
- // Set the current icon index to the other image.
- lv_item.iImage = (lv_item.iImage + 1) % 2;
- DCHECK((lv_item.iImage == 0) || (lv_item.iImage == 1));
- r = ListView_SetItem(list_view_, &lv_item);
- DCHECK(r);
- }
- }
- UpdateListViewCache(start, length, false);
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
-}
-
-void TableView::OnModelChanged() {
- if (!list_view_)
- return;
-
- UpdateGroups();
-
- int current_row_count = ListView_GetItemCount(list_view_);
- if (current_row_count > 0)
- OnItemsRemoved(0, current_row_count);
- if (model_ && model_->RowCount())
- OnItemsAdded(0, model_->RowCount());
-}
-
-void TableView::OnItemsAdded(int start, int length) {
- if (!list_view_)
- return;
-
- DCHECK(start >= 0 && length > 0 && start <= RowCount());
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
- UpdateListViewCache(start, length, true);
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
-}
-
-void TableView::OnItemsRemoved(int start, int length) {
- if (!list_view_)
- return;
-
- if (start < 0 || length < 0 || start + length > RowCount()) {
- NOTREACHED();
- return;
- }
-
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
-
- bool had_selection = (SelectedRowCount() > 0);
- int old_row_count = RowCount();
- if (start == 0 && length == RowCount()) {
- // Everything was removed.
- ListView_DeleteAllItems(list_view_);
- view_to_model_.reset();
- model_to_view_.reset();
- } else {
- // Only a portion of the data was removed.
- if (is_sorted()) {
- int new_row_count = model_->RowCount();
- std::vector<int> view_items_to_remove;
- view_items_to_remove.reserve(length);
- // Iterate through the elements, updating the view_to_model_ mapping
- // as well as collecting the rows that need to be deleted.
- for (int i = 0, removed_count = 0; i < old_row_count; ++i) {
- int model_index = ViewToModel(i);
- if (model_index >= start) {
- if (model_index < start + length) {
- // This item was removed.
- view_items_to_remove.push_back(i);
- model_index = -1;
- } else {
- model_index -= length;
- }
- }
- if (model_index >= 0) {
- view_to_model_[i - static_cast<int>(view_items_to_remove.size())] =
- model_index;
- }
- }
-
- // Update the model_to_view mapping from the updated view_to_model
- // mapping.
- for (int i = 0; i < new_row_count; ++i)
- model_to_view_[view_to_model_[i]] = i;
-
- // And finally delete the items. We do this backwards as the items were
- // added ordered smallest to largest.
- for (int i = length - 1; i >= 0; --i)
- ListView_DeleteItem(list_view_, view_items_to_remove[i]);
- } else {
- for (int i = 0; i < length; ++i)
- ListView_DeleteItem(list_view_, start);
- }
- }
-
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
-
- // If the row count goes to zero and we had a selection LVN_ITEMCHANGED isn't
- // invoked, so we handle it here.
- //
- // When the model is set to NULL all the rows are removed. We don't notify
- // the delegate in this case as setting the model to NULL is usually done as
- // the last step before being deleted and callers shouldn't have to deal with
- // getting a selection change when the model is being reset.
- if (model_ && table_view_observer_ && had_selection && RowCount() == 0)
- table_view_observer_->OnSelectionChanged();
-}
-
-void TableView::AddColumn(const ui::TableColumn& col) {
- DCHECK_EQ(0u, all_columns_.count(col.id));
- all_columns_[col.id] = col;
-}
-
-void TableView::SetColumns(const std::vector<ui::TableColumn>& columns) {
- // Remove the currently visible columns.
- while (!visible_columns_.empty())
- SetColumnVisibility(visible_columns_.front(), false);
-
- all_columns_.clear();
- for (std::vector<ui::TableColumn>::const_iterator i = columns.begin();
- i != columns.end(); ++i) {
- AddColumn(*i);
- }
-
- // Remove any sort descriptors that are no longer valid.
- SortDescriptors sort = sort_descriptors();
- for (SortDescriptors::iterator i = sort.begin(); i != sort.end();) {
- if (all_columns_.count(i->column_id) == 0)
- i = sort.erase(i);
- else
- ++i;
- }
- sort_descriptors_ = sort;
-}
-
-void TableView::OnColumnsChanged() {
- column_count_ = static_cast<int>(visible_columns_.size());
- ResetColumnSizes();
-}
-
-void TableView::SetColumnVisibility(int id, bool is_visible) {
- bool changed = false;
- for (std::vector<int>::iterator i = visible_columns_.begin();
- i != visible_columns_.end(); ++i) {
- if (*i == id) {
- if (is_visible) {
- // It's already visible, bail out early.
- return;
- } else {
- int index = static_cast<int>(i - visible_columns_.begin());
- // This could be called before the native list view has been created
- // (in CreateNativeControl, called when the view is added to a
- // Widget). In that case since the column is not in
- // visible_columns_ it will not be added later on when it is created.
- if (list_view_)
- SendMessage(list_view_, LVM_DELETECOLUMN, index, 0);
- visible_columns_.erase(i);
- changed = true;
- break;
- }
- }
- }
- if (is_visible) {
- visible_columns_.push_back(id);
- ui::TableColumn& column = all_columns_[id];
- InsertColumn(column, column_count_);
- if (column.min_visible_width == 0) {
- // ListView_GetStringWidth must be padded or else truncation will occur.
- column.min_visible_width = ListView_GetStringWidth(list_view_,
- column.title.c_str()) +
- kListViewTextPadding;
- }
- changed = true;
- }
- if (changed)
- OnColumnsChanged();
-}
-
-void TableView::SetVisibleColumns(const std::vector<int>& columns) {
- size_t old_count = visible_columns_.size();
- size_t new_count = columns.size();
- // remove the old columns
- if (list_view_) {
- for (std::vector<int>::reverse_iterator i = visible_columns_.rbegin();
- i != visible_columns_.rend(); ++i) {
- int index = static_cast<int>(i - visible_columns_.rend());
- SendMessage(list_view_, LVM_DELETECOLUMN, index, 0);
- }
- }
- visible_columns_ = columns;
- // Insert the new columns.
- if (list_view_) {
- for (std::vector<int>::iterator i = visible_columns_.begin();
- i != visible_columns_.end(); ++i) {
- int index = static_cast<int>(i - visible_columns_.end());
- InsertColumn(all_columns_[*i], index);
- }
- }
- OnColumnsChanged();
-}
-
-bool TableView::IsColumnVisible(int id) const {
- for (std::vector<int>::const_iterator i = visible_columns_.begin();
- i != visible_columns_.end(); ++i)
- if (*i == id) {
- return true;
- }
- return false;
-}
-
-const ui::TableColumn& TableView::GetColumnAtPosition(int pos) {
- return all_columns_[visible_columns_[pos]];
-}
-
-bool TableView::HasColumn(int id) {
- return all_columns_.count(id) > 0;
-}
-
-gfx::Point TableView::GetKeyboardContextMenuLocation() {
- int first_selected = FirstSelectedRow();
- int y = height() / 2;
- if (first_selected != -1) {
- RECT cell_bounds;
- RECT client_rect;
- if (ListView_GetItemRect(GetNativeControlHWND(), first_selected,
- &cell_bounds, LVIR_BOUNDS) &&
- GetClientRect(GetNativeControlHWND(), &client_rect) &&
- cell_bounds.bottom >= 0 && cell_bounds.bottom < client_rect.bottom) {
- y = cell_bounds.bottom;
- }
- }
- gfx::Point screen_loc(0, y);
- if (base::i18n::IsRTL())
- screen_loc.set_x(width());
- ConvertPointToScreen(this, &screen_loc);
- return screen_loc;
-}
-
-void TableView::SetCustomColorsEnabled(bool custom_colors_enabled) {
- custom_colors_enabled_ = custom_colors_enabled;
-}
-
-bool TableView::GetCellColors(int model_row,
- int column,
- ItemColor* foreground,
- ItemColor* background,
- LOGFONT* logfont) {
- return false;
-}
-
-// static
-LRESULT CALLBACK TableView::TableWndProc(HWND window,
- UINT message,
- WPARAM w_param,
- LPARAM l_param) {
- TableView* table_view = reinterpret_cast<TableViewWrapper*>(
- GetWindowLongPtr(window, GWLP_USERDATA))->table_view;
-
- // Is the mouse down on the table?
- static bool in_mouse_down = false;
- // Should we select on mouse up?
- static bool select_on_mouse_up = false;
-
- // If the mouse is down, this is the location of the mouse down message.
- CR_DEFINE_STATIC_LOCAL(gfx::Point, mouse_down_pos, ());
-
- switch (message) {
- case WM_CONTEXTMENU: {
- // This addresses two problems seen with context menus in right to left
- // locales:
- // 1. The mouse coordinates in the l_param were occasionally wrong in
- // weird ways. This is most often seen when right clicking on the
- // list-view twice in a row.
- // 2. Right clicking on the icon would show the scrollbar menu.
- //
- // As a work around this uses the position of the cursor and ignores
- // the position supplied in the l_param.
- if (base::i18n::IsRTL() &&
- (GET_X_LPARAM(l_param) != -1 || GET_Y_LPARAM(l_param) != -1)) {
- POINT screen_point;
- GetCursorPos(&screen_point);
- POINT table_point = screen_point;
- RECT client_rect;
- if (ScreenToClient(window, &table_point) &&
- GetClientRect(window, &client_rect) &&
- PtInRect(&client_rect, table_point)) {
- // The point is over the client area of the table, handle it ourself.
- // But first select the row if it isn't already selected.
- int view_index =
- GetViewIndexFromPoint(window, gfx::Point(table_point));
- if (view_index != -1) {
- int model_index = table_view->ViewToModel(view_index);
- if (!table_view->IsItemSelected(model_index))
- table_view->Select(model_index);
- }
- table_view->OnContextMenu(screen_point);
- return 0; // So that default processing doesn't occur.
- }
- }
- // else case: default handling is fine, so break and let the default
- // handler service the request (which will likely calls us back with
- // OnContextMenu).
- break;
- }
-
- case WM_CANCELMODE: {
- if (in_mouse_down) {
- in_mouse_down = false;
- return 0;
- }
- break;
- }
-
- case WM_ERASEBKGND:
- // We make WM_ERASEBKGND do nothing (returning 1 indicates we handled
- // the request). We do this so that the table view doesn't flicker during
- // resizing.
- return 1;
-
- case WM_PAINT: {
- LRESULT result = CallWindowProc(table_view->original_handler_, window,
- message, w_param, l_param);
- table_view->PostPaint();
- return result;
- }
-
- case WM_KEYDOWN: {
- if (!table_view->single_selection_ && w_param == 'A' &&
- GetKeyState(VK_CONTROL) < 0 && table_view->RowCount() > 0) {
- // Select everything.
- ListView_SetItemState(window, -1, LVIS_SELECTED, LVIS_SELECTED);
- // And make the first row focused.
- ListView_SetItemState(window, 0, LVIS_FOCUSED, LVIS_FOCUSED);
- return 0;
- }
- // else case: fall through to default processing.
- break;
- }
-
- case WM_LBUTTONDBLCLK: {
- if (w_param == MK_LBUTTON)
- table_view->OnDoubleClick();
- return 0;
- }
-
- case WM_MBUTTONDOWN: {
- if (w_param == MK_MBUTTON) {
- int view_index = GetViewIndexFromPoint(window, gfx::Point(l_param));
- if (view_index != -1) {
- int model_index = table_view->ViewToModel(view_index);
- // Clear all and select the row that was middle clicked.
- table_view->Select(model_index);
- table_view->OnMiddleClick();
- }
- }
- return 0;
- }
-
- case WM_LBUTTONUP: {
- if (in_mouse_down) {
- in_mouse_down = false;
- ReleaseCapture();
- SetFocus(window);
- if (select_on_mouse_up) {
- int view_index = GetViewIndexFromPoint(window, gfx::Point(l_param));
- if (view_index != -1)
- table_view->Select(table_view->ViewToModel(view_index));
- }
- return 0;
- }
- break;
- }
-
- case WM_LBUTTONDOWN: {
- // ListView treats clicking on an area outside the text of a column as
- // drag to select. This is confusing when the selection is shown across
- // the whole row. For this reason we override the default handling for
- // mouse down/move/up and treat the whole row as draggable. That is, no
- // matter where you click in the row we'll attempt to start dragging.
- //
- // Only do custom mouse handling if no other mouse buttons are down.
- if ((w_param | (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) ==
- (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) {
- if (in_mouse_down)
- return 0;
-
- int view_index = GetViewIndexFromPoint(window, gfx::Point(l_param));
- if (view_index != -1) {
- table_view->ignore_listview_change_ = true;
- in_mouse_down = true;
- select_on_mouse_up = false;
- mouse_down_pos.set_x(GET_X_LPARAM(l_param));
- mouse_down_pos.set_y(GET_Y_LPARAM(l_param));
- int model_index = table_view->ViewToModel(view_index);
- bool select = true;
- if (w_param & MK_CONTROL) {
- select = false;
- if (!table_view->IsItemSelected(model_index)) {
- if (table_view->single_selection_) {
- // Single selection mode and the row isn't selected, select
- // only it.
- table_view->Select(model_index);
- } else {
- // Not single selection, add this row to the selection.
- table_view->SetSelectedState(model_index, true);
- }
- } else {
- // Remove this row from the selection.
- table_view->SetSelectedState(model_index, false);
- }
- ListView_SetSelectionMark(window, view_index);
- } else if (!table_view->single_selection_ && w_param & MK_SHIFT) {
- int mark_view_index = ListView_GetSelectionMark(window);
- if (mark_view_index != -1) {
- // Unselect everything.
- ListView_SetItemState(window, -1, 0, LVIS_SELECTED);
- select = false;
- if (!table_view->SelectMultiple(view_index, mark_view_index)) {
- // Selection spans group boundary - reset selection to current.
- table_view->SetSelectedState(model_index, true);
- ListView_SetSelectionMark(window, view_index);
- }
- }
- }
- // Make the row the user clicked on the focused row.
- ListView_SetItemState(window, view_index, LVIS_FOCUSED,
- LVIS_FOCUSED);
- if (select) {
- if (!table_view->IsItemSelected(model_index)) {
- // Clear all.
- ListView_SetItemState(window, -1, 0, LVIS_SELECTED);
- // And select the row the user clicked on.
- table_view->SetSelectedState(model_index, true);
- } else {
- // The item is already selected, don't clear the state right away
- // in case the user drags. Instead wait for mouse up, then only
- // select the row the user clicked on.
- select_on_mouse_up = true;
- }
- ListView_SetSelectionMark(window, view_index);
- }
- table_view->ignore_listview_change_ = false;
- table_view->OnSelectedStateChanged();
- SetCapture(window);
- return 0;
- }
- // else case, continue on to default handler
- }
- break;
- }
-
- case WM_MOUSEMOVE: {
- if (in_mouse_down) {
- gfx::Point mouse_pos(GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param));
- if (View::ExceededDragThreshold(mouse_pos - mouse_down_pos)) {
- // We're about to start drag and drop, which results in no mouse up.
- // Release capture and reset state.
- ReleaseCapture();
- in_mouse_down = false;
-
- NMLISTVIEW details;
- memset(&details, 0, sizeof(details));
- details.hdr.code = LVN_BEGINDRAG;
- SendMessage(::GetParent(window), WM_NOTIFY, 0,
- reinterpret_cast<LPARAM>(&details));
- }
- return 0;
- }
- break;
- }
-
- default:
- break;
- }
- DCHECK(table_view->original_handler_);
- return CallWindowProc(table_view->original_handler_, window, message, w_param,
- l_param);
-}
-
-LRESULT CALLBACK TableView::TableHeaderWndProc(HWND window, UINT message,
- WPARAM w_param, LPARAM l_param) {
- TableView* table_view = reinterpret_cast<TableViewWrapper*>(
- GetWindowLongPtr(window, GWLP_USERDATA))->table_view;
-
- switch (message) {
- case WM_SETCURSOR:
- if (!table_view->resizable_columns_)
- // Prevents the cursor from changing to the resize cursor.
- return TRUE;
- break;
- case WM_LBUTTONDBLCLK:
- if (!table_view->resizable_columns_)
- // Prevents the double-click on the column separator from auto-resizing
- // the column.
- return TRUE;
- break;
- default:
- break;
- }
- DCHECK(table_view->header_original_handler_);
- return CallWindowProc(table_view->header_original_handler_,
- window, message, w_param, l_param);
-}
-
-HWND TableView::CreateNativeControl(HWND parent_container) {
- int style = WS_CHILD | LVS_REPORT | LVS_SHOWSELALWAYS;
- if (single_selection_)
- style |= LVS_SINGLESEL;
- // If there's only one column and the title string is empty, don't show a
- // header.
- if (all_columns_.size() == 1) {
- std::map<int, ui::TableColumn>::const_iterator first = all_columns_.begin();
- if (first->second.title.empty())
- style |= LVS_NOCOLUMNHEADER;
- }
- list_view_ = ::CreateWindowEx(WS_EX_CLIENTEDGE | GetAdditionalRTLStyle(),
- WC_LISTVIEW,
- L"",
- style,
- 0, 0, width(), height(),
- parent_container, NULL, NULL, NULL);
- ui::CheckWindowCreated(list_view_);
-
- // Reduce overdraw/flicker artifacts by double buffering. Support tooltips
- // and display elided items completely on hover (see comments in OnNotify()
- // under LVN_GETINFOTIP). Make the selection extend across the row.
- ListView_SetExtendedListViewStyle(list_view_,
- LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_FULLROWSELECT);
- l10n_util::AdjustUIFontForWindow(list_view_);
-
- // Add the columns.
- for (std::vector<int>::iterator i = visible_columns_.begin();
- i != visible_columns_.end(); ++i) {
- InsertColumn(all_columns_[*i],
- static_cast<int>(i - visible_columns_.begin()));
- }
-
- if (model_)
- model_->SetObserver(this);
-
- UpdateGroups();
-
- // Set the # of rows.
- if (model_)
- UpdateListViewCache(0, model_->RowCount(), true);
-
- if (table_type_ == ICON_AND_TEXT) {
- HIMAGELIST image_list =
- ImageList_Create(kImageSize, kImageSize, ILC_COLOR32, 2, 2);
- // We create 2 phony images because we are going to switch images at every
- // refresh in order to force a refresh of the icon area (somehow the clip
- // rect does not include the icon).
- gfx::Canvas canvas(gfx::Size(kImageSize, kImageSize), ui::SCALE_FACTOR_100P,
- false);
- {
- base::win::ScopedHICON empty_icon(IconUtil::CreateHICONFromSkBitmap(
- canvas.ExtractImageRep().sk_bitmap()));
- ImageList_AddIcon(image_list, empty_icon);
- ImageList_AddIcon(image_list, empty_icon);
- }
- ListView_SetImageList(list_view_, image_list, LVSIL_SMALL);
- }
-
- if (!resizable_columns_) {
- // To disable the resizing of columns we'll filter the events happening on
- // the header. We also need to intercept the HDM_LAYOUT to size the header
- // for the Chrome headers.
- HWND header = ListView_GetHeader(list_view_);
- DCHECK(header);
- SetWindowLongPtr(header, GWLP_USERDATA,
- reinterpret_cast<LONG_PTR>(&table_view_wrapper_));
- header_original_handler_ = ui::SetWindowProc(header,
- &TableView::TableHeaderWndProc);
- }
-
- SetWindowLongPtr(list_view_, GWLP_USERDATA,
- reinterpret_cast<LONG_PTR>(&table_view_wrapper_));
- original_handler_ =
- ui::SetWindowProc(list_view_, &TableView::TableWndProc);
-
- // Bug 964884: detach the IME attached to this window.
- // We should attach IMEs only when we need to input CJK strings.
- ::ImmAssociateContextEx(list_view_, NULL, 0);
-
- UpdateContentOffset();
- column_sizes_valid_ = false;
-
- return list_view_;
-}
-
-void TableView::ToggleSortOrder(int column_id) {
- SortDescriptors sort = sort_descriptors();
- if (!sort.empty() && sort[0].column_id == column_id) {
- sort[0].ascending = !sort[0].ascending;
- } else {
- SortDescriptor descriptor(column_id, true);
- sort.insert(sort.begin(), descriptor);
- if (sort.size() > 2) {
- // Only persist two sort descriptors.
- sort.resize(2);
- }
- }
- SetSortDescriptors(sort);
-}
-
-void TableView::UpdateItemsLParams(int start, int length) {
- LVITEM item;
- memset(&item, 0, sizeof(LVITEM));
- item.mask = LVIF_PARAM;
- int row_count = RowCount();
- for (int i = 0; i < row_count; ++i) {
- item.iItem = i;
- int model_index = ViewToModel(i);
- if (length > 0 && model_index >= start)
- model_index += length;
- item.lParam = static_cast<LPARAM>(model_index);
- ListView_SetItem(list_view_, &item);
- }
-}
-
-void TableView::SortItemsAndUpdateMapping() {
- if (!is_sorted()) {
- ListView_SortItems(list_view_, &TableView::NaturalSortFunc, this);
- view_to_model_.reset();
- model_to_view_.reset();
- return;
- }
-
- PrepareForSort();
-
- // Sort the items.
- ListView_SortItems(list_view_, &TableView::SortFunc, this);
-
- model_->ClearCollator();
-
- // Update internal mapping to match how items were actually sorted.
- int row_count = RowCount();
- model_to_view_.reset(new int[row_count]);
- view_to_model_.reset(new int[row_count]);
- LVITEM item;
- memset(&item, 0, sizeof(LVITEM));
- item.mask = LVIF_PARAM;
- for (int i = 0; i < row_count; ++i) {
- item.iItem = i;
- ListView_GetItem(list_view_, &item);
- int model_index = static_cast<int>(item.lParam);
- view_to_model_[i] = model_index;
- model_to_view_[model_index] = i;
- }
-}
-
-bool TableView::SelectMultiple(int view_index, int mark_view_index) {
- int group_id = 0;
- if (model_->HasGroups()) {
- group_id = model_->GetGroupID(ViewToModel(view_index));
- if (group_id != model_->GetGroupID(ViewToModel(mark_view_index))) {
- // User is trying to do a cross-group selection - bail out.
- return false;
- }
- }
-
- // Select from mark to mouse down location.
- for (int i = std::min(view_index, mark_view_index),
- max_i = std::max(view_index, mark_view_index); i <= max_i;
- ++i) {
- // Items between the view_index and mark_view_index are not necessarily in
- // the same group, so don't select anything outside the group the user
- // just clicked in.
- if (model_->HasGroups() &&
- model_->GetGroupID(ViewToModel(i)) != group_id) {
- continue;
- }
- SetSelectedState(ViewToModel(i), true);
- }
- return true;
-}
-
-// static
-int CALLBACK TableView::SortFunc(LPARAM model_index_1_p,
- LPARAM model_index_2_p,
- LPARAM table_view_param) {
- int model_index_1 = static_cast<int>(model_index_1_p);
- int model_index_2 = static_cast<int>(model_index_2_p);
- TableView* table_view = reinterpret_cast<TableView*>(table_view_param);
- return table_view->CompareRows(model_index_1, model_index_2);
-}
-
-// static
-int CALLBACK TableView::NaturalSortFunc(LPARAM model_index_1_p,
- LPARAM model_index_2_p,
- LPARAM table_view_param) {
- return model_index_1_p - model_index_2_p;
-}
-
-void TableView::ResetColumnSortImage(int column_id, SortDirection direction) {
- if (!list_view_ || column_id == -1)
- return;
-
- std::vector<int>::const_iterator i =
- std::find(visible_columns_.begin(), visible_columns_.end(), column_id);
- if (i == visible_columns_.end())
- return;
-
- HWND header = ListView_GetHeader(list_view_);
- if (!header)
- return;
-
- int column_index = static_cast<int>(i - visible_columns_.begin());
- HDITEM header_item;
- memset(&header_item, 0, sizeof(header_item));
- header_item.mask = HDI_FORMAT;
- Header_GetItem(header, column_index, &header_item);
- header_item.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
- if (direction == ASCENDING_SORT)
- header_item.fmt |= HDF_SORTUP;
- else if (direction == DESCENDING_SORT)
- header_item.fmt |= HDF_SORTDOWN;
- Header_SetItem(header, column_index, &header_item);
-}
-
-void TableView::InsertColumn(const ui::TableColumn& tc, int index) {
- if (!list_view_)
- return;
-
- LVCOLUMN column = { 0 };
- column.mask = LVCF_TEXT|LVCF_FMT;
- column.pszText = const_cast<LPWSTR>(tc.title.c_str());
- switch (tc.alignment) {
- case ui::TableColumn::LEFT:
- column.fmt = LVCFMT_LEFT;
- break;
- case ui::TableColumn::RIGHT:
- column.fmt = LVCFMT_RIGHT;
- break;
- case ui::TableColumn::CENTER:
- column.fmt = LVCFMT_CENTER;
- break;
- default:
- NOTREACHED();
- }
- if (tc.width != -1) {
- column.mask |= LVCF_WIDTH;
- column.cx = tc.width;
- }
- column.mask |= LVCF_SUBITEM;
- // Sub-items are 1s indexed.
- column.iSubItem = index + 1;
- SendMessage(list_view_, LVM_INSERTCOLUMN, index,
- reinterpret_cast<LPARAM>(&column));
- if (is_sorted() && sort_descriptors_[0].column_id == tc.id) {
- ResetColumnSortImage(
- tc.id,
- sort_descriptors_[0].ascending ? ASCENDING_SORT : DESCENDING_SORT);
- }
-}
-
-LRESULT TableView::OnNotify(int w_param, LPNMHDR hdr) {
- if (!model_)
- return 0;
-
- switch (hdr->code) {
- case NM_CUSTOMDRAW: {
- // Draw notification. dwDragState indicates the current stage of drawing.
- return OnCustomDraw(reinterpret_cast<NMLVCUSTOMDRAW*>(hdr));
- }
-
- case LVN_ITEMCHANGED: {
- // Notification that the state of an item has changed. The state
- // includes such things as whether the item is selected.
- NMLISTVIEW* state_change = reinterpret_cast<NMLISTVIEW*>(hdr);
- if ((state_change->uChanged & LVIF_STATE) != 0) {
- if ((state_change->uOldState & LVIS_SELECTED) !=
- (state_change->uNewState & LVIS_SELECTED)) {
- // Selected state of the item changed.
- OnSelectedStateChanged();
- }
- }
- break;
- }
-
- case HDN_BEGINTRACKW:
- case HDN_BEGINTRACKA:
- // Prevent clicks so columns cannot be resized.
- if (!resizable_columns_)
- return TRUE;
- break;
-
- case NM_DBLCLK:
- OnDoubleClick();
- break;
-
- case LVN_COLUMNCLICK: {
- const ui::TableColumn& column = GetColumnAtPosition(
- reinterpret_cast<NMLISTVIEW*>(hdr)->iSubItem);
- if (column.sortable)
- ToggleSortOrder(column.id);
- break;
- }
-
- case LVN_MARQUEEBEGIN: // We don't want the marquee selection.
- return 1;
-
- case LVN_GETINFOTIP: {
- // This is called when the user hovers items in column zero.
- // * If the text in this column is not fully visible, the dwFlags field
- // will be set to 0, and pszText will contain the full text. If you
- // return without making any changes, this text will be displayed in a
- // "labeltip" - a bubble that's overlaid (at the correct alignment!)
- // on the item. If you return with a different pszText, it will be
- // displayed as a tooltip if nonempty.
- // * Otherwise, dwFlags will be LVGIT_UNFOLDED and pszText will be
- // empty. On return, if pszText is nonempty, it will be displayed as
- // a labeltip if dwFlags has been changed to 0 (even if it bears no
- // resemblance to the item text), or as a tooltip otherwise.
- //
- // Once the tooltip for an item has been obtained, this will not be called
- // again until the user hovers a different item. If after that the
- // original item is hovered a second time, this will be called.
- //
- // When the user hovers items in other columns, they will be "unfolded"
- // (displayed as labeltips) when necessary, but this function will never
- // be called.
- //
- // Changing the LVS_EX_INFOTIP extended style to LVS_EX_LABELTIP will
- // cause all of the above to be true except that this function will not be
- // called when dwFlags would be LVGIT_UNFOLDED. Removing it entirely will
- // disable all of the above behavior.
- NMLVGETINFOTIP* info_tip = reinterpret_cast<NMLVGETINFOTIP*>(hdr);
- string16 tooltip =
- model_->GetTooltip(ViewToModel(info_tip->iItem));
- CHECK_GE(info_tip->cchTextMax, 2);
- if (tooltip.length() >= static_cast<size_t>(info_tip->cchTextMax)) {
- tooltip.erase(info_tip->cchTextMax - 2); // Ellipsis + '\0'
- const char16 kEllipsis = 0x2026;
- tooltip.push_back(kEllipsis);
- }
- if (!tooltip.empty())
- wcscpy_s(info_tip->pszText, tooltip.length() + 1, tooltip.c_str());
- return 1;
- }
-
- default:
- break;
- }
- return 0;
-}
-
-// Returns result, unless ascending is false in which case -result is returned.
-static int SwapCompareResult(int result, bool ascending) {
- return ascending ? result : -result;
-}
-
-int TableView::CompareRows(int model_row1, int model_row2) {
- if (model_->HasGroups()) {
- // By default ListView sorts the elements regardless of groups. In such
- // a situation the groups display only the items they contain. This results
- // in the visual order differing from the item indices. I could not find
- // a way to iterate over the visual order in this situation. As a workaround
- // this forces the items to be sorted by groups as well, which means the
- // visual order matches the item indices.
- int g1 = model_->GetGroupID(model_row1);
- int g2 = model_->GetGroupID(model_row2);
- if (g1 != g2)
- return g1 - g2;
- }
- int sort_result = model_->CompareValues(
- model_row1, model_row2, sort_descriptors_[0].column_id);
- if (sort_result == 0 && sort_descriptors_.size() > 1 &&
- sort_descriptors_[1].column_id != -1) {
- // Try the secondary sort.
- return SwapCompareResult(
- model_->CompareValues(model_row1, model_row2,
- sort_descriptors_[1].column_id),
- sort_descriptors_[1].ascending);
- }
- return SwapCompareResult(sort_result, sort_descriptors_[0].ascending);
-}
-
-int TableView::GetColumnWidth(int column_id) {
- if (!list_view_)
- return -1;
-
- std::vector<int>::const_iterator i =
- std::find(visible_columns_.begin(), visible_columns_.end(), column_id);
- if (i == visible_columns_.end())
- return -1;
-
- return ListView_GetColumnWidth(
- list_view_, static_cast<int>(i - visible_columns_.begin()));
-}
-
-void TableView::PaintAltText() {
- if (alt_text_.empty())
- return;
-
- HDC dc = GetDC(GetNativeControlHWND());
- gfx::Font font = GetAltTextFont();
- gfx::Rect bounds = GetAltTextBounds();
- gfx::Canvas canvas(bounds.size(), ui::SCALE_FACTOR_100P, false);
- // Pad by 1 for halo.
- canvas.DrawStringWithHalo(alt_text_, font, SK_ColorDKGRAY, SK_ColorWHITE, 1,
- 1, bounds.width() - 2, bounds.height() - 2,
- gfx::Canvas::DefaultCanvasTextAlignment());
- skia::DrawToNativeContext(
- canvas.sk_canvas(), dc, bounds.x(), bounds.y(), NULL);
- ReleaseDC(GetNativeControlHWND(), dc);
-}
-
-LRESULT TableView::OnCustomDraw(NMLVCUSTOMDRAW* draw_info) {
- switch (draw_info->nmcd.dwDrawStage) {
- case CDDS_PREPAINT: {
- return CDRF_NOTIFYITEMDRAW;
- }
- case CDDS_ITEMPREPAINT: {
- // The list-view is about to paint an item, tell it we want to
- // notified when it paints every subitem.
- LRESULT r = CDRF_NOTIFYSUBITEMDRAW;
- if (table_type_ == ICON_AND_TEXT)
- r |= CDRF_NOTIFYPOSTPAINT;
- return r;
- }
- case CDDS_ITEMPREPAINT | CDDS_SUBITEM: {
- // The list-view is painting a subitem. See if the colors should be
- // changed from the default.
- if (custom_colors_enabled_) {
- // At this time, draw_info->clrText and draw_info->clrTextBk are not
- // set. So we pass in an ItemColor to GetCellColors. If
- // ItemColor.color_is_set is true, then we use the provided color.
- ItemColor foreground = {0};
- ItemColor background = {0};
-
- LOGFONT logfont;
- GetObject(GetWindowFont(list_view_), sizeof(logfont), &logfont);
-
- if (GetCellColors(ViewToModel(
- static_cast<int>(draw_info->nmcd.dwItemSpec)),
- draw_info->iSubItem,
- &foreground,
- &background,
- &logfont)) {
- // TODO(tc): Creating/deleting a font for every cell seems like a
- // waste if the font hasn't changed. Maybe we should use a struct
- // with a bool like we do with colors?
- if (custom_cell_font_)
- DeleteObject(custom_cell_font_);
- l10n_util::AdjustUIFont(&logfont);
- custom_cell_font_ = CreateFontIndirect(&logfont);
- SelectObject(draw_info->nmcd.hdc, custom_cell_font_);
- draw_info->clrText = foreground.color_is_set
- ? skia::SkColorToCOLORREF(foreground.color)
- : CLR_DEFAULT;
- draw_info->clrTextBk = background.color_is_set
- ? skia::SkColorToCOLORREF(background.color)
- : CLR_DEFAULT;
- return CDRF_NEWFONT;
- }
- }
- return CDRF_DODEFAULT;
- }
- case CDDS_ITEMPOSTPAINT: {
- DCHECK((table_type_ == ICON_AND_TEXT) || (ImplementPostPaint()));
- int view_index = static_cast<int>(draw_info->nmcd.dwItemSpec);
- // We get notifications for empty items, just ignore them.
- if (view_index >= model_->RowCount())
- return CDRF_DODEFAULT;
- int model_index = ViewToModel(view_index);
- LRESULT r = CDRF_DODEFAULT;
- // First let's take care of painting the right icon.
- if (table_type_ == ICON_AND_TEXT) {
- gfx::ImageSkia image = model_->GetIcon(model_index);
- if (!image.isNull()) {
- // Get the rect that holds the icon.
- RECT icon_rect, client_rect;
- if (ListView_GetItemRect(list_view_, view_index, &icon_rect,
- LVIR_ICON) &&
- GetClientRect(list_view_, &client_rect)) {
- RECT intersection;
- // Client rect includes the header but we need to make sure we don't
- // paint into it.
- client_rect.top += content_offset_;
- // Make sure the region need to paint is visible.
- if (IntersectRect(&intersection, &icon_rect, &client_rect)) {
- gfx::Canvas canvas(gfx::Size(icon_rect.right - icon_rect.left,
- icon_rect.bottom - icon_rect.top),
- ui::SCALE_FACTOR_100P,
- false);
-
- // It seems the state in nmcd.uItemState is not correct.
- // We'll retrieve it explicitly.
- int selected = ListView_GetItemState(
- list_view_, view_index, LVIS_SELECTED | LVIS_DROPHILITED);
- bool drop_highlight = ((selected & LVIS_DROPHILITED) != 0);
- int bg_color_index;
- if (!enabled())
- bg_color_index = COLOR_3DFACE;
- else if (drop_highlight)
- bg_color_index = COLOR_HIGHLIGHT;
- else if (selected)
- bg_color_index = HasFocus() ? COLOR_HIGHLIGHT : COLOR_3DFACE;
- else
- bg_color_index = COLOR_WINDOW;
- // NOTE: This may be invoked without the ListView filling in the
- // background (or rather windows paints background, then invokes
- // this twice). As such, we always fill in the background.
- canvas.DrawColor(
- skia::COLORREFToSkColor(GetSysColor(bg_color_index)),
- SkXfermode::kSrc_Mode);
- // + 1 for padding (we declared the image as 18x18 in the list-
- // view when they are 16x16 so we get an extra pixel of padding).
- canvas.DrawImageInt(image, 0, 0,
- image.width(), image.height(),
- 1, 1,
- gfx::kFaviconSize, gfx::kFaviconSize, true);
-
- // Only paint the visible region of the icon.
- RECT to_draw = { intersection.left - icon_rect.left,
- intersection.top - icon_rect.top,
- 0, 0 };
- to_draw.right = to_draw.left +
- (intersection.right - intersection.left);
- to_draw.bottom = to_draw.top +
- (intersection.bottom - intersection.top);
- skia::DrawToNativeContext(canvas.sk_canvas(), draw_info->nmcd.hdc,
- intersection.left, intersection.top,
- &to_draw);
- r = CDRF_SKIPDEFAULT;
- }
- }
- }
- }
- if (ImplementPostPaint()) {
- RECT cell_rect;
- if (ListView_GetItemRect(list_view_, view_index, &cell_rect,
- LVIR_BOUNDS)) {
- PostPaint(model_index, 0, false, gfx::Rect(cell_rect),
- draw_info->nmcd.hdc);
- r = CDRF_SKIPDEFAULT;
- }
- }
- return r;
- }
- default:
- return CDRF_DODEFAULT;
- }
-}
-
-void TableView::UpdateListViewCache(int start, int length, bool add) {
- ignore_listview_change_ = true;
- UpdateListViewCache0(start, length, add);
- ignore_listview_change_ = false;
-}
-
-void TableView::ResetColumnSizes() {
- if (!list_view_)
- return;
-
- // See comment in TableColumn for what this does.
- int width = this->width();
- RECT native_bounds;
- if (GetClientRect(GetNativeControlHWND(), &native_bounds)) {
- int window_width = native_bounds.right - native_bounds.left;
- if (window_width > 0) {
- // Prefer the bounds of the window over our bounds, which may be
- // different.
- width = window_width;
- // Only set the flag when we know the true width of the table.
- column_sizes_valid_ = true;
- }
- }
-
- float percent = 0;
- int fixed_width = 0;
- int autosize_width = 0;
-
- for (std::vector<int>::const_iterator i = visible_columns_.begin();
- i != visible_columns_.end(); ++i) {
- ui::TableColumn& col = all_columns_[*i];
- int col_index = static_cast<int>(i - visible_columns_.begin());
- if (col.width == -1) {
- if (col.percent > 0) {
- percent += col.percent;
- } else {
- autosize_width += col.min_visible_width;
- }
- } else {
- fixed_width += ListView_GetColumnWidth(list_view_, col_index);
- }
- }
-
- // Now do a pass to set the actual sizes of auto-sized and
- // percent-sized columns.
- int available_width = width - fixed_width - autosize_width;
- for (std::vector<int>::const_iterator i = visible_columns_.begin();
- i != visible_columns_.end(); ++i) {
- ui::TableColumn& col = all_columns_[*i];
- if (col.width == -1) {
- int col_index = static_cast<int>(i - visible_columns_.begin());
- if (col.percent > 0) {
- if (available_width > 0) {
- int col_width =
- static_cast<int>(available_width * (col.percent / percent));
- available_width -= col_width;
- percent -= col.percent;
- ListView_SetColumnWidth(list_view_, col_index, col_width);
- }
- } else {
- int col_width = col.min_visible_width;
- // If no "percent" columns, the last column acts as one, if auto-sized.
- if (percent == 0.f && available_width > 0 &&
- col_index == column_count_ - 1) {
- col_width += available_width;
- }
- ListView_SetColumnWidth(list_view_, col_index, col_width);
- }
- }
- }
-}
-
-gfx::Size TableView::GetPreferredSize() {
- return preferred_size_;
-}
-
-void TableView::SetPreferredSize(const gfx::Size& size) {
- preferred_size_ = size;
- PreferredSizeChanged();
-}
-
-int TableView::ModelToView(int model_index) const {
- if (!model_to_view_.get())
- return model_index;
- DCHECK_GE(model_index, 0) << " negative model_index " << model_index;
- DCHECK_LT(model_index, RowCount()) << " out of bounds model_index " <<
- model_index;
- return model_to_view_[model_index];
-}
-
-int TableView::ViewToModel(int view_index) const {
- if (!view_to_model_.get())
- return view_index;
- DCHECK_GE(view_index, 0) << " negative view_index " << view_index;
- DCHECK_LT(view_index, RowCount()) << " out of bounds view_index " <<
- view_index;
- return view_to_model_[view_index];
-}
-
-void TableView::SetAltText(const string16& alt_text) {
- if (alt_text == alt_text_)
- return;
-
- alt_text_ = alt_text;
- if (!GetNativeControlHWND())
- return;
-
- RECT alt_text_bounds = GetAltTextBounds().ToRECT();
- InvalidateRect(GetNativeControlHWND(), &alt_text_bounds, FALSE);
-}
-
-void TableView::UpdateListViewCache0(int start, int length, bool add) {
- if (is_sorted()) {
- if (add)
- UpdateItemsLParams(start, length);
- else
- UpdateItemsLParams(0, 0);
- }
-
- LVITEM item = {0};
- if (add) {
- const bool has_groups = model_->HasGroups();
- for (int i = start; i < start + length; ++i) {
- item.mask = has_groups ? (LVIF_GROUPID | LVIF_PARAM) : LVIF_PARAM;
- item.iItem = i;
- if (has_groups)
- item.iGroupId = model_->GetGroupID(i);
- if (model_->ShouldIndent(i)) {
- item.mask |= LVIF_INDENT;
- item.iIndent = 1;
- }
- item.lParam = i;
- ListView_InsertItem(list_view_, &item);
- }
- }
-
- memset(&item, 0, sizeof(LVITEM));
- item.mask =
- (table_type_ == ICON_AND_TEXT) ? (LVIF_IMAGE | LVIF_TEXT) : LVIF_TEXT;
- item.stateMask = 0;
- for (int j = 0; j < column_count_; ++j) {
- ui::TableColumn& col = all_columns_[visible_columns_[j]];
- int max_text_width = ListView_GetStringWidth(list_view_, col.title.c_str());
- for (int i = start; i < start + length; ++i) {
- // Set item.
- item.iItem = add ? i : ModelToView(i);
- item.iSubItem = j;
- string16 text = model_->GetText(i, visible_columns_[j]);
- item.pszText = const_cast<LPWSTR>(text.c_str());
- ListView_SetItem(list_view_, &item);
-
- // Compute width in px, using current font.
- int string_width = ListView_GetStringWidth(list_view_, item.pszText);
- // The width of an icon belongs to the first column.
- if (j == 0 && table_type_ == ICON_AND_TEXT)
- string_width += kListViewIconWidthAndPadding;
- max_text_width = std::max(string_width, max_text_width);
- }
-
- // ListView_GetStringWidth must be padded or else truncation will occur
- // (MSDN). 15px matches the Win32/LVSCW_AUTOSIZE_USEHEADER behavior.
- max_text_width += kListViewTextPadding;
-
- // Protect against partial update.
- if (max_text_width > col.min_visible_width ||
- (start == 0 && length == model_->RowCount())) {
- col.min_visible_width = max_text_width;
- }
- }
-
- if (is_sorted()) {
- // NOTE: As most of our tables are smallish I'm not going to optimize this.
- // If our tables become large and frequently update, then it'll make sense
- // to optimize this.
-
- SortItemsAndUpdateMapping();
- }
-}
-
-void TableView::OnDoubleClick() {
- if (!ignore_listview_change_ && table_view_observer_) {
- table_view_observer_->OnDoubleClick();
- }
-}
-
-void TableView::OnMiddleClick() {
- if (!ignore_listview_change_ && table_view_observer_)
- table_view_observer_->OnMiddleClick();
-}
-
-void TableView::OnSelectedStateChanged() {
- if (!ignore_listview_change_ && table_view_observer_) {
- table_view_observer_->OnSelectionChanged();
- }
-}
-
-bool TableView::OnKeyDown(ui::KeyboardCode virtual_keycode) {
- if (!ignore_listview_change_ && table_view_observer_) {
- table_view_observer_->OnKeyDown(virtual_keycode);
- }
- return false; // Let the key event be processed as ususal.
-}
-
-void TableView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
- if (!list_view_)
- return;
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
- Layout();
- if ((autosize_columns_ || !column_sizes_valid_) && width() > 0)
- ResetColumnSizes();
- UpdateContentOffset();
- SendMessage(list_view_, WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
-}
-
-int TableView::PreviousSelectedViewIndex(int view_index) {
- DCHECK_GE(view_index, 0);
- if (!list_view_ || view_index <= 0)
- return -1;
-
- int row_count = RowCount();
- if (row_count == 0)
- return -1; // Empty table, nothing can be selected.
-
- // For some reason
- // ListView_GetNextItem(list_view_,item, LVNI_SELECTED | LVNI_ABOVE)
- // fails on Vista (always returns -1), so we iterate through the indices.
- view_index = std::min(view_index, row_count);
- while (--view_index >= 0 && !IsItemSelected(ViewToModel(view_index)));
- return view_index;
-}
-
-int TableView::LastSelectedViewIndex() {
- return PreviousSelectedViewIndex(RowCount());
-}
-
-void TableView::UpdateContentOffset() {
- content_offset_ = 0;
-
- if (!list_view_)
- return;
-
- HWND header = ListView_GetHeader(list_view_);
- if (!header)
- return;
-
- POINT origin = {0, 0};
- MapWindowPoints(header, list_view_, &origin, 1);
-
- RECT header_bounds;
- GetWindowRect(header, &header_bounds);
-
- content_offset_ = origin.y + header_bounds.bottom - header_bounds.top;
-}
-
-void TableView::UpdateGroups() {
- // Add the groups.
- if (model_ && model_->HasGroups()) {
- ListView_RemoveAllGroups(list_view_);
-
- // Windows XP seems to disable groups if we remove them, so we
- // re-enable them.
- ListView_EnableGroupView(list_view_, true);
-
- ui::TableModel::Groups groups = model_->GetGroups();
- LVGROUP group = { 0 };
- group.cbSize = sizeof(LVGROUP);
- group.mask = LVGF_HEADER | LVGF_ALIGN | LVGF_GROUPID;
- group.uAlign = LVGA_HEADER_LEFT;
- for (size_t i = 0; i < groups.size(); ++i) {
- group.pszHeader = const_cast<wchar_t*>(groups[i].title.c_str());
- group.iGroupId = groups[i].id;
- ListView_InsertGroup(list_view_, static_cast<int>(i), &group);
- }
- }
-}
-
-gfx::Rect TableView::GetAltTextBounds() {
- static const int kXOffset = 16;
- DCHECK(GetNativeControlHWND());
- RECT client_rect_rect;
- GetClientRect(GetNativeControlHWND(), &client_rect_rect);
- gfx::Rect client_rect(client_rect_rect);
- gfx::Font font = GetAltTextFont();
- // Pad height by 2 for halo.
- return gfx::Rect(kXOffset, content_offset(), client_rect.width() - kXOffset,
- std::max(kImageSize, font.GetHeight() + 2));
-}
-
-gfx::Font TableView::GetAltTextFont() {
- return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
-}
-
-void TableView::VisibilityChanged(View* starting_from, bool is_visible) {
- // GetClientRect as used by ResetColumnSize to obtain the total width
- // available to the columns only works when the native control is visible, so
- // update the column sizes in case we become visible. This depends on
- // VisibilityChanged() being called in post order on the view tree.
- if (is_visible && (autosize_columns_ || !column_sizes_valid_) && width() > 0)
- ResetColumnSizes();
-}
-
-
-//
-// TableSelectionIterator
-//
-TableSelectionIterator::TableSelectionIterator(TableView* view,
- int view_index)
- : table_view_(view),
- view_index_(view_index) {
- UpdateModelIndexFromViewIndex();
-}
-
-TableSelectionIterator& TableSelectionIterator::operator=(
- const TableSelectionIterator& other) {
- view_index_ = other.view_index_;
- model_index_ = other.model_index_;
- return *this;
-}
-
-bool TableSelectionIterator::operator==(const TableSelectionIterator& other) {
- return (other.view_index_ == view_index_);
-}
-
-bool TableSelectionIterator::operator!=(const TableSelectionIterator& other) {
- return (other.view_index_ != view_index_);
-}
-
-TableSelectionIterator& TableSelectionIterator::operator++() {
- view_index_ = table_view_->PreviousSelectedViewIndex(view_index_);
- UpdateModelIndexFromViewIndex();
- return *this;
-}
-
-int TableSelectionIterator::operator*() {
- return model_index_;
-}
-
-void TableSelectionIterator::UpdateModelIndexFromViewIndex() {
- if (view_index_ == -1)
- model_index_ = -1;
- else
- model_index_ = table_view_->ViewToModel(view_index_);
-}
-
-} // namespace views
diff --git a/ui/views/controls/table/table_view_win.h b/ui/views/controls/table/table_view_win.h
deleted file mode 100644
index 66b4719..0000000
--- a/ui/views/controls/table/table_view_win.h
+++ /dev/null
@@ -1,506 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_WIN_H_
-#define UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_WIN_H_
-
-#include <map>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/string16.h"
-#include "build/build_config.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/base/keycodes/keyboard_codes.h"
-#include "ui/base/models/table_model_observer.h"
-#include "ui/views/views_export.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-
-// TODO(port): remove the ifdef when native_control.h is ported.
-#include "ui/views/controls/native_control.h"
-
-typedef struct tagNMLVCUSTOMDRAW NMLVCUSTOMDRAW;
-#endif // defined(OS_WIN)
-
-namespace gfx {
-class Font;
-}
-
-namespace ui {
-struct TableColumn;
-class TableModel;
-}
-
-// A TableView is a view that displays multiple rows with any number of columns.
-// TableView is driven by a TableModel. The model returns the contents
-// to display. TableModel also has an Observer which is used to notify
-// TableView of changes to the model so that the display may be updated
-// appropriately.
-//
-// TableView itself has an observer that is notified when the selection
-// changes.
-//
-// Tables may be sorted either by directly invoking SetSortDescriptors or by
-// marking the column as sortable and the user doing a gesture to sort the
-// contents. TableView itself maintains the sort so that the underlying model
-// isn't effected.
-//
-// When a table is sorted the model coordinates do not necessarily match the
-// view coordinates. All table methods are in terms of the model. If you need to
-// convert to view coordinates use model_to_view.
-//
-// Sorting is done by a locale sensitive string sort. You can customize the
-// sort by way of overriding CompareValues.
-//
-// TableView is a wrapper around the window type ListView in report mode.
-namespace views {
-
-class ListView;
-class ListViewParent;
-class TableView;
-class TableViewObserver;
-
-// The cells in the first column of a table can contain:
-// - only text
-// - a small icon (16x16) and some text
-// - a check box and some text
-enum TableTypes {
- TEXT_ONLY = 0,
- ICON_AND_TEXT,
- CHECK_BOX_AND_TEXT
-};
-
-// Returned from SelectionBegin/SelectionEnd
-class VIEWS_EXPORT TableSelectionIterator {
- public:
- TableSelectionIterator(TableView* view, int view_index);
- TableSelectionIterator& operator=(const TableSelectionIterator& other);
- bool operator==(const TableSelectionIterator& other);
- bool operator!=(const TableSelectionIterator& other);
- TableSelectionIterator& operator++();
- int operator*();
-
- private:
- void UpdateModelIndexFromViewIndex();
-
- TableView* table_view_;
- int view_index_;
-
- // The index in terms of the model. This is returned from the * operator. This
- // is cached to avoid dependencies on the view_to_model mapping.
- int model_index_;
-};
-
-#if defined(OS_WIN)
-// TODO(port): Port TableView.
-class VIEWS_EXPORT TableView : public NativeControl,
- public ui::TableModelObserver {
- public:
- typedef TableSelectionIterator iterator;
-
- // A helper struct for GetCellColors. Set |color_is_set| to true if color is
- // set. See OnCustomDraw for more details on why we need this.
- struct ItemColor {
- bool color_is_set;
- SkColor color;
- };
-
- // Describes a sorted column.
- struct SortDescriptor {
- SortDescriptor() : column_id(-1), ascending(true) {}
- SortDescriptor(int column_id, bool ascending)
- : column_id(column_id),
- ascending(ascending) { }
-
- // ID of the sorted column.
- int column_id;
-
- // Is the sort ascending?
- bool ascending;
- };
-
- typedef std::vector<SortDescriptor> SortDescriptors;
-
- // Creates a new table using the model and columns specified.
- // The table type applies to the content of the first column (text, icon and
- // text, checkbox and text).
- // When autosize_columns is true, columns always fill the available width. If
- // false, columns are not resized when the table is resized. An extra empty
- // column at the right fills the remaining space.
- // When resizable_columns is true, users can resize columns by dragging the
- // separator on the column header. NOTE: Right now this is always true. The
- // code to set it false is still in place to be a base for future, better
- // resizing behavior (see http://b/issue?id=874646 ), but no one uses or
- // tests the case where this flag is false.
- // Note that setting both resizable_columns and autosize_columns to false is
- // probably not a good idea, as there is no way for the user to increase a
- // column's size in that case.
- TableView(ui::TableModel* model, const std::vector<ui::TableColumn>& columns,
- TableTypes table_type, bool single_selection,
- bool resizable_columns, bool autosize_columns);
- virtual ~TableView();
-
- // Assigns a new model to the table view, detaching the old one if present.
- // If |model| is NULL, the table view cannot be used after this call. This
- // should be called in the containing view's destructor to avoid destruction
- // issues when the model needs to be deleted before the table.
- void SetModel(ui::TableModel* model);
- ui::TableModel* model() const { return model_; }
-
- // Returns this. Provided for implementations that need to wrap this in a
- // ScrollView.
- View* CreateParentIfNecessary();
-
- // Resorts the contents.
- void SetSortDescriptors(const SortDescriptors& sort_descriptors);
-
- // Current sort.
- const SortDescriptors& sort_descriptors() const { return sort_descriptors_; }
-
- // Returns the number of rows in the TableView.
- int RowCount() const;
-
- // Returns the number of selected rows.
- int SelectedRowCount();
-
- // Selects the specified item, making sure it's visible.
- void Select(int model_row);
-
- // Sets the selected state of an item (without sending any selection
- // notifications). Note that this routine does NOT set the focus to the
- // item at the given index.
- void SetSelectedState(int model_row, bool state);
-
- // Sets the focus to the item at the given index.
- void SetFocusOnItem(int model_row);
-
- // Returns the first selected row in terms of the model.
- int FirstSelectedRow();
-
- // Returns true if the item at the specified index is selected.
- bool IsItemSelected(int model_row);
-
- // Returns true if the item at the specified index has the focus.
- bool ItemHasTheFocus(int model_row);
-
- // Returns an iterator over the selection. The iterator proceeds from the
- // last index to the first.
- //
- // NOTE: the iterator iterates over the visual order (but returns coordinates
- // in terms of the model).
- iterator SelectionBegin();
- iterator SelectionEnd();
-
- // ui::TableModelObserver methods.
- virtual void OnModelChanged();
- virtual void OnItemsChanged(int start, int length);
- virtual void OnItemsAdded(int start, int length);
- virtual void OnItemsRemoved(int start, int length);
-
- void SetObserver(TableViewObserver* observer) {
- table_view_observer_ = observer;
- }
- TableViewObserver* observer() const { return table_view_observer_; }
-
- // Replaces the set of known columns without changing the current visible
- // columns.
- void SetColumns(const std::vector<ui::TableColumn>& columns);
- void AddColumn(const ui::TableColumn& col);
- bool HasColumn(int id);
-
- // Sets which columns (by id) are displayed. All transient size and position
- // information is lost.
- void SetVisibleColumns(const std::vector<int>& columns);
- void SetColumnVisibility(int id, bool is_visible);
- bool IsColumnVisible(int id) const;
-
- // Resets the size of the columns based on the sizes passed to the
- // constructor. Your normally needn't invoked this, it's done for you the
- // first time the TableView is given a valid size.
- void ResetColumnSizes();
-
- // Sometimes we may want to size the TableView to a specific width and
- // height.
- virtual gfx::Size GetPreferredSize();
- void SetPreferredSize(const gfx::Size& size);
-
- // Is the table sorted?
- bool is_sorted() const { return !sort_descriptors_.empty(); }
-
- // Maps from the index in terms of the model to that of the view.
- int ModelToView(int model_index) const;
-
- // Maps from the index in terms of the view to that of the model.
- int ViewToModel(int view_index) const;
-
- // Sets the text to display on top of the table. This is useful if the table
- // is empty and you want to inform the user why.
- void SetAltText(const string16& alt_text);
-
- protected:
- // Overriden to return the position of the first selected row.
- virtual gfx::Point GetKeyboardContextMenuLocation() OVERRIDE;
-
- // Subclasses that want to customize the colors of a particular row/column,
- // must invoke this passing in true. The default value is false, such that
- // GetCellColors is never invoked.
- void SetCustomColorsEnabled(bool custom_colors_enabled);
-
- // Notification from the ListView that the selected state of an item has
- // changed.
- virtual void OnSelectedStateChanged();
-
- // Notification from the ListView that the used double clicked the table.
- virtual void OnDoubleClick();
-
- // Notification from the ListView that the user middle clicked the table.
- virtual void OnMiddleClick();
-
- // Overridden from NativeControl. Notifies the observer.
- virtual bool OnKeyDown(ui::KeyboardCode virtual_keycode) OVERRIDE;
-
- // View override.
- virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
-
- // Invoked to customize the colors or font at a particular cell. If you
- // change the colors or font, return true. This is only invoked if
- // SetCustomColorsEnabled(true) has been invoked.
- virtual bool GetCellColors(int model_row,
- int column,
- ItemColor* foreground,
- ItemColor* background,
- LOGFONT* logfont);
-
- // Subclasses that want to perform some custom painting (on top of the regular
- // list view painting) should return true here and implement the PostPaint
- // method.
- virtual bool ImplementPostPaint() { return false; }
- // Subclasses can implement in this method extra-painting for cells.
- virtual void PostPaint(int model_row, int column, bool selected,
- const gfx::Rect& bounds, HDC device_context) { }
- virtual void PostPaint() {}
-
- virtual HWND CreateNativeControl(HWND parent_container);
-
- virtual LRESULT OnNotify(int w_param, LPNMHDR l_param);
-
- // Used to sort the two rows. Returns a value < 0, == 0 or > 0 indicating
- // whether the row2 comes before row1, row2 is the same as row1 or row1 comes
- // after row2. This invokes CompareValues on the model with the sorted column.
- virtual int CompareRows(int model_row1, int model_row2);
-
- // Called before sorting. This does nothing and is intended for subclasses
- // that need to cache state used during sorting.
- virtual void PrepareForSort() {}
-
- // Returns the width of the specified column by id, or -1 if the column isn't
- // visible.
- int GetColumnWidth(int column_id);
-
- // Returns the offset from the top of the client area to the start of the
- // content.
- int content_offset() const { return content_offset_; }
-
- // Draws the alt_text_. Does nothing if there is no alt_text_.
- void PaintAltText();
-
- // Size (width and height) of images.
- static const int kImageSize;
-
- private:
- // Direction of a sort.
- enum SortDirection {
- ASCENDING_SORT,
- DESCENDING_SORT,
- NO_SORT
- };
-
- // We need this wrapper to pass the table view to the windows proc handler
- // when subclassing the list view and list view header, as the reinterpret
- // cast from GetWindowLongPtr would break the pointer if it is pointing to a
- // subclass (in the OO sense of TableView).
- struct TableViewWrapper {
- explicit TableViewWrapper(TableView* view) : table_view(view) { }
- TableView* table_view;
- };
-
- friend class ListViewParent;
- friend class TableSelectionIterator;
- friend class GroupModelTableViewTest;
- FRIEND_TEST_ALL_PREFIXES(GroupModelTableViewTest, ShiftSelectAcrossGroups);
- FRIEND_TEST_ALL_PREFIXES(GroupModelTableViewTest, ShiftSelectSameGroup);
-
- LRESULT OnCustomDraw(NMLVCUSTOMDRAW* draw_info);
-
- // Invoked when the user clicks on a column to toggle the sort order. If
- // column_id is the primary sorted column the direction of the sort is
- // toggled, otherwise column_id is made the primary sorted column.
- void ToggleSortOrder(int column_id);
-
- // Updates the lparam of each of the list view items to be the model index.
- // If length is > 0, all items with an index >= start get offset by length.
- // This is used during sorting to determine how the items were sorted.
- void UpdateItemsLParams(int start, int length);
-
- // Does the actual sort and updates the mappings (view_to_model and
- // model_to_view) appropriately.
- void SortItemsAndUpdateMapping();
-
- // Selects multiple items from the current view row to the marked view row
- // (implements shift-click behavior). |view_index| is the most recent row
- // that the user clicked on, and so there is no guarantee that
- // |view_index| > |mark_view_index| or vice-versa. Returns false if the
- // selection attempt was rejected because it crossed group boundaries.
- bool SelectMultiple(int view_index, int mark_view_index);
-
- // Method invoked by ListView to compare the two values. Invokes CompareRows.
- static int CALLBACK SortFunc(LPARAM model_index_1_p,
- LPARAM model_index_2_p,
- LPARAM table_view_param);
-
- // Method invoked by ListView when sorting back to natural state. Returns
- // model_index_1_p - model_index_2_p.
- static int CALLBACK NaturalSortFunc(LPARAM model_index_1_p,
- LPARAM model_index_2_p,
- LPARAM table_view_param);
-
- // Resets the sort image displayed for the specified column.
- void ResetColumnSortImage(int column_id, SortDirection direction);
-
- // Adds a new column.
- void InsertColumn(const ui::TableColumn& tc, int index);
-
- // Update headers and internal state after columns have changed
- void OnColumnsChanged();
-
- // Updates the ListView with values from the model. See UpdateListViewCache0
- // for a complete description.
- // This turns off redrawing, and invokes UpdateListViewCache0 to do the
- // actual updating.
- void UpdateListViewCache(int start, int length, bool add);
-
- // Updates ListView with values from the model.
- // If add is true, this adds length items starting at index start.
- // If add is not true, the items are not added, the but the values in the
- // range start - [start + length] are updated from the model.
- void UpdateListViewCache0(int start, int length, bool add);
-
- // Returns the index of the selected item before |view_index|, or -1 if
- // |view_index| is the first selected item.
- //
- // WARNING: this returns coordinates in terms of the view, NOT the model.
- int PreviousSelectedViewIndex(int view_index);
-
- // Returns the last selected view index in the table view, or -1 if the table
- // is empty, or nothing is selected.
- //
- // WARNING: this returns coordinates in terms of the view, NOT the model.
- int LastSelectedViewIndex();
-
- // The TableColumn visible at position pos.
- const ui::TableColumn& GetColumnAtPosition(int pos);
-
- // Window procedure of the list view class. We subclass the list view to
- // ignore WM_ERASEBKGND, which gives smoother painting during resizing.
- static LRESULT CALLBACK TableWndProc(HWND window,
- UINT message,
- WPARAM w_param,
- LPARAM l_param);
-
- // Window procedure of the header class. We subclass the header of the table
- // to disable resizing of columns.
- static LRESULT CALLBACK TableHeaderWndProc(HWND window, UINT message,
- WPARAM w_param, LPARAM l_param);
-
- // Updates content_offset_ from the position of the header.
- void UpdateContentOffset();
-
- // Reloads the groups from the model if there is one and it has groups.
- void UpdateGroups();
-
- // Returns the bounds of the alt text.
- gfx::Rect GetAltTextBounds();
-
- // Returns the font used for alt text.
- gfx::Font GetAltTextFont();
-
- // Overriden in order to update the column sizes, which can only be sized
- // accurately when the native control is available.
- virtual void VisibilityChanged(View* starting_from, bool is_visible);
-
- ui::TableModel* model_;
- TableTypes table_type_;
- TableViewObserver* table_view_observer_;
-
- // An ordered list of id's into |all_columns_| representing current visible
- // columns.
- std::vector<int> visible_columns_;
-
- // Mapping of an int id to a TableColumn representing all possible columns.
- std::map<int, ui::TableColumn> all_columns_;
-
- // Cached value of columns_.size()
- int column_count_;
-
- // Selection mode.
- bool single_selection_;
-
- // If true, any events that would normally be propagated to the observer
- // are ignored. For example, if this is true and the selection changes in
- // the listview, the observer is not notified.
- bool ignore_listview_change_;
-
- // Reflects the value passed to SetCustomColorsEnabled.
- bool custom_colors_enabled_;
-
- // Whether or not columns should automatically be resized to fill the
- // the available width when the list view is resized.
- bool autosize_columns_;
-
- // Whether or not the user can resize columns.
- bool resizable_columns_;
-
- // Whether the column sizes have been determined.
- bool column_sizes_valid_;
-
- // NOTE: While this has the name View in it, it's not a view. Rather it's
- // a wrapper around the List-View window.
- HWND list_view_;
-
- // The list view's header original proc handler. It is required when
- // subclassing.
- WNDPROC header_original_handler_;
-
- // Window procedure of the listview before we subclassed it.
- WNDPROC original_handler_;
-
- // A wrapper around 'this' used when "subclassing" the list view and header.
- TableViewWrapper table_view_wrapper_;
-
- // A custom font we use when overriding the font type for a specific cell.
- HFONT custom_cell_font_;
-
- // The preferred size of the table view.
- gfx::Size preferred_size_;
-
- int content_offset_;
-
- // Current sort.
- SortDescriptors sort_descriptors_;
-
- // Mappings used when sorted.
- scoped_ptr<int[]> view_to_model_;
- scoped_ptr<int[]> model_to_view_;
-
- string16 alt_text_;
-
- DISALLOW_COPY_AND_ASSIGN(TableView);
-};
-#endif // defined(OS_WIN)
-
-} // namespace views
-
-#endif // UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_WIN_H_
diff --git a/ui/views/examples/table_example.cc b/ui/views/examples/table_example.cc
index 546b6ed..e0b876d 100644
--- a/ui/views/examples/table_example.cc
+++ b/ui/views/examples/table_example.cc
@@ -12,7 +12,6 @@
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/controls/button/checkbox.h"
-#include "ui/views/controls/table/group_table_model.h"
#include "ui/views/layout/grid_layout.h"
namespace views {
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index 045f829..857fc8c 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -203,19 +203,14 @@
'controls/tabbed_pane/tabbed_pane.cc',
'controls/tabbed_pane/tabbed_pane.h',
'controls/tabbed_pane/tabbed_pane_listener.h',
- 'controls/table/group_table_model.h',
- 'controls/table/group_table_view.h',
- 'controls/table/group_table_view_views.cc',
- 'controls/table/group_table_view_views.h',
'controls/table/table_header.cc',
'controls/table/table_header.h',
'controls/table/table_utils.cc',
'controls/table/table_utils.h',
+ 'controls/table/table_view.cc',
'controls/table/table_view.h',
'controls/table/table_view_observer.h',
'controls/table/table_view_row_background_painter.h',
- 'controls/table/table_view_views.cc',
- 'controls/table/table_view_views.h',
'controls/textfield/native_textfield_views.cc',
'controls/textfield/native_textfield_views.h',
'controls/textfield/native_textfield_win.cc',
@@ -443,9 +438,6 @@
'controls/native_control.h',
'controls/scrollbar/bitmap_scroll_bar.cc',
'controls/scrollbar/bitmap_scroll_bar.h',
- 'controls/table/group_table_model.h',
- 'controls/table/group_table_view.cc',
- 'controls/table/group_table_view.h',
'controls/table/table_view_observer.h',
'widget/aero_tooltip_manager.cc',
'widget/aero_tooltip_manager.h',
@@ -660,7 +652,7 @@
'controls/slider_unittest.cc',
'controls/tabbed_pane/tabbed_pane_unittest.cc',
'controls/table/table_utils_unittest.cc',
- 'controls/table/table_view_views_unittest.cc',
+ 'controls/table/table_view_unittest.cc',
'controls/table/test_table_model.cc',
'controls/table/test_table_model.h',
'controls/textfield/native_textfield_views_unittest.cc',