| Index: views/controls/tree/tree_view.cc
|
| diff --git a/views/controls/tree/tree_view.cc b/views/controls/tree/tree_view.cc
|
| deleted file mode 100644
|
| index 77692b403bcf368bc8d11ee7e341f316a96486a3..0000000000000000000000000000000000000000
|
| --- a/views/controls/tree/tree_view.cc
|
| +++ /dev/null
|
| @@ -1,811 +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/tree/tree_view.h"
|
| -
|
| -#include <vector>
|
| -
|
| -#include "base/i18n/rtl.h"
|
| -#include "base/logging.h"
|
| -#include "base/stl_util.h"
|
| -#include "base/win/win_util.h"
|
| -#include "grit/ui_resources.h"
|
| -#include "ui/base/accessibility/accessible_view_state.h"
|
| -#include "ui/base/keycodes/keyboard_code_conversion_win.h"
|
| -#include "ui/base/keycodes/keyboard_codes.h"
|
| -#include "ui/base/l10n/l10n_util_win.h"
|
| -#include "ui/base/models/accelerator.h"
|
| -#include "ui/base/resource/resource_bundle.h"
|
| -#include "ui/base/win/hwnd_util.h"
|
| -#include "ui/gfx/canvas_skia.h"
|
| -#include "ui/gfx/canvas_skia_paint.h"
|
| -#include "ui/gfx/favicon_size.h"
|
| -#include "ui/gfx/icon_util.h"
|
| -#include "ui/gfx/point.h"
|
| -#include "ui/views/focus/focus_manager.h"
|
| -#include "ui/views/widget/widget.h"
|
| -
|
| -using ui::TreeModel;
|
| -using ui::TreeModelNode;
|
| -
|
| -namespace views {
|
| -
|
| -TreeView::TreeView()
|
| - : tree_view_(NULL),
|
| - model_(NULL),
|
| - auto_expand_children_(false),
|
| - editable_(true),
|
| - next_id_(0),
|
| - controller_(NULL),
|
| - editing_node_(NULL),
|
| - root_shown_(true),
|
| - lines_at_root_(false),
|
| - process_enter_(false),
|
| - show_context_menu_only_when_node_selected_(true),
|
| - select_on_right_mouse_down_(true),
|
| - ALLOW_THIS_IN_INITIALIZER_LIST(wrapper_(this)),
|
| - original_handler_(NULL),
|
| - drag_enabled_(false),
|
| - observer_added_(false),
|
| - has_custom_icons_(false),
|
| - image_list_(NULL) {
|
| -}
|
| -
|
| -TreeView::~TreeView() {
|
| - Cleanup();
|
| -}
|
| -
|
| -void TreeView::GetAccessibleState(ui::AccessibleViewState* state) {
|
| - state->role = ui::AccessibilityTypes::ROLE_OUTLINE;
|
| - state->state = ui::AccessibilityTypes::STATE_READONLY;
|
| -}
|
| -
|
| -void TreeView::SetModel(TreeModel* model) {
|
| - if (model == model_)
|
| - return;
|
| - if (model_ && tree_view_)
|
| - DeleteRootItems();
|
| -
|
| - RemoveObserverFromModel();
|
| -
|
| - model_ = model;
|
| - if (tree_view_ && model_) {
|
| - CreateRootItems();
|
| - AddObserverToModel();
|
| - HIMAGELIST last_image_list = image_list_;
|
| - image_list_ = CreateImageList();
|
| - TreeView_SetImageList(tree_view_, image_list_, TVSIL_NORMAL);
|
| - if (last_image_list)
|
| - ImageList_Destroy(last_image_list);
|
| - }
|
| -}
|
| -
|
| -// Sets whether the user can edit the nodes. The default is true.
|
| -void TreeView::SetEditable(bool editable) {
|
| - if (editable == editable_)
|
| - return;
|
| - editable_ = editable;
|
| - if (!tree_view_)
|
| - return;
|
| - LONG_PTR style = GetWindowLongPtr(tree_view_, GWL_STYLE);
|
| - style &= ~TVS_EDITLABELS;
|
| - SetWindowLongPtr(tree_view_, GWL_STYLE, style);
|
| -}
|
| -
|
| -void TreeView::StartEditing(TreeModelNode* node) {
|
| - DCHECK(node && tree_view_);
|
| - // Cancel the current edit.
|
| - CancelEdit();
|
| - // Make sure all ancestors are expanded.
|
| - if (model_->GetParent(node))
|
| - Expand(model_->GetParent(node));
|
| - const NodeDetails* details = GetNodeDetails(node);
|
| - // Tree needs focus for editing to work.
|
| - SetFocus(tree_view_);
|
| - // Select the node, else if the user commits the edit the selection reverts.
|
| - SetSelectedNode(node);
|
| - TreeView_EditLabel(tree_view_, details->tree_item);
|
| -}
|
| -
|
| -void TreeView::CancelEdit() {
|
| - DCHECK(tree_view_);
|
| - TreeView_EndEditLabelNow(tree_view_, TRUE);
|
| -}
|
| -
|
| -void TreeView::CommitEdit() {
|
| - DCHECK(tree_view_);
|
| - TreeView_EndEditLabelNow(tree_view_, FALSE);
|
| -}
|
| -
|
| -TreeModelNode* TreeView::GetEditingNode() {
|
| - // I couldn't find a way to dynamically query for this, so it is cached.
|
| - return editing_node_;
|
| -}
|
| -
|
| -void TreeView::SetSelectedNode(TreeModelNode* node) {
|
| - DCHECK(tree_view_);
|
| - if (!node) {
|
| - TreeView_SelectItem(tree_view_, NULL);
|
| - return;
|
| - }
|
| - if (node != model_->GetRoot())
|
| - Expand(model_->GetParent(node));
|
| - if (!root_shown_ && node == model_->GetRoot()) {
|
| - // If the root isn't shown, we can't select it, clear out the selection
|
| - // instead.
|
| - TreeView_SelectItem(tree_view_, NULL);
|
| - } else {
|
| - // Select the node and make sure it is visible.
|
| - TreeView_SelectItem(tree_view_, GetNodeDetails(node)->tree_item);
|
| - }
|
| -}
|
| -
|
| -TreeModelNode* TreeView::GetSelectedNode() {
|
| - if (!tree_view_)
|
| - return NULL;
|
| - HTREEITEM selected_item = TreeView_GetSelection(tree_view_);
|
| - if (!selected_item)
|
| - return NULL;
|
| - NodeDetails* details = GetNodeDetailsByTreeItem(selected_item);
|
| - DCHECK(details);
|
| - return details->node;
|
| -}
|
| -
|
| -void TreeView::Expand(TreeModelNode* node) {
|
| - DCHECK(model_ && node);
|
| - if (!root_shown_ && model_->GetRoot() == node) {
|
| - // Can only expand the root if it is showing.
|
| - return;
|
| - }
|
| - TreeModelNode* parent = model_->GetParent(node);
|
| - if (parent) {
|
| - // Make sure all the parents are expanded.
|
| - Expand(parent);
|
| - }
|
| - // And expand this item.
|
| - TreeView_Expand(tree_view_, GetNodeDetails(node)->tree_item, TVE_EXPAND);
|
| -}
|
| -
|
| -void TreeView::ExpandAll(TreeModelNode* node) {
|
| - DCHECK(node);
|
| - // Expand the node.
|
| - if (node != model_->GetRoot() || root_shown_)
|
| - TreeView_Expand(tree_view_, GetNodeDetails(node)->tree_item, TVE_EXPAND);
|
| - // And recursively expand all the children.
|
| - for (int i = model_->GetChildCount(node) - 1; i >= 0; --i) {
|
| - TreeModelNode* child = model_->GetChild(node, i);
|
| - ExpandAll(child);
|
| - }
|
| -}
|
| -
|
| -bool TreeView::IsExpanded(TreeModelNode* node) {
|
| - if (!tree_view_)
|
| - return false;
|
| - TreeModelNode* parent = model_->GetParent(node);
|
| - if (!parent)
|
| - return true;
|
| - if (!IsExpanded(parent))
|
| - return false;
|
| - NodeDetails* details = GetNodeDetails(node);
|
| - return (TreeView_GetItemState(tree_view_, details->tree_item, TVIS_EXPANDED) &
|
| - TVIS_EXPANDED) != 0;
|
| -}
|
| -
|
| -void TreeView::SetRootShown(bool root_shown) {
|
| - if (root_shown_ == root_shown)
|
| - return;
|
| - root_shown_ = root_shown;
|
| - if (!model_ || !tree_view_)
|
| - return;
|
| - // Repopulate the tree.
|
| - DeleteRootItems();
|
| - CreateRootItems();
|
| -}
|
| -
|
| -void TreeView::TreeNodesAdded(TreeModel* model,
|
| - TreeModelNode* parent,
|
| - int start,
|
| - int count) {
|
| - DCHECK(parent && start >= 0 && count > 0);
|
| - if (node_to_details_map_.find(parent) == node_to_details_map_.end() &&
|
| - (root_shown_ || parent != model_->GetRoot())) {
|
| - // User hasn't navigated to this entry yet. Ignore the change.
|
| - return;
|
| - }
|
| - HTREEITEM parent_tree_item = NULL;
|
| - if (root_shown_ || parent != model_->GetRoot()) {
|
| - const NodeDetails* details = GetNodeDetails(parent);
|
| - if (!details->loaded_children) {
|
| - if (count == model_->GetChildCount(parent)) {
|
| - // Reset the treeviews child count. This triggers the treeview to call
|
| - // us back.
|
| - TV_ITEM tv_item = {0};
|
| - tv_item.mask = TVIF_CHILDREN;
|
| - tv_item.cChildren = count;
|
| - tv_item.hItem = details->tree_item;
|
| - TreeView_SetItem(tree_view_, &tv_item);
|
| - }
|
| -
|
| - // Ignore the change, we haven't actually created entries in the tree
|
| - // for the children.
|
| - return;
|
| - }
|
| - parent_tree_item = details->tree_item;
|
| - }
|
| -
|
| - // The user has expanded this node, add the items to it.
|
| - for (int i = 0; i < count; ++i) {
|
| - if (i == 0 && start == 0) {
|
| - CreateItem(parent_tree_item, TVI_FIRST, model_->GetChild(parent, 0));
|
| - } else {
|
| - TreeModelNode* previous_sibling = model_->GetChild(parent, i + start - 1);
|
| - CreateItem(parent_tree_item,
|
| - GetNodeDetails(previous_sibling)->tree_item,
|
| - model_->GetChild(parent, i + start));
|
| - }
|
| - }
|
| -}
|
| -
|
| -void TreeView::TreeNodesRemoved(TreeModel* model,
|
| - TreeModelNode* parent,
|
| - int start,
|
| - int count) {
|
| - DCHECK(parent && start >= 0 && count > 0);
|
| -
|
| - HTREEITEM tree_item;
|
| - if (!root_shown_ && parent == model->GetRoot()) {
|
| - // NOTE: we can't call GetTreeItemForNodeDuringMutation here as in this
|
| - // configuration the root has no treeitem.
|
| - tree_item = TreeView_GetRoot(tree_view_);
|
| - } else {
|
| - HTREEITEM parent_tree_item = GetTreeItemForNodeDuringMutation(parent);
|
| - if (!parent_tree_item)
|
| - return;
|
| -
|
| - tree_item = TreeView_GetChild(tree_view_, parent_tree_item);
|
| - }
|
| -
|
| - // Find the last item. Windows doesn't offer a convenient way to get the
|
| - // TREEITEM at a particular index, so we iterate.
|
| - for (int i = 0; i < (start + count - 1); ++i) {
|
| - tree_item = TreeView_GetNextSibling(tree_view_, tree_item);
|
| - }
|
| -
|
| - // NOTE: the direction doesn't matter here. I've made it backwards to
|
| - // reinforce we're deleting from the end forward.
|
| - for (int i = count - 1; i >= 0; --i) {
|
| - HTREEITEM previous = (start + i) > 0 ?
|
| - TreeView_GetPrevSibling(tree_view_, tree_item) : NULL;
|
| - RecursivelyDelete(GetNodeDetailsByTreeItem(tree_item));
|
| - tree_item = previous;
|
| - }
|
| -}
|
| -
|
| -void TreeView::TreeNodeChanged(TreeModel* model, TreeModelNode* node) {
|
| - if (node_to_details_map_.find(node) == node_to_details_map_.end()) {
|
| - // User hasn't navigated to this entry yet. Ignore the change.
|
| - return;
|
| - }
|
| - const NodeDetails* details = GetNodeDetails(node);
|
| - TV_ITEM tv_item = {0};
|
| - tv_item.mask = TVIF_TEXT;
|
| - tv_item.hItem = details->tree_item;
|
| - tv_item.pszText = LPSTR_TEXTCALLBACK;
|
| - TreeView_SetItem(tree_view_, &tv_item);
|
| -}
|
| -
|
| -gfx::Point TreeView::GetKeyboardContextMenuLocation() {
|
| - int y = height() / 2;
|
| - if (GetSelectedNode()) {
|
| - RECT bounds;
|
| - RECT client_rect;
|
| - if (TreeView_GetItemRect(tree_view_,
|
| - GetNodeDetails(GetSelectedNode())->tree_item,
|
| - &bounds, TRUE) &&
|
| - GetClientRect(tree_view_, &client_rect) &&
|
| - bounds.bottom >= 0 && bounds.bottom < client_rect.bottom) {
|
| - y = bounds.bottom;
|
| - }
|
| - }
|
| - gfx::Point screen_loc(0, y);
|
| - if (base::i18n::IsRTL())
|
| - screen_loc.set_x(width());
|
| - ConvertPointToScreen(this, &screen_loc);
|
| - return screen_loc;
|
| -}
|
| -
|
| -HWND TreeView::CreateNativeControl(HWND parent_container) {
|
| - int style = WS_CHILD | TVS_HASBUTTONS | TVS_HASLINES | TVS_SHOWSELALWAYS;
|
| - if (!drag_enabled_)
|
| - style |= TVS_DISABLEDRAGDROP;
|
| - if (editable_)
|
| - style |= TVS_EDITLABELS;
|
| - if (lines_at_root_)
|
| - style |= TVS_LINESATROOT;
|
| - tree_view_ = ::CreateWindowEx(WS_EX_CLIENTEDGE | GetAdditionalExStyle(),
|
| - WC_TREEVIEW,
|
| - L"",
|
| - style,
|
| - 0, 0, width(), height(),
|
| - parent_container, NULL, NULL, NULL);
|
| - ui::CheckWindowCreated(tree_view_);
|
| - SetWindowLongPtr(tree_view_, GWLP_USERDATA,
|
| - reinterpret_cast<LONG_PTR>(&wrapper_));
|
| - original_handler_ = ui::SetWindowProc(tree_view_, &TreeWndProc);
|
| - l10n_util::AdjustUIFontForWindow(tree_view_);
|
| -
|
| - if (model_) {
|
| - CreateRootItems();
|
| - AddObserverToModel();
|
| - image_list_ = CreateImageList();
|
| - TreeView_SetImageList(tree_view_, image_list_, TVSIL_NORMAL);
|
| - }
|
| -
|
| - // Bug 964884: detach the IME attached to this window.
|
| - // We should attach IMEs only when we need to input CJK strings.
|
| - ::ImmAssociateContextEx(tree_view_, NULL, 0);
|
| - return tree_view_;
|
| -}
|
| -
|
| -LRESULT TreeView::OnNotify(int w_param, LPNMHDR l_param) {
|
| - switch (l_param->code) {
|
| - case TVN_GETDISPINFO: {
|
| - // Windows is requesting more information about an item.
|
| - // WARNING: At the time this is called the tree_item of the NodeDetails
|
| - // in the maps is NULL.
|
| - DCHECK(model_);
|
| - NMTVDISPINFO* info = reinterpret_cast<NMTVDISPINFO*>(l_param);
|
| -
|
| - // WARNING: its possible for Windows to send a TVN_GETDISPINFO message
|
| - // after the WM_DESTROY time of the native control. Since the details
|
| - // map will be cleaned up on OnDestroy(), don't try to access it in
|
| - // this case.
|
| - if (!id_to_details_map_.empty()) {
|
| - const NodeDetails* details =
|
| - GetNodeDetailsByID(static_cast<int>(info->item.lParam));
|
| - if (info->item.mask & TVIF_CHILDREN)
|
| - info->item.cChildren = model_->GetChildCount(details->node);
|
| - if (info->item.mask & TVIF_TEXT) {
|
| - DCHECK(info->item.cchTextMax);
|
| -
|
| - string16 text = details->node->GetTitle();
|
| - // Adjust the string direction if such adjustment is required.
|
| - base::i18n::AdjustStringForLocaleDirection(&text);
|
| -
|
| - wcsncpy_s(info->item.pszText, info->item.cchTextMax, text.c_str(),
|
| - _TRUNCATE);
|
| - }
|
| - // Instructs windows to cache the values for this node.
|
| - info->item.mask |= TVIF_DI_SETITEM;
|
| - } else {
|
| - if (info->item.mask & TVIF_CHILDREN)
|
| - info->item.cChildren = 0;
|
| -
|
| - if (info->item.mask & TVIF_TEXT)
|
| - wcsncpy_s(info->item.pszText, info->item.cchTextMax, L"", _TRUNCATE);
|
| - }
|
| -
|
| - // Return value ignored.
|
| - return 0;
|
| - }
|
| -
|
| - case TVN_ITEMEXPANDING: {
|
| - // Notification that a node is expanding. If we haven't populated the
|
| - // tree view with the contents of the model, we do it here.
|
| - DCHECK(model_);
|
| - NMTREEVIEW* info = reinterpret_cast<NMTREEVIEW*>(l_param);
|
| - NodeDetails* details =
|
| - GetNodeDetailsByID(static_cast<int>(info->itemNew.lParam));
|
| - if (!details->loaded_children) {
|
| - details->loaded_children = true;
|
| - for (int i = 0; i < model_->GetChildCount(details->node); ++i) {
|
| - CreateItem(details->tree_item, TVI_LAST,
|
| - model_->GetChild(details->node, i));
|
| - if (auto_expand_children_)
|
| - Expand(model_->GetChild(details->node, i));
|
| - }
|
| - }
|
| - // Return FALSE to allow the item to be expanded.
|
| - return FALSE;
|
| - }
|
| -
|
| - case TVN_SELCHANGED:
|
| - if (controller_)
|
| - controller_->OnTreeViewSelectionChanged(this);
|
| - break;
|
| -
|
| - case TVN_BEGINLABELEDIT: {
|
| - NMTVDISPINFO* info = reinterpret_cast<NMTVDISPINFO*>(l_param);
|
| - NodeDetails* details =
|
| - GetNodeDetailsByID(static_cast<int>(info->item.lParam));
|
| - // Return FALSE to allow editing.
|
| - if (!controller_ || controller_->CanEdit(this, details->node)) {
|
| - editing_node_ = details->node;
|
| - return FALSE;
|
| - }
|
| - return TRUE;
|
| - }
|
| -
|
| - case TVN_ENDLABELEDIT: {
|
| - NMTVDISPINFO* info = reinterpret_cast<NMTVDISPINFO*>(l_param);
|
| - if (info->item.pszText) {
|
| - // User accepted edit.
|
| - NodeDetails* details =
|
| - GetNodeDetailsByID(static_cast<int>(info->item.lParam));
|
| - model_->SetTitle(details->node, info->item.pszText);
|
| - editing_node_ = NULL;
|
| - // Return FALSE so that the tree item doesn't change its text (if the
|
| - // model changed the value, it should have sent out notification which
|
| - // will have updated the value).
|
| - return FALSE;
|
| - }
|
| - editing_node_ = NULL;
|
| - // Return value ignored.
|
| - return 0;
|
| - }
|
| -
|
| - case TVN_KEYDOWN:
|
| - if (controller_) {
|
| - NMTVKEYDOWN* key_down_message =
|
| - reinterpret_cast<NMTVKEYDOWN*>(l_param);
|
| - controller_->OnTreeViewKeyDown(
|
| - ui::KeyboardCodeForWindowsKeyCode(key_down_message->wVKey));
|
| - }
|
| - break;
|
| -
|
| - default:
|
| - break;
|
| - }
|
| - return 0;
|
| -}
|
| -
|
| -void TreeView::OnDestroy() {
|
| - Cleanup();
|
| -}
|
| -
|
| -bool TreeView::OnKeyDown(ui::KeyboardCode virtual_key_code) {
|
| - if (virtual_key_code == VK_F2) {
|
| - if (!GetEditingNode()) {
|
| - TreeModelNode* selected_node = GetSelectedNode();
|
| - if (selected_node)
|
| - StartEditing(selected_node);
|
| - }
|
| - return true;
|
| - } else if (virtual_key_code == ui::VKEY_RETURN && !process_enter_) {
|
| - Widget* widget = GetWidget();
|
| - DCHECK(widget);
|
| - ui::Accelerator accelerator(ui::Accelerator(virtual_key_code,
|
| - base::win::IsShiftPressed(),
|
| - base::win::IsCtrlPressed(),
|
| - base::win::IsAltPressed()));
|
| - GetFocusManager()->ProcessAccelerator(accelerator);
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -void TreeView::OnContextMenu(const POINT& location) {
|
| - if (!context_menu_controller())
|
| - return;
|
| -
|
| - if (location.x == -1 && location.y == -1) {
|
| - // Let NativeControl's implementation handle keyboard gesture.
|
| - NativeControl::OnContextMenu(location);
|
| - return;
|
| - }
|
| -
|
| - if (show_context_menu_only_when_node_selected_) {
|
| - if (!GetSelectedNode())
|
| - return;
|
| -
|
| - // Make sure the mouse is over the selected node.
|
| - TVHITTESTINFO hit_info;
|
| - gfx::Point local_loc(location);
|
| - ConvertPointToView(NULL, this, &local_loc);
|
| - hit_info.pt = local_loc.ToPOINT();
|
| - HTREEITEM hit_item = TreeView_HitTest(tree_view_, &hit_info);
|
| - if (!hit_item ||
|
| - GetNodeDetails(GetSelectedNode())->tree_item != hit_item ||
|
| - (hit_info.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT |
|
| - TVHT_ONITEMINDENT)) == 0) {
|
| - return;
|
| - }
|
| - }
|
| - ShowContextMenu(gfx::Point(location), true);
|
| -}
|
| -
|
| -TreeModelNode* TreeView::GetNodeForTreeItem(HTREEITEM tree_item) {
|
| - NodeDetails* details = GetNodeDetailsByTreeItem(tree_item);
|
| - return details ? details->node : NULL;
|
| -}
|
| -
|
| -HTREEITEM TreeView::GetTreeItemForNode(TreeModelNode* node) {
|
| - NodeDetails* details = GetNodeDetails(node);
|
| - return details ? details->tree_item : NULL;
|
| -}
|
| -
|
| -void TreeView::Cleanup() {
|
| - RemoveObserverFromModel();
|
| -
|
| - // Both node_to_details_map_ and node_to_details_map_ have the same value,
|
| - // as such only need to delete from one.
|
| - STLDeleteContainerPairSecondPointers(id_to_details_map_.begin(),
|
| - id_to_details_map_.end());
|
| - id_to_details_map_.clear();
|
| - node_to_details_map_.clear();
|
| -
|
| - if (image_list_) {
|
| - ImageList_Destroy(image_list_);
|
| - image_list_ = NULL;
|
| - }
|
| -}
|
| -
|
| -void TreeView::AddObserverToModel() {
|
| - if (model_ && !observer_added_) {
|
| - model_->AddObserver(this);
|
| - observer_added_ = true;
|
| - }
|
| -}
|
| -
|
| -void TreeView::RemoveObserverFromModel() {
|
| - if (model_ && observer_added_) {
|
| - model_->RemoveObserver(this);
|
| - observer_added_ = false;
|
| - }
|
| -}
|
| -
|
| -void TreeView::DeleteRootItems() {
|
| - HTREEITEM root = TreeView_GetRoot(tree_view_);
|
| - if (root) {
|
| - if (root_shown_) {
|
| - RecursivelyDelete(GetNodeDetailsByTreeItem(root));
|
| - } else {
|
| - do {
|
| - RecursivelyDelete(GetNodeDetailsByTreeItem(root));
|
| - } while ((root = TreeView_GetRoot(tree_view_)));
|
| - }
|
| - }
|
| -}
|
| -
|
| -void TreeView::CreateRootItems() {
|
| - DCHECK(model_);
|
| - DCHECK(tree_view_);
|
| - TreeModelNode* root = model_->GetRoot();
|
| - if (root_shown_) {
|
| - CreateItem(NULL, TVI_LAST, root);
|
| - } else {
|
| - for (int i = 0; i < model_->GetChildCount(root); ++i)
|
| - CreateItem(NULL, TVI_LAST, model_->GetChild(root, i));
|
| - }
|
| -}
|
| -
|
| -void TreeView::CreateItem(HTREEITEM parent_item,
|
| - HTREEITEM after,
|
| - TreeModelNode* node) {
|
| - DCHECK(node);
|
| - TVINSERTSTRUCT insert_struct = {0};
|
| - insert_struct.hParent = parent_item;
|
| - insert_struct.hInsertAfter = after;
|
| - insert_struct.itemex.mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_TEXT |
|
| - TVIF_SELECTEDIMAGE | TVIF_IMAGE;
|
| - // Call us back for the text.
|
| - insert_struct.itemex.pszText = LPSTR_TEXTCALLBACK;
|
| - // And the number of children.
|
| - insert_struct.itemex.cChildren = I_CHILDRENCALLBACK;
|
| - // Set the index of the icons to use. These are relative to the imagelist
|
| - // created in CreateImageList.
|
| - int icon_index = model_->GetIconIndex(node);
|
| - if (icon_index == -1) {
|
| - insert_struct.itemex.iImage = 0;
|
| - insert_struct.itemex.iSelectedImage = 1;
|
| - } else {
|
| - // The first two images are the default ones.
|
| - insert_struct.itemex.iImage = icon_index + 2;
|
| - insert_struct.itemex.iSelectedImage = icon_index + 2;
|
| - }
|
| - int node_id = next_id_++;
|
| - insert_struct.itemex.lParam = node_id;
|
| -
|
| - // Invoking TreeView_InsertItem triggers OnNotify to be called. As such,
|
| - // we set the map entries before adding the item.
|
| - NodeDetails* node_details = new NodeDetails(node_id, node);
|
| -
|
| - DCHECK(node_to_details_map_.count(node) == 0);
|
| - DCHECK(id_to_details_map_.count(node_id) == 0);
|
| -
|
| - node_to_details_map_[node] = node_details;
|
| - id_to_details_map_[node_id] = node_details;
|
| -
|
| - node_details->tree_item = TreeView_InsertItem(tree_view_, &insert_struct);
|
| -}
|
| -
|
| -void TreeView::RecursivelyDelete(NodeDetails* node) {
|
| - DCHECK(node);
|
| - HTREEITEM item = node->tree_item;
|
| - DCHECK(item);
|
| -
|
| - // Recurse through children.
|
| - for (HTREEITEM child = TreeView_GetChild(tree_view_, item); child ;) {
|
| - HTREEITEM next = TreeView_GetNextSibling(tree_view_, child);
|
| - RecursivelyDelete(GetNodeDetailsByTreeItem(child));
|
| - child = next;
|
| - }
|
| -
|
| - TreeView_DeleteItem(tree_view_, item);
|
| -
|
| - // finally, it is safe to delete the data for this node.
|
| - id_to_details_map_.erase(node->id);
|
| - node_to_details_map_.erase(node->node);
|
| - delete node;
|
| -}
|
| -
|
| -TreeView::NodeDetails* TreeView::GetNodeDetails(TreeModelNode* node) {
|
| - DCHECK(node &&
|
| - node_to_details_map_.find(node) != node_to_details_map_.end());
|
| - return node_to_details_map_[node];
|
| -}
|
| -
|
| -// Returns the NodeDetails by identifier (lparam of the HTREEITEM).
|
| -TreeView::NodeDetails* TreeView::GetNodeDetailsByID(int id) {
|
| - DCHECK(id_to_details_map_.find(id) != id_to_details_map_.end());
|
| - return id_to_details_map_[id];
|
| -}
|
| -
|
| -TreeView::NodeDetails* TreeView::GetNodeDetailsByTreeItem(HTREEITEM tree_item) {
|
| - DCHECK(tree_view_ && tree_item);
|
| - TV_ITEM tv_item = {0};
|
| - tv_item.hItem = tree_item;
|
| - tv_item.mask = TVIF_PARAM;
|
| - if (TreeView_GetItem(tree_view_, &tv_item))
|
| - return GetNodeDetailsByID(static_cast<int>(tv_item.lParam));
|
| - return NULL;
|
| -}
|
| -
|
| -HIMAGELIST TreeView::CreateImageList() {
|
| - std::vector<SkBitmap> model_images;
|
| - model_->GetIcons(&model_images);
|
| -
|
| - bool rtl = base::i18n::IsRTL();
|
| - // Creates the default image list used for trees.
|
| - SkBitmap* closed_icon =
|
| - ResourceBundle::GetSharedInstance().GetBitmapNamed(
|
| - (rtl ? IDR_FOLDER_CLOSED_RTL : IDR_FOLDER_CLOSED));
|
| - SkBitmap* opened_icon =
|
| - ResourceBundle::GetSharedInstance().GetBitmapNamed(
|
| - (rtl ? IDR_FOLDER_OPEN_RTL : IDR_FOLDER_OPEN));
|
| - int width = closed_icon->width();
|
| - int height = closed_icon->height();
|
| - DCHECK(opened_icon->width() == width && opened_icon->height() == height);
|
| - HIMAGELIST image_list =
|
| - ImageList_Create(width, height, ILC_COLOR32, model_images.size() + 2,
|
| - model_images.size() + 2);
|
| - if (image_list) {
|
| - // NOTE: the order the images are added in effects the selected
|
| - // image index when adding items to the tree. If you change the
|
| - // order you'll undoubtedly need to update itemex.iSelectedImage
|
| - // when the item is added.
|
| - HICON h_closed_icon = IconUtil::CreateHICONFromSkBitmap(*closed_icon);
|
| - HICON h_opened_icon = IconUtil::CreateHICONFromSkBitmap(*opened_icon);
|
| - ImageList_AddIcon(image_list, h_closed_icon);
|
| - ImageList_AddIcon(image_list, h_opened_icon);
|
| - DestroyIcon(h_closed_icon);
|
| - DestroyIcon(h_opened_icon);
|
| - for (size_t i = 0; i < model_images.size(); ++i) {
|
| - HICON model_icon;
|
| -
|
| - // Need to resize the provided icons to be the same size as
|
| - // IDR_FOLDER_CLOSED if they aren't already.
|
| - if (model_images[i].width() != width ||
|
| - model_images[i].height() != height) {
|
| - gfx::CanvasSkia canvas(width, height, false);
|
| - // Make the background completely transparent.
|
| - canvas.sk_canvas()->drawColor(SK_ColorBLACK, SkXfermode::kClear_Mode);
|
| -
|
| - // Draw our icons into this canvas.
|
| - int height_offset = (height - model_images[i].height()) / 2;
|
| - int width_offset = (width - model_images[i].width()) / 2;
|
| - canvas.DrawBitmapInt(model_images[i], width_offset, height_offset);
|
| - model_icon = IconUtil::CreateHICONFromSkBitmap(canvas.ExtractBitmap());
|
| - } else {
|
| - model_icon = IconUtil::CreateHICONFromSkBitmap(model_images[i]);
|
| - }
|
| - ImageList_AddIcon(image_list, model_icon);
|
| - DestroyIcon(model_icon);
|
| - }
|
| - }
|
| - return image_list;
|
| -}
|
| -
|
| -HTREEITEM TreeView::GetTreeItemForNodeDuringMutation(TreeModelNode* node) {
|
| - if (node_to_details_map_.find(node) == node_to_details_map_.end()) {
|
| - // User hasn't navigated to this entry yet. Ignore the change.
|
| - return NULL;
|
| - }
|
| - if (!root_shown_ || node != model_->GetRoot()) {
|
| - const NodeDetails* details = GetNodeDetails(node);
|
| - if (!details->loaded_children)
|
| - return NULL;
|
| - return details->tree_item;
|
| - }
|
| - return TreeView_GetRoot(tree_view_);
|
| -}
|
| -
|
| -LRESULT CALLBACK TreeView::TreeWndProc(HWND window,
|
| - UINT message,
|
| - WPARAM w_param,
|
| - LPARAM l_param) {
|
| - TreeViewWrapper* wrapper = reinterpret_cast<TreeViewWrapper*>(
|
| - GetWindowLongPtr(window, GWLP_USERDATA));
|
| - DCHECK(wrapper);
|
| - TreeView* tree = wrapper->tree_view;
|
| -
|
| - // We handle the messages WM_ERASEBKGND and WM_PAINT such that we paint into
|
| - // a DIB first and then perform a BitBlt from the DIB into the underlying
|
| - // window's DC. This double buffering code prevents the tree view from
|
| - // flickering during resize.
|
| - switch (message) {
|
| - case WM_ERASEBKGND:
|
| - return 1;
|
| -
|
| - case WM_PAINT: {
|
| - gfx::CanvasSkiaPaint canvas(window);
|
| - if (canvas.isEmpty())
|
| - return 0;
|
| -
|
| - HDC dc = skia::BeginPlatformPaint(canvas.sk_canvas());
|
| - if (base::i18n::IsRTL()) {
|
| - // gfx::CanvasSkia ends up configuring the DC with a mode of
|
| - // GM_ADVANCED. For some reason a graphics mode of ADVANCED triggers
|
| - // all the text to be mirrored when RTL. Set the mode back to COMPATIBLE
|
| - // and explicitly set the layout. Additionally SetWorldTransform and
|
| - // COMPATIBLE don't play nicely together. We need to use
|
| - // SetViewportOrgEx when using a mode of COMPATIBLE.
|
| - //
|
| - // Reset the transform to the identify transform. Even though
|
| - // SetWorldTransform and COMPATIBLE don't play nicely, bits of the
|
| - // transform still carry over when we set the mode.
|
| - XFORM xform = {0};
|
| - xform.eM11 = xform.eM22 = 1;
|
| - SetWorldTransform(dc, &xform);
|
| -
|
| - // Set the mode and layout.
|
| - SetGraphicsMode(dc, GM_COMPATIBLE);
|
| - SetLayout(dc, LAYOUT_RTL);
|
| -
|
| - // Transform the viewport such that the origin of the dc is that of
|
| - // the dirty region. This way when we invoke WM_PRINTCLIENT tree-view
|
| - // draws the dirty region at the origin of the DC so that when we
|
| - // copy the bits everything lines up nicely. Without this we end up
|
| - // copying the upper-left corner to the redraw region.
|
| - SetViewportOrgEx(dc, -canvas.paintStruct().rcPaint.left,
|
| - -canvas.paintStruct().rcPaint.top, NULL);
|
| - }
|
| - SendMessage(window, WM_PRINTCLIENT, reinterpret_cast<WPARAM>(dc), 0);
|
| - if (base::i18n::IsRTL()) {
|
| - // Reset the origin of the dc back to 0. This way when we copy the bits
|
| - // over we copy the right bits.
|
| - SetViewportOrgEx(dc, 0, 0, NULL);
|
| - }
|
| - skia::EndPlatformPaint(canvas.sk_canvas());
|
| - return 0;
|
| - }
|
| -
|
| - case WM_RBUTTONDOWN:
|
| - if (tree->select_on_right_mouse_down_) {
|
| - TVHITTESTINFO hit_info;
|
| - hit_info.pt = gfx::Point(l_param).ToPOINT();
|
| - HTREEITEM hit_item = TreeView_HitTest(window, &hit_info);
|
| - if (hit_item && (hit_info.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT |
|
| - TVHT_ONITEMINDENT)) != 0)
|
| - TreeView_SelectItem(tree->tree_view_, hit_item);
|
| - }
|
| - // Fall through and let the default handler process as well.
|
| - break;
|
| - }
|
| - WNDPROC handler = tree->original_handler_;
|
| - DCHECK(handler);
|
| - return CallWindowProc(handler, window, message, w_param, l_param);
|
| -}
|
| -
|
| -} // namespace views
|
|
|