blob: 5ca0f0125fe1d86462c411912e6f5df105bfce08 [file] [log] [blame]
// 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_
#pragma once
#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_array<int> view_to_model_;
scoped_array<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_