diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-05 00:42:17 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-05 00:42:17 +0000 |
commit | 9c2fd4b882fd801e607d48befc8b4cac50865393 (patch) | |
tree | 35df102517da24d1e3e1c81e908b2193affbc2f6 /ui | |
parent | 4ea6de7e64205a2d897bc28aa2a05504174956b7 (diff) | |
download | chromium_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.h | 28 | ||||
-rw-r--r-- | ui/views/controls/table/group_table_view.h | 11 | ||||
-rw-r--r-- | ui/views/controls/table/group_table_view_views.cc | 31 | ||||
-rw-r--r-- | ui/views/controls/table/group_table_view_views.h | 37 | ||||
-rw-r--r-- | ui/views/controls/table/group_table_view_win.cc | 202 | ||||
-rw-r--r-- | ui/views/controls/table/group_table_view_win.h | 75 | ||||
-rw-r--r-- | ui/views/controls/table/table_grouper.h | 5 | ||||
-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.h | 360 | ||||
-rw-r--r-- | ui/views/controls/table/table_view_unittest.cc | 928 | ||||
-rw-r--r-- | ui/views/controls/table/table_view_views.h | 361 | ||||
-rw-r--r-- | ui/views/controls/table/table_view_views_unittest.cc | 741 | ||||
-rw-r--r-- | ui/views/controls/table/table_view_win.cc | 1646 | ||||
-rw-r--r-- | ui/views/controls/table/table_view_win.h | 506 | ||||
-rw-r--r-- | ui/views/examples/table_example.cc | 1 | ||||
-rw-r--r-- | ui/views/views.gyp | 12 |
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', |