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 "grit/ui_resources.h" 12 #include "ui/base/accessibility/accessible_view_state.h" 13 #include "ui/base/resource/resource_bundle.h" 14 #include "ui/events/event.h" 15 #include "ui/events/keycodes/keyboard_codes.h" 16 #include "ui/gfx/canvas.h" 17 #include "ui/gfx/image/image.h" 18 #include "ui/gfx/rect_conversions.h" 19 #include "ui/gfx/skia_util.h" 20 #include "ui/native_theme/native_theme.h" 21 #include "ui/views/controls/prefix_selector.h" 22 #include "ui/views/controls/scroll_view.h" 23 #include "ui/views/controls/textfield/textfield.h" 24 #include "ui/views/controls/tree/tree_view_controller.h" 25 #include "ui/views/ime/input_method.h" 26 27 using ui::TreeModel; 28 using ui::TreeModelNode; 29 30 namespace views { 31 32 // Insets around the view. 33 static const int kHorizontalInset = 2; 34 static const int kVerticalInset = 2; 35 // Padding before/after the image. 36 static const int kImagePadding = 4; 37 // Size of the arrow region. 38 static const int kArrowRegionSize = 12; 39 // Padding around the text (on each side). 40 static const int kTextVerticalPadding = 3; 41 static const int kTextHorizontalPadding = 2; 42 // How much children are indented from their parent. 43 static const int kIndent = 20; 44 45 // static 46 const char TreeView::kViewClassName[] = "TreeView"; 47 48 namespace { 49 50 // Returns the color id for the background of selected text. |has_focus| 51 // indicates if the tree has focus. 52 ui::NativeTheme::ColorId text_background_color_id(bool has_focus) { 53 return has_focus ? 54 ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused : 55 ui::NativeTheme::kColorId_TreeSelectionBackgroundUnfocused; 56 } 57 58 // Returns the color id for text. |has_focus| indicates if the tree has focus 59 // and |is_selected| is true if the item is selected. 60 ui::NativeTheme::ColorId text_color_id(bool has_focus, bool is_selected) { 61 if (is_selected) { 62 if (has_focus) 63 return ui::NativeTheme::kColorId_TreeSelectedText; 64 return ui::NativeTheme::kColorId_TreeSelectedTextUnfocused; 65 } 66 return ui::NativeTheme::kColorId_TreeText; 67 } 68 69 } // namespace 70 71 TreeView::TreeView() 72 : model_(NULL), 73 selected_node_(NULL), 74 editing_(false), 75 editor_(NULL), 76 focus_manager_(NULL), 77 auto_expand_children_(false), 78 editable_(true), 79 controller_(NULL), 80 root_shown_(true), 81 has_custom_icons_(false), 82 row_height_(font_.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 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 103 View* TreeView::CreateParentIfNecessary() { 104 ScrollView* scroll_view = ScrollView::CreateScrollViewWithBorder(); 105 scroll_view->SetContents(this); 106 return scroll_view; 107 } 108 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 136 void TreeView::SetEditable(bool editable) { 137 if (editable == editable_) 138 return; 139 editable_ = editable; 140 CancelEdit(); 141 } 142 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_->SetFont(font_); 162 empty_editor_size_ = editor_->GetPreferredSize(); 163 editor_->SetController(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 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 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 212 TreeModelNode* TreeView::GetEditingNode() { 213 return editing_ ? selected_node_->model_node() : NULL; 214 } 215 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::AccessibilityTypes::EVENT_FOCUS, true); 249 } 250 } 251 252 TreeModelNode* TreeView::GetSelectedNode() { 253 return selected_node_ ? selected_node_->model_node() : NULL; 254 } 255 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 275 void TreeView::Expand(TreeModelNode* node) { 276 if (ExpandImpl(node)) 277 DrawnNodesChanged(); 278 // TODO: need to support auto_expand_children_. 279 } 280 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 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 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 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 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 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 353 gfx::Size TreeView::GetPreferredSize() { 354 return preferred_size_; 355 } 356 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 368 bool TreeView::OnMousePressed(const ui::MouseEvent& event) { 369 return OnClickOrTap(event); 370 } 371 372 ui::TextInputClient* TreeView::GetTextInputClient() { 373 if (!selector_) 374 selector_.reset(new PrefixSelector(this)); 375 return selector_.get(); 376 } 377 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 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 406 void TreeView::GetAccessibleState(ui::AccessibleViewState* state) { 407 state->role = ui::AccessibilityTypes::ROLE_OUTLINE; 408 state->state = ui::AccessibilityTypes::STATE_READONLY; 409 if (!selected_node_) 410 return; 411 412 // Get selected item info. 413 state->role = ui::AccessibilityTypes::ROLE_OUTLINEITEM; 414 state->name = selected_node_->model_node()->GetTitle(); 415 } 416 417 const char* TreeView::GetClassName() const { 418 return kViewClassName; 419 } 420 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 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 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 484 void TreeView::ContentsChanged(Textfield* sender, 485 const string16& new_contents) { 486 } 487 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 505 void TreeView::OnWillChangeFocus(View* focused_before, View* focused_now) { 506 } 507 508 void TreeView::OnDidChangeFocus(View* focused_before, View* focused_now) { 509 CommitEdit(); 510 } 511 512 int TreeView::GetRowCount() { 513 int row_count = root_.NumExpandedNodes(); 514 if (!root_shown_) 515 row_count--; 516 return row_count; 517 } 518 519 int TreeView::GetSelectedRow() { 520 ui::TreeModelNode* model_node = GetSelectedNode(); 521 return model_node ? GetRowForNode(model_node) : -1; 522 } 523 524 void TreeView::SetSelectedRow(int row) { 525 SetSelectedNode(GetNodeForRow(row)); 526 } 527 528 string16 TreeView::GetTextForRow(int row) { 529 return GetNodeForRow(row)->GetTitle(); 530 } 531 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 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 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 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 629 void TreeView::OnBlur() { 630 GetInputMethod()->OnBlur(); 631 SchedulePaintForNode(selected_node_); 632 if (selector_) 633 selector_->OnViewBlur(); 634 } 635 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 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 689 void TreeView::ConfigureInternalNode(TreeModelNode* model_node, 690 InternalNode* node) { 691 node->Reset(model_node); 692 UpdateNodeTextWidth(node); 693 } 694 695 void TreeView::UpdateNodeTextWidth(InternalNode* node) { 696 int width = 0, height = 0; 697 gfx::Canvas::SizeStringInt(node->model_node()->GetTitle(), font_, 698 &width, &height, 0, gfx::Canvas::NO_ELLIPSIS); 699 node->set_text_width(width); 700 } 701 702 void TreeView::DrawnNodesChanged() { 703 UpdatePreferredSize(); 704 PreferredSizeChanged(); 705 SchedulePaint(); 706 } 707 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 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_.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 739 void TreeView::SchedulePaintForNode(InternalNode* node) { 740 if (!node) 741 return; // Explicitly allow NULL to be passed in. 742 SchedulePaintInRect(GetBoundsForNode(node)); 743 } 744 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 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 canvas->DrawStringInt(node->model_node()->GetTitle(), font_, 807 GetNativeTheme()->GetSystemColor(color_id), 808 text_bounds.x() + kTextHorizontalPadding, 809 text_bounds.y() + kTextVerticalPadding, 810 text_bounds.width() - kTextHorizontalPadding * 2, 811 text_bounds.height() - kTextVerticalPadding * 2); 812 } 813 } 814 815 void TreeView::PaintExpandControl(gfx::Canvas* canvas, 816 const gfx::Rect& node_bounds, 817 bool expanded) { 818 int center_x; 819 if (base::i18n::IsRTL()) { 820 center_x = node_bounds.right() - kArrowRegionSize + 821 (kArrowRegionSize - 4) / 2; 822 } else { 823 center_x = node_bounds.x() + (kArrowRegionSize - 4) / 2; 824 } 825 int center_y = node_bounds.y() + node_bounds.height() / 2; 826 const SkColor arrow_color = GetNativeTheme()->GetSystemColor( 827 ui::NativeTheme::kColorId_TreeArrow); 828 // TODO: this should come from an image. 829 if (!expanded) { 830 int delta = base::i18n::IsRTL() ? 1 : -1; 831 for (int i = 0; i < 4; ++i) { 832 canvas->FillRect(gfx::Rect(center_x + delta * (2 - i), 833 center_y - (3 - i), 1, (3 - i) * 2 + 1), 834 arrow_color); 835 } 836 } else { 837 center_y -= 2; 838 for (int i = 0; i < 4; ++i) { 839 canvas->FillRect(gfx::Rect(center_x - (3 - i), center_y + i, 840 (3 - i) * 2 + 1, 1), arrow_color); 841 } 842 } 843 } 844 845 TreeView::InternalNode* TreeView::GetInternalNodeForModelNode( 846 ui::TreeModelNode* model_node, 847 GetInternalNodeCreateType create_type) { 848 if (model_node == root_.model_node()) 849 return &root_; 850 InternalNode* parent_internal_node = 851 GetInternalNodeForModelNode(model_->GetParent(model_node), create_type); 852 if (!parent_internal_node) 853 return NULL; 854 if (!parent_internal_node->loaded_children()) { 855 if (create_type == DONT_CREATE_IF_NOT_LOADED) 856 return NULL; 857 LoadChildren(parent_internal_node); 858 } 859 return parent_internal_node->GetChild( 860 model_->GetIndexOf(parent_internal_node->model_node(), model_node)); 861 } 862 863 gfx::Rect TreeView::GetBoundsForNode(InternalNode* node) { 864 int row, depth; 865 row = GetRowForInternalNode(node, &depth); 866 return GetBoundsForNodeImpl(node, row, depth); 867 } 868 869 gfx::Rect TreeView::GetBoundsForNodeImpl(InternalNode* node, 870 int row, 871 int depth) { 872 gfx::Rect rect(depth * kIndent + kHorizontalInset, 873 row * row_height_ + kVerticalInset, 874 text_offset_ + node->text_width() + 875 kTextHorizontalPadding * 2, 876 row_height_); 877 rect.set_x(GetMirroredXWithWidthInView(rect.x(), rect.width())); 878 return rect; 879 } 880 881 int TreeView::GetRowForInternalNode(InternalNode* node, int* depth) { 882 DCHECK(!node->parent() || IsExpanded(node->parent()->model_node())); 883 *depth = -1; 884 int row = -1; 885 InternalNode* tmp_node = node; 886 while (tmp_node->parent()) { 887 int index_in_parent = tmp_node->parent()->GetIndexOf(tmp_node); 888 (*depth)++; 889 row++; // For node. 890 for (int i = 0; i < index_in_parent; ++i) 891 row += tmp_node->parent()->GetChild(i)->NumExpandedNodes(); 892 tmp_node = tmp_node->parent(); 893 } 894 if (root_shown_) { 895 (*depth)++; 896 row++; 897 } 898 return row; 899 } 900 901 TreeView::InternalNode* TreeView::GetNodeByRow(int row, int* depth) { 902 int current_row = root_row(); 903 *depth = 0; 904 return GetNodeByRowImpl(&root_, row, root_depth(), ¤t_row, depth); 905 } 906 907 TreeView::InternalNode* TreeView::GetNodeByRowImpl(InternalNode* node, 908 int target_row, 909 int current_depth, 910 int* current_row, 911 int* node_depth) { 912 if (*current_row == target_row) { 913 *node_depth = current_depth; 914 return node; 915 } 916 (*current_row)++; 917 if (node->is_expanded()) { 918 current_depth++; 919 for (int i = 0; i < node->child_count(); ++i) { 920 InternalNode* result = GetNodeByRowImpl( 921 node->GetChild(i), target_row, current_depth, current_row, 922 node_depth); 923 if (result) 924 return result; 925 } 926 } 927 return NULL; 928 } 929 930 void TreeView::IncrementSelection(IncrementType type) { 931 if (!model_) 932 return; 933 934 if (!GetSelectedNode()) { 935 // If nothing is selected select the first or last node. 936 if (!root_.child_count()) 937 return; 938 if (type == INCREMENT_PREVIOUS) { 939 int row_count = GetRowCount(); 940 int depth = 0; 941 DCHECK(row_count); 942 InternalNode* node = GetNodeByRow(row_count - 1, &depth); 943 SetSelectedNode(node->model_node()); 944 } else if (root_shown_) { 945 SetSelectedNode(root_.model_node()); 946 } else { 947 SetSelectedNode(root_.GetChild(0)->model_node()); 948 } 949 return; 950 } 951 952 int depth = 0; 953 int delta = type == INCREMENT_PREVIOUS ? -1 : 1; 954 int row = GetRowForInternalNode(selected_node_, &depth); 955 int new_row = std::min(GetRowCount() - 1, std::max(0, row + delta)); 956 if (new_row == row) 957 return; // At the end/beginning. 958 SetSelectedNode(GetNodeByRow(new_row, &depth)->model_node()); 959 } 960 961 void TreeView::CollapseOrSelectParent() { 962 if (selected_node_) { 963 if (selected_node_->is_expanded()) 964 Collapse(selected_node_->model_node()); 965 else if (selected_node_->parent()) 966 SetSelectedNode(selected_node_->parent()->model_node()); 967 } 968 } 969 970 void TreeView::ExpandOrSelectChild() { 971 if (selected_node_) { 972 if (!selected_node_->is_expanded()) 973 Expand(selected_node_->model_node()); 974 else if (selected_node_->child_count()) 975 SetSelectedNode(selected_node_->GetChild(0)->model_node()); 976 } 977 } 978 979 bool TreeView::ExpandImpl(TreeModelNode* model_node) { 980 TreeModelNode* parent = model_->GetParent(model_node); 981 if (!parent) { 982 // Node should be the root. 983 DCHECK_EQ(root_.model_node(), model_node); 984 bool was_expanded = root_.is_expanded(); 985 root_.set_is_expanded(true); 986 return !was_expanded; 987 } 988 989 // Expand all the parents. 990 bool return_value = ExpandImpl(parent); 991 InternalNode* internal_node = 992 GetInternalNodeForModelNode(model_node, CREATE_IF_NOT_LOADED); 993 DCHECK(internal_node); 994 if (!internal_node->is_expanded()) { 995 if (!internal_node->loaded_children()) 996 LoadChildren(internal_node); 997 internal_node->set_is_expanded(true); 998 return_value = true; 999 } 1000 return return_value; 1001 } 1002 1003 // InternalNode ---------------------------------------------------------------- 1004 1005 TreeView::InternalNode::InternalNode() 1006 : model_node_(NULL), 1007 loaded_children_(false), 1008 is_expanded_(false), 1009 text_width_(0) { 1010 } 1011 1012 TreeView::InternalNode::~InternalNode() { 1013 } 1014 1015 void TreeView::InternalNode::Reset(ui::TreeModelNode* node) { 1016 model_node_ = node; 1017 loaded_children_ = false; 1018 is_expanded_ = false; 1019 text_width_ = 0; 1020 } 1021 1022 int TreeView::InternalNode::NumExpandedNodes() const { 1023 int result = 1; // For this. 1024 if (!is_expanded_) 1025 return result; 1026 for (int i = 0; i < child_count(); ++i) 1027 result += GetChild(i)->NumExpandedNodes(); 1028 return result; 1029 } 1030 1031 int TreeView::InternalNode::GetMaxWidth(int indent, int depth) { 1032 int max_width = text_width_ + indent * depth; 1033 if (!is_expanded_) 1034 return max_width; 1035 for (int i = 0; i < child_count(); ++i) { 1036 max_width = std::max(max_width, 1037 GetChild(i)->GetMaxWidth(indent, depth + 1)); 1038 } 1039 return max_width; 1040 } 1041 1042 } // namespace views 1043