summaryrefslogtreecommitdiffstats
path: root/views
diff options
context:
space:
mode:
authorjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-12 00:26:44 +0000
committerjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-12 00:26:44 +0000
commitd0545b7f65cfee49e1bdfc10d256886c1fd89818 (patch)
tree65b8d32b4937e379fff7f522937095bbdf9f3654 /views
parentcdcb3bd006d0823d45187f2c7fe3c3adc0ebb031 (diff)
downloadchromium_src-d0545b7f65cfee49e1bdfc10d256886c1fd89818.zip
chromium_src-d0545b7f65cfee49e1bdfc10d256886c1fd89818.tar.gz
chromium_src-d0545b7f65cfee49e1bdfc10d256886c1fd89818.tar.bz2
Refactoring of the TableView so it uses NativeViewControl.
At this point it does not support grouping or sorting. Mostly refactoring things from TableView to TableView2 and NativeTableWin. BUG=None TEST=Unit-tests and view examples for now. R=sky Review URL: http://codereview.chromium.org/387021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31741 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r--views/controls/table/native_table_win.cc893
-rw-r--r--views/controls/table/native_table_win.h130
-rw-r--r--views/controls/table/native_table_wrapper.h76
-rw-r--r--views/controls/table/table_view2.cc338
-rw-r--r--views/controls/table/table_view2.h238
-rw-r--r--views/controls/table/table_view_observer.h4
-rw-r--r--views/controls/table/table_view_unittest.cc213
-rw-r--r--views/examples/examples_main.cc14
-rw-r--r--views/examples/examples_main.h2
-rw-r--r--views/examples/table2_example.h168
-rw-r--r--views/examples/table_example.h169
-rw-r--r--views/views.gyp7
12 files changed, 2249 insertions, 3 deletions
diff --git a/views/controls/table/native_table_win.cc b/views/controls/table/native_table_win.cc
new file mode 100644
index 0000000..a7564d6
--- /dev/null
+++ b/views/controls/table/native_table_win.cc
@@ -0,0 +1,893 @@
+// Copyright (c) 2009 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 "views/controls/table/native_table_win.h"
+
+#include <commctrl.h>
+#include <windowsx.h>
+
+#include "app/gfx/canvas.h"
+#include "app/gfx/favicon_size.h"
+#include "app/gfx/icon_util.h"
+#include "app/l10n_util.h"
+#include "app/l10n_util_win.h"
+#include "app/table_model.h"
+#include "base/logging.h"
+#include "base/win_util.h"
+#include "skia/ext/skia_utils_win.h"
+#include "views/controls/table/table_view2.h"
+#include "views/controls/table/table_view_observer.h"
+#include "views/widget/widget.h"
+
+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;
+
+// static
+const int NativeTableWin::kImageSize = 18;
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeTableWin, public:
+
+NativeTableWin::NativeTableWin(TableView2* table)
+ : ignore_listview_change_(false),
+ table_(table),
+ content_offset_(0),
+ header_original_handler_(NULL),
+ original_handler_(NULL) {
+ // Associates the actual HWND with the table so the table is the one
+ // considered as having the focus (not the wrapper) when the HWND is
+ // focused directly (with a click for example).
+ set_focus_view(table);
+}
+
+NativeTableWin::~NativeTableWin() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeTableWin, NativeTableWrapper implementation:
+
+int NativeTableWin::GetRowCount() {
+ if (!native_view())
+ return 0;
+ return ListView_GetItemCount(native_view());
+}
+
+void NativeTableWin::InsertColumn(const TableColumn& tc, int index) {
+ if (!native_view())
+ return;
+
+ LVCOLUMN column = { 0 };
+ column.mask = LVCF_TEXT|LVCF_FMT;
+ column.pszText = const_cast<LPWSTR>(tc.title.c_str());
+ switch (tc.alignment) {
+ case TableColumn::LEFT:
+ column.fmt = LVCFMT_LEFT;
+ break;
+ case TableColumn::RIGHT:
+ column.fmt = LVCFMT_RIGHT;
+ break;
+ case 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(native_view(), LVM_INSERTCOLUMN, index,
+ reinterpret_cast<LPARAM>(&column));
+}
+
+void NativeTableWin::RemoveColumn(int index) {
+ if (!native_view())
+ return;
+ SendMessage(native_view(), LVM_DELETECOLUMN, index, 0);
+ if (table_->model()->RowCount() > 0)
+ OnRowsChanged(0, table_->model()->RowCount() - 1);
+}
+
+View* NativeTableWin::GetView() {
+ return this;
+}
+
+void NativeTableWin::SetFocus() {
+ // Focus the associated HWND.
+ Focus();
+}
+
+gfx::NativeView NativeTableWin::GetTestingHandle() const {
+ return native_view();
+}
+
+int NativeTableWin::GetColumnWidth(int column_index) {
+ if (!native_view())
+ return 0;
+ return ListView_GetColumnWidth(native_view(), column_index);
+}
+
+void NativeTableWin::SetColumnWidth(int column_index, int width) {
+ if (!native_view())
+ return;
+ ListView_SetColumnWidth(native_view(), column_index, width);
+}
+
+int NativeTableWin::GetSelectedRowCount() {
+ if (!native_view())
+ return 0;
+ return ListView_GetSelectedCount(native_view());
+}
+
+int NativeTableWin::GetFirstSelectedRow() {
+ if (!native_view())
+ return -1;
+ return ListView_GetNextItem(native_view(), -1, LVNI_ALL | LVIS_SELECTED);
+}
+
+int NativeTableWin::GetFirstFocusedRow() {
+ if (!native_view())
+ return -1;
+ return ListView_GetNextItem(native_view(), -1, LVNI_ALL | LVIS_FOCUSED);
+}
+
+void NativeTableWin::ClearSelection() {
+ if (native_view())
+ ListView_SetItemState(native_view(), -1, 0, LVIS_SELECTED);
+}
+
+void NativeTableWin::ClearRowFocus() {
+ if (native_view())
+ ListView_SetItemState(native_view(), -1, 0, LVIS_FOCUSED);
+}
+
+void NativeTableWin::SetSelectedState(int model_row, bool state) {
+ if (!native_view())
+ return;
+
+ SendMessage(native_view(), WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
+ ListView_SetItemState(native_view(), model_row,
+ state ? LVIS_SELECTED : 0, LVIS_SELECTED);
+ // Make the selected row visible.
+ ListView_EnsureVisible(native_view(), model_row, FALSE);
+ SendMessage(native_view(), WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
+}
+
+void NativeTableWin::SetFocusState(int model_row, bool state) {
+ if (!native_view())
+ return;
+ ListView_SetItemState(native_view(), model_row,
+ state ? LVIS_FOCUSED : 0, LVIS_FOCUSED)
+}
+
+bool NativeTableWin::IsRowSelected(int model_row) {
+ if (!native_view())
+ return false;
+ return ListView_GetItemState(native_view(), model_row, LVIS_SELECTED) ==
+ LVIS_SELECTED;
+}
+
+bool NativeTableWin::IsRowFocused(int model_row) {
+ if (!native_view())
+ return false;
+ return ListView_GetItemState(native_view(), model_row, LVIS_FOCUSED) ==
+ LVIS_FOCUSED;
+}
+
+void NativeTableWin::OnRowsChanged(int start, int length) {
+ if (!native_view())
+ return;
+
+ SendMessage(native_view(), WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
+ UpdateListViewCache(start, length, false);
+ SendMessage(native_view(), WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
+}
+
+void NativeTableWin::OnRowsAdded(int start, int length) {
+ if (!native_view())
+ return;
+
+ SendMessage(native_view(), WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
+ UpdateListViewCache(start, length, true);
+ SendMessage(native_view(), WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
+}
+
+void NativeTableWin::OnRowsRemoved(int start, int length) {
+ if (!native_view())
+ return;
+
+ SendMessage(native_view(), WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
+
+ bool had_selection = (GetSelectedRowCount() > 0);
+ int old_row_count = GetRowCount();
+ if (start == 0 && length == GetRowCount()) {
+ // Everything was removed.
+ ListView_DeleteAllItems(native_view());
+ } else {
+ for (int i = 0; i < length; ++i)
+ ListView_DeleteItem(native_view(), start);
+ }
+
+ SendMessage(native_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 (table_->model() && table_->observer() && had_selection &&
+ GetRowCount() == 0) {
+ table_->observer()->OnSelectionChanged();
+ }
+}
+
+gfx::Rect NativeTableWin::GetBounds() {
+ RECT native_bounds;
+ if (!native_view() || GetClientRect(native_view(), &native_bounds))
+ return gfx::Rect();
+ return gfx::Rect(native_bounds);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeTableWin, View overrides:
+
+gfx::Size NativeTableWin::GetPreferredSize() {
+ SIZE sz = {0};
+ SendMessage(native_view(), BCM_GETIDEALSIZE, 0,
+ reinterpret_cast<LPARAM>(&sz));
+
+ return gfx::Size(sz.cx, sz.cy);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeTableWin, NativeControlWin overrides:
+
+bool NativeTableWin::ProcessMessage(UINT message, WPARAM w_param,
+ LPARAM l_param, LRESULT* result) {
+ if (message == WM_NOTIFY) {
+ LPNMHDR hdr = reinterpret_cast<LPNMHDR>(l_param);
+ switch (hdr->code) {
+ case NM_CUSTOMDRAW: {
+ // Draw notification. dwDragState indicates the current stage of
+ // drawing.
+ *result = OnCustomDraw(reinterpret_cast<NMLVCUSTOMDRAW*>(hdr));
+ return true;
+ }
+
+ case LVN_ITEMCHANGED: {
+ // Notification that the state of an item has changed. The state
+ // includes such things as whether the item is selected or checked.
+ 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();
+ }
+ if ((state_change->uOldState & LVIS_STATEIMAGEMASK) !=
+ (state_change->uNewState & LVIS_STATEIMAGEMASK)) {
+ // Checked state of the item changed.
+ bool is_checked =
+ ((state_change->uNewState & LVIS_STATEIMAGEMASK) ==
+ INDEXTOSTATEIMAGEMASK(2));
+ OnCheckedStateChanged(state_change->iItem, is_checked);
+ }
+ }
+ break;
+ }
+
+ case HDN_BEGINTRACKW:
+ case HDN_BEGINTRACKA:
+ // Prevent clicks so columns cannot be resized.
+ if (!table_->resizable_columns())
+ return true;
+ break;
+
+ case NM_DBLCLK:
+ OnDoubleClick();
+ break;
+
+ case LVN_COLUMNCLICK: {
+ const TableColumn& column = table_->GetVisibleColumnAt(
+ reinterpret_cast<NMLISTVIEW*>(hdr)->iSubItem);
+ break;
+ }
+
+ case LVN_MARQUEEBEGIN: // We don't want the marque selection.
+ return true;
+
+ default:
+ break;
+ }
+ }
+
+ return NativeControlWin::ProcessMessage(message, w_param, l_param, result);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeTableWin, protected:
+
+void NativeTableWin::CreateNativeControl() {
+ int style = WS_CHILD | LVS_REPORT | LVS_SHOWSELALWAYS;
+ if (table_->single_selection())
+ style |= LVS_SINGLESEL;
+ // If there's only one column and the title string is empty, don't show a
+ // header.
+ if (table_->GetVisibleColumnCount() == 1U) {
+ if (table_->GetVisibleColumnAt(1).title.empty())
+ style |= LVS_NOCOLUMNHEADER;
+ }
+ HWND hwnd = ::CreateWindowEx(WS_EX_CLIENTEDGE | GetAdditionalRTLStyle(),
+ WC_LISTVIEW,
+ L"",
+ style,
+ 0, 0, width(), height(),
+ table_->GetWidget()->GetNativeView(),
+ NULL, NULL, NULL);
+
+ // Make the selection extend across the row.
+ // Reduce overdraw/flicker artifacts by double buffering.
+ DWORD list_view_style = LVS_EX_FULLROWSELECT;
+ if (win_util::GetWinVersion() > win_util::WINVERSION_2000) {
+ list_view_style |= LVS_EX_DOUBLEBUFFER;
+ }
+ if (table_->type() == CHECK_BOX_AND_TEXT)
+ list_view_style |= LVS_EX_CHECKBOXES;
+ ListView_SetExtendedListViewStyleEx(hwnd, 0, list_view_style);
+ l10n_util::AdjustUIFontForWindow(hwnd);
+
+ NativeControlCreated(hwnd);
+ // native_view() is now valid.
+
+ // Add the columns.
+ for (size_t i = 0; i < table_->GetVisibleColumnCount(); ++i)
+ InsertColumn(table_->GetVisibleColumnAt(i), i);
+
+ if (table_->model())
+ UpdateListViewCache(0, table_->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(kImageSize, kImageSize, false);
+ // Make the background completely transparent.
+ canvas.drawColor(SK_ColorBLACK, SkXfermode::kClear_Mode);
+ HICON empty_icon =
+ IconUtil::CreateHICONFromSkBitmap(canvas.ExtractBitmap());
+ ImageList_AddIcon(image_list, empty_icon);
+ ImageList_AddIcon(image_list, empty_icon);
+ DeleteObject(empty_icon);
+ ListView_SetImageList(native_view(), image_list, LVSIL_SMALL);
+ }
+
+ if (!table_->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(native_view());
+ DCHECK(header);
+ SetWindowLongPtr(header, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
+ header_original_handler_ =
+ win_util::SetWindowProc(header, &NativeTableWin::TableHeaderWndProc);
+ }
+
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
+ original_handler_ =
+ win_util::SetWindowProc(hwnd, &NativeTableWin::TableWndProc);
+
+ // Bug 964884: detach the IME attached to this window.
+ // We should attach IMEs only when we need to input CJK strings.
+ ::ImmAssociateContextEx(hwnd, NULL, 0);
+
+ UpdateContentOffset();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeTableWin, private:
+
+void NativeTableWin::Select(int model_row) {
+ if (!native_view())
+ return;
+
+ DCHECK(model_row >= 0 && model_row < table_->model()->RowCount());
+ SendMessage(native_view(), WM_SETREDRAW, static_cast<WPARAM>(FALSE), 0);
+ ignore_listview_change_ = true;
+
+ // Unselect everything.
+ ClearSelection();
+
+ // Select the specified item.
+ SetSelectedState(model_row, true);
+ SetFocusState(model_row, true);
+
+ // Make it visible.
+ ListView_EnsureVisible(native_view(), model_row, FALSE);
+ ignore_listview_change_ = false;
+ SendMessage(native_view(), WM_SETREDRAW, static_cast<WPARAM>(TRUE), 0);
+ if (table_->observer())
+ table_->observer()->OnSelectionChanged();
+}
+
+void NativeTableWin::OnSelectedStateChanged() {
+ if (!ignore_listview_change_ && table_->observer())
+ table_->observer()->OnSelectionChanged();
+}
+
+void NativeTableWin::OnDoubleClick() {
+ if (!ignore_listview_change_ && table_->observer())
+ table_->observer()->OnDoubleClick();
+}
+
+void NativeTableWin::OnMiddleClick() {
+ if (!ignore_listview_change_ && table_->observer())
+ table_->observer()->OnMiddleClick();
+}
+
+bool NativeTableWin::OnKeyDown(base::KeyboardCode virtual_keycode) {
+ if (!ignore_listview_change_ && table_->observer())
+ table_->observer()->OnKeyDown(virtual_keycode);
+ return false; // Let the key event be processed as ususal.
+}
+
+void NativeTableWin::OnCheckedStateChanged(int model_row, bool is_checked) {
+ if (!ignore_listview_change_)
+ table_->model()->SetChecked(model_row, is_checked);
+}
+
+LRESULT NativeTableWin::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): {
+ // TODO(jcampan): implement custom colors and fonts.
+ return CDRF_DODEFAULT;
+ }
+ case CDDS_ITEMPOSTPAINT: {
+ DCHECK(table_->type() == ICON_AND_TEXT);
+ int view_index = static_cast<int>(draw_info->nmcd.dwItemSpec);
+ // We get notifications for empty items, just ignore them.
+ if (view_index >= table_->model()->RowCount())
+ return CDRF_DODEFAULT;
+ LRESULT r = CDRF_DODEFAULT;
+ // First let's take care of painting the right icon.
+ if (table_->type() == ICON_AND_TEXT) {
+ SkBitmap image = table_->model()->GetIcon(view_index);
+ if (!image.isNull()) {
+ // Get the rect that holds the icon.
+ RECT icon_rect, client_rect;
+ if (ListView_GetItemRect(native_view(), view_index, &icon_rect,
+ LVIR_ICON) &&
+ GetClientRect(native_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(icon_rect.right - icon_rect.left,
+ icon_rect.bottom - icon_rect.top, false);
+
+ // It seems the state in nmcd.uItemState is not correct.
+ // We'll retrieve it explicitly.
+ int selected = ListView_GetItemState(
+ native_view(), view_index, LVIS_SELECTED | LVIS_DROPHILITED);
+ bool drop_highlight = ((selected & LVIS_DROPHILITED) != 0);
+ int bg_color_index;
+ if (!IsEnabled())
+ 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.DrawBitmapInt(image, 0, 0,
+ image.width(), image.height(),
+ 1, 1, kFavIconSize, 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);
+ canvas.getTopPlatformDevice().drawToHDC(draw_info->nmcd.hdc,
+ intersection.left,
+ intersection.top,
+ &to_draw);
+ r = CDRF_SKIPDEFAULT;
+ }
+ }
+ }
+ }
+ return r;
+ }
+ default:
+ return CDRF_DODEFAULT;
+ }
+}
+
+void NativeTableWin::UpdateListViewCache(int start, int length, bool add) {
+ LVITEM item = {0};
+ int start_column = 0;
+ int max_row = start + length;
+ if (add) {
+ item.mask |= LVIF_PARAM;
+ for (int i = start; i < max_row; ++i) {
+ item.iItem = i;
+ item.lParam = i;
+ // We do not want to notify of the check state when we insert the items.
+ ignore_listview_change_ = true;
+ ListView_InsertItem(native_view(), &item);
+ ignore_listview_change_ = false;
+ if (table_->type() == CHECK_BOX_AND_TEXT &&
+ table_->model()->IsChecked(i)) {
+ // Setting the state notifies of the check state change.
+ ListView_SetCheckState(native_view(), i, true);
+ }
+ }
+ }
+
+ memset(&item, 0, sizeof(LVITEM));
+ item.stateMask = 0;
+ item.mask = LVIF_TEXT;
+ if (table_->type() == ICON_AND_TEXT)
+ item.mask |= LVIF_IMAGE;
+
+ for (size_t j = start_column; j < table_->GetVisibleColumnCount(); ++j) {
+ TableColumn& col = table_->GetVisibleColumnAt(j);
+ int max_text_width = ListView_GetStringWidth(native_view(),
+ col.title.c_str());
+ for (int i = start; i < max_row; ++i) {
+ item.iItem = i;
+ item.iSubItem = j;
+ std::wstring text = table_->model()->GetText(i, col.id);
+ item.pszText = const_cast<LPWSTR>(text.c_str());
+ item.iImage = 0;
+ ListView_SetItem(native_view(), &item);
+
+ // Compute width in px, using current font.
+ int string_width = ListView_GetStringWidth(native_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 == table_->model()->RowCount())) {
+ col.min_visible_width = max_text_width;
+ }
+ }
+}
+
+void NativeTableWin::UpdateContentOffset() {
+ content_offset_ = 0;
+
+ if (!native_view())
+ return;
+
+ HWND header = ListView_GetHeader(native_view());
+ if (!header)
+ return;
+
+ POINT origin = {0, 0};
+ MapWindowPoints(header, native_view(), &origin, 1);
+
+ RECT header_bounds;
+ GetWindowRect(header, &header_bounds);
+
+ content_offset_ = origin.y + header_bounds.bottom - header_bounds.top;
+}
+
+static int GetViewIndexFromMouseEvent(HWND window, LPARAM l_param) {
+ int x = GET_X_LPARAM(l_param);
+ int y = GET_Y_LPARAM(l_param);
+ LVHITTESTINFO hit_info = {0};
+ hit_info.pt.x = x;
+ hit_info.pt.y = y;
+ return ListView_HitTest(window, &hit_info);
+}
+
+// static
+LRESULT CALLBACK NativeTableWin::TableWndProc(HWND window,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ NativeTableWin* native_table_win = reinterpret_cast<NativeTableWin*>(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+ TableView2* table = native_table_win->table_;
+
+ // 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.
+ static int mouse_down_x, mouse_down_y;
+
+ 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 (table->UILayoutIsRightToLeft() &&
+ (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.
+ LVHITTESTINFO hit_info = {0};
+ hit_info.pt.x = table_point.x;
+ hit_info.pt.y = table_point.y;
+ int view_index = ListView_HitTest(window, &hit_info);
+ // TODO(jcampan): fix code below
+ // if (view_index != -1 && table->IsItemSelected(view_index))
+ // table->Select(view_index);
+ // table->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_KEYDOWN: {
+ if (!table->single_selection() && w_param == 'A' &&
+ GetKeyState(VK_CONTROL) < 0 && table->model()->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 if (w_param == VK_DELETE && table->observer()) {
+ table->observer()->OnTableView2Delete(table);
+ return 0;
+ }
+ // else case: fall through to default processing.
+ break;
+ }
+
+ case WM_LBUTTONDBLCLK: {
+ if (w_param == MK_LBUTTON)
+ native_table_win->OnDoubleClick();
+ return 0;
+ }
+
+ case WM_MBUTTONDOWN: {
+ if (w_param == MK_MBUTTON) {
+ int view_index = GetViewIndexFromMouseEvent(window, l_param);
+ if (view_index != -1) {
+ // Clear all and select the row that was middle clicked.
+ native_table_win->Select(view_index);
+ native_table_win->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 = GetViewIndexFromMouseEvent(window, l_param);
+ if (view_index != -1)
+ native_table_win->Select(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 = GetViewIndexFromMouseEvent(window, l_param);
+ if (view_index != -1) {
+ native_table_win->ignore_listview_change_ = true;
+ in_mouse_down = true;
+ select_on_mouse_up = false;
+ mouse_down_x = GET_X_LPARAM(l_param);
+ mouse_down_y = GET_Y_LPARAM(l_param);
+ bool select = true;
+ if (w_param & MK_CONTROL) {
+ select = false;
+ if (!native_table_win->IsRowSelected(view_index)) {
+ if (table->single_selection()) {
+ // Single selection mode and the row isn't selected, select
+ // only it.
+ native_table_win->Select(view_index);
+ } else {
+ // Not single selection, add this row to the selection.
+ native_table_win->SetSelectedState(view_index, true);
+ }
+ } else {
+ // Remove this row from the selection.
+ native_table_win->SetSelectedState(view_index, false);
+ }
+ ListView_SetSelectionMark(window, view_index);
+ } else if (!table->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;
+
+ // 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) {
+ native_table_win->SetSelectedState(i, true);
+ }
+ }
+ }
+ // Make the row the user clicked on the focused row.
+ ListView_SetItemState(window, view_index, LVIS_FOCUSED,
+ LVIS_FOCUSED);
+ if (select) {
+ if (!native_table_win->IsRowSelected(view_index)) {
+ // Clear all.
+ ListView_SetItemState(window, -1, 0, LVIS_SELECTED);
+ // And select the row the user clicked on.
+ native_table_win->SetSelectedState(view_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);
+ }
+ native_table_win->ignore_listview_change_ = false;
+ table->observer()->OnSelectionChanged();
+ SetCapture(window);
+ return 0;
+ }
+ // else case, continue on to default handler
+ }
+ break;
+ }
+
+ case WM_MOUSEMOVE: {
+ if (in_mouse_down) {
+ int x = GET_X_LPARAM(l_param);
+ int y = GET_Y_LPARAM(l_param);
+ if (View::ExceededDragThreshold(x - mouse_down_x, y - mouse_down_y)) {
+ // 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(native_table_win->original_handler_);
+ return CallWindowProc(native_table_win->original_handler_, window, message,
+ w_param, l_param);
+}
+
+LRESULT CALLBACK NativeTableWin::TableHeaderWndProc(HWND window, UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ NativeTableWin* native_table_win = reinterpret_cast<NativeTableWin*>(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+
+ switch (message) {
+ case WM_SETCURSOR:
+ if (!native_table_win->table_->resizable_columns())
+ // Prevents the cursor from changing to the resize cursor.
+ return TRUE;
+ break;
+ // TODO(jcampan): we should also block single click messages on the
+ // separator as right now columns can still be resized.
+ case WM_LBUTTONDBLCLK:
+ if (!native_table_win->table_->resizable_columns())
+ // Prevents the double-click on the column separator from auto-resizing
+ // the column.
+ return TRUE;
+ break;
+ default:
+ break;
+ }
+ DCHECK(native_table_win->header_original_handler_);
+ return CallWindowProc(native_table_win->header_original_handler_,
+ window, message, w_param, l_param);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeButtonWrapper, public:
+
+// static
+NativeTableWrapper* NativeTableWrapper::CreateNativeWrapper(TableView2* table) {
+ return new NativeTableWin(table);
+}
+
+} // namespace views
diff --git a/views/controls/table/native_table_win.h b/views/controls/table/native_table_win.h
new file mode 100644
index 0000000..6b6c8ef
--- /dev/null
+++ b/views/controls/table/native_table_win.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2009 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_NATIVE_TABLE_WIN_H_
+#define VIEWS_CONTROLS_TABLE_NATIVE_TABLE_WIN_H_
+
+#include <windows.h>
+
+#include "app/table_model.h"
+#include "views/controls/native_control_win.h"
+#include "views/controls/table/native_table_wrapper.h"
+
+typedef struct tagNMLVCUSTOMDRAW NMLVCUSTOMDRAW;
+
+namespace views {
+
+class TableView2;
+
+// A View that hosts a native Windows table.
+class NativeTableWin : public NativeControlWin, public NativeTableWrapper {
+ public:
+ explicit NativeTableWin(TableView2* table);
+ virtual ~NativeTableWin();
+
+ // NativeTableWrapper implementation:
+ virtual int GetRowCount();
+ virtual View* GetView();
+ virtual void SetFocus();
+ virtual gfx::NativeView GetTestingHandle() const;
+ virtual void InsertColumn(const TableColumn& column, int index);
+ virtual void RemoveColumn(int index);
+ virtual int GetColumnWidth(int column_index);
+ virtual void SetColumnWidth(int column_index, int width);
+ virtual int GetSelectedRowCount();
+ virtual int GetFirstSelectedRow();
+ virtual int GetFirstFocusedRow();
+ virtual void ClearSelection();
+ virtual void ClearRowFocus();
+ virtual void SetSelectedState(int model_row, bool state);
+ virtual void SetFocusState(int model_row, bool state);
+ virtual bool IsRowSelected(int model_row);
+ virtual bool IsRowFocused(int model_row);
+ virtual void OnRowsChanged(int start, int length);
+ virtual void OnRowsAdded(int start, int length);
+ virtual void OnRowsRemoved(int start, int length);
+ virtual gfx::Rect GetBounds();
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize();
+
+ // Overridden from NativeControlWin:
+ virtual bool ProcessMessage(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ LRESULT* result);
+
+ protected:
+ virtual void CreateNativeControl();
+
+ private:
+ // Makes |model_row| the only selected and focused row.
+ void Select(int model_row);
+
+ // 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(base::KeyboardCode virtual_keycode);
+
+ // Notification from the ListView that the checked state of the item has
+ // changed.
+ void OnCheckedStateChanged(int model_row, bool is_checked);
+
+ // Custom drawing of our icons.
+ LRESULT OnCustomDraw(NMLVCUSTOMDRAW* draw_info);
+
+ void UpdateListViewCache(int start, int length, bool add);
+
+ void UpdateContentOffset();
+
+ // 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);
+
+ // 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_;
+
+ // The Table we are bound to.
+ TableView2* table_;
+
+ // The Y offset from the top of the table to the actual content (passed the
+ // header if any).
+ int content_offset_;
+
+ // 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_;
+
+ // Size (width and height) of images.
+ static const int kImageSize;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeTableWin);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_TABLE_NATIVE_TABLE_WIN_H_
diff --git a/views/controls/table/native_table_wrapper.h b/views/controls/table/native_table_wrapper.h
new file mode 100644
index 0000000..5f635e5
--- /dev/null
+++ b/views/controls/table/native_table_wrapper.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2009 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_NATIVE_TABLE_WRAPPER_H_
+#define VIEWS_CONTROLS_TABLE_NATIVE_TABLE_WRAPPER_H_
+
+#include "app/gfx/native_widget_types.h"
+
+namespace views {
+
+class TableView2;
+class View;
+
+// An interface implemented by an object that provides a platform-native
+// table.
+class NativeTableWrapper {
+ public:
+ // Returns the number of rows in the table.
+ virtual int GetRowCount() = 0;
+
+ // Inserts/removes a column at the specified index.
+ virtual void InsertColumn(const TableColumn& column, int index) = 0;
+ virtual void RemoveColumn(int index) = 0;
+
+ // Returns the number of rows that are selected.
+ virtual int GetSelectedRowCount() = 0;
+
+ // Returns the first row that is selected/focused in terms of the model.
+ virtual int GetFirstSelectedRow() = 0;
+ virtual int GetFirstFocusedRow() = 0;
+
+ // Unselect all rows.
+ virtual void ClearSelection() = 0;
+
+ virtual void ClearRowFocus() = 0;
+
+ virtual void SetSelectedState(int model_row, bool state) = 0;
+
+ virtual void SetFocusState(int model_row, bool state) = 0;
+
+ // Returns true if the row at the specified index is selected.
+ virtual bool IsRowSelected(int model_row) = 0;
+
+ // Returns true if the row at the specified index has the focus.
+ virtual bool IsRowFocused(int model_row) = 0;
+
+ // Retrieves the views::View that hosts the native control.
+ virtual View* GetView() = 0;
+
+ // Sets the focus to the table.
+ virtual void SetFocus() = 0;
+
+ // Returns a handle to the underlying native view for testing.
+ virtual gfx::NativeView GetTestingHandle() const = 0;
+
+ // Gets/sets the columns width.
+ virtual int GetColumnWidth(int column_index) = 0;
+ virtual void SetColumnWidth(int column_index, int width) = 0;
+
+ // Called by the table view to indicate that some rows have changed, been
+ // added or been removed.
+ virtual void OnRowsChanged(int start, int length) = 0;
+ virtual void OnRowsAdded(int start, int length) = 0;
+ virtual void OnRowsRemoved(int start, int length) = 0;
+
+ // Returns the bounds of the native table.
+ virtual gfx::Rect GetBounds() = 0;
+
+ // Creates an appropriate NativeButtonWrapper for the platform.
+ static NativeTableWrapper* CreateNativeWrapper(TableView2* table);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_TABLE_NATIVE_TABLE_WRAPPER_H_
diff --git a/views/controls/table/table_view2.cc b/views/controls/table/table_view2.cc
new file mode 100644
index 0000000..36ba072
--- /dev/null
+++ b/views/controls/table/table_view2.cc
@@ -0,0 +1,338 @@
+
+// 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.
+
+#include "views/controls/table/table_view2.h"
+
+#include "app/table_model.h"
+#include "views/controls/table/table_view_observer.h"
+
+namespace views {
+
+// TableView2 ------------------------------------------------------------------
+
+TableView2::TableView2(TableModel* model,
+ const std::vector<TableColumn>& columns,
+ TableTypes table_type,
+ bool single_selection,
+ bool resizable_columns,
+ bool autosize_columns)
+ : model_(model),
+ table_type_(table_type),
+ table_view_observer_(NULL),
+ visible_columns_(),
+ all_columns_(),
+ column_count_(static_cast<int>(columns.size())),
+ single_selection_(single_selection),
+ resizable_columns_(resizable_columns),
+ autosize_columns_(autosize_columns),
+ native_wrapper_(NULL) {
+ for (std::vector<TableColumn>::const_iterator i = columns.begin();
+ i != columns.end(); ++i) {
+ AddColumn(*i);
+ visible_columns_.push_back(i->id);
+ }
+}
+
+TableView2::~TableView2() {
+ if (model_)
+ model_->SetObserver(NULL);
+}
+
+void TableView2::SetModel(TableModel* model) {
+ if (model == model_)
+ return;
+
+ if (model_)
+ model_->SetObserver(NULL);
+ model_ = model;
+ if (model_)
+ model_->SetObserver(this);
+ if (native_wrapper_)
+ OnModelChanged();
+}
+
+int TableView2::GetRowCount() {
+ if (!native_wrapper_)
+ return 0;
+ return native_wrapper_->GetRowCount();
+}
+
+int TableView2::SelectedRowCount() {
+ if (!native_wrapper_)
+ return 0;
+ return native_wrapper_->GetSelectedRowCount();
+}
+
+void TableView2::ClearSelection() {
+ if (native_wrapper_)
+ native_wrapper_->ClearSelection();
+}
+
+void TableView2::ClearRowFocus() {
+ if (native_wrapper_)
+ native_wrapper_->ClearRowFocus();
+}
+
+int TableView2::GetFirstSelectedRow() {
+ if (!native_wrapper_)
+ return -1;
+ return native_wrapper_->GetFirstSelectedRow();
+}
+
+int TableView2::GetFirstFocusedRow() {
+ if (!native_wrapper_)
+ return -1;
+ return native_wrapper_->GetFirstFocusedRow();
+}
+
+void TableView2::SelectRow(int model_row) {
+ if (!native_wrapper_)
+ return;
+
+ native_wrapper_->ClearSelection();
+ native_wrapper_->SetSelectedState(model_row, true);
+ if (table_view_observer_)
+ table_view_observer_->OnSelectionChanged();
+}
+
+void TableView2::FocusRow(int model_row) {
+ if (!native_wrapper_)
+ return;
+
+ native_wrapper_->SetFocusState(model_row, true);
+}
+
+bool TableView2::IsRowSelected(int model_row) {
+ if (!native_wrapper_)
+ return false;
+
+ return native_wrapper_->IsRowSelected(model_row);
+}
+
+bool TableView2::IsRowFocused(int model_row) {
+ if (!native_wrapper_)
+ return false;
+
+ return native_wrapper_->IsRowFocused(model_row);
+}
+
+void TableView2::OnModelChanged() {
+ if (!native_wrapper_)
+ return;
+
+ int current_row_count = native_wrapper_->GetRowCount();
+ if (current_row_count > 0)
+ OnItemsRemoved(0, current_row_count);
+ if (model_ && model_->RowCount())
+ OnItemsAdded(0, model_->RowCount());
+}
+
+void TableView2::OnItemsChanged(int start, int length) {
+ if (!native_wrapper_)
+ return;
+
+ if (length == -1) {
+ DCHECK_GE(start, 0);
+ length = model_->RowCount() - start;
+ }
+ native_wrapper_->OnRowsChanged(start, length);
+}
+
+void TableView2::OnItemsAdded(int start, int length) {
+ if (!native_wrapper_)
+ return;
+
+ DCHECK(start >= 0 && length >= 0 && start <= native_wrapper_->GetRowCount());
+
+ native_wrapper_->OnRowsAdded(start, length);
+}
+
+void TableView2::OnItemsRemoved(int start, int length) {
+ if (!native_wrapper_)
+ return;
+
+ DCHECK(start >= 0 && length >= 0 &&
+ start + length <= native_wrapper_->GetRowCount());
+
+ native_wrapper_->OnRowsRemoved(start, length);
+}
+
+void TableView2::AddColumn(const TableColumn& col) {
+ DCHECK_EQ(0U, all_columns_.count(col.id));
+ all_columns_[col.id] = col;
+}
+
+void TableView2::SetColumns(const std::vector<TableColumn>& columns) {
+ // Remove the currently visible columns.
+ while (!visible_columns_.empty())
+ SetColumnVisibility(visible_columns_.front(), false);
+
+ all_columns_.clear();
+ for (std::vector<TableColumn>::const_iterator i = columns.begin();
+ i != columns.end(); ++i) {
+ AddColumn(*i);
+ }
+}
+
+void TableView2::OnColumnsChanged() {
+ column_count_ = static_cast<int>(visible_columns_.size());
+ ResetColumnSizes();
+}
+
+bool TableView2::HasColumn(int id) {
+ return all_columns_.count(id) > 0;
+}
+
+void TableView2::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 (native_wrapper_)
+ native_wrapper_->RemoveColumn(index);
+ visible_columns_.erase(i);
+ changed = true;
+ break;
+ }
+ }
+ }
+ if (is_visible) {
+ DCHECK(native_wrapper_);
+ visible_columns_.push_back(id);
+ TableColumn& column = all_columns_[id];
+ native_wrapper_->InsertColumn(column, column_count_);
+ changed = true;
+ }
+ if (changed)
+ OnColumnsChanged();
+
+}
+
+bool TableView2::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;
+}
+
+void TableView2::ResetColumnSizes() {
+ if (!native_wrapper_)
+ return;
+
+ // See comment in TableColumn for what this does.
+ int width = this->width();
+ gfx::Rect native_bounds = native_wrapper_->GetBounds();
+ if (!native_bounds.IsEmpty()) {
+ if (native_bounds.width() > 0) {
+ // Prefer the bounds of the window over our bounds, which may be
+ // different.
+ width = native_bounds.width();
+ }
+ }
+
+ 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) {
+ 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 += native_wrapper_->GetColumnWidth(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) {
+ 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;
+ native_wrapper_->SetColumnWidth(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;
+ }
+ native_wrapper_->SetColumnWidth(col_index, col_width);
+ }
+ }
+ }
+}
+
+void TableView2::DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current) {
+ Layout();
+}
+
+void TableView2::Layout() {
+ if (native_wrapper_) {
+ native_wrapper_->GetView()->SetBounds(0, 0, width(), height());
+ native_wrapper_->GetView()->Layout();
+ }
+}
+
+size_t TableView2::GetVisibleColumnCount() {
+ return visible_columns_.size();
+}
+
+void TableView2::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
+ if (is_add && !native_wrapper_ && GetWidget()) {
+ // The native wrapper's lifetime will be managed by the view hierarchy after
+ // we call AddChildView.
+ native_wrapper_ = CreateWrapper();
+ AddChildView(native_wrapper_->GetView());
+ }
+}
+
+gfx::NativeView TableView2::GetTestingHandle() {
+ return native_wrapper_->GetTestingHandle();
+}
+
+TableColumn TableView2::GetVisibleColumnAt(int index) {
+ DCHECK(index < static_cast<int>(visible_columns_.size()));
+ std::map<int, TableColumn>::iterator iter =
+ all_columns_.find(index);
+ DCHECK(iter != all_columns_.end());
+ return iter->second;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeTable2, protected:
+
+NativeTableWrapper* TableView2::CreateWrapper() {
+ return NativeTableWrapper::CreateNativeWrapper(this);
+}
+
+} // namespace views
diff --git a/views/controls/table/table_view2.h b/views/controls/table/table_view2.h
new file mode 100644
index 0000000..3c8e821
--- /dev/null
+++ b/views/controls/table/table_view2.h
@@ -0,0 +1,238 @@
+// 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_VIEW2_H_
+#define VIEWS_CONTROLS_TABLE_TABLE_VIEW2_H_
+
+#include "build/build_config.h"
+
+#include <map>
+#include <vector>
+
+#include "app/table_model_observer.h"
+#include "base/gfx/rect.h"
+#include "base/scoped_ptr.h"
+#include "views/controls/table/table_view.h"
+#include "views/controls/table/native_table_wrapper.h"
+#include "views/view.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+struct TableColumn;
+class TableModel;
+class SkBitmap;
+
+// A TableView2 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.
+//
+// TableView2 itself has an observer that is notified when the selection
+// changes.
+//
+// TableView2 is the current port of TableView to use a NativeControl for
+// portability.
+//
+// TODO(jcampan): add sorting.
+// TODO(jcampan): add group support.
+
+namespace views {
+
+class ListView;
+class ListViewParent;
+class TableView;
+class TableViewObserver;
+class View;
+
+class TableView2 : public View, 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;
+ };
+
+ // 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.
+ TableView2(TableModel* model, const std::vector<TableColumn>& columns,
+ TableTypes table_type, bool single_selection,
+ bool resizable_columns, bool autosize_columns);
+ virtual ~TableView2();
+
+ // 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_; }
+
+ // Returns the number of rows in the table.
+ int GetRowCount();
+
+ // Returns the number of selected rows.
+ int SelectedRowCount();
+
+ // Makes all row not selected.
+ void ClearSelection();
+
+ // Makes all row not focused.
+ void ClearRowFocus();
+
+ // Returns the index of the first selected row.
+ int GetFirstSelectedRow();
+
+ // Returns the index of the first focused row.
+ int GetFirstFocusedRow();
+
+ // Selects the specified row, making sure it's visible.
+ void SelectRow(int model_row);
+
+ // Sets the focus to the row at the given index.
+ void FocusRow(int model_row);
+
+ // Returns true if the row at the specified index is selected.
+ bool IsRowSelected(int model_row);
+
+ // Returns true if the row at the specified index has the focus.
+ bool IsRowFocused(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;
+
+ TableColumn GetVisibleColumnAt(int index);
+ size_t GetVisibleColumnCount();
+
+ // 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();
+
+ bool single_selection() const {
+ return single_selection_;
+ }
+
+ TableTypes type() const {
+ return table_type_;
+ }
+
+ bool resizable_columns() const {
+ return resizable_columns_;
+ }
+
+ bool autosize_columns() const {
+ return autosize_columns_;
+ }
+
+ virtual void DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current);
+ virtual void Layout();
+
+ // Used by tests.
+ virtual gfx::NativeView GetTestingHandle();
+
+ protected:
+ virtual NativeTableWrapper* CreateWrapper();
+ virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
+
+ private:
+ // 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(TableView2* view) : table_view(view) { }
+ TableView2* table_view;
+ };
+
+ friend class ListViewParent;
+ friend class TableSelectionIterator;
+
+ // Adds a new column.
+ void InsertColumn(const TableColumn& tc, int index);
+
+ // Update headers and internal state after columns have changed
+ void OnColumnsChanged();
+
+ 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_;
+
+ // Whether or not the user can resize columns.
+ bool resizable_columns_;
+
+ // Whether or not columns should automatically be resized to fill the
+ // the available width when the list view is resized.
+ bool autosize_columns_;
+
+ // Mappings used when sorted.
+ // scoped_array<int> view_to_model_;
+ // scoped_array<int> model_to_view_;
+
+ // The object that actually implements the table.
+ NativeTableWrapper* native_wrapper_;
+
+ DISALLOW_COPY_AND_ASSIGN(TableView2);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_TABLE_TABLE_VIEW2_H_
+
diff --git a/views/controls/table/table_view_observer.h b/views/controls/table/table_view_observer.h
index f4cacbf..a86bb8e 100644
--- a/views/controls/table/table_view_observer.h
+++ b/views/controls/table/table_view_observer.h
@@ -10,6 +10,7 @@
namespace views {
class TableView;
+class TableView2;
// TableViewObserver is notified about the TableView selection.
class TableViewObserver {
@@ -30,6 +31,9 @@ class TableViewObserver {
// Invoked when the user presses the delete key.
virtual void OnTableViewDelete(TableView* table_view) {}
+
+ // Invoked when the user presses the delete key.
+ virtual void OnTableView2Delete(TableView2* table_view) {}
};
} // namespace views
diff --git a/views/controls/table/table_view_unittest.cc b/views/controls/table/table_view_unittest.cc
index d79a5bc..7ced10c 100644
--- a/views/controls/table/table_view_unittest.cc
+++ b/views/controls/table/table_view_unittest.cc
@@ -10,6 +10,7 @@
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "views/controls/table/table_view.h"
+#include "views/controls/table/table_view2.h"
#include "views/window/window_delegate.h"
#include "views/window/window_win.h"
@@ -29,6 +30,11 @@ class TestTableModel : public TableModel {
public:
TestTableModel();
+ struct CheckNotification {
+ int row;
+ bool state;
+ };
+
// Adds a new row at index |row| with values |c1_value| and |c2_value|.
void AddRow(int row, int c1_value, int c2_value);
@@ -43,6 +49,11 @@ class TestTableModel : public TableModel {
virtual std::wstring GetText(int row, int column_id);
virtual void SetObserver(TableModelObserver* observer);
virtual int CompareValues(int row1, int row2, int column_id);
+ virtual bool IsChecked(int row);
+ virtual void SetChecked(int row, bool is_checked);
+
+ // Contains a record of the SetChecked calls.
+ std::vector<CheckNotification> check_notifications_;
private:
TableModelObserver* observer_;
@@ -99,6 +110,16 @@ int TestTableModel::CompareValues(int row1, int row2, int column_id) {
return rows_[row1][column_id] - rows_[row2][column_id];
}
+bool TestTableModel::IsChecked(int row) {
+ // Let's make the first row the only checked one.
+ return (row == 1);
+}
+
+void TestTableModel::SetChecked(int row, bool is_checked) {
+ CheckNotification check_notification = { row, is_checked };
+ check_notifications_.push_back(check_notification);
+}
+
// TableViewTest ---------------------------------------------------------------
class TableViewTest : public testing::Test, views::WindowDelegate {
@@ -149,9 +170,8 @@ void TableViewTest::SetUp() {
table_ = new TableView(model_.get(), columns, views::ICON_AND_TEXT,
false, false, false);
window_ =
- views::Window::CreateChromeWindow(NULL,
- gfx::Rect(100, 100, 512, 512),
- this);
+ views::Window::CreateChromeWindow(NULL, gfx::Rect(100, 100, 512, 512),
+ this);
}
void TableViewTest::TearDown() {
@@ -381,3 +401,190 @@ TEST_F(NullModelTableViewTest, NullModel) {
// There's nothing explicit to test. If there is a bug in TableView relating
// to a NULL model we'll crash.
}
+
+////////////////////////////////////////////////////////////////////////////////
+// TableView2 Tests
+
+class TableView2Test : public testing::Test, views::WindowDelegate {
+ public:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ virtual views::View* GetContentsView() {
+ return table_;
+ }
+
+ // Returns the contents of a cell in the table.
+ std::wstring GetCellValue(int row, int column);
+
+ protected:
+ // Creates the model.
+ TestTableModel* CreateModel();
+
+ virtual views::TableTypes GetTableType() {
+ return views::TEXT_ONLY;
+ }
+
+ scoped_ptr<TestTableModel> model_;
+
+ // The table. This is owned by the window.
+ views::TableView2* table_;
+
+ private:
+ MessageLoopForUI message_loop_;
+ views::Window* window_;
+};
+
+void TableView2Test::SetUp() {
+ OleInitialize(NULL);
+ model_.reset(CreateModel());
+ std::vector<TableColumn> columns;
+ columns.resize(2);
+ columns[0].id = 0;
+ columns[1].id = 1;
+ table_ = new views::TableView2(model_.get(), columns, GetTableType(),
+ false, false, false);
+ window_ = views::Window::CreateChromeWindow(NULL,
+ gfx::Rect(100, 100, 512, 512),
+ this);
+ window_->Show();
+}
+
+void TableView2Test::TearDown() {
+ window_->Close();
+ // Temporary workaround to avoid leak of RootView::pending_paint_task_.
+ message_loop_.RunAllPending();
+ OleUninitialize();
+}
+
+TestTableModel* TableView2Test::CreateModel() {
+ return new TestTableModel();
+}
+
+std::wstring TableView2Test::GetCellValue(int row, int column) {
+#if defined(OS_WIN)
+ wchar_t str[128] = {0};
+ LVITEM item = {0};
+ item.mask = LVIF_TEXT;
+ item.iItem = row;
+ item.iSubItem = column;
+ item.pszText = str;
+ item.cchTextMax = 128;
+ BOOL r = ListView_GetItem(table_->GetTestingHandle(), &item);
+ DCHECK(r);
+ return std::wstring(str);
+#else
+ NOTIMPLEMENTED();
+#endif
+}
+
+// Tests that the table correctly reflects changes to the model.
+TEST_F(TableView2Test, ModelChangesTest) {
+ EXPECT_EQ(3, table_->GetRowCount());
+ EXPECT_EQ(L"0", GetCellValue(0, 0));
+ EXPECT_EQ(L"1", GetCellValue(1, 0));
+ EXPECT_EQ(L"2", GetCellValue(2, 1));
+
+ // Test adding rows and that OnItemsAdded works.
+ model_->AddRow(3, 3, 3);
+ model_->AddRow(4, 4, 4);
+ table_->OnItemsAdded(3, 2);
+ EXPECT_EQ(5, table_->GetRowCount());
+ EXPECT_EQ(L"3", GetCellValue(3, 0));
+ EXPECT_EQ(L"4", GetCellValue(4, 1));
+
+ // Test removing rows and that OnItemsRemoved works.
+ model_->RemoveRow(1);
+ model_->RemoveRow(1);
+ table_->OnItemsRemoved(1, 2);
+ EXPECT_EQ(3, table_->GetRowCount());
+ EXPECT_EQ(L"0", GetCellValue(0, 0));
+ EXPECT_EQ(L"3", GetCellValue(1, 0));
+ EXPECT_EQ(L"4", GetCellValue(2, 1));
+
+ // Test changing rows and that OnItemsChanged works.
+ model_->ChangeRow(1, 1, 1);
+ model_->ChangeRow(2, 2, 2);
+ table_->OnItemsChanged(1, 2);
+ EXPECT_EQ(L"0", GetCellValue(0, 0));
+ EXPECT_EQ(L"1", GetCellValue(1, 0));
+ EXPECT_EQ(L"2", GetCellValue(2, 1));
+
+ // Test adding and removing rows and using OnModelChanged.
+ model_->RemoveRow(2);
+ model_->AddRow(2, 5, 5);
+ model_->AddRow(3, 6, 6);
+ table_->OnModelChanged();
+ EXPECT_EQ(4, table_->GetRowCount());
+ EXPECT_EQ(L"0", GetCellValue(0, 0));
+ EXPECT_EQ(L"1", GetCellValue(1, 0));
+ EXPECT_EQ(L"5", GetCellValue(2, 1));
+ EXPECT_EQ(L"6", GetCellValue(3, 1));
+}
+
+// Test the selection on a single-selection table.
+TEST_F(TableView2Test, SingleSelectionTest) {
+ EXPECT_EQ(0, table_->SelectedRowCount());
+ EXPECT_EQ(-1, table_->GetFirstSelectedRow());
+
+ table_->SelectRow(0);
+ EXPECT_EQ(1, table_->SelectedRowCount());
+ EXPECT_EQ(0, table_->GetFirstSelectedRow());
+
+ table_->SelectRow(2);
+ EXPECT_EQ(1, table_->SelectedRowCount());
+ EXPECT_EQ(2, table_->GetFirstSelectedRow());
+
+ table_->ClearSelection();
+ EXPECT_EQ(0, table_->SelectedRowCount());
+ EXPECT_EQ(-1, table_->GetFirstSelectedRow());
+}
+
+// Test the row focus on a single-selection table.
+TEST_F(TableView2Test, RowFocusTest) {
+ EXPECT_EQ(-1, table_->GetFirstFocusedRow());
+
+ table_->FocusRow(0);
+ EXPECT_EQ(0, table_->GetFirstFocusedRow());
+
+ table_->FocusRow(2);
+ EXPECT_EQ(2, table_->GetFirstFocusedRow());
+
+ table_->ClearRowFocus();
+ EXPECT_EQ(-1, table_->GetFirstSelectedRow());
+}
+
+class CheckTableView2Test : public TableView2Test {
+ protected:
+ virtual views::TableTypes GetTableType() {
+ return views::CHECK_BOX_AND_TEXT;
+ }
+
+ // Sets the row check state natively.
+ void SetRowCheckState(int row, bool state) {
+#if defined(OS_WIN)
+ ListView_SetCheckState(table_->GetTestingHandle(), row, state);
+#else
+ NOTIMPLEMENTED();
+#endif
+ }
+};
+
+TEST_F(CheckTableView2Test, TestCheckTable) {
+ // Test that we were notified of the initial check states.
+ ASSERT_EQ(1U, model_->check_notifications_.size());
+ EXPECT_EQ(1, model_->check_notifications_[0].row);
+
+ // Test that we get the notification correctly.
+ model_->check_notifications_.clear();
+ SetRowCheckState(1, false);
+ SetRowCheckState(0, true);
+ SetRowCheckState(0, false);
+ ASSERT_LE(3U, model_->check_notifications_.size());
+ EXPECT_EQ(1, model_->check_notifications_[0].row);
+ EXPECT_FALSE(model_->check_notifications_[0].state);
+ EXPECT_EQ(0, model_->check_notifications_[1].row);
+ EXPECT_TRUE(model_->check_notifications_[1].state);
+ EXPECT_EQ(0, model_->check_notifications_[2].row);
+ EXPECT_FALSE(model_->check_notifications_[2].state);
+}
diff --git a/views/examples/examples_main.cc b/views/examples/examples_main.cc
index d061871..4750c89 100644
--- a/views/examples/examples_main.cc
+++ b/views/examples/examples_main.cc
@@ -17,6 +17,8 @@
#include "views/examples/radio_button_example.h"
#include "views/examples/scroll_view_example.h"
#include "views/examples/tabbed_pane_example.h"
+#include "views/examples/table_example.h"
+#include "views/examples/table2_example.h"
#include "views/examples/textfield_example.h"
#include "views/focus/accelerator_handler.h"
#include "views/grid_layout.h"
@@ -28,6 +30,10 @@ views::View* ExamplesMain::GetContentsView() {
return contents_;
}
+void ExamplesMain::WindowClosing() {
+ MessageLoopForUI::current()->Quit();
+}
+
void ExamplesMain::SetStatus(const std::wstring& status) {
status_label_->SetText(status);
}
@@ -99,6 +105,14 @@ void ExamplesMain::Run() {
tabbed_pane->AddTab(scroll_view_example.GetExampleTitle(),
scroll_view_example.GetExampleView());
+ examples::TableExample table_example(this);
+ tabbed_pane->AddTab(table_example.GetExampleTitle(),
+ table_example.GetExampleView());
+
+ examples::Table2Example table2_example(this);
+ tabbed_pane->AddTab(table2_example.GetExampleTitle(),
+ table2_example.GetExampleView());
+
window->Show();
views::AcceleratorHandler accelerator_handler;
MessageLoopForUI::current()->Run(&accelerator_handler);
diff --git a/views/examples/examples_main.h b/views/examples/examples_main.h
index c1d4a2b7..2b8366e 100644
--- a/views/examples/examples_main.h
+++ b/views/examples/examples_main.h
@@ -24,7 +24,9 @@ class ExamplesMain : public views::WindowDelegate {
virtual ~ExamplesMain() {}
// views::WindowDelegate implementation:
+ virtual bool CanResize() const { return true; }
virtual views::View* GetContentsView();
+ virtual void WindowClosing();
// Prints a message in the status area, at the bottom of the window.
void SetStatus(const std::wstring& status);
diff --git a/views/examples/table2_example.h b/views/examples/table2_example.h
new file mode 100644
index 0000000..fc8f900
--- /dev/null
+++ b/views/examples/table2_example.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2009 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_EXAMPLES_TABLE2_EXAMPLE_H_
+#define VIEWS_EXAMPLES_TABLE2_EXAMPLE_H_
+
+#include <vector>
+
+#include "app/table_model.h"
+#include "base/string_util.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "views/controls/button/checkbox.h"
+#include "views/controls/table/table_view2.h"
+#include "views/examples/example_base.h"
+
+namespace examples {
+
+class Table2Example
+ : public ExampleBase,
+ public TableModel,
+ public views::ButtonListener,
+ public views::TableViewObserver {
+ public:
+ explicit Table2Example(ExamplesMain* main) : ExampleBase(main) {
+ }
+
+ virtual ~Table2Example() {}
+
+ virtual std::wstring GetExampleTitle() {
+ return L"Table2";
+ }
+
+ virtual void CreateExampleView(views::View* container) {
+ column1_visible_checkbox_ = new views::Checkbox(L"Fruit column visible");
+ column1_visible_checkbox_->SetChecked(true);
+ column1_visible_checkbox_->set_listener(this);
+ column2_visible_checkbox_ = new views::Checkbox(L"Color column visible");
+ column2_visible_checkbox_->SetChecked(true);
+ column2_visible_checkbox_->set_listener(this);
+ column3_visible_checkbox_ = new views::Checkbox(L"Origin column visible");
+ column3_visible_checkbox_->SetChecked(true);
+ column3_visible_checkbox_->set_listener(this);
+ column4_visible_checkbox_ = new views::Checkbox(L"Price column visible");
+ column4_visible_checkbox_->SetChecked(true);
+ column4_visible_checkbox_->set_listener(this);
+
+ views::GridLayout* layout = new views::GridLayout(container);
+ container->SetLayoutManager(layout);
+
+ std::vector<TableColumn> columns;
+ columns.push_back(TableColumn(0, L"Fruit", TableColumn::LEFT, 100));
+ columns.push_back(TableColumn(1, L"Color", TableColumn::LEFT, 100));
+ columns.push_back(TableColumn(2, L"Origin", TableColumn::LEFT, 100));
+ columns.push_back(TableColumn(3, L"Price", TableColumn::LEFT, 100));
+ table_ = new views::TableView2(this, columns, views::ICON_AND_TEXT,
+ true, true, true);
+ table_->SetObserver(this);
+ icon1.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
+ icon1.allocPixels();
+ SkCanvas canvas1(icon1);
+ canvas1.drawColor(SK_ColorRED);
+
+ icon2.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
+ icon2.allocPixels();
+ SkCanvas canvas2(icon2);
+ canvas2.drawColor(SK_ColorBLUE);
+
+ views::ColumnSet* column_set = layout->AddColumnSet(0);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
+ views::GridLayout::USE_PREF, 0, 0);
+ layout->StartRow(1 /* expand */, 0);
+ layout->AddView(table_);
+
+ column_set = layout->AddColumnSet(1);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+ 0.5f, views::GridLayout::USE_PREF, 0, 0);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+ 0.5f, views::GridLayout::USE_PREF, 0, 0);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+ 0.5f, views::GridLayout::USE_PREF, 0, 0);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+ 0.5f, views::GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0 /* no expand */, 1);
+
+ layout->AddView(column1_visible_checkbox_);
+ layout->AddView(column2_visible_checkbox_);
+ layout->AddView(column3_visible_checkbox_);
+ layout->AddView(column4_visible_checkbox_);
+ }
+
+ // TableModel implementation:
+ virtual int RowCount() {
+ return 10;
+ }
+
+ virtual std::wstring GetText(int row, int column_id) {
+ std::wstring cells[5][5] = {
+ { L"Orange", L"Orange", L"South america", L"$5" },
+ { L"Apple", L"Green", L"Canada", L"$3" },
+ { L"Blue berries", L"Blue", L"Mexico", L"$10.3" },
+ { L"Strawberries", L"Red", L"California", L"$7" },
+ { L"Cantaloupe", L"Orange", L"South america", L"$5" },
+ };
+ return cells[row % 5][column_id];
+ }
+
+ virtual SkBitmap GetIcon(int row) {
+ return row % 2 ? icon1 : icon2;
+ }
+
+ virtual void SetObserver(TableModelObserver* observer) {
+ }
+
+ // TableViewObserver implementation:
+ virtual void OnSelectionChanged() {
+ PrintStatus(L"Selection changed");
+ }
+
+ virtual void OnDoubleClick() {}
+
+ virtual void OnMiddleClick() {}
+
+ virtual void OnKeyDown(base::KeyboardCode virtual_keycode) {}
+
+ virtual void OnTableViewDelete(views::TableView* table_view) {}
+
+ virtual void OnTableView2Delete(views::TableView2* table_view) {}
+
+ // ButtonListener implementation:
+ virtual void ButtonPressed(views::Button* sender, const views::Event& event) {
+ int index = 0;
+ bool show = true;
+ if (sender == column1_visible_checkbox_) {
+ index = 0;
+ show = column1_visible_checkbox_->checked();
+ } else if (sender == column2_visible_checkbox_) {
+ index = 1;
+ show = column2_visible_checkbox_->checked();
+ } else if (sender == column3_visible_checkbox_) {
+ index = 2;
+ show = column3_visible_checkbox_->checked();
+ } else if (sender == column4_visible_checkbox_) {
+ index = 3;
+ show = column4_visible_checkbox_->checked();
+ }
+ table_->SetColumnVisibility(index, show);
+ }
+
+ private:
+ // The table to be tested.
+ views::TableView2* table_;
+
+ views::Checkbox* column1_visible_checkbox_;
+ views::Checkbox* column2_visible_checkbox_;
+ views::Checkbox* column3_visible_checkbox_;
+ views::Checkbox* column4_visible_checkbox_;
+
+ SkBitmap icon1;
+ SkBitmap icon2;
+
+ DISALLOW_COPY_AND_ASSIGN(Table2Example);
+};
+
+} // namespace examples
+
+#endif // VIEWS_EXAMPLES_TABLE2_EXAMPLE_H_
diff --git a/views/examples/table_example.h b/views/examples/table_example.h
new file mode 100644
index 0000000..1fcf33f
--- /dev/null
+++ b/views/examples/table_example.h
@@ -0,0 +1,169 @@
+// Copyright (c) 2009 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_EXAMPLES_TABLE_EXAMPLE_H_
+#define VIEWS_EXAMPLES_TABLE_EXAMPLE_H_
+
+#include <vector>
+
+#include "app/table_model.h"
+#include "base/string_util.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "views/controls/table/table_view.h"
+#include "views/controls/table/table_view_observer.h"
+#include "views/examples/example_base.h"
+#include "views/fill_layout.h"
+
+namespace examples {
+
+class TableExample
+ : public ExampleBase,
+ public TableModel,
+ public views::ButtonListener,
+ public views::TableViewObserver {
+ public:
+ explicit TableExample(ExamplesMain* main) : ExampleBase(main) {
+ }
+
+ virtual ~TableExample() {}
+
+ virtual std::wstring GetExampleTitle() {
+ return L"Table";
+ }
+
+ virtual void CreateExampleView(views::View* container) {
+ column1_visible_checkbox_ = new views::Checkbox(L"Fruit column visible");
+ column1_visible_checkbox_->SetChecked(true);
+ column1_visible_checkbox_->set_listener(this);
+ column2_visible_checkbox_ = new views::Checkbox(L"Color column visible");
+ column2_visible_checkbox_->SetChecked(true);
+ column2_visible_checkbox_->set_listener(this);
+ column3_visible_checkbox_ = new views::Checkbox(L"Origin column visible");
+ column3_visible_checkbox_->SetChecked(true);
+ column3_visible_checkbox_->set_listener(this);
+ column4_visible_checkbox_ = new views::Checkbox(L"Price column visible");
+ column4_visible_checkbox_->SetChecked(true);
+ column4_visible_checkbox_->set_listener(this);
+
+ views::GridLayout* layout = new views::GridLayout(container);
+ container->SetLayoutManager(layout);
+
+ std::vector<TableColumn> columns;
+ columns.push_back(TableColumn(0, L"Fruit", TableColumn::LEFT, 100));
+ columns.push_back(TableColumn(1, L"Color", TableColumn::LEFT, 100));
+ columns.push_back(TableColumn(2, L"Origin", TableColumn::LEFT, 100));
+ columns.push_back(TableColumn(3, L"Price", TableColumn::LEFT, 100));
+ table_ = new views::TableView(this, columns, views::ICON_AND_TEXT,
+ true, true, true);
+ table_->SetObserver(this);
+ icon1.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
+ icon1.allocPixels();
+ SkCanvas canvas1(icon1);
+ canvas1.drawColor(SK_ColorRED);
+
+ icon2.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
+ icon2.allocPixels();
+ SkCanvas canvas2(icon2);
+ canvas2.drawColor(SK_ColorBLUE);
+
+ views::ColumnSet* column_set = layout->AddColumnSet(0);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
+ views::GridLayout::USE_PREF, 0, 0);
+ layout->StartRow(1 /* expand */, 0);
+ layout->AddView(table_);
+
+ column_set = layout->AddColumnSet(1);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+ 0.5f, views::GridLayout::USE_PREF, 0, 0);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+ 0.5f, views::GridLayout::USE_PREF, 0, 0);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+ 0.5f, views::GridLayout::USE_PREF, 0, 0);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
+ 0.5f, views::GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0 /* no expand */, 1);
+
+ layout->AddView(column1_visible_checkbox_);
+ layout->AddView(column2_visible_checkbox_);
+ layout->AddView(column3_visible_checkbox_);
+ layout->AddView(column4_visible_checkbox_);
+ }
+
+ // TableModel implementation:
+ virtual int RowCount() {
+ return 10;
+ }
+
+ virtual std::wstring GetText(int row, int column_id) {
+ std::wstring cells[5][5] = {
+ { L"Orange", L"Orange", L"South america", L"$5" },
+ { L"Apple", L"Green", L"Canada", L"$3" },
+ { L"Blue berries", L"Blue", L"Mexico", L"$10.3" },
+ { L"Strawberries", L"Red", L"California", L"$7" },
+ { L"Cantaloupe", L"Orange", L"South america", L"$5" },
+ };
+ return cells[row % 5][column_id];
+ }
+
+ virtual SkBitmap GetIcon(int row) {
+ return row % 2 ? icon1 : icon2;
+ }
+
+ virtual void SetObserver(TableModelObserver* observer) {
+ }
+
+ // TableViewObserver implementation:
+ virtual void OnSelectionChanged() {
+ PrintStatus(L"Selection changed");
+ }
+
+ virtual void OnDoubleClick() {}
+
+ virtual void OnMiddleClick() {}
+
+ virtual void OnKeyDown(base::KeyboardCode virtual_keycode) {}
+
+ virtual void OnTableViewDelete(views::TableView* table_view) {}
+
+ virtual void OnTableView2Delete(views::TableView2* table_view) {}
+
+ // ButtonListener implementation:
+ virtual void ButtonPressed(views::Button* sender, const views::Event& event) {
+ int index = 0;
+ bool show = true;
+ if (sender == column1_visible_checkbox_) {
+ index = 0;
+ show = column1_visible_checkbox_->checked();
+ } else if (sender == column2_visible_checkbox_) {
+ index = 1;
+ show = column2_visible_checkbox_->checked();
+ } else if (sender == column3_visible_checkbox_) {
+ index = 2;
+ show = column3_visible_checkbox_->checked();
+ } else if (sender == column4_visible_checkbox_) {
+ index = 3;
+ show = column4_visible_checkbox_->checked();
+ }
+ table_->SetColumnVisibility(index, show);
+ }
+
+ private:
+ // The table to be tested.
+ views::TableView* table_;
+
+ SkBitmap icon1;
+ SkBitmap icon2;
+
+ views::Checkbox* column1_visible_checkbox_;
+ views::Checkbox* column2_visible_checkbox_;
+ views::Checkbox* column3_visible_checkbox_;
+ views::Checkbox* column4_visible_checkbox_;
+
+ DISALLOW_COPY_AND_ASSIGN(TableExample);
+};
+
+} // namespace examples
+
+#endif // VIEWS_EXAMPLES_TABLE_EXAMPLE_H_
diff --git a/views/views.gyp b/views/views.gyp
index 4e86d4d..60d89f6 100644
--- a/views/views.gyp
+++ b/views/views.gyp
@@ -184,10 +184,15 @@
'controls/tabbed_pane/native_tabbed_pane_win.cc',
'controls/tabbed_pane/native_tabbed_pane_win.h',
'controls/tabbed_pane/native_tabbed_pane_wrapper.h',
+ 'controls/table/native_table_wrapper.h',
+ 'controls/table/native_table_win.cc',
+ 'controls/table/native_table_win.h',
'controls/table/group_table_view.cc',
'controls/table/group_table_view.h',
'controls/table/table_view.cc',
'controls/table/table_view.h',
+ 'controls/table/table_view2.cc',
+ 'controls/table/table_view2.h',
'controls/table/table_view_observer.h',
'controls/textfield/textfield.cc',
'controls/textfield/textfield.h',
@@ -344,6 +349,8 @@
'dependencies': [
'../base/base.gyp:base',
'../skia/skia.gyp:skia',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
'views',
],
'include_dirs': [