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