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.
5 #include "ui/views/controls/tree/tree_view.h"
9 #include "base/i18n/rtl.h"
10 #include "base/message_loop/message_loop.h"
11 #include "grit/ui_resources.h"
12 #include "ui/base/accessibility/accessible_view_state.h"
13 #include "ui/base/resource/resource_bundle.h"
14 #include "ui/events/event.h"
15 #include "ui/events/keycodes/keyboard_codes.h"
16 #include "ui/gfx/canvas.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/views/controls/prefix_selector.h"
22 #include "ui/views/controls/scroll_view.h"
23 #include "ui/views/controls/textfield/textfield.h"
24 #include "ui/views/controls/tree/tree_view_controller.h"
25 #include "ui/views/ime/input_method.h"
28 using ui::TreeModelNode;
32 // Insets around the view.
33 static const int kHorizontalInset = 2;
34 static const int kVerticalInset = 2;
35 // Padding before/after the image.
36 static const int kImagePadding = 4;
37 // Size of the arrow region.
38 static const int kArrowRegionSize = 12;
39 // Padding around the text (on each side).
40 static const int kTextVerticalPadding = 3;
41 static const int kTextHorizontalPadding = 2;
42 // How much children are indented from their parent.
43 static const int kIndent = 20;
47 // Returns the color id for the background of selected text. |has_focus|
48 // indicates if the tree has focus.
49 ui::NativeTheme::ColorId text_background_color_id(bool has_focus) {
51 ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused :
52 ui::NativeTheme::kColorId_TreeSelectionBackgroundUnfocused;
55 // Returns the color id for text. |has_focus| indicates if the tree has focus
56 // and |is_selected| is true if the item is selected.
57 ui::NativeTheme::ColorId text_color_id(bool has_focus, bool is_selected) {
60 return ui::NativeTheme::kColorId_TreeSelectedText;
61 return ui::NativeTheme::kColorId_TreeSelectedTextUnfocused;
63 return ui::NativeTheme::kColorId_TreeText;
74 auto_expand_children_(false),
78 has_custom_icons_(false),
79 row_height_(font_.GetHeight() + kTextVerticalPadding * 2) {
81 closed_icon_ = *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
82 (base::i18n::IsRTL() ? IDR_FOLDER_CLOSED_RTL
83 : IDR_FOLDER_CLOSED)).ToImageSkia();
84 open_icon_ = *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
85 (base::i18n::IsRTL() ? IDR_FOLDER_OPEN_RTL
86 : IDR_FOLDER_OPEN)).ToImageSkia();
87 text_offset_ = closed_icon_.width() + kImagePadding + kImagePadding +
91 TreeView::~TreeView() {
93 model_->RemoveObserver(this);
95 focus_manager_->RemoveFocusChangeListener(this);
96 focus_manager_ = NULL;
100 View* TreeView::CreateParentIfNecessary() {
101 ScrollView* scroll_view = ScrollView::CreateScrollViewWithBorder();
102 scroll_view->SetContents(this);
106 void TreeView::SetModel(TreeModel* model) {
110 model_->RemoveObserver(this);
115 selected_node_ = NULL;
118 model_->AddObserver(this);
119 model_->GetIcons(&icons_);
122 ConfigureInternalNode(model_->GetRoot(), &root_);
123 LoadChildren(&root_);
124 root_.set_is_expanded(true);
126 selected_node_ = &root_;
127 else if (root_.child_count())
128 selected_node_ = root_.GetChild(0);
133 void TreeView::SetEditable(bool editable) {
134 if (editable == editable_)
136 editable_ = editable;
140 void TreeView::StartEditing(TreeModelNode* node) {
142 // Cancel the current edit.
144 // Make sure all ancestors are expanded.
145 if (model_->GetParent(node))
146 Expand(model_->GetParent(node));
147 // Select the node, else if the user commits the edit the selection reverts.
148 SetSelectedNode(node);
149 if (GetSelectedNode() != node)
150 return; // Selection failed for some reason, don't start editing.
154 editor_ = new Textfield;
155 // Add the editor immediately as GetPreferredSize returns the wrong thing if
157 AddChildView(editor_);
158 editor_->SetFont(font_);
159 empty_editor_size_ = editor_->GetPreferredSize();
160 editor_->SetController(this);
162 editor_->SetText(selected_node_->model_node()->GetTitle());
164 editor_->SetVisible(true);
165 SchedulePaintForNode(selected_node_);
166 editor_->RequestFocus();
167 editor_->SelectAll(false);
169 // Listen for focus changes so that we can cancel editing.
170 focus_manager_ = GetFocusManager();
172 focus_manager_->AddFocusChangeListener(this);
174 // Accelerators to commit/cancel edit.
175 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
176 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
179 void TreeView::CancelEdit() {
183 // WARNING: don't touch |selected_node_|, it may be bogus.
186 if (focus_manager_) {
187 focus_manager_->RemoveFocusChangeListener(this);
188 focus_manager_ = NULL;
190 editor_->SetVisible(false);
193 RemoveAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
194 RemoveAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
197 void TreeView::CommitEdit() {
201 DCHECK(selected_node_);
202 const bool editor_has_focus = editor_->HasFocus();
203 model_->SetTitle(GetSelectedNode(), editor_->text());
205 if (editor_has_focus)
209 TreeModelNode* TreeView::GetEditingNode() {
210 return editing_ ? selected_node_->model_node() : NULL;
213 void TreeView::SetSelectedNode(TreeModelNode* model_node) {
214 if (editing_ || model_node != selected_node_)
216 if (model_node && model_->GetParent(model_node))
217 Expand(model_->GetParent(model_node));
218 if (model_node && model_node == root_.model_node() && !root_shown_)
219 return; // Ignore requests to select the root when not shown.
220 InternalNode* node = model_node ? GetInternalNodeForModelNode(
221 model_node, CREATE_IF_NOT_LOADED) : NULL;
222 bool was_empty_selection = (selected_node_ == NULL);
223 bool changed = (selected_node_ != node);
225 SchedulePaintForNode(selected_node_);
226 selected_node_ = node;
227 if (selected_node_ == &root_ && !root_shown_)
228 selected_node_ = NULL;
229 if (selected_node_ && selected_node_ != &root_)
230 Expand(model_->GetParent(selected_node_->model_node()));
231 SchedulePaintForNode(selected_node_);
235 ScrollRectToVisible(GetBoundsForNode(selected_node_));
237 // Notify controller if the old selection was empty to handle the case of
238 // remove explicitly resetting selected_node_ before invoking this.
239 if (controller_ && (changed || was_empty_selection))
240 controller_->OnTreeViewSelectionChanged(this);
243 TreeModelNode* TreeView::GetSelectedNode() {
244 return selected_node_ ? selected_node_->model_node() : NULL;
247 void TreeView::Collapse(ui::TreeModelNode* model_node) {
248 // Don't collapse the root if the root isn't shown, otherwise nothing is
250 if (model_node == root_.model_node() && !root_shown_)
253 GetInternalNodeForModelNode(model_node, DONT_CREATE_IF_NOT_LOADED);
256 bool was_expanded = IsExpanded(model_node);
257 if (node->is_expanded()) {
258 if (selected_node_ && selected_node_->HasAncestor(node))
259 SetSelectedNode(model_node);
260 node->set_is_expanded(false);
266 void TreeView::Expand(TreeModelNode* node) {
267 if (ExpandImpl(node))
269 // TODO: need to support auto_expand_children_.
272 void TreeView::ExpandAll(TreeModelNode* node) {
275 bool expanded_at_least_one = ExpandImpl(node);
276 // And recursively expand all the children.
277 for (int i = model_->GetChildCount(node) - 1; i >= 0; --i) {
278 TreeModelNode* child = model_->GetChild(node, i);
279 if (ExpandImpl(child))
280 expanded_at_least_one = true;
282 if (expanded_at_least_one)
286 bool TreeView::IsExpanded(TreeModelNode* model_node) {
288 // NULL check primarily for convenience for uses in this class so don't have
289 // to add NULL checks every where we look up the parent.
292 InternalNode* node = GetInternalNodeForModelNode(
293 model_node, DONT_CREATE_IF_NOT_LOADED);
298 if (!node->is_expanded())
300 node = node->parent();
305 void TreeView::SetRootShown(bool root_shown) {
306 if (root_shown_ == root_shown)
308 root_shown_ = root_shown;
309 if (!root_shown_ && selected_node_ == &root_) {
310 if (model_->GetChildCount(root_.model_node()))
311 SetSelectedNode(model_->GetChild(root_.model_node(), 0));
313 SetSelectedNode(NULL);
318 ui::TreeModelNode* TreeView::GetNodeForRow(int row) {
320 InternalNode* node = GetNodeByRow(row, &depth);
321 return node ? node->model_node() : NULL;
324 int TreeView::GetRowForNode(ui::TreeModelNode* node) {
325 InternalNode* internal_node =
326 GetInternalNodeForModelNode(node, DONT_CREATE_IF_NOT_LOADED);
330 return GetRowForInternalNode(internal_node, &depth);
333 void TreeView::Layout() {
334 int width = preferred_size_.width();
335 int height = preferred_size_.height();
337 width = std::max(parent()->width(), width);
338 height = std::max(parent()->height(), height);
340 SetBounds(x(), y(), width, height);
344 gfx::Size TreeView::GetPreferredSize() {
345 return preferred_size_;
348 bool TreeView::AcceleratorPressed(const ui::Accelerator& accelerator) {
349 if (accelerator.key_code() == ui::VKEY_RETURN) {
352 DCHECK_EQ(ui::VKEY_ESCAPE, accelerator.key_code());
359 bool TreeView::OnMousePressed(const ui::MouseEvent& event) {
360 return OnClickOrTap(event);
363 ui::TextInputClient* TreeView::GetTextInputClient() {
365 selector_.reset(new PrefixSelector(this));
366 return selector_.get();
369 void TreeView::OnGestureEvent(ui::GestureEvent* event) {
370 if (event->type() == ui::ET_GESTURE_TAP) {
371 if (OnClickOrTap(*event))
376 void TreeView::ShowContextMenu(const gfx::Point& p,
377 ui::MenuSourceType source_type) {
380 if (source_type == ui::MENU_SOURCE_MOUSE) {
381 // Only invoke View's implementation (which notifies the
382 // ContextMenuController) if over a node.
383 gfx::Point local_point(p);
384 ConvertPointToTarget(NULL, this, &local_point);
385 int row = (local_point.y() - kVerticalInset) / row_height_;
387 InternalNode* node = GetNodeByRow(row, &depth);
390 gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth));
391 if (!bounds.Contains(local_point))
394 View::ShowContextMenu(p, source_type);
397 void TreeView::GetAccessibleState(ui::AccessibleViewState* state) {
398 state->role = ui::AccessibilityTypes::ROLE_OUTLINE;
399 state->state = ui::AccessibilityTypes::STATE_READONLY;
402 void TreeView::TreeNodesAdded(TreeModel* model,
403 TreeModelNode* parent,
406 InternalNode* parent_node =
407 GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED);
408 if (!parent_node || !parent_node->loaded_children())
410 for (int i = 0; i < count; ++i) {
411 InternalNode* child = new InternalNode;
412 ConfigureInternalNode(model_->GetChild(parent, start + i), child);
413 parent_node->Add(child, start + i);
415 if (IsExpanded(parent))
419 void TreeView::TreeNodesRemoved(TreeModel* model,
420 TreeModelNode* parent,
423 InternalNode* parent_node =
424 GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED);
425 if (!parent_node || !parent_node->loaded_children())
427 bool reset_selection = false;
428 for (int i = 0; i < count; ++i) {
429 InternalNode* child_removing = parent_node->GetChild(start);
430 if (selected_node_ && selected_node_->HasAncestor(child_removing))
431 reset_selection = true;
432 delete parent_node->Remove(child_removing);
434 if (reset_selection) {
435 // selected_node_ is no longer valid (at the time we enter this function
436 // its model_node() is likely deleted). Explicitly NULL out the field
437 // rather than invoking SetSelectedNode() otherwise, we'll try and use a
439 selected_node_ = NULL;
440 TreeModelNode* to_select = parent;
441 if (parent == root_.model_node() && !root_shown_) {
442 to_select = model_->GetChildCount(parent) > 0 ?
443 model_->GetChild(parent, 0) : NULL;
445 SetSelectedNode(to_select);
447 if (IsExpanded(parent))
451 void TreeView::TreeNodeChanged(TreeModel* model, TreeModelNode* model_node) {
453 GetInternalNodeForModelNode(model_node, DONT_CREATE_IF_NOT_LOADED);
456 int old_width = node->text_width();
457 UpdateNodeTextWidth(node);
458 if (old_width != node->text_width() &&
459 ((node == &root_ && root_shown_) ||
460 (node != &root_ && IsExpanded(node->parent()->model_node())))) {
465 void TreeView::ContentsChanged(Textfield* sender,
466 const string16& new_contents) {
469 bool TreeView::HandleKeyEvent(Textfield* sender,
470 const ui::KeyEvent& key_event) {
471 switch (key_event.key_code()) {
472 case ui::VKEY_RETURN:
476 case ui::VKEY_ESCAPE:
486 void TreeView::OnWillChangeFocus(View* focused_before, View* focused_now) {
489 void TreeView::OnDidChangeFocus(View* focused_before, View* focused_now) {
493 int TreeView::GetRowCount() {
494 int row_count = root_.NumExpandedNodes();
500 int TreeView::GetSelectedRow() {
501 ui::TreeModelNode* model_node = GetSelectedNode();
502 return model_node ? GetRowForNode(model_node) : -1;
505 void TreeView::SetSelectedRow(int row) {
506 SetSelectedNode(GetNodeForRow(row));
509 string16 TreeView::GetTextForRow(int row) {
510 return GetNodeForRow(row)->GetTitle();
513 gfx::Point TreeView::GetKeyboardContextMenuLocation() {
514 int y = height() / 2;
515 if (selected_node_) {
516 gfx::Rect node_bounds(GetBoundsForNode(selected_node_));
517 gfx::Rect vis_bounds(GetVisibleBounds());
518 if (node_bounds.y() >= vis_bounds.y() &&
519 node_bounds.y() < vis_bounds.bottom()) {
523 gfx::Point screen_loc(0, y);
524 if (base::i18n::IsRTL())
525 screen_loc.set_x(width());
526 ConvertPointToScreen(this, &screen_loc);
530 bool TreeView::OnKeyPressed(const ui::KeyEvent& event) {
534 switch (event.key_code()) {
537 TreeModelNode* selected_node = GetSelectedNode();
538 if (selected_node && (!controller_ ||
539 controller_->CanEdit(this, selected_node))) {
540 StartEditing(selected_node);
547 IncrementSelection(event.key_code() == ui::VKEY_UP ?
548 INCREMENT_PREVIOUS : INCREMENT_NEXT);
552 if (base::i18n::IsRTL())
553 ExpandOrSelectChild();
555 CollapseOrSelectParent();
559 if (base::i18n::IsRTL())
560 CollapseOrSelectParent();
562 ExpandOrSelectChild();
571 void TreeView::OnPaint(gfx::Canvas* canvas) {
572 // Don't invoke View::OnPaint so that we can render our own focus border.
573 canvas->DrawColor(GetNativeTheme()->GetSystemColor(
574 ui::NativeTheme::kColorId_TreeBackground));
579 if (canvas->sk_canvas()->getClipBounds(&sk_clip_rect)) {
580 // Pixels partially inside the clip rect should be included.
581 gfx::Rect clip_rect = gfx::ToEnclosingRect(
582 gfx::SkRectToRectF(sk_clip_rect));
583 min_y = clip_rect.y();
584 max_y = clip_rect.bottom();
586 gfx::Rect vis_bounds = GetVisibleBounds();
587 min_y = vis_bounds.y();
588 max_y = vis_bounds.bottom();
592 int min_row = std::max(0, (min_y - kVerticalInset) / row_height_);
593 int max_row = (max_y - kVerticalInset) / row_height_;
594 if ((max_y - kVerticalInset) % row_height_ != 0)
596 int current_row = root_row();
597 PaintRows(canvas, min_row, max_row, &root_, root_depth(), ¤t_row);
600 void TreeView::OnFocus() {
601 GetInputMethod()->OnFocus();
603 SchedulePaintForNode(selected_node_);
605 // Notify the InputMethod so that it knows to query the TextInputClient.
606 if (GetInputMethod())
607 GetInputMethod()->OnCaretBoundsChanged(this);
610 void TreeView::OnBlur() {
611 GetInputMethod()->OnBlur();
612 SchedulePaintForNode(selected_node_);
614 selector_->OnViewBlur();
617 bool TreeView::OnClickOrTap(const ui::LocatedEvent& event) {
621 int row = (event.y() - kVerticalInset) / row_height_;
623 InternalNode* node = GetNodeByRow(row, &depth);
625 gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth));
626 if (bounds.Contains(event.location())) {
627 int relative_x = event.x() - bounds.x();
628 if (base::i18n::IsRTL())
629 relative_x = bounds.width() - relative_x;
630 if (relative_x < kArrowRegionSize &&
631 model_->GetChildCount(node->model_node())) {
632 if (node->is_expanded())
633 Collapse(node->model_node());
635 Expand(node->model_node());
636 } else if (relative_x > kArrowRegionSize) {
637 SetSelectedNode(node->model_node());
638 bool should_toggle = false;
639 if (event.type() == ui::ET_GESTURE_TAP) {
640 const ui::GestureEvent& gesture =
641 static_cast<const ui::GestureEvent&>(event);
642 should_toggle = gesture.details().tap_count() == 2;
644 should_toggle = (event.flags() & ui::EF_IS_DOUBLE_CLICK) != 0;
647 if (node->is_expanded())
648 Collapse(node->model_node());
650 Expand(node->model_node());
658 void TreeView::LoadChildren(InternalNode* node) {
659 DCHECK_EQ(0, node->child_count());
660 DCHECK(!node->loaded_children());
661 node->set_loaded_children(true);
662 for (int i = 0, child_count = model_->GetChildCount(node->model_node());
663 i < child_count; ++i) {
664 InternalNode* child = new InternalNode;
665 ConfigureInternalNode(model_->GetChild(node->model_node(), i), child);
666 node->Add(child, node->child_count());
670 void TreeView::ConfigureInternalNode(TreeModelNode* model_node,
671 InternalNode* node) {
672 node->Reset(model_node);
673 UpdateNodeTextWidth(node);
676 void TreeView::UpdateNodeTextWidth(InternalNode* node) {
677 int width = 0, height = 0;
678 gfx::Canvas::SizeStringInt(node->model_node()->GetTitle(), font_,
679 &width, &height, 0, gfx::Canvas::NO_ELLIPSIS);
680 node->set_text_width(width);
683 void TreeView::DrawnNodesChanged() {
684 UpdatePreferredSize();
685 PreferredSizeChanged();
689 void TreeView::UpdatePreferredSize() {
690 preferred_size_ = gfx::Size();
694 preferred_size_.SetSize(
695 root_.GetMaxWidth(text_offset_, root_shown_ ? 1 : 0) +
696 kTextHorizontalPadding * 2,
697 row_height_ * GetRowCount() + kVerticalInset * 2);
700 void TreeView::LayoutEditor() {
704 DCHECK(selected_node_);
705 // Position the editor so that its text aligns with the text we drew.
706 gfx::Rect row_bounds = GetBoundsForNode(selected_node_);
708 GetMirroredXWithWidthInView(row_bounds.x(), row_bounds.width()));
709 row_bounds.set_x(row_bounds.x() + text_offset_);
710 row_bounds.set_width(row_bounds.width() - text_offset_);
711 row_bounds.Inset(kTextHorizontalPadding, kTextVerticalPadding);
712 row_bounds.Inset(-empty_editor_size_.width() / 2,
713 -(empty_editor_size_.height() - font_.GetHeight()) / 2);
714 // Give a little extra space for editing.
715 row_bounds.set_width(row_bounds.width() + 50);
716 editor_->SetBoundsRect(row_bounds);
720 void TreeView::SchedulePaintForNode(InternalNode* node) {
722 return; // Explicitly allow NULL to be passed in.
723 SchedulePaintInRect(GetBoundsForNode(node));
726 void TreeView::PaintRows(gfx::Canvas* canvas,
735 if (*row >= min_row && *row < max_row)
736 PaintRow(canvas, node, *row, depth);
738 if (!node->is_expanded())
741 for (int i = 0; i < node->child_count() && *row < max_row; ++i)
742 PaintRows(canvas, min_row, max_row, node->GetChild(i), depth, row);
745 void TreeView::PaintRow(gfx::Canvas* canvas,
749 gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth));
751 if (model_->GetChildCount(node->model_node()))
752 PaintExpandControl(canvas, bounds, node->is_expanded());
756 int icon_index = model_->GetIconIndex(node->model_node());
757 if (icon_index != -1)
758 icon = icons_[icon_index];
759 else if (node == selected_node_)
763 int icon_x = kArrowRegionSize + kImagePadding +
764 (open_icon_.width() - icon.width()) / 2;
765 if (base::i18n::IsRTL())
766 icon_x = bounds.right() - icon_x - open_icon_.width();
768 icon_x += bounds.x();
769 canvas->DrawImageInt(
771 bounds.y() + (bounds.height() - icon.height()) / 2);
773 if (!editing_ || node != selected_node_) {
774 gfx::Rect text_bounds(bounds.x() + text_offset_, bounds.y(),
775 bounds.width() - text_offset_, bounds.height());
776 if (base::i18n::IsRTL())
777 text_bounds.set_x(bounds.x());
778 if (node == selected_node_) {
779 const SkColor bg_color = GetNativeTheme()->GetSystemColor(
780 text_background_color_id(HasFocus()));
781 canvas->FillRect(text_bounds, bg_color);
783 canvas->DrawFocusRect(text_bounds);
785 const ui::NativeTheme::ColorId color_id =
786 text_color_id(HasFocus(), node == selected_node_);
787 canvas->DrawStringInt(node->model_node()->GetTitle(), font_,
788 GetNativeTheme()->GetSystemColor(color_id),
789 text_bounds.x() + kTextHorizontalPadding,
790 text_bounds.y() + kTextVerticalPadding,
791 text_bounds.width() - kTextHorizontalPadding * 2,
792 text_bounds.height() - kTextVerticalPadding * 2);
796 void TreeView::PaintExpandControl(gfx::Canvas* canvas,
797 const gfx::Rect& node_bounds,
800 if (base::i18n::IsRTL()) {
801 center_x = node_bounds.right() - kArrowRegionSize +
802 (kArrowRegionSize - 4) / 2;
804 center_x = node_bounds.x() + (kArrowRegionSize - 4) / 2;
806 int center_y = node_bounds.y() + node_bounds.height() / 2;
807 const SkColor arrow_color = GetNativeTheme()->GetSystemColor(
808 ui::NativeTheme::kColorId_TreeArrow);
809 // TODO: this should come from an image.
811 int delta = base::i18n::IsRTL() ? 1 : -1;
812 for (int i = 0; i < 4; ++i) {
813 canvas->FillRect(gfx::Rect(center_x + delta * (2 - i),
814 center_y - (3 - i), 1, (3 - i) * 2 + 1),
819 for (int i = 0; i < 4; ++i) {
820 canvas->FillRect(gfx::Rect(center_x - (3 - i), center_y + i,
821 (3 - i) * 2 + 1, 1), arrow_color);
826 TreeView::InternalNode* TreeView::GetInternalNodeForModelNode(
827 ui::TreeModelNode* model_node,
828 GetInternalNodeCreateType create_type) {
829 if (model_node == root_.model_node())
831 InternalNode* parent_internal_node =
832 GetInternalNodeForModelNode(model_->GetParent(model_node), create_type);
833 if (!parent_internal_node)
835 if (!parent_internal_node->loaded_children()) {
836 if (create_type == DONT_CREATE_IF_NOT_LOADED)
838 LoadChildren(parent_internal_node);
840 return parent_internal_node->GetChild(
841 model_->GetIndexOf(parent_internal_node->model_node(), model_node));
844 gfx::Rect TreeView::GetBoundsForNode(InternalNode* node) {
846 row = GetRowForInternalNode(node, &depth);
847 return GetBoundsForNodeImpl(node, row, depth);
850 gfx::Rect TreeView::GetBoundsForNodeImpl(InternalNode* node,
853 gfx::Rect rect(depth * kIndent + kHorizontalInset,
854 row * row_height_ + kVerticalInset,
855 text_offset_ + node->text_width() +
856 kTextHorizontalPadding * 2,
858 rect.set_x(GetMirroredXWithWidthInView(rect.x(), rect.width()));
862 int TreeView::GetRowForInternalNode(InternalNode* node, int* depth) {
863 DCHECK(!node->parent() || IsExpanded(node->parent()->model_node()));
866 InternalNode* tmp_node = node;
867 while (tmp_node->parent()) {
868 int index_in_parent = tmp_node->parent()->GetIndexOf(tmp_node);
871 for (int i = 0; i < index_in_parent; ++i)
872 row += tmp_node->parent()->GetChild(i)->NumExpandedNodes();
873 tmp_node = tmp_node->parent();
882 TreeView::InternalNode* TreeView::GetNodeByRow(int row, int* depth) {
883 int current_row = root_row();
885 return GetNodeByRowImpl(&root_, row, root_depth(), ¤t_row, depth);
888 TreeView::InternalNode* TreeView::GetNodeByRowImpl(InternalNode* node,
893 if (*current_row == target_row) {
894 *node_depth = current_depth;
898 if (node->is_expanded()) {
900 for (int i = 0; i < node->child_count(); ++i) {
901 InternalNode* result = GetNodeByRowImpl(
902 node->GetChild(i), target_row, current_depth, current_row,
911 void TreeView::IncrementSelection(IncrementType type) {
915 if (!GetSelectedNode()) {
916 // If nothing is selected select the first or last node.
917 if (!root_.child_count())
919 if (type == INCREMENT_PREVIOUS) {
920 int row_count = GetRowCount();
923 InternalNode* node = GetNodeByRow(row_count - 1, &depth);
924 SetSelectedNode(node->model_node());
925 } else if (root_shown_) {
926 SetSelectedNode(root_.model_node());
928 SetSelectedNode(root_.GetChild(0)->model_node());
934 int delta = type == INCREMENT_PREVIOUS ? -1 : 1;
935 int row = GetRowForInternalNode(selected_node_, &depth);
936 int new_row = std::min(GetRowCount() - 1, std::max(0, row + delta));
938 return; // At the end/beginning.
939 SetSelectedNode(GetNodeByRow(new_row, &depth)->model_node());
942 void TreeView::CollapseOrSelectParent() {
943 if (selected_node_) {
944 if (selected_node_->is_expanded())
945 Collapse(selected_node_->model_node());
946 else if (selected_node_->parent())
947 SetSelectedNode(selected_node_->parent()->model_node());
951 void TreeView::ExpandOrSelectChild() {
952 if (selected_node_) {
953 if (!selected_node_->is_expanded())
954 Expand(selected_node_->model_node());
955 else if (selected_node_->child_count())
956 SetSelectedNode(selected_node_->GetChild(0)->model_node());
960 bool TreeView::ExpandImpl(TreeModelNode* model_node) {
961 TreeModelNode* parent = model_->GetParent(model_node);
963 // Node should be the root.
964 DCHECK_EQ(root_.model_node(), model_node);
965 bool was_expanded = root_.is_expanded();
966 root_.set_is_expanded(true);
967 return !was_expanded;
970 // Expand all the parents.
971 bool return_value = ExpandImpl(parent);
972 InternalNode* internal_node =
973 GetInternalNodeForModelNode(model_node, CREATE_IF_NOT_LOADED);
974 DCHECK(internal_node);
975 if (!internal_node->is_expanded()) {
976 if (!internal_node->loaded_children())
977 LoadChildren(internal_node);
978 internal_node->set_is_expanded(true);
984 // InternalNode ----------------------------------------------------------------
986 TreeView::InternalNode::InternalNode()
988 loaded_children_(false),
993 TreeView::InternalNode::~InternalNode() {
996 void TreeView::InternalNode::Reset(ui::TreeModelNode* node) {
998 loaded_children_ = false;
999 is_expanded_ = false;
1003 int TreeView::InternalNode::NumExpandedNodes() const {
1004 int result = 1; // For this.
1007 for (int i = 0; i < child_count(); ++i)
1008 result += GetChild(i)->NumExpandedNodes();
1012 int TreeView::InternalNode::GetMaxWidth(int indent, int depth) {
1013 int max_width = text_width_ + indent * depth;
1016 for (int i = 0; i < child_count(); ++i) {
1017 max_width = std::max(max_width,
1018 GetChild(i)->GetMaxWidth(indent, depth + 1));
1023 } // namespace views