diff options
Diffstat (limited to 'views/controls/table/table_view.h')
-rw-r--r-- | views/controls/table/table_view.h | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/views/controls/table/table_view.h b/views/controls/table/table_view.h new file mode 100644 index 0000000..8061d27 --- /dev/null +++ b/views/controls/table/table_view.h @@ -0,0 +1,676 @@ +// Copyright (c) 2006-2008 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 VIEWS_CONTROLS_TABLE_TABLE_VIEW_H_ +#define VIEWS_CONTROLS_TABLE_TABLE_VIEW_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif // defined(OS_WIN) + +#include <map> +#include <unicode/coll.h> +#include <unicode/uchar.h> +#include <vector> + +#include "app/l10n_util.h" +#include "base/logging.h" +#include "skia/include/SkColor.h" +#if defined(OS_WIN) +// TODO(port): remove the ifdef when native_control.h is ported. +#include "views/controls/native_control.h" +#endif // defined(OS_WIN) + +class SkBitmap; + +// 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; +struct TableColumn; + +// 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 +}; + +// Any time the TableModel changes, it must notify its observer. +class TableModelObserver { + public: + // Invoked when the model has been completely changed. + virtual void OnModelChanged() = 0; + + // Invoked when a range of items has changed. + virtual void OnItemsChanged(int start, int length) = 0; + + // Invoked when new items are added. + virtual void OnItemsAdded(int start, int length) = 0; + + // Invoked when a range of items has been removed. + virtual void OnItemsRemoved(int start, int length) = 0; +}; + +// The model driving the TableView. +class TableModel { + public: + // See HasGroups, get GetGroupID for details as to how this is used. + struct Group { + // The title text for the group. + std::wstring title; + + // Unique id for the group. + int id; + }; + typedef std::vector<Group> Groups; + + // Number of rows in the model. + virtual int RowCount() = 0; + + // Returns the value at a particular location in text. + virtual std::wstring GetText(int row, int column_id) = 0; + + // Returns the small icon (16x16) that should be displayed in the first + // column before the text. This is only used when the TableView was created + // with the ICON_AND_TEXT table type. Returns an isNull() bitmap if there is + // no bitmap. + virtual SkBitmap GetIcon(int row); + + // Sets whether a particular row is checked. This is only invoked + // if the TableView was created with show_check_in_first_column true. + virtual void SetChecked(int row, bool is_checked) { + NOTREACHED(); + } + + // Returns whether a particular row is checked. This is only invoked + // if the TableView was created with show_check_in_first_column true. + virtual bool IsChecked(int row) { + return false; + } + + // Returns true if the TableView has groups. Groups provide a way to visually + // delineate the rows in a table view. When groups are enabled table view + // shows a visual separator for each group, followed by all the rows in + // the group. + // + // On win2k a visual separator is not rendered for the group headers. + virtual bool HasGroups() { return false; } + + // Returns the groups. + // This is only used if HasGroups returns true. + virtual Groups GetGroups() { + // If you override HasGroups to return true, you must override this as + // well. + NOTREACHED(); + return std::vector<Group>(); + } + + // Returns the group id of the specified row. + // This is only used if HasGroups returns true. + virtual int GetGroupID(int row) { + // If you override HasGroups to return true, you must override this as + // well. + NOTREACHED(); + return 0; + } + + // Sets the observer for the model. The TableView should NOT take ownership + // of the observer. + virtual void SetObserver(TableModelObserver* observer) = 0; + + // Compares the values in the column with id |column_id| for the two rows. + // Returns a value < 0, == 0 or > 0 as to whether the first value is + // <, == or > the second value. + // + // This implementation does a case insensitive locale specific string + // comparison. + virtual int CompareValues(int row1, int row2, int column_id); + + protected: + // Returns the collator used by CompareValues. + Collator* GetCollator(); +}; + +// TableColumn specifies the title, alignment and size of a particular column. +struct TableColumn { + enum Alignment { + LEFT, RIGHT, CENTER + }; + + TableColumn() + : id(0), + title(), + alignment(LEFT), + width(-1), + percent(), + min_visible_width(0), + sortable(false) { + } + TableColumn(int id, const std::wstring title, Alignment alignment, int width) + : id(id), + title(title), + alignment(alignment), + width(width), + percent(0), + min_visible_width(0), + sortable(false) { + } + TableColumn(int id, const std::wstring title, Alignment alignment, int width, + float percent) + : id(id), + title(title), + alignment(alignment), + width(width), + percent(percent), + min_visible_width(0), + sortable(false) { + } + // It's common (but not required) to use the title's IDS_* tag as the column + // id. In this case, the provided conveniences look up the title string on + // bahalf of the caller. + TableColumn(int id, Alignment alignment, int width) + : id(id), + alignment(alignment), + width(width), + percent(0), + min_visible_width(0), + sortable(false) { + title = l10n_util::GetString(id); + } + TableColumn(int id, Alignment alignment, int width, float percent) + : id(id), + alignment(alignment), + width(width), + percent(percent), + min_visible_width(0), + sortable(false) { + title = l10n_util::GetString(id); + } + + // A unique identifier for the column. + int id; + + // The title for the column. + std::wstring title; + + // Alignment for the content. + Alignment alignment; + + // The size of a column may be specified in two ways: + // 1. A fixed width. Set the width field to a positive number and the + // column will be given that width, in pixels. + // 2. As a percentage of the available width. If width is -1, and percent is + // > 0, the column is given a width of + // available_width * percent / total_percent. + // 3. If the width == -1 and percent == 0, the column is autosized based on + // the width of the column header text. + // + // Sizing is done in four passes. Fixed width columns are given + // their width, percentages are applied, autosized columns are autosized, + // and finally percentages are applied again taking into account the widths + // of autosized columns. + int width; + float percent; + + // The minimum width required for all items in this column + // (including the header) + // to be visible. + int min_visible_width; + + // Is this column sortable? Default is false + bool sortable; +}; + +// Returned from SelectionBegin/SelectionEnd +class 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_; +}; + +// TableViewObserver is notified about the TableView selection. +class TableViewObserver { + public: + virtual ~TableViewObserver() {} + + // Invoked when the selection changes. + virtual void OnSelectionChanged() = 0; + + // Optional method invoked when the user double clicks on the table. + virtual void OnDoubleClick() {} + + // Optional method invoked when the user hits a key with the table in focus. + virtual void OnKeyDown(unsigned short virtual_keycode) {} + + // Invoked when the user presses the delete key. + virtual void OnTableViewDelete(TableView* table_view) {} +}; + +#if defined(OS_WIN) +// TODO(port): Port TableView. +class TableView : public NativeControl, + public 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(TableModel* model, const std::vector<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(TableModel* model); + TableModel* model() const { return model_; } + + // Resorts the contents. + void SetSortDescriptors(const SortDescriptors& sort_descriptors); + + // Current sort. + const SortDescriptors& sort_descriptors() const { return sort_descriptors_; } + + void DidChangeBounds(const gfx::Rect& previous, + const gfx::Rect& current); + + // Returns the number of rows in the TableView. + int RowCount(); + + // 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(); + + // 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<TableColumn>& columns); + void AddColumn(const 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 set_preferred_size(const gfx::Size& size) { preferred_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 model_to_view(int model_index) const { + return model_to_view_.get() ? model_to_view_[model_index] : model_index; + } + + // Maps from the index in terms of the view to that of the model. + int view_to_model(int view_index) const { + return view_to_model_.get() ? view_to_model_[view_index] : view_index; + } + + protected: + // Overriden to return the position of the first selected row. + virtual gfx::Point GetKeyboardContextMenuLocation(); + + // 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(); + + // Subclasses can implement this method if they need to be notified of a key + // press event. Other wise, it appeals to table_view_observer_ + virtual void OnKeyDown(unsigned short virtual_keycode); + + // 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 CRect& bounds, HDC device_context) { } + virtual void PostPaint() {} + + virtual HWND CreateNativeControl(HWND parent_container); + + virtual LRESULT OnNotify(int w_param, LPNMHDR l_param); + + // Overriden to destroy the image list. + virtual void OnDestroy(); + + // 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_; } + + // 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; + + 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(); + + // 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 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); + + // Notification from the ListView that the checked state of the item has + // changed. + void OnCheckedStateChanged(int model_row, bool is_checked); + + // 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 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(); + + 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, 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 the columns have been sized in the ListView. This is + // set to true the first time Layout() is invoked and we have a valid size. + bool sized_columns_; + + // 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_; + + // 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_; + + DISALLOW_COPY_AND_ASSIGN(TableView); +}; +#endif // defined(OS_WIN) + +} // namespace views + +#endif // VIEWS_CONTROLS_TABLE_TABLE_VIEW_H_ |