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
28 using ui::TreeModel;
29 using ui::TreeModelNode;
30
31 namespace views {
32
33 // Insets around the view.
34 static const int kHorizontalInset = 2;
35 static const int kVerticalInset = 2;
36 // Padding before/after the image.
37 static const int kImagePadding = 4;
38 // Size of the arrow region.
39 static const int kArrowRegionSize = 12;
40 // Padding around the text (on each side).
41 static const int kTextVerticalPadding = 3;
42 static const int kTextHorizontalPadding = 2;
43 // How much children are indented from their parent.
44 static const int kIndent = 20;
45
46 // static
47 const char TreeView::kViewClassName[] = "TreeView";
48
49 namespace {
50
51 // Returns the color id for the background of selected text. |has_focus|
52 // indicates if the tree has focus.
text_background_color_id(bool has_focus)53 ui::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.
text_color_id(bool has_focus,bool is_selected)61 ui::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
TreeView()72 TreeView::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
~TreeView()94 TreeView::~TreeView() {
95 if (model_)
96 model_->RemoveObserver(this);
97 if (focus_manager_) {
98 focus_manager_->RemoveFocusChangeListener(this);
99 focus_manager_ = NULL;
100 }
101 }
102
CreateParentIfNecessary()103 View* TreeView::CreateParentIfNecessary() {
104 ScrollView* scroll_view = ScrollView::CreateScrollViewWithBorder();
105 scroll_view->SetContents(this);
106 return scroll_view;
107 }
108
SetModel(TreeModel * model)109 void 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
SetEditable(bool editable)136 void TreeView::SetEditable(bool editable) {
137 if (editable == editable_)
138 return;
139 editable_ = editable;
140 CancelEdit();
141 }
142
StartEditing(TreeModelNode * node)143 void 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
CancelEdit()182 void 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
CommitEdit()200 void 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
GetEditingNode()212 TreeModelNode* TreeView::GetEditingNode() {
213 return editing_ ? selected_node_->model_node() : NULL;
214 }
215
SetSelectedNode(TreeModelNode * model_node)216 void 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
GetSelectedNode()252 TreeModelNode* TreeView::GetSelectedNode() {
253 return selected_node_ ? selected_node_->model_node() : NULL;
254 }
255
Collapse(ui::TreeModelNode * model_node)256 void 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
Expand(TreeModelNode * node)275 void TreeView::Expand(TreeModelNode* node) {
276 if (ExpandImpl(node))
277 DrawnNodesChanged();
278 // TODO: need to support auto_expand_children_.
279 }
280
ExpandAll(TreeModelNode * node)281 void 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
IsExpanded(TreeModelNode * model_node)295 bool 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
SetRootShown(bool root_shown)314 void 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
GetNodeForRow(int row)327 ui::TreeModelNode* TreeView::GetNodeForRow(int row) {
328 int depth = 0;
329 InternalNode* node = GetNodeByRow(row, &depth);
330 return node ? node->model_node() : NULL;
331 }
332
GetRowForNode(ui::TreeModelNode * node)333 int 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
Layout()342 void 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
GetPreferredSize() const353 gfx::Size TreeView::GetPreferredSize() const {
354 return preferred_size_;
355 }
356
AcceleratorPressed(const ui::Accelerator & accelerator)357 bool 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
OnMousePressed(const ui::MouseEvent & event)368 bool TreeView::OnMousePressed(const ui::MouseEvent& event) {
369 return OnClickOrTap(event);
370 }
371
GetTextInputClient()372 ui::TextInputClient* TreeView::GetTextInputClient() {
373 if (!selector_)
374 selector_.reset(new PrefixSelector(this));
375 return selector_.get();
376 }
377
OnGestureEvent(ui::GestureEvent * event)378 void TreeView::OnGestureEvent(ui::GestureEvent* event) {
379 if (event->type() == ui::ET_GESTURE_TAP) {
380 if (OnClickOrTap(*event))
381 event->SetHandled();
382 }
383 }
384
ShowContextMenu(const gfx::Point & p,ui::MenuSourceType source_type)385 void 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
GetAccessibleState(ui::AXViewState * state)406 void 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
GetClassName() const417 const char* TreeView::GetClassName() const {
418 return kViewClassName;
419 }
420
TreeNodesAdded(TreeModel * model,TreeModelNode * parent,int start,int count)421 void 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
TreeNodesRemoved(TreeModel * model,TreeModelNode * parent,int start,int count)438 void 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
TreeNodeChanged(TreeModel * model,TreeModelNode * model_node)470 void 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
ContentsChanged(Textfield * sender,const base::string16 & new_contents)484 void TreeView::ContentsChanged(Textfield* sender,
485 const base::string16& new_contents) {
486 }
487
HandleKeyEvent(Textfield * sender,const ui::KeyEvent & key_event)488 bool 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
OnWillChangeFocus(View * focused_before,View * focused_now)505 void TreeView::OnWillChangeFocus(View* focused_before, View* focused_now) {
506 }
507
OnDidChangeFocus(View * focused_before,View * focused_now)508 void TreeView::OnDidChangeFocus(View* focused_before, View* focused_now) {
509 CommitEdit();
510 }
511
GetRowCount()512 int TreeView::GetRowCount() {
513 int row_count = root_.NumExpandedNodes();
514 if (!root_shown_)
515 row_count--;
516 return row_count;
517 }
518
GetSelectedRow()519 int TreeView::GetSelectedRow() {
520 ui::TreeModelNode* model_node = GetSelectedNode();
521 return model_node ? GetRowForNode(model_node) : -1;
522 }
523
SetSelectedRow(int row)524 void TreeView::SetSelectedRow(int row) {
525 SetSelectedNode(GetNodeForRow(row));
526 }
527
GetTextForRow(int row)528 base::string16 TreeView::GetTextForRow(int row) {
529 return GetNodeForRow(row)->GetTitle();
530 }
531
GetKeyboardContextMenuLocation()532 gfx::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
OnKeyPressed(const ui::KeyEvent & event)549 bool 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
OnPaint(gfx::Canvas * canvas)590 void 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(), ¤t_row);
617 }
618
OnFocus()619 void 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
OnBlur()629 void TreeView::OnBlur() {
630 GetInputMethod()->OnBlur();
631 SchedulePaintForNode(selected_node_);
632 if (selector_)
633 selector_->OnViewBlur();
634 }
635
OnClickOrTap(const ui::LocatedEvent & event)636 bool 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
LoadChildren(InternalNode * node)677 void 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
ConfigureInternalNode(TreeModelNode * model_node,InternalNode * node)689 void TreeView::ConfigureInternalNode(TreeModelNode* model_node,
690 InternalNode* node) {
691 node->Reset(model_node);
692 UpdateNodeTextWidth(node);
693 }
694
UpdateNodeTextWidth(InternalNode * node)695 void 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
DrawnNodesChanged()702 void TreeView::DrawnNodesChanged() {
703 UpdatePreferredSize();
704 PreferredSizeChanged();
705 SchedulePaint();
706 }
707
UpdatePreferredSize()708 void 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
LayoutEditor()719 void 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
SchedulePaintForNode(InternalNode * node)739 void TreeView::SchedulePaintForNode(InternalNode* node) {
740 if (!node)
741 return; // Explicitly allow NULL to be passed in.
742 SchedulePaintInRect(GetBoundsForNode(node));
743 }
744
PaintRows(gfx::Canvas * canvas,int min_row,int max_row,InternalNode * node,int depth,int * row)745 void 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
PaintRow(gfx::Canvas * canvas,InternalNode * node,int row,int depth)764 void 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
PaintExpandControl(gfx::Canvas * canvas,const gfx::Rect & node_bounds,bool expanded)817 void 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
GetInternalNodeForModelNode(ui::TreeModelNode * model_node,GetInternalNodeCreateType create_type)847 TreeView::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
GetBoundsForNode(InternalNode * node)865 gfx::Rect TreeView::GetBoundsForNode(InternalNode* node) {
866 int row, depth;
867 row = GetRowForInternalNode(node, &depth);
868 return GetBoundsForNodeImpl(node, row, depth);
869 }
870
GetBoundsForNodeImpl(InternalNode * node,int row,int depth)871 gfx::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
GetRowForInternalNode(InternalNode * node,int * depth)883 int 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
GetNodeByRow(int row,int * depth)903 TreeView::InternalNode* TreeView::GetNodeByRow(int row, int* depth) {
904 int current_row = root_row();
905 *depth = 0;
906 return GetNodeByRowImpl(&root_, row, root_depth(), ¤t_row, depth);
907 }
908
GetNodeByRowImpl(InternalNode * node,int target_row,int current_depth,int * current_row,int * node_depth)909 TreeView::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
IncrementSelection(IncrementType type)932 void 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
CollapseOrSelectParent()963 void 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
ExpandOrSelectChild()972 void 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
ExpandImpl(TreeModelNode * model_node)981 bool 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
InternalNode()1007 TreeView::InternalNode::InternalNode()
1008 : model_node_(NULL),
1009 loaded_children_(false),
1010 is_expanded_(false),
1011 text_width_(0) {
1012 }
1013
~InternalNode()1014 TreeView::InternalNode::~InternalNode() {
1015 }
1016
Reset(ui::TreeModelNode * node)1017 void TreeView::InternalNode::Reset(ui::TreeModelNode* node) {
1018 model_node_ = node;
1019 loaded_children_ = false;
1020 is_expanded_ = false;
1021 text_width_ = 0;
1022 }
1023
NumExpandedNodes() const1024 int 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
GetMaxWidth(int indent,int depth)1033 int 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