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/events/event.h" 14 #include "ui/base/keycodes/keyboard_codes.h" 15 #include "ui/base/resource/resource_bundle.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 namespace { 46 47 // Returns the color id for the background of selected text. |has_focus| 48 // indicates if the tree has focus. 49 ui::NativeTheme::ColorId text_background_color_id(bool has_focus) { 50 return has_focus ? 51 ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused : 52 ui::NativeTheme::kColorId_TreeSelectionBackgroundUnfocused; 53 } 54 55 // Returns the color id for text. |has_focus| indicates if the tree has focus 56 // and |is_selected| is true if the item is selected. 57 ui::NativeTheme::ColorId text_color_id(bool has_focus, bool is_selected) { 58 if (is_selected) { 59 if (has_focus) 60 return ui::NativeTheme::kColorId_TreeSelectedText; 61 return ui::NativeTheme::kColorId_TreeSelectedTextUnfocused; 62 } 63 return ui::NativeTheme::kColorId_TreeText; 64 } 65 66 } // namespace 67 68 TreeView::TreeView() 69 : model_(NULL), 70 selected_node_(NULL), 71 editing_(false), 72 editor_(NULL), 73 focus_manager_(NULL), 74 auto_expand_children_(false), 75 editable_(true), 76 controller_(NULL), 77 root_shown_(true), 78 has_custom_icons_(false), 79 row_height_(font_.GetHeight() + kTextVerticalPadding * 2) { 80 set_focusable(true); 81 closed_icon_ = *ui::ResourceBundle::GetSharedInstance().GetImageNamed( 82 (base::i18n::IsRTL() ? IDR_FOLDER_CLOSED_RTL 83 : IDR_FOLDER_CLOSED)).ToImageSkia(); 84 open_icon_ = *ui::ResourceBundle::GetSharedInstance().GetImageNamed( 85 (base::i18n::IsRTL() ? IDR_FOLDER_OPEN_RTL 86 : IDR_FOLDER_OPEN)).ToImageSkia(); 87 text_offset_ = closed_icon_.width() + kImagePadding + kImagePadding + 88 kArrowRegionSize; 89 } 90 91 TreeView::~TreeView() { 92 if (model_) 93 model_->RemoveObserver(this); 94 if (focus_manager_) { 95 focus_manager_->RemoveFocusChangeListener(this); 96 focus_manager_ = NULL; 97 } 98 } 99 100 View* TreeView::CreateParentIfNecessary() { 101 ScrollView* scroll_view = ScrollView::CreateScrollViewWithBorder(); 102 scroll_view->SetContents(this); 103 return scroll_view; 104 } 105 106 void TreeView::SetModel(TreeModel* model) { 107 if (model == model_) 108 return; 109 if (model_) 110 model_->RemoveObserver(this); 111 112 CancelEdit(); 113 114 model_ = model; 115 selected_node_ = NULL; 116 icons_.clear(); 117 if (model_) { 118 model_->AddObserver(this); 119 model_->GetIcons(&icons_); 120 121 root_.RemoveAll(); 122 ConfigureInternalNode(model_->GetRoot(), &root_); 123 LoadChildren(&root_); 124 root_.set_is_expanded(true); 125 if (root_shown_) 126 selected_node_ = &root_; 127 else if (root_.child_count()) 128 selected_node_ = root_.GetChild(0); 129 } 130 DrawnNodesChanged(); 131 } 132 133 void TreeView::SetEditable(bool editable) { 134 if (editable == editable_) 135 return; 136 editable_ = editable; 137 CancelEdit(); 138 } 139 140 void TreeView::StartEditing(TreeModelNode* node) { 141 DCHECK(node); 142 // Cancel the current edit. 143 CancelEdit(); 144 // Make sure all ancestors are expanded. 145 if (model_->GetParent(node)) 146 Expand(model_->GetParent(node)); 147 // Select the node, else if the user commits the edit the selection reverts. 148 SetSelectedNode(node); 149 if (GetSelectedNode() != node) 150 return; // Selection failed for some reason, don't start editing. 151 DCHECK(!editing_); 152 editing_ = true; 153 if (!editor_) { 154 editor_ = new Textfield; 155 // Add the editor immediately as GetPreferredSize returns the wrong thing if 156 // not parented. 157 AddChildView(editor_); 158 editor_->SetFont(font_); 159 empty_editor_size_ = editor_->GetPreferredSize(); 160 editor_->SetController(this); 161 } 162 editor_->SetText(selected_node_->model_node()->GetTitle()); 163 LayoutEditor(); 164 editor_->SetVisible(true); 165 SchedulePaintForNode(selected_node_); 166 editor_->RequestFocus(); 167 editor_->SelectAll(false); 168 169 // Listen for focus changes so that we can cancel editing. 170 focus_manager_ = GetFocusManager(); 171 if (focus_manager_) 172 focus_manager_->AddFocusChangeListener(this); 173 174 // Accelerators to commit/cancel edit. 175 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); 176 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); 177 } 178 179 void TreeView::CancelEdit() { 180 if (!editing_) 181 return; 182 183 // WARNING: don't touch |selected_node_|, it may be bogus. 184 185 editing_ = false; 186 if (focus_manager_) { 187 focus_manager_->RemoveFocusChangeListener(this); 188 focus_manager_ = NULL; 189 } 190 editor_->SetVisible(false); 191 SchedulePaint(); 192 193 RemoveAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); 194 RemoveAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); 195 } 196 197 void TreeView::CommitEdit() { 198 if (!editing_) 199 return; 200 201 DCHECK(selected_node_); 202 const bool editor_has_focus = editor_->HasFocus(); 203 model_->SetTitle(GetSelectedNode(), editor_->text()); 204 CancelEdit(); 205 if (editor_has_focus) 206 RequestFocus(); 207 } 208 209 TreeModelNode* TreeView::GetEditingNode() { 210 return editing_ ? selected_node_->model_node() : NULL; 211 } 212 213 void TreeView::SetSelectedNode(TreeModelNode* model_node) { 214 if (editing_ || model_node != selected_node_) 215 CancelEdit(); 216 if (model_node && model_->GetParent(model_node)) 217 Expand(model_->GetParent(model_node)); 218 if (model_node && model_node == root_.model_node() && !root_shown_) 219 return; // Ignore requests to select the root when not shown. 220 InternalNode* node = model_node ? GetInternalNodeForModelNode( 221 model_node, CREATE_IF_NOT_LOADED) : NULL; 222 bool was_empty_selection = (selected_node_ == NULL); 223 bool changed = (selected_node_ != node); 224 if (changed) { 225 SchedulePaintForNode(selected_node_); 226 selected_node_ = node; 227 if (selected_node_ == &root_ && !root_shown_) 228 selected_node_ = NULL; 229 if (selected_node_ && selected_node_ != &root_) 230 Expand(model_->GetParent(selected_node_->model_node())); 231 SchedulePaintForNode(selected_node_); 232 } 233 234 if (selected_node_) 235 ScrollRectToVisible(GetBoundsForNode(selected_node_)); 236 237 // Notify controller if the old selection was empty to handle the case of 238 // remove explicitly resetting selected_node_ before invoking this. 239 if (controller_ && (changed || was_empty_selection)) 240 controller_->OnTreeViewSelectionChanged(this); 241 } 242 243 TreeModelNode* TreeView::GetSelectedNode() { 244 return selected_node_ ? selected_node_->model_node() : NULL; 245 } 246 247 void TreeView::Collapse(ui::TreeModelNode* model_node) { 248 // Don't collapse the root if the root isn't shown, otherwise nothing is 249 // displayed. 250 if (model_node == root_.model_node() && !root_shown_) 251 return; 252 InternalNode* node = 253 GetInternalNodeForModelNode(model_node, DONT_CREATE_IF_NOT_LOADED); 254 if (!node) 255 return; 256 bool was_expanded = IsExpanded(model_node); 257 if (node->is_expanded()) { 258 if (selected_node_ && selected_node_->HasAncestor(node)) 259 SetSelectedNode(model_node); 260 node->set_is_expanded(false); 261 } 262 if (was_expanded) 263 DrawnNodesChanged(); 264 } 265 266 void TreeView::Expand(TreeModelNode* node) { 267 if (ExpandImpl(node)) 268 DrawnNodesChanged(); 269 // TODO: need to support auto_expand_children_. 270 } 271 272 void TreeView::ExpandAll(TreeModelNode* node) { 273 DCHECK(node); 274 // Expand the node. 275 bool expanded_at_least_one = ExpandImpl(node); 276 // And recursively expand all the children. 277 for (int i = model_->GetChildCount(node) - 1; i >= 0; --i) { 278 TreeModelNode* child = model_->GetChild(node, i); 279 if (ExpandImpl(child)) 280 expanded_at_least_one = true; 281 } 282 if (expanded_at_least_one) 283 DrawnNodesChanged(); 284 } 285 286 bool TreeView::IsExpanded(TreeModelNode* model_node) { 287 if (!model_node) { 288 // NULL check primarily for convenience for uses in this class so don't have 289 // to add NULL checks every where we look up the parent. 290 return true; 291 } 292 InternalNode* node = GetInternalNodeForModelNode( 293 model_node, DONT_CREATE_IF_NOT_LOADED); 294 if (!node) 295 return false; 296 297 while (node) { 298 if (!node->is_expanded()) 299 return false; 300 node = node->parent(); 301 } 302 return true; 303 } 304 305 void TreeView::SetRootShown(bool root_shown) { 306 if (root_shown_ == root_shown) 307 return; 308 root_shown_ = root_shown; 309 if (!root_shown_ && selected_node_ == &root_) { 310 if (model_->GetChildCount(root_.model_node())) 311 SetSelectedNode(model_->GetChild(root_.model_node(), 0)); 312 else 313 SetSelectedNode(NULL); 314 } 315 DrawnNodesChanged(); 316 } 317 318 ui::TreeModelNode* TreeView::GetNodeForRow(int row) { 319 int depth = 0; 320 InternalNode* node = GetNodeByRow(row, &depth); 321 return node ? node->model_node() : NULL; 322 } 323 324 int TreeView::GetRowForNode(ui::TreeModelNode* node) { 325 InternalNode* internal_node = 326 GetInternalNodeForModelNode(node, DONT_CREATE_IF_NOT_LOADED); 327 if (!internal_node) 328 return -1; 329 int depth = 0; 330 return GetRowForInternalNode(internal_node, &depth); 331 } 332 333 void TreeView::Layout() { 334 int width = preferred_size_.width(); 335 int height = preferred_size_.height(); 336 if (parent()) { 337 width = std::max(parent()->width(), width); 338 height = std::max(parent()->height(), height); 339 } 340 SetBounds(x(), y(), width, height); 341 LayoutEditor(); 342 } 343 344 gfx::Size TreeView::GetPreferredSize() { 345 return preferred_size_; 346 } 347 348 bool TreeView::AcceleratorPressed(const ui::Accelerator& accelerator) { 349 if (accelerator.key_code() == ui::VKEY_RETURN) { 350 CommitEdit(); 351 } else { 352 DCHECK_EQ(ui::VKEY_ESCAPE, accelerator.key_code()); 353 CancelEdit(); 354 RequestFocus(); 355 } 356 return true; 357 } 358 359 bool TreeView::OnMousePressed(const ui::MouseEvent& event) { 360 return OnClickOrTap(event); 361 } 362 363 ui::TextInputClient* TreeView::GetTextInputClient() { 364 if (!selector_) 365 selector_.reset(new PrefixSelector(this)); 366 return selector_.get(); 367 } 368 369 void TreeView::OnGestureEvent(ui::GestureEvent* event) { 370 if (event->type() == ui::ET_GESTURE_TAP) { 371 if (OnClickOrTap(*event)) 372 event->SetHandled(); 373 } 374 } 375 376 void TreeView::ShowContextMenu(const gfx::Point& p, 377 ui::MenuSourceType source_type) { 378 if (!model_) 379 return; 380 if (source_type == ui::MENU_SOURCE_MOUSE) { 381 // Only invoke View's implementation (which notifies the 382 // ContextMenuController) if over a node. 383 gfx::Point local_point(p); 384 ConvertPointToTarget(NULL, this, &local_point); 385 int row = (local_point.y() - kVerticalInset) / row_height_; 386 int depth = 0; 387 InternalNode* node = GetNodeByRow(row, &depth); 388 if (!node) 389 return; 390 gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth)); 391 if (!bounds.Contains(local_point)) 392 return; 393 } 394 View::ShowContextMenu(p, source_type); 395 } 396 397 void TreeView::GetAccessibleState(ui::AccessibleViewState* state) { 398 state->role = ui::AccessibilityTypes::ROLE_OUTLINE; 399 state->state = ui::AccessibilityTypes::STATE_READONLY; 400 } 401 402 void TreeView::TreeNodesAdded(TreeModel* model, 403 TreeModelNode* parent, 404 int start, 405 int count) { 406 InternalNode* parent_node = 407 GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED); 408 if (!parent_node || !parent_node->loaded_children()) 409 return; 410 for (int i = 0; i < count; ++i) { 411 InternalNode* child = new InternalNode; 412 ConfigureInternalNode(model_->GetChild(parent, start + i), child); 413 parent_node->Add(child, start + i); 414 } 415 if (IsExpanded(parent)) 416 DrawnNodesChanged(); 417 } 418 419 void TreeView::TreeNodesRemoved(TreeModel* model, 420 TreeModelNode* parent, 421 int start, 422 int count) { 423 InternalNode* parent_node = 424 GetInternalNodeForModelNode(parent, DONT_CREATE_IF_NOT_LOADED); 425 if (!parent_node || !parent_node->loaded_children()) 426 return; 427 bool reset_selection = false; 428 for (int i = 0; i < count; ++i) { 429 InternalNode* child_removing = parent_node->GetChild(start); 430 if (selected_node_ && selected_node_->HasAncestor(child_removing)) 431 reset_selection = true; 432 delete parent_node->Remove(child_removing); 433 } 434 if (reset_selection) { 435 // selected_node_ is no longer valid (at the time we enter this function 436 // its model_node() is likely deleted). Explicitly NULL out the field 437 // rather than invoking SetSelectedNode() otherwise, we'll try and use a 438 // deleted value. 439 selected_node_ = NULL; 440 TreeModelNode* to_select = parent; 441 if (parent == root_.model_node() && !root_shown_) { 442 to_select = model_->GetChildCount(parent) > 0 ? 443 model_->GetChild(parent, 0) : NULL; 444 } 445 SetSelectedNode(to_select); 446 } 447 if (IsExpanded(parent)) 448 DrawnNodesChanged(); 449 } 450 451 void TreeView::TreeNodeChanged(TreeModel* model, TreeModelNode* model_node) { 452 InternalNode* node = 453 GetInternalNodeForModelNode(model_node, DONT_CREATE_IF_NOT_LOADED); 454 if (!node) 455 return; 456 int old_width = node->text_width(); 457 UpdateNodeTextWidth(node); 458 if (old_width != node->text_width() && 459 ((node == &root_ && root_shown_) || 460 (node != &root_ && IsExpanded(node->parent()->model_node())))) { 461 DrawnNodesChanged(); 462 } 463 } 464 465 void TreeView::ContentsChanged(Textfield* sender, 466 const string16& new_contents) { 467 } 468 469 bool TreeView::HandleKeyEvent(Textfield* sender, 470 const ui::KeyEvent& key_event) { 471 switch (key_event.key_code()) { 472 case ui::VKEY_RETURN: 473 CommitEdit(); 474 return true; 475 476 case ui::VKEY_ESCAPE: 477 CancelEdit(); 478 RequestFocus(); 479 return true; 480 481 default: 482 return false; 483 } 484 } 485 486 void TreeView::OnWillChangeFocus(View* focused_before, View* focused_now) { 487 } 488 489 void TreeView::OnDidChangeFocus(View* focused_before, View* focused_now) { 490 CommitEdit(); 491 } 492 493 int TreeView::GetRowCount() { 494 int row_count = root_.NumExpandedNodes(); 495 if (!root_shown_) 496 row_count--; 497 return row_count; 498 } 499 500 int TreeView::GetSelectedRow() { 501 ui::TreeModelNode* model_node = GetSelectedNode(); 502 return model_node ? GetRowForNode(model_node) : -1; 503 } 504 505 void TreeView::SetSelectedRow(int row) { 506 SetSelectedNode(GetNodeForRow(row)); 507 } 508 509 string16 TreeView::GetTextForRow(int row) { 510 return GetNodeForRow(row)->GetTitle(); 511 } 512 513 gfx::Point TreeView::GetKeyboardContextMenuLocation() { 514 int y = height() / 2; 515 if (selected_node_) { 516 gfx::Rect node_bounds(GetBoundsForNode(selected_node_)); 517 gfx::Rect vis_bounds(GetVisibleBounds()); 518 if (node_bounds.y() >= vis_bounds.y() && 519 node_bounds.y() < vis_bounds.bottom()) { 520 y = node_bounds.y(); 521 } 522 } 523 gfx::Point screen_loc(0, y); 524 if (base::i18n::IsRTL()) 525 screen_loc.set_x(width()); 526 ConvertPointToScreen(this, &screen_loc); 527 return screen_loc; 528 } 529 530 bool TreeView::OnKeyPressed(const ui::KeyEvent& event) { 531 if (!HasFocus()) 532 return false; 533 534 switch (event.key_code()) { 535 case ui::VKEY_F2: 536 if (!editing_) { 537 TreeModelNode* selected_node = GetSelectedNode(); 538 if (selected_node && (!controller_ || 539 controller_->CanEdit(this, selected_node))) { 540 StartEditing(selected_node); 541 } 542 } 543 return true; 544 545 case ui::VKEY_UP: 546 case ui::VKEY_DOWN: 547 IncrementSelection(event.key_code() == ui::VKEY_UP ? 548 INCREMENT_PREVIOUS : INCREMENT_NEXT); 549 return true; 550 551 case ui::VKEY_LEFT: 552 if (base::i18n::IsRTL()) 553 ExpandOrSelectChild(); 554 else 555 CollapseOrSelectParent(); 556 return true; 557 558 case ui::VKEY_RIGHT: 559 if (base::i18n::IsRTL()) 560 CollapseOrSelectParent(); 561 else 562 ExpandOrSelectChild(); 563 return true; 564 565 default: 566 break; 567 } 568 return false; 569 } 570 571 void TreeView::OnPaint(gfx::Canvas* canvas) { 572 // Don't invoke View::OnPaint so that we can render our own focus border. 573 canvas->DrawColor(GetNativeTheme()->GetSystemColor( 574 ui::NativeTheme::kColorId_TreeBackground)); 575 576 int min_y, max_y; 577 { 578 SkRect sk_clip_rect; 579 if (canvas->sk_canvas()->getClipBounds(&sk_clip_rect)) { 580 // Pixels partially inside the clip rect should be included. 581 gfx::Rect clip_rect = gfx::ToEnclosingRect( 582 gfx::SkRectToRectF(sk_clip_rect)); 583 min_y = clip_rect.y(); 584 max_y = clip_rect.bottom(); 585 } else { 586 gfx::Rect vis_bounds = GetVisibleBounds(); 587 min_y = vis_bounds.y(); 588 max_y = vis_bounds.bottom(); 589 } 590 } 591 592 int min_row = std::max(0, (min_y - kVerticalInset) / row_height_); 593 int max_row = (max_y - kVerticalInset) / row_height_; 594 if ((max_y - kVerticalInset) % row_height_ != 0) 595 max_row++; 596 int current_row = root_row(); 597 PaintRows(canvas, min_row, max_row, &root_, root_depth(), ¤t_row); 598 } 599 600 void TreeView::OnFocus() { 601 GetInputMethod()->OnFocus(); 602 View::OnFocus(); 603 SchedulePaintForNode(selected_node_); 604 605 // Notify the InputMethod so that it knows to query the TextInputClient. 606 if (GetInputMethod()) 607 GetInputMethod()->OnCaretBoundsChanged(this); 608 } 609 610 void TreeView::OnBlur() { 611 GetInputMethod()->OnBlur(); 612 SchedulePaintForNode(selected_node_); 613 if (selector_) 614 selector_->OnViewBlur(); 615 } 616 617 bool TreeView::OnClickOrTap(const ui::LocatedEvent& event) { 618 CommitEdit(); 619 RequestFocus(); 620 621 int row = (event.y() - kVerticalInset) / row_height_; 622 int depth = 0; 623 InternalNode* node = GetNodeByRow(row, &depth); 624 if (node) { 625 gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth)); 626 if (bounds.Contains(event.location())) { 627 int relative_x = event.x() - bounds.x(); 628 if (base::i18n::IsRTL()) 629 relative_x = bounds.width() - relative_x; 630 if (relative_x < kArrowRegionSize && 631 model_->GetChildCount(node->model_node())) { 632 if (node->is_expanded()) 633 Collapse(node->model_node()); 634 else 635 Expand(node->model_node()); 636 } else if (relative_x > kArrowRegionSize) { 637 SetSelectedNode(node->model_node()); 638 bool should_toggle = false; 639 if (event.type() == ui::ET_GESTURE_TAP) { 640 const ui::GestureEvent& gesture = 641 static_cast<const ui::GestureEvent&>(event); 642 should_toggle = gesture.details().tap_count() == 2; 643 } else { 644 should_toggle = (event.flags() & ui::EF_IS_DOUBLE_CLICK) != 0; 645 } 646 if (should_toggle) { 647 if (node->is_expanded()) 648 Collapse(node->model_node()); 649 else 650 Expand(node->model_node()); 651 } 652 } 653 } 654 } 655 return true; 656 } 657 658 void TreeView::LoadChildren(InternalNode* node) { 659 DCHECK_EQ(0, node->child_count()); 660 DCHECK(!node->loaded_children()); 661 node->set_loaded_children(true); 662 for (int i = 0, child_count = model_->GetChildCount(node->model_node()); 663 i < child_count; ++i) { 664 InternalNode* child = new InternalNode; 665 ConfigureInternalNode(model_->GetChild(node->model_node(), i), child); 666 node->Add(child, node->child_count()); 667 } 668 } 669 670 void TreeView::ConfigureInternalNode(TreeModelNode* model_node, 671 InternalNode* node) { 672 node->Reset(model_node); 673 UpdateNodeTextWidth(node); 674 } 675 676 void TreeView::UpdateNodeTextWidth(InternalNode* node) { 677 int width = 0, height = 0; 678 gfx::Canvas::SizeStringInt(node->model_node()->GetTitle(), font_, 679 &width, &height, 0, gfx::Canvas::NO_ELLIPSIS); 680 node->set_text_width(width); 681 } 682 683 void TreeView::DrawnNodesChanged() { 684 UpdatePreferredSize(); 685 PreferredSizeChanged(); 686 SchedulePaint(); 687 } 688 689 void TreeView::UpdatePreferredSize() { 690 preferred_size_ = gfx::Size(); 691 if (!model_) 692 return; 693 694 preferred_size_.SetSize( 695 root_.GetMaxWidth(text_offset_, root_shown_ ? 1 : 0) + 696 kTextHorizontalPadding * 2, 697 row_height_ * GetRowCount() + kVerticalInset * 2); 698 } 699 700 void TreeView::LayoutEditor() { 701 if (!editing_) 702 return; 703 704 DCHECK(selected_node_); 705 // Position the editor so that its text aligns with the text we drew. 706 gfx::Rect row_bounds = GetBoundsForNode(selected_node_); 707 row_bounds.set_x( 708 GetMirroredXWithWidthInView(row_bounds.x(), row_bounds.width())); 709 row_bounds.set_x(row_bounds.x() + text_offset_); 710 row_bounds.set_width(row_bounds.width() - text_offset_); 711 row_bounds.Inset(kTextHorizontalPadding, kTextVerticalPadding); 712 row_bounds.Inset(-empty_editor_size_.width() / 2, 713 -(empty_editor_size_.height() - font_.GetHeight()) / 2); 714 // Give a little extra space for editing. 715 row_bounds.set_width(row_bounds.width() + 50); 716 editor_->SetBoundsRect(row_bounds); 717 editor_->Layout(); 718 } 719 720 void TreeView::SchedulePaintForNode(InternalNode* node) { 721 if (!node) 722 return; // Explicitly allow NULL to be passed in. 723 SchedulePaintInRect(GetBoundsForNode(node)); 724 } 725 726 void TreeView::PaintRows(gfx::Canvas* canvas, 727 int min_row, 728 int max_row, 729 InternalNode* node, 730 int depth, 731 int* row) { 732 if (*row >= max_row) 733 return; 734 735 if (*row >= min_row && *row < max_row) 736 PaintRow(canvas, node, *row, depth); 737 (*row)++; 738 if (!node->is_expanded()) 739 return; 740 depth++; 741 for (int i = 0; i < node->child_count() && *row < max_row; ++i) 742 PaintRows(canvas, min_row, max_row, node->GetChild(i), depth, row); 743 } 744 745 void TreeView::PaintRow(gfx::Canvas* canvas, 746 InternalNode* node, 747 int row, 748 int depth) { 749 gfx::Rect bounds(GetBoundsForNodeImpl(node, row, depth)); 750 751 if (model_->GetChildCount(node->model_node())) 752 PaintExpandControl(canvas, bounds, node->is_expanded()); 753 754 // Paint the icon. 755 gfx::ImageSkia icon; 756 int icon_index = model_->GetIconIndex(node->model_node()); 757 if (icon_index != -1) 758 icon = icons_[icon_index]; 759 else if (node == selected_node_) 760 icon = open_icon_; 761 else 762 icon = closed_icon_; 763 int icon_x = kArrowRegionSize + kImagePadding + 764 (open_icon_.width() - icon.width()) / 2; 765 if (base::i18n::IsRTL()) 766 icon_x = bounds.right() - icon_x - open_icon_.width(); 767 else 768 icon_x += bounds.x(); 769 canvas->DrawImageInt( 770 icon, icon_x, 771 bounds.y() + (bounds.height() - icon.height()) / 2); 772 773 if (!editing_ || node != selected_node_) { 774 gfx::Rect text_bounds(bounds.x() + text_offset_, bounds.y(), 775 bounds.width() - text_offset_, bounds.height()); 776 if (base::i18n::IsRTL()) 777 text_bounds.set_x(bounds.x()); 778 if (node == selected_node_) { 779 const SkColor bg_color = GetNativeTheme()->GetSystemColor( 780 text_background_color_id(HasFocus())); 781 canvas->FillRect(text_bounds, bg_color); 782 if (HasFocus()) 783 canvas->DrawFocusRect(text_bounds); 784 } 785 const ui::NativeTheme::ColorId color_id = 786 text_color_id(HasFocus(), node == selected_node_); 787 canvas->DrawStringInt(node->model_node()->GetTitle(), font_, 788 GetNativeTheme()->GetSystemColor(color_id), 789 text_bounds.x() + kTextHorizontalPadding, 790 text_bounds.y() + kTextVerticalPadding, 791 text_bounds.width() - kTextHorizontalPadding * 2, 792 text_bounds.height() - kTextVerticalPadding * 2); 793 } 794 } 795 796 void TreeView::PaintExpandControl(gfx::Canvas* canvas, 797 const gfx::Rect& node_bounds, 798 bool expanded) { 799 int center_x; 800 if (base::i18n::IsRTL()) { 801 center_x = node_bounds.right() - kArrowRegionSize + 802 (kArrowRegionSize - 4) / 2; 803 } else { 804 center_x = node_bounds.x() + (kArrowRegionSize - 4) / 2; 805 } 806 int center_y = node_bounds.y() + node_bounds.height() / 2; 807 const SkColor arrow_color = GetNativeTheme()->GetSystemColor( 808 ui::NativeTheme::kColorId_TreeArrow); 809 // TODO: this should come from an image. 810 if (!expanded) { 811 int delta = base::i18n::IsRTL() ? 1 : -1; 812 for (int i = 0; i < 4; ++i) { 813 canvas->FillRect(gfx::Rect(center_x + delta * (2 - i), 814 center_y - (3 - i), 1, (3 - i) * 2 + 1), 815 arrow_color); 816 } 817 } else { 818 center_y -= 2; 819 for (int i = 0; i < 4; ++i) { 820 canvas->FillRect(gfx::Rect(center_x - (3 - i), center_y + i, 821 (3 - i) * 2 + 1, 1), arrow_color); 822 } 823 } 824 } 825 826 TreeView::InternalNode* TreeView::GetInternalNodeForModelNode( 827 ui::TreeModelNode* model_node, 828 GetInternalNodeCreateType create_type) { 829 if (model_node == root_.model_node()) 830 return &root_; 831 InternalNode* parent_internal_node = 832 GetInternalNodeForModelNode(model_->GetParent(model_node), create_type); 833 if (!parent_internal_node) 834 return NULL; 835 if (!parent_internal_node->loaded_children()) { 836 if (create_type == DONT_CREATE_IF_NOT_LOADED) 837 return NULL; 838 LoadChildren(parent_internal_node); 839 } 840 return parent_internal_node->GetChild( 841 model_->GetIndexOf(parent_internal_node->model_node(), model_node)); 842 } 843 844 gfx::Rect TreeView::GetBoundsForNode(InternalNode* node) { 845 int row, depth; 846 row = GetRowForInternalNode(node, &depth); 847 return GetBoundsForNodeImpl(node, row, depth); 848 } 849 850 gfx::Rect TreeView::GetBoundsForNodeImpl(InternalNode* node, 851 int row, 852 int depth) { 853 gfx::Rect rect(depth * kIndent + kHorizontalInset, 854 row * row_height_ + kVerticalInset, 855 text_offset_ + node->text_width() + 856 kTextHorizontalPadding * 2, 857 row_height_); 858 rect.set_x(GetMirroredXWithWidthInView(rect.x(), rect.width())); 859 return rect; 860 } 861 862 int TreeView::GetRowForInternalNode(InternalNode* node, int* depth) { 863 DCHECK(!node->parent() || IsExpanded(node->parent()->model_node())); 864 *depth = -1; 865 int row = -1; 866 InternalNode* tmp_node = node; 867 while (tmp_node->parent()) { 868 int index_in_parent = tmp_node->parent()->GetIndexOf(tmp_node); 869 (*depth)++; 870 row++; // For node. 871 for (int i = 0; i < index_in_parent; ++i) 872 row += tmp_node->parent()->GetChild(i)->NumExpandedNodes(); 873 tmp_node = tmp_node->parent(); 874 } 875 if (root_shown_) { 876 (*depth)++; 877 row++; 878 } 879 return row; 880 } 881 882 TreeView::InternalNode* TreeView::GetNodeByRow(int row, int* depth) { 883 int current_row = root_row(); 884 *depth = 0; 885 return GetNodeByRowImpl(&root_, row, root_depth(), ¤t_row, depth); 886 } 887 888 TreeView::InternalNode* TreeView::GetNodeByRowImpl(InternalNode* node, 889 int target_row, 890 int current_depth, 891 int* current_row, 892 int* node_depth) { 893 if (*current_row == target_row) { 894 *node_depth = current_depth; 895 return node; 896 } 897 (*current_row)++; 898 if (node->is_expanded()) { 899 current_depth++; 900 for (int i = 0; i < node->child_count(); ++i) { 901 InternalNode* result = GetNodeByRowImpl( 902 node->GetChild(i), target_row, current_depth, current_row, 903 node_depth); 904 if (result) 905 return result; 906 } 907 } 908 return NULL; 909 } 910 911 void TreeView::IncrementSelection(IncrementType type) { 912 if (!model_) 913 return; 914 915 if (!GetSelectedNode()) { 916 // If nothing is selected select the first or last node. 917 if (!root_.child_count()) 918 return; 919 if (type == INCREMENT_PREVIOUS) { 920 int row_count = GetRowCount(); 921 int depth = 0; 922 DCHECK(row_count); 923 InternalNode* node = GetNodeByRow(row_count - 1, &depth); 924 SetSelectedNode(node->model_node()); 925 } else if (root_shown_) { 926 SetSelectedNode(root_.model_node()); 927 } else { 928 SetSelectedNode(root_.GetChild(0)->model_node()); 929 } 930 return; 931 } 932 933 int depth = 0; 934 int delta = type == INCREMENT_PREVIOUS ? -1 : 1; 935 int row = GetRowForInternalNode(selected_node_, &depth); 936 int new_row = std::min(GetRowCount() - 1, std::max(0, row + delta)); 937 if (new_row == row) 938 return; // At the end/beginning. 939 SetSelectedNode(GetNodeByRow(new_row, &depth)->model_node()); 940 } 941 942 void TreeView::CollapseOrSelectParent() { 943 if (selected_node_) { 944 if (selected_node_->is_expanded()) 945 Collapse(selected_node_->model_node()); 946 else if (selected_node_->parent()) 947 SetSelectedNode(selected_node_->parent()->model_node()); 948 } 949 } 950 951 void TreeView::ExpandOrSelectChild() { 952 if (selected_node_) { 953 if (!selected_node_->is_expanded()) 954 Expand(selected_node_->model_node()); 955 else if (selected_node_->child_count()) 956 SetSelectedNode(selected_node_->GetChild(0)->model_node()); 957 } 958 } 959 960 bool TreeView::ExpandImpl(TreeModelNode* model_node) { 961 TreeModelNode* parent = model_->GetParent(model_node); 962 if (!parent) { 963 // Node should be the root. 964 DCHECK_EQ(root_.model_node(), model_node); 965 bool was_expanded = root_.is_expanded(); 966 root_.set_is_expanded(true); 967 return !was_expanded; 968 } 969 970 // Expand all the parents. 971 bool return_value = ExpandImpl(parent); 972 InternalNode* internal_node = 973 GetInternalNodeForModelNode(model_node, CREATE_IF_NOT_LOADED); 974 DCHECK(internal_node); 975 if (!internal_node->is_expanded()) { 976 if (!internal_node->loaded_children()) 977 LoadChildren(internal_node); 978 internal_node->set_is_expanded(true); 979 return_value = true; 980 } 981 return return_value; 982 } 983 984 // InternalNode ---------------------------------------------------------------- 985 986 TreeView::InternalNode::InternalNode() 987 : model_node_(NULL), 988 loaded_children_(false), 989 is_expanded_(false), 990 text_width_(0) { 991 } 992 993 TreeView::InternalNode::~InternalNode() { 994 } 995 996 void TreeView::InternalNode::Reset(ui::TreeModelNode* node) { 997 model_node_ = node; 998 loaded_children_ = false; 999 is_expanded_ = false; 1000 text_width_ = 0; 1001 } 1002 1003 int TreeView::InternalNode::NumExpandedNodes() const { 1004 int result = 1; // For this. 1005 if (!is_expanded_) 1006 return result; 1007 for (int i = 0; i < child_count(); ++i) 1008 result += GetChild(i)->NumExpandedNodes(); 1009 return result; 1010 } 1011 1012 int TreeView::InternalNode::GetMaxWidth(int indent, int depth) { 1013 int max_width = text_width_ + indent * depth; 1014 if (!is_expanded_) 1015 return max_width; 1016 for (int i = 0; i < child_count(); ++i) { 1017 max_width = std::max(max_width, 1018 GetChild(i)->GetMaxWidth(indent, depth + 1)); 1019 } 1020 return max_width; 1021 } 1022 1023 } // namespace views 1024