// 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/tree/tree_view.h"

#include <shellapi.h>

#include "app/gfx/chrome_canvas.h"
#include "app/gfx/icon_util.h"
#include "app/l10n_util.h"
#include "app/l10n_util_win.h"
#include "app/resource_bundle.h"
#include "base/stl_util-inl.h"
#include "base/win_util.h"
#include "grit/theme_resources.h"
#include "views/focus/focus_manager.h"
#include "views/widget/widget.h"

namespace views {

TreeView::TreeView()
    : tree_view_(NULL),
      model_(NULL),
      editable_(true),
      next_id_(0),
      controller_(NULL),
      editing_node_(NULL),
      root_shown_(true),
      process_enter_(false),
      show_context_menu_only_when_node_selected_(true),
      select_on_right_mouse_down_(true),
      wrapper_(this),
      original_handler_(NULL),
      drag_enabled_(false),
      has_custom_icons_(false),
      image_list_(NULL) {
}

TreeView::~TreeView() {
  if (model_)
    model_->SetObserver(NULL);
  // Both param_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());
  if (image_list_)
    ImageList_Destroy(image_list_);
}

void TreeView::SetModel(TreeModel* model) {
  if (model == model_)
    return;
  if(model_ && tree_view_)
    DeleteRootItems();
  if (model_)
    model_->SetObserver(NULL);
  model_ = model;
  if (tree_view_ && model_) {
    CreateRootItems();
    model_->SetObserver(this);
    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() {
  DCHECK(model_);
  ExpandAll(model_->GetRoot());
}

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) {
  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_)
    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()) {
    // 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 parent_tree_item = GetTreeItemForNodeDuringMutation(parent);
  if (!parent_tree_item)
    return;

  // Find the last item. Windows doesn't offer a convenient way to get the
  // TREEITEM at a particular index, so we iterate.
  HTREEITEM tree_item = TreeView_GetChild(tree_view_, parent_tree_item);
  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;
  }
}

namespace {

// Callback function used to compare two items. The first two args are the
// LPARAMs of the HTREEITEMs being compared. The last arg maps from LPARAM
// to order. This is invoked from TreeNodeChildrenReordered.
int CALLBACK CompareTreeItems(LPARAM item1_lparam,
                              LPARAM item2_lparam,
                              LPARAM map_as_lparam) {
  std::map<int, int>& mapping =
      *reinterpret_cast<std::map<int, int>*>(map_as_lparam);
  return mapping[static_cast<int>(item1_lparam)] -
         mapping[static_cast<int>(item2_lparam)];
}

}  // namespace

void TreeView::TreeNodeChildrenReordered(TreeModel* model,
                                         TreeModelNode* parent) {
  DCHECK(parent);
  if (model_->GetChildCount(parent) <= 1)
    return;

  TVSORTCB sort_details;
  sort_details.hParent = GetTreeItemForNodeDuringMutation(parent);
  if (!sort_details.hParent)
    return;

  std::map<int, int> lparam_to_order_map;
  for (int i = 0; i < model_->GetChildCount(parent); ++i) {
    TreeModelNode* node = model_->GetChild(parent, i);
    lparam_to_order_map[GetNodeDetails(node)->id] = i;
  }

  sort_details.lpfnCompare = &CompareTreeItems;
  sort_details.lParam = reinterpret_cast<LPARAM>(&lparam_to_order_map);
  TreeView_SortChildrenCB(tree_view_, &sort_details, 0);
}

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 (UILayoutIsRightToLeft())
    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;
  tree_view_ = ::CreateWindowEx(WS_EX_CLIENTEDGE | GetAdditionalExStyle(),
                                WC_TREEVIEW,
                                L"",
                                style,
                                0, 0, width(), height(),
                                parent_container, NULL, NULL, NULL);
  SetWindowLongPtr(tree_view_, GWLP_USERDATA,
                   reinterpret_cast<LONG_PTR>(&wrapper_));
  original_handler_ = win_util::SetWindowProc(tree_view_,
                                              &TreeWndProc);
  l10n_util::AdjustUIFontForWindow(tree_view_);

  if (model_) {
    CreateRootItems();
    model_->SetObserver(this);
    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.
  win_util::IMEAttach(tree_view_, false);
  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);
      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) {
        std::wstring text = details->node->GetTitle();
        DCHECK(info->item.cchTextMax);

        // Adjust the string direction if such adjustment is required.
        std::wstring localized_text;
        if (l10n_util::AdjustStringForLocaleDirection(text, &localized_text))
          text.swap(localized_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;
      // 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));
      }
      // 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(key_down_message->wVKey);
      }
      break;

    default:
      break;
  }
  return 0;
}

bool TreeView::OnKeyDown(int 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 == VK_RETURN && !process_enter_) {
    Widget* widget = GetWidget();
    DCHECK(widget);
    FocusManager* fm = FocusManager::GetFocusManager(widget->GetNativeView());
    DCHECK(fm);
    Accelerator accelerator(Accelerator(static_cast<int>(virtual_key_code),
                                        win_util::IsShiftPressed(),
                                        win_util::IsCtrlPressed(),
                                        win_util::IsAltPressed()));
    fm->ProcessAccelerator(accelerator);
    return true;
  }
  return false;
}

void TreeView::OnContextMenu(const CPoint& location) {
  if (!GetContextMenuController())
    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.x = local_loc.x();
    hit_info.pt.y = local_loc.y();
    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(location.x, location.y, 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::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_);
  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);

  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 ; child = TreeView_GetNextSibling(tree_view_, child)) {
    RecursivelyDelete(GetNodeDetailsByTreeItem(child));
  }

  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::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 = UILayoutIsRightToLeft();
  // 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 = 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: {
      ChromeCanvasPaint canvas(window);
      if (canvas.isEmpty())
        return 0;

      HDC dc = canvas.beginPlatformPaint();
      if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
        // ChromeCanvas 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 (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
        // 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);
      }
      canvas.endPlatformPaint();
      return 0;
    }

    case WM_RBUTTONDOWN:
      if (tree->select_on_right_mouse_down_) {
        TVHITTESTINFO hit_info;
        hit_info.pt.x = GET_X_LPARAM(l_param);
        hit_info.pt.y = GET_Y_LPARAM(l_param);
        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