| Index: views/controls/table/native_table_win.cc
|
| diff --git a/views/controls/table/native_table_win.cc b/views/controls/table/native_table_win.cc
|
| deleted file mode 100644
|
| index 23b1780d695ef1eba83bcd6b33c23249d1c2ad62..0000000000000000000000000000000000000000
|
| --- a/views/controls/table/native_table_win.cc
|
| +++ /dev/null
|
| @@ -1,914 +0,0 @@
|
| -// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "views/controls/table/native_table_win.h"
|
| -
|
| -#include <commctrl.h>
|
| -#include <windowsx.h>
|
| -
|
| -#include <algorithm>
|
| -
|
| -#include "base/logging.h"
|
| -#include "base/win/scoped_gdi_object.h"
|
| -#include "skia/ext/skia_utils_win.h"
|
| -#include "ui/base/l10n/l10n_util.h"
|
| -#include "ui/base/l10n/l10n_util_win.h"
|
| -#include "ui/base/models/table_model.h"
|
| -#include "ui/base/win/hwnd_util.h"
|
| -#include "ui/gfx/canvas_skia.h"
|
| -#include "ui/gfx/favicon_size.h"
|
| -#include "ui/gfx/icon_util.h"
|
| -#include "ui/views/widget/widget.h"
|
| -#include "views/controls/table/table_view2.h"
|
| -#include "views/controls/table/table_view_observer.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() const {
|
| - if (!native_view())
|
| - return 0;
|
| - return ListView_GetItemCount(native_view());
|
| -}
|
| -
|
| -void NativeTableWin::InsertColumn(const ui::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 ui::TableColumn::LEFT:
|
| - column.fmt = LVCFMT_LEFT;
|
| - break;
|
| - case ui::TableColumn::RIGHT:
|
| - column.fmt = LVCFMT_RIGHT;
|
| - break;
|
| - case ui::TableColumn::CENTER:
|
| - column.fmt = LVCFMT_CENTER;
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - }
|
| - if (tc.width != -1) {
|
| - column.mask |= LVCF_WIDTH;
|
| - column.cx = tc.width;
|
| - }
|
| - column.mask |= LVCF_SUBITEM;
|
| - // Sub-items are 1s indexed.
|
| - column.iSubItem = index + 1;
|
| - SendMessage(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.
|
| - OnFocus();
|
| -}
|
| -
|
| -gfx::NativeView NativeTableWin::GetTestingHandle() const {
|
| - return native_view();
|
| -}
|
| -
|
| -int NativeTableWin::GetColumnWidth(int column_index) const {
|
| - 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() const {
|
| - if (!native_view())
|
| - return 0;
|
| - return ListView_GetSelectedCount(native_view());
|
| -}
|
| -
|
| -int NativeTableWin::GetFirstSelectedRow() const {
|
| - if (!native_view())
|
| - return -1;
|
| - return ListView_GetNextItem(native_view(), -1, LVNI_ALL | LVIS_SELECTED);
|
| -}
|
| -
|
| -int NativeTableWin::GetFirstFocusedRow() const {
|
| - 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) const {
|
| - if (!native_view())
|
| - return false;
|
| - return ListView_GetItemState(native_view(), model_row, LVIS_SELECTED) ==
|
| - LVIS_SELECTED;
|
| -}
|
| -
|
| -bool NativeTableWin::IsRowFocused(int model_row) const {
|
| - 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() const {
|
| - 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.
|
| - NMLISTVIEW* state_change = reinterpret_cast<NMLISTVIEW*>(hdr);
|
| - if ((state_change->uChanged & LVIF_STATE) != 0) {
|
| - if ((state_change->uOldState & LVIS_SELECTED) !=
|
| - (state_change->uNewState & LVIS_SELECTED)) {
|
| - // Selected state of the item changed.
|
| - OnSelectedStateChanged();
|
| - }
|
| - }
|
| - break;
|
| - }
|
| -
|
| - case HDN_BEGINTRACKW:
|
| - case HDN_BEGINTRACKA:
|
| - // Prevent clicks so columns cannot be resized.
|
| - if (!table_->resizable_columns())
|
| - return true;
|
| - break;
|
| -
|
| - case NM_DBLCLK:
|
| - OnDoubleClick();
|
| - break;
|
| -
|
| - case LVN_COLUMNCLICK: {
|
| - const ui::TableColumn& column = table_->GetVisibleColumnAt(
|
| - reinterpret_cast<NMLISTVIEW*>(hdr)->iSubItem);
|
| - break;
|
| - }
|
| -
|
| - case LVN_MARQUEEBEGIN: // We don't want the marquee selection.
|
| - return true;
|
| -
|
| - case LVN_GETINFOTIP: {
|
| - // This is called when the user hovers items in column zero.
|
| - // * If the text in this column is not fully visible, the dwFlags
|
| - // field will be set to 0, and pszText will contain the full text.
|
| - // If you return without making any changes, this text will be
|
| - // displayed in a "labeltip" - a bubble that's overlaid (at the
|
| - // correct alignment!) on the item. If you return with a different
|
| - // pszText, it will be displayed as a tooltip if nonempty.
|
| - // * Otherwise, dwFlags will be LVGIT_UNFOLDED and pszText will be
|
| - // empty. On return, if pszText is nonempty, it will be displayed
|
| - // as a labeltip if dwFlags has been changed to 0 (even if it bears
|
| - // no resemblance to the item text), or as a tooltip otherwise.
|
| - //
|
| - // Once the tooltip for an item has been obtained, this will not be
|
| - // called again until the user hovers a different item. If after that
|
| - // the original item is hovered a second time, this will be called.
|
| - //
|
| - // When the user hovers items in other columns, they will be "unfolded"
|
| - // (displayed as labeltips) when necessary, but this function will
|
| - // never be called.
|
| - //
|
| - // Changing the LVS_EX_INFOTIP extended style to LVS_EX_LABELTIP will
|
| - // cause all of the above to be true except that this function will not
|
| - // be called when dwFlags would be LVGIT_UNFOLDED. Removing it entirely
|
| - // will disable all of the above behavior.
|
| - NMLVGETINFOTIP* info_tip = reinterpret_cast<NMLVGETINFOTIP*>(hdr);
|
| - string16 tooltip = table_->model()->GetTooltip(info_tip->iItem);
|
| - CHECK_GE(info_tip->cchTextMax, 2);
|
| - if (tooltip.length() >= static_cast<size_t>(info_tip->cchTextMax)) {
|
| - // Elide the tooltip if necessary.
|
| - tooltip.erase(info_tip->cchTextMax - 2); // Ellipsis + '\0'
|
| - const wchar_t kEllipsis = L'\x2026';
|
| - tooltip += kEllipsis;
|
| - }
|
| - if (!tooltip.empty())
|
| - wcscpy_s(info_tip->pszText, tooltip.length() + 1, tooltip.c_str());
|
| - 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);
|
| - ui::CheckWindowCreated(hwnd);
|
| -
|
| - // Reduce overdraw/flicker artifacts by double buffering. Support tooltips
|
| - // and display elided items completely on hover (see comments in OnNotify()
|
| - // under LVN_GETINFOTIP). Make the selection extend across the row.
|
| - ListView_SetExtendedListViewStyle(hwnd,
|
| - LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_FULLROWSELECT);
|
| - 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::CanvasSkia canvas(kImageSize, kImageSize, false);
|
| - // Make the background completely transparent.
|
| - canvas.sk_canvas()->drawColor(SK_ColorBLACK, SkXfermode::kClear_Mode);
|
| - {
|
| - base::win::ScopedHICON empty_icon(
|
| - IconUtil::CreateHICONFromSkBitmap(canvas.ExtractBitmap()));
|
| - ImageList_AddIcon(image_list, empty_icon);
|
| - ImageList_AddIcon(image_list, 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_ =
|
| - ui::SetWindowProc(header, &NativeTableWin::TableHeaderWndProc);
|
| - }
|
| -
|
| - SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
|
| - original_handler_ =
|
| - ui::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(ui::KeyboardCode virtual_keycode) {
|
| - if (!ignore_listview_change_ && table_->observer())
|
| - table_->observer()->OnKeyDown(virtual_keycode);
|
| - return false; // Let the key event be processed as ususal.
|
| -}
|
| -
|
| -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::CanvasSkia 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.sk_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,
|
| - gfx::kFaviconSize, gfx::kFaviconSize, true);
|
| -
|
| - // Only paint the visible region of the icon.
|
| - RECT to_draw = { intersection.left - icon_rect.left,
|
| - intersection.top - icon_rect.top,
|
| - 0, 0 };
|
| - to_draw.right = to_draw.left +
|
| - (intersection.right - intersection.left);
|
| - to_draw.bottom = to_draw.top +
|
| - (intersection.bottom - intersection.top);
|
| - skia::DrawToNativeContext(canvas.sk_canvas(), draw_info->nmcd.hdc,
|
| - intersection.left, intersection.top,
|
| - &to_draw);
|
| - r = CDRF_SKIPDEFAULT;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - 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;
|
| - ignore_listview_change_ = true;
|
| - ListView_InsertItem(native_view(), &item);
|
| - ignore_listview_change_ = false;
|
| - }
|
| - }
|
| -
|
| - 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) {
|
| - ui::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;
|
| - string16 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 (base::i18n::IsRTL() &&
|
| - (GET_X_LPARAM(l_param) != -1 || GET_Y_LPARAM(l_param) != -1)) {
|
| - POINT screen_point;
|
| - GetCursorPos(&screen_point);
|
| - POINT table_point = screen_point;
|
| - RECT client_rect;
|
| - if (ScreenToClient(window, &table_point) &&
|
| - GetClientRect(window, &client_rect) &&
|
| - PtInRect(&client_rect, table_point)) {
|
| - // The point is over the client area of the table, handle it ourself.
|
| - // But first select the row if it isn't already selected.
|
| - 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);
|
| -}
|
| -
|
| -////////////////////////////////////////////////////////////////////////////////
|
| -// NativeTableWrapper, public:
|
| -
|
| -// static
|
| -NativeTableWrapper* NativeTableWrapper::CreateNativeWrapper(TableView2* table) {
|
| - return new NativeTableWin(table);
|
| -}
|
| -
|
| -} // namespace views
|
|
|