1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/views/controls/tree/tree_view.h"
6
7#include <algorithm>
8
9#include "base/i18n/rtl.h"
10#include "base/message_loop/message_loop.h"
11#include "ui/accessibility/ax_view_state.h"
12#include "ui/base/resource/resource_bundle.h"
13#include "ui/events/event.h"
14#include "ui/events/keycodes/keyboard_codes.h"
15#include "ui/gfx/canvas.h"
16#include "ui/gfx/geometry/rect.h"
17#include "ui/gfx/image/image.h"
18#include "ui/gfx/rect_conversions.h"
19#include "ui/gfx/skia_util.h"
20#include "ui/native_theme/native_theme.h"
21#include "ui/resources/grit/ui_resources.h"
22#include "ui/views/controls/prefix_selector.h"
23#include "ui/views/controls/scroll_view.h"
24#include "ui/views/controls/textfield/textfield.h"
25#include "ui/views/controls/tree/tree_view_controller.h"
26#include "ui/views/ime/input_method.h"
27
28using ui::TreeModel;
29using ui::TreeModelNode;
30
31namespace views {
32
33// Insets around the view.
34static const int kHorizontalInset = 2;
35static const int kVerticalInset = 2;
36// Padding before/after the image.
37static const int kImagePadding = 4;
38// Size of the arrow region.
39static const int kArrowRegionSize = 12;
40// Padding around the text (on each side).
41static const int kTextVerticalPadding = 3;
42static const int kTextHorizontalPadding = 2;
43// How much children are indented from their parent.
44static const int kIndent = 20;
45
46// static
47const char TreeView::kViewClassName[] = "TreeView";
48
49namespace {
50
51// Returns the color id for the background of selected text. |has_focus|
52// indicates if the tree has focus.
53ui::NativeTheme::ColorId text_background_color_id(bool has_focus) {
54  return has_focus ?
55      ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused :
56      ui::NativeTheme::kColorId_TreeSelectionBackgroundUnfocused;
57}
58
59// Returns the color id for text. |has_focus| indicates if the tree has focus
60// and |is_selected| is true if the item is selected.
61ui::NativeTheme::ColorId text_color_id(bool has_focus, bool is_selected) {
62  if (is_selected) {
63    if (has_focus)
64      return ui::NativeTheme::kColorId_TreeSelectedText;
65    return ui::NativeTheme::kColorId_TreeSelectedTextUnfocused;
66  }
67  return ui::NativeTheme::kColorId_TreeText;
68}
69
70}  // namespace
71
72TreeView::TreeView()
73    : model_(NULL),
74      selected_node_(NULL),
75      editing_(false),
76      editor_(NULL),
77      focus_manager_(NULL),
78      auto_expand_children_(false),
79      editable_(true),
80      controller_(NULL),
81      root_shown_(true),
82      row_height_(font_list_.GetHeight() + kTextVerticalPadding * 2) {
83  SetFocusable(true);
84  closed_icon_ = *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
85      (base::i18n::IsRTL() ? IDR_FOLDER_CLOSED_RTL
86                           : IDR_FOLDER_CLOSED)).ToImageSkia();
87  open_icon_ = *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
88      (base::i18n::IsRTL() ? IDR_FOLDER_OPEN_RTL
89                           : IDR_FOLDER_OPEN)).ToImageSkia();
90  text_offset_ = closed_icon_.width() + kImagePadding + kImagePadding +
91      kArrowRegionSize;
92}
93
94TreeView::~TreeView() {
95  if (model_)
96    model_->RemoveObserver(this);
97  if (focus_manager_) {
98    focus_manager_->RemoveFocusChangeListener(this);
99    focus_manager_ = NULL;
100  }
101}
102
103View* TreeView::CreateParentIfNecessary() {
104  ScrollView* scroll_view = ScrollView::CreateScrollViewWithBorder();
105  scroll_view->SetContents(this);
106  return scroll_view;
107}
108
109void TreeView::SetModel(TreeModel* model) {
110  if (model == model_)
111    return;
112  if (model_)
113    model_->RemoveObserver(this);
114
115  CancelEdit();
116
117  model_ = model;
118  selected_node_ = NULL;
119  icons_.clear();
120  if (model_) {
121    model_->AddObserver(this);
122    model_->GetIcons(&icons_);
123
124    root_.RemoveAll();
125    ConfigureInternalNode(model_->GetRoot(), &root_);
126    LoadChildren(&root_);
127    root_.set_is_expanded(true);
128    if (root_shown_)
129      selected_node_ = &root_;
130    else if (root_.child_count())
131      selected_node_ = root_.GetChild(0);
132  }
133  DrawnNodesChanged();
134}
135
136void TreeView::SetEditable(bool editable) {
137  if (editable == editable_)
138    return;
139  editable_ = editable;
140  CancelEdit();
141}
142
143void TreeView::StartEditing(TreeModelNode* node) {
144  DCHECK(node);
145  // Cancel the current edit.
146  CancelEdit();
147  // Make sure all ancestors are expanded.
148  if (model_->GetParent(node))
149    Expand(model_->GetParent(node));
150  // Select the node, else if the user commits the edit the selection reverts.
151  SetSelectedNode(node);
152  if (GetSelectedNode() != node)
153    return;  // Selection failed for some reason, don't start editing.
154  DCHECK(!editing_);
155  editing_ = true;
156  if (!editor_) {
157    editor_ = new Textfield;
158    // Add the editor immediately as GetPreferredSize returns the wrong thing if
159    // not parented.
160    AddChildView(editor_);
161    editor_->SetFontList(font_list_);
162    empty_editor_size_ = editor_->GetPreferredSize();
163    editor_->set_controller(this);
164  }
165  editor_->SetText(selected_node_->model_node()->GetTitle());
166  LayoutEditor();
167  editor_->SetVisible(true);
168  SchedulePaintForNode(selected_node_);
169  editor_->RequestFocus();
170  editor_->SelectAll(false);
171
172  // Listen for focus changes so that we can cancel editing.
173  focus_manager_ = GetFocusManager();
174  if (focus_manager_)
175    focus_manager_->AddFocusChangeListener(this);
176
177  // Accelerators to commit/cancel edit.
178  AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
179  AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
180}
181
182void TreeView::CancelEdit() {
183  if (!editing_)
184    return;
185
186  // WARNING: don't touch |selected_node_|, it may be bogus.
187
188  editing_ = false;
189  if (focus_manager_) {
190    focus_manager_->RemoveFocusChangeListener(this);
191    focus_manager_ = NULL;
192  }
193  editor_->SetVisible(false);
194  SchedulePaint();
195
196  RemoveAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
197  RemoveAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
198}
199
200void TreeView::CommitEdit() {
201  if (!editing_)
202    return;
203
204  DCHECK(selected_node_);
205  const bool editor_has_focus = editor_->HasFocus();
206  model_->SetTitle(GetSelectedNode(), editor_->text());
207  CancelEdit();
208  if (editor_has_focus)
209    RequestFocus();
210}
211
212TreeModelNode* TreeView::GetEditingNode() {
213  return editing_ ? selected_node_->model_node() : NULL;
214}
215
216void TreeView::SetSelectedNode(TreeModelNode* model_node) {
217  if (editing_ || model_node != selected_node_)
218    CancelEdit();
219  if (model_node && model_->GetParent(model_node))
220    Expand(model_->GetParent(model_node));
221  if (model_node && model_node == root_.model_node() && !root_shown_)
222    return;  // Ignore requests to select the root when not shown.
223  InternalNode* node = model_node ? GetInternalNodeForModelNode(
224      model_node, CREATE_IF_NOT_LOADED) : NULL;
225  bool was_empty_selection = (selected_node_ == NULL);
226  bool changed = (selected_node_ != node);
227  if (changed) {
228    SchedulePaintForNode(selected_node_);
229    selected_node_ = node;
230    if (selected_node_ == &root_ && !root_shown_)
231      selected_node_ = NULL;
232    if (selected_node_ && selected_node_ != &root_)
233      Expand(model_->GetParent(selected_node_->model_node()));
234    SchedulePaintForNode(selected_node_);
235  }
236
237  if (selected_node_)
238    ScrollRectToVisible(GetBoundsForNode(selected_node_));
239
240  // Notify controller if the old selection was empty to handle the case of
241  // remove explicitly resetting selected_node_ before invoking this.
242  if (controller_ && (changed || was_empty_selection))
243    controller_->OnTreeViewSelectionChanged(this);
244
245  if (changed) {
246    // TODO(dmazzoni): Decide if EVENT_SELECTION_CHANGED is a better choice for
247    // sub-item selection event.
248    NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
249  }
250}
251
252TreeModelNode* TreeView::GetSelectedNode() {
253  return selected_node_ ? selected_node_->model_node() : NULL;
254}
255
256void TreeView::Collapse(ui::TreeModelNode* model_node) {
257  // Don't collapse the root if the root isn't shown, otherwise nothing is
258  // displayed.
259  if (model_node == root_.model_node() && !root_shown_)
260    return;
261  InternalNode* node =
262      GetInternalNodeForModelNode(model_node, DONT_CREATE_IF_NOT_LOADED);
263  if (!node)
264    return;
265  bool was_expanded = IsExpanded(model_node);
266  if (node->is_expanded()) {
267    if (selected_node_ && selected_node_->HasAncestor(node))
268      SetSelectedNode(model_node);
269    node->set_is_expanded(false);
270  }
271  if (was_expanded)
272    DrawnNodesChanged();
273}
274
275void TreeView::Expand(TreeModelNode* node) {
276  if (ExpandImpl(node))
277    DrawnNodesChanged();
278  // TODO: need to support auto_expand_children_.
279}
280
281void TreeView::ExpandAll(TreeModelNode* node) {
282  DCHECK(node);
283  // Expand the node.
284  bool expanded_at_least_one = ExpandImpl(node);
285  // And recursively expand all the children.
286  for (int i = model_->GetChildCount(node) - 1; i >= 0; --i) {
287    TreeModelNode* child = model_->GetChild(node, i);
288    if (ExpandImpl(child))
289      expanded_at_least_one = true;
290  }
291  if (expanded_at_least_one)
292    DrawnNodesChanged();
293}
294
295bool TreeView::IsExpanded(TreeModelNode* model_node) {
296  if (!model_node) {
297    // NULL check primarily for convenience for uses in this class so don't have
298    // to add NULL checks every where we look up the parent.
299    return true;
300  }
301  InternalNode* node = GetInternalNodeForModelNode(
302      model_node, DONT_CREATE_IF_NOT_LOADED);
303  if (!node)
304    return false;
305
306  while (node) {
307    if (!node->is_expanded())
308      return false;
309    node = node->parent();
310  }
311  return true;
312}
313
314void TreeView::SetRootShown(bool root_shown) {
315  if (root_shown_ == root_shown)
316    return;
317  root_shown_ = root_shown;
318  if (!root_shown_ && selected_node_ == &root_) {
319    if (model_->GetChildCount(root_.model_node()))
320      SetSelectedNode(model_->GetChild(root_.model_node(), 0));
321    else
322      SetSelectedNode(NULL);
323  }
324  DrawnNodesChanged();
325}
326
327ui::TreeModelNode* TreeView::GetNodeForRow(int row) {
328  int depth = 0;
329  InternalNode* node = GetNodeByRow(row, &depth);
330  return node ? node->model_node() : NULL;
331}
332
333int TreeView::GetRowForNode(ui::TreeModelNode* node) {
334  InternalNode* internal_node =
335      GetInternalNodeForModelNode(node, DONT_CREATE_IF_NOT_LOADED);
336  if (!internal_node)
337    return -1;
338  int depth = 0;
339  return GetRowForInternalNode(internal_node, &depth);
340}
341
342void TreeView::Layout() {
343  int width = preferred_size_.width();
344  int height = preferred_size_.height();
345  if (parent()) {
346    width = std::max(parent()->width(), width);
347    height = std::max(parent()->height(), height);
348  }
349  SetBounds(x(), y(), width, height);
350  LayoutEditor();
351}
352
353gfx::Size TreeView::GetPreferredSize() const {
354  return preferred_size_;
355}
356
357bool TreeView::AcceleratorPressed(const ui::Accelerator& accelerator) {
358  if (accelerator.key_code() == ui::VKEY_RETURN) {
359    CommitEdit();
360  } else {
361    DCHECK_EQ(ui::VKEY_ESCAPE, accelerator.key_code());
362    CancelEdit();
363    RequestFocus();
364  }
365  return true;
366}
367
368bool TreeView::OnMousePressed(const ui::MouseEvent& event) {
369  return OnClickOrTap(event);
370}
371
372ui::TextInputClient* TreeView::GetTextInputClient() {
373  if (!selector_)
374    selector_.reset(new PrefixSelector(this));
375  return selector_.get();
376}
377
378void TreeView::OnGestureEvent(ui::GestureEvent* event) {
379  if (event->type() == ui::ET_GESTURE_TAP) {
380    if (OnClickOrTap(*event))
381      event->SetHandled();
382  }
383}
384
385void TreeView::ShowContextMenu(const gfx::Point& p,
386                               ui::MenuSourceType source_type) {
387  if (!model_)
388    return;
389  if (source_type == ui::MENU_SOURCE_MOUSE) {
390    // Only invoke View's implementation (which notifies the
391    // ContextMenuController) if over a node.
392    gfx::Point local_point(p);
393    ConvertPointFromScreen(this, &local_point);
394    int row = (local_point.y() - kVerticalInset) / row_height_;
395    int depth = 0;
396    InternalNode* node = GetNodeByRow(row, &depth);
397    if (!node)
398      return;
399    gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth));
400    if (!bounds.Contains(local_point))
401      return;
402  }
403  View::ShowContextMenu(p, source_type);
404}
405
406void TreeView::GetAccessibleState(ui::AXViewState* state) {
407  state->role = ui::AX_ROLE_TREE;
408  state->AddStateFlag(ui::AX_STATE_READ_ONLY);
409  if (!selected_node_)
410    return;
411
412  // Get selected item info.
413  state->role = ui::AX_ROLE_TREE_ITEM;
414  state->name = selected_node_->model_node()->GetTitle();
415}
416
417const char* TreeView::GetClassName() const {
418  return kViewClassName;
419}
420
421void TreeView::TreeNodesAdded(TreeModel* model,
422                              TreeModelNode* parent,
423                              int start,
424                              int count) {
425  InternalNode* parent_node =
426      GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED);
427  if (!parent_node || !parent_node->loaded_children())
428    return;
429  for (int i = 0; i < count; ++i) {
430    InternalNode* child = new InternalNode;
431    ConfigureInternalNode(model_->GetChild(parent, start + i), child);
432    parent_node->Add(child, start + i);
433  }
434  if (IsExpanded(parent))
435    DrawnNodesChanged();
436}
437
438void TreeView::TreeNodesRemoved(TreeModel* model,
439                                TreeModelNode* parent,
440                                int start,
441                                int count) {
442  InternalNode* parent_node =
443      GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED);
444  if (!parent_node || !parent_node->loaded_children())
445    return;
446  bool reset_selection = false;
447  for (int i = 0; i < count; ++i) {
448    InternalNode* child_removing = parent_node->GetChild(start);
449    if (selected_node_ && selected_node_->HasAncestor(child_removing))
450      reset_selection = true;
451    delete parent_node->Remove(child_removing);
452  }
453  if (reset_selection) {
454    // selected_node_ is no longer valid (at the time we enter this function
455    // its model_node() is likely deleted). Explicitly NULL out the field
456    // rather than invoking SetSelectedNode() otherwise, we'll try and use a
457    // deleted value.
458    selected_node_ = NULL;
459    TreeModelNode* to_select = parent;
460    if (parent == root_.model_node() && !root_shown_) {
461      to_select = model_->GetChildCount(parent) > 0 ?
462          model_->GetChild(parent, 0) : NULL;
463    }
464    SetSelectedNode(to_select);
465  }
466  if (IsExpanded(parent))
467    DrawnNodesChanged();
468}
469
470void TreeView::TreeNodeChanged(TreeModel* model, TreeModelNode* model_node) {
471  InternalNode* node =
472      GetInternalNodeForModelNode(model_node, DONT_CREATE_IF_NOT_LOADED);
473  if (!node)
474    return;
475  int old_width = node->text_width();
476  UpdateNodeTextWidth(node);
477  if (old_width != node->text_width() &&
478      ((node == &root_ && root_shown_) ||
479       (node != &root_ && IsExpanded(node->parent()->model_node())))) {
480    DrawnNodesChanged();
481  }
482}
483
484void TreeView::ContentsChanged(Textfield* sender,
485                               const base::string16& new_contents) {
486}
487
488bool TreeView::HandleKeyEvent(Textfield* sender,
489                              const ui::KeyEvent& key_event) {
490  switch (key_event.key_code()) {
491    case ui::VKEY_RETURN:
492      CommitEdit();
493      return true;
494
495    case ui::VKEY_ESCAPE:
496      CancelEdit();
497      RequestFocus();
498      return true;
499
500    default:
501      return false;
502  }
503}
504
505void TreeView::OnWillChangeFocus(View* focused_before, View* focused_now) {
506}
507
508void TreeView::OnDidChangeFocus(View* focused_before, View* focused_now) {
509  CommitEdit();
510}
511
512int TreeView::GetRowCount() {
513  int row_count = root_.NumExpandedNodes();
514  if (!root_shown_)
515    row_count--;
516  return row_count;
517}
518
519int TreeView::GetSelectedRow() {
520  ui::TreeModelNode* model_node = GetSelectedNode();
521  return model_node ? GetRowForNode(model_node) : -1;
522}
523
524void TreeView::SetSelectedRow(int row) {
525  SetSelectedNode(GetNodeForRow(row));
526}
527
528base::string16 TreeView::GetTextForRow(int row) {
529  return GetNodeForRow(row)->GetTitle();
530}
531
532gfx::Point TreeView::GetKeyboardContextMenuLocation() {
533  int y = height() / 2;
534  if (selected_node_) {
535    gfx::Rect node_bounds(GetBoundsForNode(selected_node_));
536    gfx::Rect vis_bounds(GetVisibleBounds());
537    if (node_bounds.y() >= vis_bounds.y() &&
538        node_bounds.y() < vis_bounds.bottom()) {
539      y = node_bounds.y();
540    }
541  }
542  gfx::Point screen_loc(0, y);
543  if (base::i18n::IsRTL())
544    screen_loc.set_x(width());
545  ConvertPointToScreen(this, &screen_loc);
546  return screen_loc;
547}
548
549bool TreeView::OnKeyPressed(const ui::KeyEvent& event) {
550  if (!HasFocus())
551    return false;
552
553  switch (event.key_code()) {
554    case ui::VKEY_F2:
555      if (!editing_) {
556        TreeModelNode* selected_node = GetSelectedNode();
557        if (selected_node && (!controller_ ||
558                              controller_->CanEdit(this, selected_node))) {
559          StartEditing(selected_node);
560        }
561      }
562      return true;
563
564    case ui::VKEY_UP:
565    case ui::VKEY_DOWN:
566      IncrementSelection(event.key_code() == ui::VKEY_UP ?
567                         INCREMENT_PREVIOUS : INCREMENT_NEXT);
568      return true;
569
570    case ui::VKEY_LEFT:
571      if (base::i18n::IsRTL())
572        ExpandOrSelectChild();
573      else
574        CollapseOrSelectParent();
575      return true;
576
577    case ui::VKEY_RIGHT:
578      if (base::i18n::IsRTL())
579        CollapseOrSelectParent();
580      else
581        ExpandOrSelectChild();
582      return true;
583
584    default:
585      break;
586  }
587  return false;
588}
589
590void TreeView::OnPaint(gfx::Canvas* canvas) {
591  // Don't invoke View::OnPaint so that we can render our own focus border.
592  canvas->DrawColor(GetNativeTheme()->GetSystemColor(
593                        ui::NativeTheme::kColorId_TreeBackground));
594
595  int min_y, max_y;
596  {
597    SkRect sk_clip_rect;
598    if (canvas->sk_canvas()->getClipBounds(&sk_clip_rect)) {
599      // Pixels partially inside the clip rect should be included.
600      gfx::Rect clip_rect = gfx::ToEnclosingRect(
601          gfx::SkRectToRectF(sk_clip_rect));
602      min_y = clip_rect.y();
603      max_y = clip_rect.bottom();
604    } else {
605      gfx::Rect vis_bounds = GetVisibleBounds();
606      min_y = vis_bounds.y();
607      max_y = vis_bounds.bottom();
608    }
609  }
610
611  int min_row = std::max(0, (min_y - kVerticalInset) / row_height_);
612  int max_row = (max_y - kVerticalInset) / row_height_;
613  if ((max_y - kVerticalInset) % row_height_ != 0)
614    max_row++;
615  int current_row = root_row();
616  PaintRows(canvas, min_row, max_row, &root_, root_depth(), &current_row);
617}
618
619void TreeView::OnFocus() {
620  GetInputMethod()->OnFocus();
621  View::OnFocus();
622  SchedulePaintForNode(selected_node_);
623
624  // Notify the InputMethod so that it knows to query the TextInputClient.
625  if (GetInputMethod())
626    GetInputMethod()->OnCaretBoundsChanged(this);
627}
628
629void TreeView::OnBlur() {
630  GetInputMethod()->OnBlur();
631  SchedulePaintForNode(selected_node_);
632  if (selector_)
633    selector_->OnViewBlur();
634}
635
636bool TreeView::OnClickOrTap(const ui::LocatedEvent& event) {
637  CommitEdit();
638  RequestFocus();
639
640  int row = (event.y() - kVerticalInset) / row_height_;
641  int depth = 0;
642  InternalNode* node = GetNodeByRow(row, &depth);
643  if (node) {
644    gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth));
645    if (bounds.Contains(event.location())) {
646      int relative_x = event.x() - bounds.x();
647      if (base::i18n::IsRTL())
648        relative_x = bounds.width() - relative_x;
649      if (relative_x < kArrowRegionSize &&
650          model_->GetChildCount(node->model_node())) {
651        if (node->is_expanded())
652          Collapse(node->model_node());
653        else
654          Expand(node->model_node());
655      } else if (relative_x > kArrowRegionSize) {
656        SetSelectedNode(node->model_node());
657        bool should_toggle = false;
658        if (event.type() == ui::ET_GESTURE_TAP) {
659          const ui::GestureEvent& gesture =
660              static_cast<const ui::GestureEvent&>(event);
661          should_toggle = gesture.details().tap_count() == 2;
662        } else {
663          should_toggle = (event.flags() & ui::EF_IS_DOUBLE_CLICK) != 0;
664        }
665        if (should_toggle) {
666          if (node->is_expanded())
667            Collapse(node->model_node());
668          else
669            Expand(node->model_node());
670        }
671      }
672    }
673  }
674  return true;
675}
676
677void TreeView::LoadChildren(InternalNode* node) {
678  DCHECK_EQ(0, node->child_count());
679  DCHECK(!node->loaded_children());
680  node->set_loaded_children(true);
681  for (int i = 0, child_count = model_->GetChildCount(node->model_node());
682       i < child_count; ++i) {
683    InternalNode* child = new InternalNode;
684    ConfigureInternalNode(model_->GetChild(node->model_node(), i), child);
685    node->Add(child, node->child_count());
686  }
687}
688
689void TreeView::ConfigureInternalNode(TreeModelNode* model_node,
690                                     InternalNode* node) {
691  node->Reset(model_node);
692  UpdateNodeTextWidth(node);
693}
694
695void TreeView::UpdateNodeTextWidth(InternalNode* node) {
696  int width = 0, height = 0;
697  gfx::Canvas::SizeStringInt(node->model_node()->GetTitle(), font_list_,
698                             &width, &height, 0, gfx::Canvas::NO_ELLIPSIS);
699  node->set_text_width(width);
700}
701
702void TreeView::DrawnNodesChanged() {
703  UpdatePreferredSize();
704  PreferredSizeChanged();
705  SchedulePaint();
706}
707
708void TreeView::UpdatePreferredSize() {
709  preferred_size_ = gfx::Size();
710  if (!model_)
711    return;
712
713  preferred_size_.SetSize(
714      root_.GetMaxWidth(text_offset_, root_shown_ ? 1 : 0) +
715      kTextHorizontalPadding * 2,
716      row_height_ * GetRowCount() + kVerticalInset * 2);
717}
718
719void TreeView::LayoutEditor() {
720  if (!editing_)
721    return;
722
723  DCHECK(selected_node_);
724  // Position the editor so that its text aligns with the text we drew.
725  gfx::Rect row_bounds = GetBoundsForNode(selected_node_);
726  row_bounds.set_x(
727      GetMirroredXWithWidthInView(row_bounds.x(), row_bounds.width()));
728  row_bounds.set_x(row_bounds.x() + text_offset_);
729  row_bounds.set_width(row_bounds.width() - text_offset_);
730  row_bounds.Inset(kTextHorizontalPadding, kTextVerticalPadding);
731  row_bounds.Inset(-empty_editor_size_.width() / 2,
732                   -(empty_editor_size_.height() - font_list_.GetHeight()) / 2);
733  // Give a little extra space for editing.
734  row_bounds.set_width(row_bounds.width() + 50);
735  editor_->SetBoundsRect(row_bounds);
736  editor_->Layout();
737}
738
739void TreeView::SchedulePaintForNode(InternalNode* node) {
740  if (!node)
741    return;  // Explicitly allow NULL to be passed in.
742  SchedulePaintInRect(GetBoundsForNode(node));
743}
744
745void TreeView::PaintRows(gfx::Canvas* canvas,
746                         int min_row,
747                         int max_row,
748                         InternalNode* node,
749                         int depth,
750                         int* row) {
751  if (*row >= max_row)
752    return;
753
754  if (*row >= min_row && *row < max_row)
755    PaintRow(canvas, node, *row, depth);
756  (*row)++;
757  if (!node->is_expanded())
758    return;
759  depth++;
760  for (int i = 0; i < node->child_count() && *row < max_row; ++i)
761    PaintRows(canvas, min_row, max_row, node->GetChild(i), depth, row);
762}
763
764void TreeView::PaintRow(gfx::Canvas* canvas,
765                        InternalNode* node,
766                        int row,
767                        int depth) {
768  gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth));
769
770  if (model_->GetChildCount(node->model_node()))
771    PaintExpandControl(canvas, bounds, node->is_expanded());
772
773  // Paint the icon.
774  gfx::ImageSkia icon;
775  int icon_index = model_->GetIconIndex(node->model_node());
776  if (icon_index != -1)
777    icon = icons_[icon_index];
778  else if (node == selected_node_)
779    icon = open_icon_;
780  else
781    icon = closed_icon_;
782  int icon_x = kArrowRegionSize + kImagePadding +
783               (open_icon_.width() - icon.width()) / 2;
784  if (base::i18n::IsRTL())
785    icon_x = bounds.right() - icon_x - open_icon_.width();
786  else
787    icon_x += bounds.x();
788  canvas->DrawImageInt(
789      icon, icon_x,
790      bounds.y() + (bounds.height() - icon.height()) / 2);
791
792  if (!editing_ || node != selected_node_) {
793    gfx::Rect text_bounds(bounds.x() + text_offset_, bounds.y(),
794                          bounds.width() - text_offset_, bounds.height());
795    if (base::i18n::IsRTL())
796      text_bounds.set_x(bounds.x());
797    if (node == selected_node_) {
798      const SkColor bg_color = GetNativeTheme()->GetSystemColor(
799          text_background_color_id(HasFocus()));
800      canvas->FillRect(text_bounds, bg_color);
801      if (HasFocus())
802        canvas->DrawFocusRect(text_bounds);
803    }
804    const ui::NativeTheme::ColorId color_id =
805        text_color_id(HasFocus(), node == selected_node_);
806    const gfx::Rect internal_bounds(
807        text_bounds.x() + kTextHorizontalPadding,
808        text_bounds.y() + kTextVerticalPadding,
809        text_bounds.width() - kTextHorizontalPadding * 2,
810        text_bounds.height() - kTextVerticalPadding * 2);
811    canvas->DrawStringRect(node->model_node()->GetTitle(), font_list_,
812                           GetNativeTheme()->GetSystemColor(color_id),
813                           internal_bounds);
814  }
815}
816
817void TreeView::PaintExpandControl(gfx::Canvas* canvas,
818                                  const gfx::Rect& node_bounds,
819                                  bool expanded) {
820  int center_x;
821  if (base::i18n::IsRTL()) {
822    center_x = node_bounds.right() - kArrowRegionSize +
823               (kArrowRegionSize - 4) / 2;
824  } else {
825    center_x = node_bounds.x() + (kArrowRegionSize - 4) / 2;
826  }
827  int center_y = node_bounds.y() + node_bounds.height() / 2;
828  const SkColor arrow_color = GetNativeTheme()->GetSystemColor(
829      ui::NativeTheme::kColorId_TreeArrow);
830  // TODO: this should come from an image.
831  if (!expanded) {
832    int delta = base::i18n::IsRTL() ? 1 : -1;
833    for (int i = 0; i < 4; ++i) {
834      canvas->FillRect(gfx::Rect(center_x + delta * (2 - i),
835                                 center_y - (3 - i), 1, (3 - i) * 2 + 1),
836                       arrow_color);
837    }
838  } else {
839    center_y -= 2;
840    for (int i = 0; i < 4; ++i) {
841      canvas->FillRect(gfx::Rect(center_x - (3 - i), center_y + i,
842                                 (3 - i) * 2 + 1, 1), arrow_color);
843    }
844  }
845}
846
847TreeView::InternalNode* TreeView::GetInternalNodeForModelNode(
848    ui::TreeModelNode* model_node,
849    GetInternalNodeCreateType create_type) {
850  if (model_node == root_.model_node())
851    return &root_;
852  InternalNode* parent_internal_node =
853      GetInternalNodeForModelNode(model_->GetParent(model_node), create_type);
854  if (!parent_internal_node)
855    return NULL;
856  if (!parent_internal_node->loaded_children()) {
857    if (create_type == DONT_CREATE_IF_NOT_LOADED)
858      return NULL;
859    LoadChildren(parent_internal_node);
860  }
861  return parent_internal_node->GetChild(
862      model_->GetIndexOf(parent_internal_node->model_node(), model_node));
863}
864
865gfx::Rect TreeView::GetBoundsForNode(InternalNode* node) {
866  int row, depth;
867  row = GetRowForInternalNode(node, &depth);
868  return GetBoundsForNodeImpl(node, row, depth);
869}
870
871gfx::Rect TreeView::GetBoundsForNodeImpl(InternalNode* node,
872                                         int row,
873                                         int depth) {
874  gfx::Rect rect(depth * kIndent + kHorizontalInset,
875                 row * row_height_ + kVerticalInset,
876                 text_offset_ + node->text_width() +
877                 kTextHorizontalPadding * 2,
878                 row_height_);
879  rect.set_x(GetMirroredXWithWidthInView(rect.x(), rect.width()));
880  return rect;
881}
882
883int TreeView::GetRowForInternalNode(InternalNode* node, int* depth) {
884  DCHECK(!node->parent() || IsExpanded(node->parent()->model_node()));
885  *depth = -1;
886  int row = -1;
887  InternalNode* tmp_node = node;
888  while (tmp_node->parent()) {
889    int index_in_parent = tmp_node->parent()->GetIndexOf(tmp_node);
890    (*depth)++;
891    row++;  // For node.
892    for (int i = 0; i < index_in_parent; ++i)
893      row += tmp_node->parent()->GetChild(i)->NumExpandedNodes();
894    tmp_node = tmp_node->parent();
895  }
896  if (root_shown_) {
897    (*depth)++;
898    row++;
899  }
900  return row;
901}
902
903TreeView::InternalNode* TreeView::GetNodeByRow(int row, int* depth) {
904  int current_row = root_row();
905  *depth = 0;
906  return GetNodeByRowImpl(&root_, row, root_depth(), &current_row, depth);
907}
908
909TreeView::InternalNode* TreeView::GetNodeByRowImpl(InternalNode* node,
910                                                   int target_row,
911                                                   int current_depth,
912                                                   int* current_row,
913                                                   int* node_depth) {
914  if (*current_row == target_row) {
915    *node_depth = current_depth;
916    return node;
917  }
918  (*current_row)++;
919  if (node->is_expanded()) {
920    current_depth++;
921    for (int i = 0; i < node->child_count(); ++i) {
922      InternalNode* result = GetNodeByRowImpl(
923          node->GetChild(i), target_row, current_depth, current_row,
924          node_depth);
925      if (result)
926        return result;
927    }
928  }
929  return NULL;
930}
931
932void TreeView::IncrementSelection(IncrementType type) {
933  if (!model_)
934    return;
935
936  if (!GetSelectedNode()) {
937    // If nothing is selected select the first or last node.
938    if (!root_.child_count())
939      return;
940    if (type == INCREMENT_PREVIOUS) {
941      int row_count = GetRowCount();
942      int depth = 0;
943      DCHECK(row_count);
944      InternalNode* node = GetNodeByRow(row_count - 1, &depth);
945      SetSelectedNode(node->model_node());
946    } else if (root_shown_) {
947      SetSelectedNode(root_.model_node());
948    } else {
949      SetSelectedNode(root_.GetChild(0)->model_node());
950    }
951    return;
952  }
953
954  int depth = 0;
955  int delta = type == INCREMENT_PREVIOUS ? -1 : 1;
956  int row = GetRowForInternalNode(selected_node_, &depth);
957  int new_row = std::min(GetRowCount() - 1, std::max(0, row + delta));
958  if (new_row == row)
959    return;  // At the end/beginning.
960  SetSelectedNode(GetNodeByRow(new_row, &depth)->model_node());
961}
962
963void TreeView::CollapseOrSelectParent() {
964  if (selected_node_) {
965    if (selected_node_->is_expanded())
966      Collapse(selected_node_->model_node());
967    else if (selected_node_->parent())
968      SetSelectedNode(selected_node_->parent()->model_node());
969  }
970}
971
972void TreeView::ExpandOrSelectChild() {
973  if (selected_node_) {
974    if (!selected_node_->is_expanded())
975      Expand(selected_node_->model_node());
976    else if (selected_node_->child_count())
977      SetSelectedNode(selected_node_->GetChild(0)->model_node());
978  }
979}
980
981bool TreeView::ExpandImpl(TreeModelNode* model_node) {
982  TreeModelNode* parent = model_->GetParent(model_node);
983  if (!parent) {
984    // Node should be the root.
985    DCHECK_EQ(root_.model_node(), model_node);
986    bool was_expanded = root_.is_expanded();
987    root_.set_is_expanded(true);
988    return !was_expanded;
989  }
990
991  // Expand all the parents.
992  bool return_value = ExpandImpl(parent);
993  InternalNode* internal_node =
994      GetInternalNodeForModelNode(model_node, CREATE_IF_NOT_LOADED);
995  DCHECK(internal_node);
996  if (!internal_node->is_expanded()) {
997    if (!internal_node->loaded_children())
998      LoadChildren(internal_node);
999    internal_node->set_is_expanded(true);
1000    return_value = true;
1001  }
1002  return return_value;
1003}
1004
1005// InternalNode ----------------------------------------------------------------
1006
1007TreeView::InternalNode::InternalNode()
1008    : model_node_(NULL),
1009      loaded_children_(false),
1010      is_expanded_(false),
1011      text_width_(0) {
1012}
1013
1014TreeView::InternalNode::~InternalNode() {
1015}
1016
1017void TreeView::InternalNode::Reset(ui::TreeModelNode* node) {
1018  model_node_ = node;
1019  loaded_children_ = false;
1020  is_expanded_ = false;
1021  text_width_ = 0;
1022}
1023
1024int TreeView::InternalNode::NumExpandedNodes() const {
1025  int result = 1;  // For this.
1026  if (!is_expanded_)
1027    return result;
1028  for (int i = 0; i < child_count(); ++i)
1029    result += GetChild(i)->NumExpandedNodes();
1030  return result;
1031}
1032
1033int TreeView::InternalNode::GetMaxWidth(int indent, int depth) {
1034  int max_width = text_width_ + indent * depth;
1035  if (!is_expanded_)
1036    return max_width;
1037  for (int i = 0; i < child_count(); ++i) {
1038    max_width = std::max(max_width,
1039                         GetChild(i)->GetMaxWidth(indent, depth + 1));
1040  }
1041  return max_width;
1042}
1043
1044}  // namespace views
1045