• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(), &current_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(), &current_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