diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-14 15:49:40 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-14 15:49:40 +0000 |
commit | 44cbd9e3734527f73a83f8a864be0bb5ccae0a7a (patch) | |
tree | a997fb0565558d63e0eab62b631ef984de3e9596 /ui | |
parent | 0c1c047d641a599ffffa280ab50d564cedb3e436 (diff) | |
download | chromium_src-44cbd9e3734527f73a83f8a864be0bb5ccae0a7a.zip chromium_src-44cbd9e3734527f73a83f8a864be0bb5ccae0a7a.tar.gz chromium_src-44cbd9e3734527f73a83f8a864be0bb5ccae0a7a.tar.bz2 |
Move models from app to ui/base/models
BUG=none
TEST=none
TBR=brettw
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71446 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/base/models/accelerator.h | 89 | ||||
-rw-r--r-- | ui/base/models/accelerator_cocoa.h | 59 | ||||
-rw-r--r-- | ui/base/models/accelerator_gtk.h | 59 | ||||
-rw-r--r-- | ui/base/models/button_menu_item_model.cc | 135 | ||||
-rw-r--r-- | ui/base/models/button_menu_item_model.h | 113 | ||||
-rw-r--r-- | ui/base/models/combobox_model.h | 27 | ||||
-rw-r--r-- | ui/base/models/menu_model.cc | 45 | ||||
-rw-r--r-- | ui/base/models/menu_model.h | 125 | ||||
-rw-r--r-- | ui/base/models/simple_menu_model.cc | 314 | ||||
-rw-r--r-- | ui/base/models/simple_menu_model.h | 146 | ||||
-rw-r--r-- | ui/base/models/table_model.cc | 138 | ||||
-rw-r--r-- | ui/base/models/table_model.h | 145 | ||||
-rw-r--r-- | ui/base/models/table_model_observer.h | 33 | ||||
-rw-r--r-- | ui/base/models/tree_model.cc | 20 | ||||
-rw-r--r-- | ui/base/models/tree_model.h | 99 | ||||
-rw-r--r-- | ui/base/models/tree_node_iterator.h | 75 | ||||
-rw-r--r-- | ui/base/models/tree_node_iterator_unittest.cc | 41 | ||||
-rw-r--r-- | ui/base/models/tree_node_model.h | 319 | ||||
-rw-r--r-- | ui/base/models/tree_node_model_unittest.cc | 295 |
19 files changed, 2277 insertions, 0 deletions
diff --git a/ui/base/models/accelerator.h b/ui/base/models/accelerator.h new file mode 100644 index 0000000..0017985 --- /dev/null +++ b/ui/base/models/accelerator.h @@ -0,0 +1,89 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_ACCELERATOR_H_ +#define UI_BASE_MODELS_ACCELERATOR_H_ +#pragma once + +#include "ui/base/keycodes/keyboard_codes.h" + +namespace ui { + +// This is a cross-platform base class for accelerator keys used in menus. It is +// meant to be subclassed for concrete toolkit implementations. + +class Accelerator { + public: + Accelerator() : key_code_(ui::VKEY_UNKNOWN), modifiers_(0) {} + + Accelerator(ui::KeyboardCode keycode, int modifiers) + : key_code_(keycode), + modifiers_(modifiers) {} + + Accelerator(const Accelerator& accelerator) { + key_code_ = accelerator.key_code_; + modifiers_ = accelerator.modifiers_; + } + + virtual ~Accelerator() {} + + Accelerator& operator=(const Accelerator& accelerator) { + if (this != &accelerator) { + key_code_ = accelerator.key_code_; + modifiers_ = accelerator.modifiers_; + } + return *this; + } + + // We define the < operator so that the KeyboardShortcut can be used as a key + // in a std::map. + bool operator <(const Accelerator& rhs) const { + if (key_code_ != rhs.key_code_) + return key_code_ < rhs.key_code_; + return modifiers_ < rhs.modifiers_; + } + + bool operator ==(const Accelerator& rhs) const { + return (key_code_ == rhs.key_code_) && (modifiers_ == rhs.modifiers_); + } + + bool operator !=(const Accelerator& rhs) const { + return !(*this == rhs); + } + + ui::KeyboardCode GetKeyCode() const { + return key_code_; + } + + int modifiers() const { + return modifiers_; + } + + protected: + // The keycode (VK_...). + ui::KeyboardCode key_code_; + + // The state of the Shift/Ctrl/Alt keys (platform-dependent). + int modifiers_; +}; + +// Since acclerator code is one of the few things that can't be cross platform +// in the chrome UI, separate out just the GetAcceleratorForCommandId() from +// the menu delegates. +class AcceleratorProvider { + public: + // Gets the accelerator for the specified command id. Returns true if the + // command id has a valid accelerator, false otherwise. + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) = 0; + + protected: + virtual ~AcceleratorProvider() {} +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_ACCELERATOR_H_ + diff --git a/ui/base/models/accelerator_cocoa.h b/ui/base/models/accelerator_cocoa.h new file mode 100644 index 0000000..7c3db16 --- /dev/null +++ b/ui/base/models/accelerator_cocoa.h @@ -0,0 +1,59 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_ACCELERATOR_COCOA_H_ +#define UI_BASE_MODELS_ACCELERATOR_COCOA_H_ +#pragma once + +#include <Foundation/Foundation.h> + +#include "base/scoped_nsobject.h" +#include "ui/base/models/accelerator.h" + +namespace ui { + +// This is a subclass of the cross-platform Accelerator, but with more direct +// support for Cocoa key equivalents. Note that the typical use case for this +// class is to initialize it with a string literal, which is why it sends +// |-copy| to the |key_code| paramater in the constructor. +class AcceleratorCocoa : public Accelerator { + public: + AcceleratorCocoa(NSString* key_code, NSUInteger mask) + : Accelerator(ui::VKEY_UNKNOWN, mask), + characters_([key_code copy]) { + } + + AcceleratorCocoa(const AcceleratorCocoa& accelerator) + : Accelerator(accelerator) { + characters_.reset([accelerator.characters_ copy]); + } + + AcceleratorCocoa() : Accelerator() {} + virtual ~AcceleratorCocoa() {} + + AcceleratorCocoa& operator=(const AcceleratorCocoa& accelerator) { + if (this != &accelerator) { + *static_cast<Accelerator*>(this) = accelerator; + characters_.reset([accelerator.characters_ copy]); + } + return *this; + } + + bool operator==(const AcceleratorCocoa& rhs) const { + return [characters_ isEqualToString:rhs.characters_.get()] && + (modifiers_ == rhs.modifiers_); + } + + NSString* characters() const { + return characters_.get(); + } + + private: + // String of characters for the key equivalent. + scoped_nsobject<NSString> characters_; +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_ACCELERATOR_COCOA_H_ diff --git a/ui/base/models/accelerator_gtk.h b/ui/base/models/accelerator_gtk.h new file mode 100644 index 0000000..c38abb2 --- /dev/null +++ b/ui/base/models/accelerator_gtk.h @@ -0,0 +1,59 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_ACCELERATOR_GTK_H_ +#define UI_BASE_MODELS_ACCELERATOR_GTK_H_ +#pragma once + +#include <gdk/gdk.h> + +#include "ui/base/keycodes/keyboard_code_conversion_gtk.h" +#include "ui/base/keycodes/keyboard_codes_posix.h" +#include "ui/base/models/accelerator.h" + +namespace ui { + +class AcceleratorGtk : public Accelerator { + public: + AcceleratorGtk(ui::KeyboardCode key_code, + bool shift_pressed, bool ctrl_pressed, bool alt_pressed) + : gdk_keyval_(0) { + key_code_ = key_code; + modifiers_ = 0; + if (shift_pressed) + modifiers_ |= GDK_SHIFT_MASK; + if (ctrl_pressed) + modifiers_ |= GDK_CONTROL_MASK; + if (alt_pressed) + modifiers_ |= GDK_MOD1_MASK; + } + + AcceleratorGtk(guint keyval, GdkModifierType modifier_type) { + key_code_ = ui::WindowsKeyCodeForGdkKeyCode(keyval); + gdk_keyval_ = keyval; + modifiers_ = modifier_type; + } + + AcceleratorGtk() : gdk_keyval_(0) { } + virtual ~AcceleratorGtk() { } + + guint GetGdkKeyCode() const { + return gdk_keyval_ > 0 ? + // The second parameter is false because accelerator keys are + // expressed in terms of the non-shift-modified key. + gdk_keyval_ : ui::GdkKeyCodeForWindowsKeyCode(key_code_, false); + } + + GdkModifierType gdk_modifier_type() { + return static_cast<GdkModifierType>(modifiers()); + } + + private: + // The GDK keycode. + guint gdk_keyval_; +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_ACCELERATOR_GTK_H_ diff --git a/ui/base/models/button_menu_item_model.cc b/ui/base/models/button_menu_item_model.cc new file mode 100644 index 0000000..0ebb11f --- /dev/null +++ b/ui/base/models/button_menu_item_model.cc @@ -0,0 +1,135 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/models/button_menu_item_model.h" + +#include "app/l10n_util.h" + +namespace ui { + +bool ButtonMenuItemModel::Delegate::IsItemForCommandIdDynamic( + int command_id) const { + return false; +} + +string16 ButtonMenuItemModel::Delegate::GetLabelForCommandId( + int command_id) const { + return string16(); +} + +bool ButtonMenuItemModel::Delegate::IsCommandIdEnabled(int command_id) const { + return true; +} + +bool ButtonMenuItemModel::Delegate::DoesCommandIdDismissMenu( + int command_id) const { + return true; +} + +struct ButtonMenuItemModel::Item { + int command_id; + ButtonType type; + string16 label; + int icon_idr; + bool part_of_group; +}; + +ButtonMenuItemModel::ButtonMenuItemModel( + int string_id, + ButtonMenuItemModel::Delegate* delegate) + : item_label_(l10n_util::GetStringUTF16(string_id)), + delegate_(delegate) { +} + +ButtonMenuItemModel::~ButtonMenuItemModel() { +} + +void ButtonMenuItemModel::AddGroupItemWithStringId( + int command_id, int string_id) { + Item item = { command_id, TYPE_BUTTON, l10n_util::GetStringUTF16(string_id), + -1, true }; + items_.push_back(item); +} + +void ButtonMenuItemModel::AddItemWithImage(int command_id, + int icon_idr) { + Item item = { command_id, TYPE_BUTTON, string16(), icon_idr, false }; + items_.push_back(item); +} + +void ButtonMenuItemModel::AddButtonLabel(int command_id, int string_id) { + Item item = { command_id, TYPE_BUTTON_LABEL, + l10n_util::GetStringUTF16(string_id), -1, false }; + items_.push_back(item); +} + +void ButtonMenuItemModel::AddSpace() { + Item item = { 0, TYPE_SPACE, string16(), -1, false }; + items_.push_back(item); +} + +int ButtonMenuItemModel::GetItemCount() const { + return static_cast<int>(items_.size()); +} + +ButtonMenuItemModel::ButtonType ButtonMenuItemModel::GetTypeAt( + int index) const { + return items_[index].type; +} + +int ButtonMenuItemModel::GetCommandIdAt(int index) const { + return items_[index].command_id; +} + +bool ButtonMenuItemModel::IsItemDynamicAt(int index) const { + if (delegate_) + return delegate_->IsItemForCommandIdDynamic(GetCommandIdAt(index)); + return false; +} + +string16 ButtonMenuItemModel::GetLabelAt(int index) const { + if (IsItemDynamicAt(index)) + return delegate_->GetLabelForCommandId(GetCommandIdAt(index)); + return items_[index].label; +} + +bool ButtonMenuItemModel::GetIconAt(int index, int* icon_idr) const { + if (items_[index].icon_idr == -1) + return false; + + *icon_idr = items_[index].icon_idr; + return true; +} + +bool ButtonMenuItemModel::PartOfGroup(int index) const { + return items_[index].part_of_group; +} + +void ButtonMenuItemModel::ActivatedCommand(int command_id) { + if (delegate_) + delegate_->ExecuteCommand(command_id); +} + +bool ButtonMenuItemModel::IsEnabledAt(int index) const { + return IsCommandIdEnabled(items_[index].command_id); +} + +bool ButtonMenuItemModel::DismissesMenuAt(int index) const { + return DoesCommandIdDismissMenu(items_[index].command_id); +} + +bool ButtonMenuItemModel::IsCommandIdEnabled(int command_id) const { + if (delegate_) + return delegate_->IsCommandIdEnabled(command_id); + return true; +} + +bool ButtonMenuItemModel::DoesCommandIdDismissMenu(int command_id) const { + if (delegate_) + return delegate_->DoesCommandIdDismissMenu(command_id); + return true; +} + + +} // namespace ui diff --git a/ui/base/models/button_menu_item_model.h b/ui/base/models/button_menu_item_model.h new file mode 100644 index 0000000..7c83e5e --- /dev/null +++ b/ui/base/models/button_menu_item_model.h @@ -0,0 +1,113 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_BUTTON_MENU_ITEM_MODEL_H_ +#define UI_BASE_MODELS_BUTTON_MENU_ITEM_MODEL_H_ +#pragma once + +#include <vector> + +#include "base/string16.h" + +namespace ui { + +// A model representing the rows of buttons that should be inserted in a button +// containing menu item. +class ButtonMenuItemModel { + public: + // Types of buttons. + enum ButtonType { + TYPE_SPACE, + TYPE_BUTTON, + TYPE_BUTTON_LABEL + }; + + class Delegate { + public: + // Some command ids have labels that change over time. + virtual bool IsItemForCommandIdDynamic(int command_id) const; + virtual string16 GetLabelForCommandId(int command_id) const; + + // Performs the action associated with the specified command id. + virtual void ExecuteCommand(int command_id) = 0; + virtual bool IsCommandIdEnabled(int command_id) const; + virtual bool DoesCommandIdDismissMenu(int command_id) const; + + protected: + virtual ~Delegate() {} + }; + + ButtonMenuItemModel(int string_id, ButtonMenuItemModel::Delegate* delegate); + ~ButtonMenuItemModel(); + + // Adds a button that will emit |command_id|. All buttons created through + // this method will have the same size, based on the largest button. + void AddGroupItemWithStringId(int command_id, int string_id); + + // Adds a button that has an icon instead of a label. + void AddItemWithImage(int command_id, int icon_idr); + + // Adds a non-clickable button with a desensitized label that doesn't do + // anything. Usually combined with IsItemForCommandIdDynamic() to add + // information. + void AddButtonLabel(int command_id, int string_id); + + // Adds a small horizontal space. + void AddSpace(); + + // Returns the number of items for iteration. + int GetItemCount() const; + + // Returns what kind of item is at |index|. + ButtonType GetTypeAt(int index) const; + + // Changes a position into a command ID. + int GetCommandIdAt(int index) const; + + // Whether the label for item |index| changes. + bool IsItemDynamicAt(int index) const; + + // Returns the current label value for the button at |index|. + string16 GetLabelAt(int index) const; + + // If the button at |index| should have an icon instead, returns true and + // sets the IDR |icon|. + bool GetIconAt(int index, int* icon) const; + + // If the button at |index| should have its size equalized along with all + // other items that have their PartOfGroup bit set. + bool PartOfGroup(int index) const; + + // Called from implementations. + void ActivatedCommand(int command_id); + + // Returns the enabled state of the button at |index|. + bool IsEnabledAt(int index) const; + + // Returns whether clicking on the button at |index| dismisses the menu. + bool DismissesMenuAt(int index) const; + + // Returns the enabled state of the command specified by |command_id|. + bool IsCommandIdEnabled(int command_id) const; + + // Returns whether clicking on |command_id| dismisses the menu. + bool DoesCommandIdDismissMenu(int command_id) const; + + const string16& label() const { return item_label_; } + + private: + // The non-clickable label to the left of the buttons. + string16 item_label_; + + struct Item; + std::vector<Item> items_; + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(ButtonMenuItemModel); +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_BUTTON_MENU_ITEM_MODEL_H_ diff --git a/ui/base/models/combobox_model.h b/ui/base/models/combobox_model.h new file mode 100644 index 0000000..57dd3c3 --- /dev/null +++ b/ui/base/models/combobox_model.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_COMBOBOX_MODEL_H_ +#define UI_BASE_MODELS_COMBOBOX_MODEL_H_ +#pragma once + +#include "base/string16.h" + +namespace ui { + +// The interface for models backing a combobox. +class ComboboxModel { + public: + virtual ~ComboboxModel() {} + + // Return the number of items in the combo box. + virtual int GetItemCount() = 0; + + // Return the string that should be used to represent a given item. + virtual string16 GetItemAt(int index) = 0; +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_COMBOBOX_MODEL_H_ diff --git a/ui/base/models/menu_model.cc b/ui/base/models/menu_model.cc new file mode 100644 index 0000000..c7defd9 --- /dev/null +++ b/ui/base/models/menu_model.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/models/menu_model.h" + +namespace ui { + +int MenuModel::GetFirstItemIndex(gfx::NativeMenu native_menu) const { + return 0; +} + +bool MenuModel::IsVisibleAt(int index) const { + return true; +} + +bool MenuModel::GetModelAndIndexForCommandId(int command_id, + MenuModel** model, int* index) { + int item_count = (*model)->GetItemCount(); + for (int i = 0; i < item_count; ++i) { + if ((*model)->GetTypeAt(i) == TYPE_SUBMENU) { + MenuModel* submenu_model = (*model)->GetSubmenuModelAt(i); + if (GetModelAndIndexForCommandId(command_id, &submenu_model, index)) { + *model = submenu_model; + return true; + } + } + if ((*model)->GetCommandIdAt(i) == command_id) { + *index = i; + return true; + } + } + return false; +} + +const gfx::Font* MenuModel::GetLabelFontAt(int index) const { + return NULL; +} + +// Default implementation ignores the disposition. +void MenuModel::ActivatedAtWithDisposition(int index, int disposition) { + ActivatedAt(index); +} + +} // namespace ui diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h new file mode 100644 index 0000000..108f3b0 --- /dev/null +++ b/ui/base/models/menu_model.h @@ -0,0 +1,125 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_MENU_MODEL_H_ +#define UI_BASE_MODELS_MENU_MODEL_H_ +#pragma once + +#include "base/scoped_ptr.h" +#include "base/string16.h" +#include "gfx/native_widget_types.h" + +class SkBitmap; + +namespace gfx { +class Font; +} + +namespace ui { + +class Accelerator; +class ButtonMenuItemModel; + +// An interface implemented by an object that provides the content of a menu. +class MenuModel { + public: + // The type of item. + enum ItemType { + TYPE_COMMAND, + TYPE_CHECK, + TYPE_RADIO, + TYPE_SEPARATOR, + TYPE_BUTTON_ITEM, + TYPE_SUBMENU + }; + + virtual ~MenuModel() {} + + // Returns true if any of the items within the model have icons. Not all + // platforms support icons in menus natively and so this is a hint for + // triggering a custom rendering mode. + virtual bool HasIcons() const = 0; + + // Returns the index of the first item. This is 0 for most menus except the + // system menu on Windows. |native_menu| is the menu to locate the start index + // within. It is guaranteed to be reset to a clean default state. + // IMPORTANT: If the model implementation returns something _other_ than 0 + // here, it must offset the values for |index| it passes to the + // methods below by this number - this is NOT done automatically! + virtual int GetFirstItemIndex(gfx::NativeMenu native_menu) const; + + // Returns the number of items in the menu. + virtual int GetItemCount() const = 0; + + // Returns the type of item at the specified index. + virtual ItemType GetTypeAt(int index) const = 0; + + // Returns the command id of the item at the specified index. + virtual int GetCommandIdAt(int index) const = 0; + + // Returns the label of the item at the specified index. + virtual string16 GetLabelAt(int index) const = 0; + + // Returns true if the menu item (label/icon) at the specified index can + // change over the course of the menu's lifetime. If this function returns + // true, the label and icon of the menu item will be updated each time the + // menu is shown. + virtual bool IsItemDynamicAt(int index) const = 0; + + // Returns the font use for the label at the specified index. + // If NULL, then use default font. + virtual const gfx::Font* GetLabelFontAt(int index) const; + + // Gets the acclerator information for the specified index, returning true if + // there is a shortcut accelerator for the item, false otherwise. + virtual bool GetAcceleratorAt(int index, + ui::Accelerator* accelerator) const = 0; + + // Returns the checked state of the item at the specified index. + virtual bool IsItemCheckedAt(int index) const = 0; + + // Returns the id of the group of radio items that the item at the specified + // index belongs to. + virtual int GetGroupIdAt(int index) const = 0; + + // Gets the icon for the item at the specified index, returning true if there + // is an icon, false otherwise. + virtual bool GetIconAt(int index, SkBitmap* icon) const = 0; + + // Returns the model for a menu item with a line of buttons at |index|. + virtual ButtonMenuItemModel* GetButtonMenuItemAt(int index) const = 0; + + // Returns the enabled state of the item at the specified index. + virtual bool IsEnabledAt(int index) const = 0; + + // Returns true if the menu item is visible. + virtual bool IsVisibleAt(int index) const; + + // Returns the model for the submenu at the specified index. + virtual MenuModel* GetSubmenuModelAt(int index) const = 0; + + // Called when the highlighted menu item changes to the item at the specified + // index. + virtual void HighlightChangedTo(int index) = 0; + + // Called when the item at the specified index has been activated. + virtual void ActivatedAt(int index) = 0; + + // Called when the item has been activated with a given disposition (for the + // case where the activation involves a navigation). + virtual void ActivatedAtWithDisposition(int index, int disposition); + + // Called when the menu is about to be shown. + virtual void MenuWillShow() {} + + // Retrieves the model and index that contains a specific command id. Returns + // true if an item with the specified command id is found. |model| is inout, + // and specifies the model to start searching from. + static bool GetModelAndIndexForCommandId(int command_id, MenuModel** model, + int* index); +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_MENU_MODEL_H_ diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc new file mode 100644 index 0000000..4552593 --- /dev/null +++ b/ui/base/models/simple_menu_model.cc @@ -0,0 +1,314 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/models/simple_menu_model.h" + +#include "app/l10n_util.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace ui { + +const int kSeparatorId = -1; + +struct SimpleMenuModel::Item { + int command_id; + string16 label; + SkBitmap icon; + ItemType type; + int group_id; + MenuModel* submenu; + ButtonMenuItemModel* button_model; +}; + +//////////////////////////////////////////////////////////////////////////////// +// SimpleMenuModel::Delegate, public: + +bool SimpleMenuModel::Delegate::IsCommandIdVisible(int command_id) const { + return true; +} + +bool SimpleMenuModel::Delegate::IsItemForCommandIdDynamic( + int command_id) const { + return false; +} + +string16 SimpleMenuModel::Delegate::GetLabelForCommandId(int command_id) const { + return string16(); +} + +bool SimpleMenuModel::Delegate::GetIconForCommandId( + int command_id, SkBitmap* bitmap) const { + return false; +} + +void SimpleMenuModel::Delegate::CommandIdHighlighted(int command_id) { +} + +//////////////////////////////////////////////////////////////////////////////// +// SimpleMenuModel, public: + +SimpleMenuModel::SimpleMenuModel(Delegate* delegate) : delegate_(delegate) { +} + +SimpleMenuModel::~SimpleMenuModel() { +} + +void SimpleMenuModel::AddItem(int command_id, const string16& label) { + Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL, NULL }; + AppendItem(item); +} + +void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) { + AddItem(command_id, l10n_util::GetStringUTF16(string_id)); +} + +void SimpleMenuModel::AddSeparator() { + Item item = { kSeparatorId, string16(), SkBitmap(), TYPE_SEPARATOR, -1, + NULL, NULL }; + AppendItem(item); +} + +void SimpleMenuModel::AddCheckItem(int command_id, const string16& label) { + Item item = { command_id, label, SkBitmap(), TYPE_CHECK, -1, NULL }; + AppendItem(item); +} + +void SimpleMenuModel::AddCheckItemWithStringId(int command_id, int string_id) { + AddCheckItem(command_id, l10n_util::GetStringUTF16(string_id)); +} + +void SimpleMenuModel::AddRadioItem(int command_id, const string16& label, + int group_id) { + Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL, + NULL }; + AppendItem(item); +} + +void SimpleMenuModel::AddRadioItemWithStringId(int command_id, int string_id, + int group_id) { + AddRadioItem(command_id, l10n_util::GetStringUTF16(string_id), group_id); +} + +void SimpleMenuModel::AddButtonItem(int command_id, + ButtonMenuItemModel* model) { + Item item = { command_id, string16(), SkBitmap(), TYPE_BUTTON_ITEM, -1, NULL, + model }; + AppendItem(item); +} + +void SimpleMenuModel::AddSubMenu(int command_id, const string16& label, + MenuModel* model) { + Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model, NULL }; + AppendItem(item); +} + +void SimpleMenuModel::AddSubMenuWithStringId(int command_id, + int string_id, MenuModel* model) { + AddSubMenu(command_id, l10n_util::GetStringUTF16(string_id), model); +} + +void SimpleMenuModel::InsertItemAt( + int index, int command_id, const string16& label) { + Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL, NULL }; + InsertItemAtIndex(item, index); +} + +void SimpleMenuModel::InsertItemWithStringIdAt( + int index, int command_id, int string_id) { + InsertItemAt(index, command_id, l10n_util::GetStringUTF16(string_id)); +} + +void SimpleMenuModel::InsertSeparatorAt(int index) { + Item item = { kSeparatorId, string16(), SkBitmap(), TYPE_SEPARATOR, -1, + NULL, NULL }; + InsertItemAtIndex(item, index); +} + +void SimpleMenuModel::InsertCheckItemAt( + int index, int command_id, const string16& label) { + Item item = { command_id, label, SkBitmap(), TYPE_CHECK, -1, NULL, NULL }; + InsertItemAtIndex(item, index); +} + +void SimpleMenuModel::InsertCheckItemWithStringIdAt( + int index, int command_id, int string_id) { + InsertCheckItemAt( + FlipIndex(index), command_id, l10n_util::GetStringUTF16(string_id)); +} + +void SimpleMenuModel::InsertRadioItemAt( + int index, int command_id, const string16& label, int group_id) { + Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL, + NULL }; + InsertItemAtIndex(item, index); +} + +void SimpleMenuModel::InsertRadioItemWithStringIdAt( + int index, int command_id, int string_id, int group_id) { + InsertRadioItemAt( + index, command_id, l10n_util::GetStringUTF16(string_id), group_id); +} + +void SimpleMenuModel::InsertSubMenuAt( + int index, int command_id, const string16& label, MenuModel* model) { + Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model, NULL }; + InsertItemAtIndex(item, index); +} + +void SimpleMenuModel::InsertSubMenuWithStringIdAt( + int index, int command_id, int string_id, MenuModel* model) { + InsertSubMenuAt(index, command_id, l10n_util::GetStringUTF16(string_id), + model); +} + +void SimpleMenuModel::SetIcon(int index, const SkBitmap& icon) { + items_[index].icon = icon; +} + +void SimpleMenuModel::Clear() { + items_.clear(); +} + +int SimpleMenuModel::GetIndexOfCommandId(int command_id) { + std::vector<Item>::iterator itr; + for (itr = items_.begin(); itr != items_.end(); itr++) { + if ((*itr).command_id == command_id) { + return FlipIndex(static_cast<int>(std::distance(items_.begin(), itr))); + } + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +// SimpleMenuModel, MenuModel implementation: + +bool SimpleMenuModel::HasIcons() const { + for (std::vector<Item>::const_iterator iter = items_.begin(); + iter != items_.end(); ++iter) { + if (!iter->icon.isNull()) + return true; + } + + return false; +} + +int SimpleMenuModel::GetItemCount() const { + return static_cast<int>(items_.size()); +} + +MenuModel::ItemType SimpleMenuModel::GetTypeAt(int index) const { + return items_.at(FlipIndex(index)).type; +} + +int SimpleMenuModel::GetCommandIdAt(int index) const { + return items_.at(FlipIndex(index)).command_id; +} + +string16 SimpleMenuModel::GetLabelAt(int index) const { + if (IsItemDynamicAt(index)) + return delegate_->GetLabelForCommandId(GetCommandIdAt(index)); + return items_.at(FlipIndex(index)).label; +} + +bool SimpleMenuModel::IsItemDynamicAt(int index) const { + if (delegate_) + return delegate_->IsItemForCommandIdDynamic(GetCommandIdAt(index)); + return false; +} + +bool SimpleMenuModel::GetAcceleratorAt(int index, + ui::Accelerator* accelerator) const { + if (delegate_) { + return delegate_->GetAcceleratorForCommandId(GetCommandIdAt(index), + accelerator); + } + return false; +} + +bool SimpleMenuModel::IsItemCheckedAt(int index) const { + if (!delegate_) + return false; + int item_index = FlipIndex(index); + MenuModel::ItemType item_type = items_[item_index].type; + return (item_type == TYPE_CHECK || item_type == TYPE_RADIO) ? + delegate_->IsCommandIdChecked(GetCommandIdAt(index)) : false; +} + +int SimpleMenuModel::GetGroupIdAt(int index) const { + return items_.at(FlipIndex(index)).group_id; +} + +bool SimpleMenuModel::GetIconAt(int index, SkBitmap* icon) const { + if (IsItemDynamicAt(index)) + return delegate_->GetIconForCommandId(GetCommandIdAt(index), icon); + + if (items_[index].icon.isNull()) + return false; + + *icon = items_[index].icon; + return true; +} + +ButtonMenuItemModel* SimpleMenuModel::GetButtonMenuItemAt(int index) const { + return items_.at(FlipIndex(index)).button_model; +} + +bool SimpleMenuModel::IsEnabledAt(int index) const { + int command_id = GetCommandIdAt(index); + if (!delegate_ || command_id == kSeparatorId || + items_.at(FlipIndex(index)).button_model) + return true; + return delegate_->IsCommandIdEnabled(command_id); +} + +bool SimpleMenuModel::IsVisibleAt(int index) const { + int command_id = GetCommandIdAt(index); + if (!delegate_ || command_id == kSeparatorId || + items_.at(FlipIndex(index)).button_model) + return true; + return delegate_->IsCommandIdVisible(command_id); +} + +void SimpleMenuModel::HighlightChangedTo(int index) { + if (delegate_) + delegate_->CommandIdHighlighted(GetCommandIdAt(index)); +} + +void SimpleMenuModel::ActivatedAt(int index) { + if (delegate_) + delegate_->ExecuteCommand(GetCommandIdAt(index)); +} + +MenuModel* SimpleMenuModel::GetSubmenuModelAt(int index) const { + return items_.at(FlipIndex(index)).submenu; +} + +int SimpleMenuModel::FlipIndex(int index) const { + return index; +} + +//////////////////////////////////////////////////////////////////////////////// +// SimpleMenuModel, Private: + +void SimpleMenuModel::AppendItem(const Item& item) { + ValidateItem(item); + items_.push_back(item); +} + +void SimpleMenuModel::InsertItemAtIndex(const Item& item, int index) { + ValidateItem(item); + items_.insert(items_.begin() + FlipIndex(index), item); +} + +void SimpleMenuModel::ValidateItem(const Item& item) { +#ifndef NDEBUG + if (item.type == TYPE_SEPARATOR) { + DCHECK_EQ(item.command_id, kSeparatorId); + } else { + DCHECK_GE(item.command_id, 0); + } +#endif // NDEBUG +} + +} // namespace ui diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h new file mode 100644 index 0000000..e500e7d --- /dev/null +++ b/ui/base/models/simple_menu_model.h @@ -0,0 +1,146 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_SIMPLE_MENU_MODEL_H_ +#define UI_BASE_MODELS_SIMPLE_MENU_MODEL_H_ +#pragma once + +#include <vector> + +#include "base/string16.h" +#include "ui/base/models/menu_model.h" + +namespace ui { + +class ButtonMenuItemModel; + +// A simple MenuModel implementation with an imperative API for adding menu +// items. This makes it easy to construct fixed menus. Menus populated by +// dynamic data sources may be better off implementing MenuModel directly. +// The breadth of MenuModel is not exposed through this API. +class SimpleMenuModel : public MenuModel { + public: + class Delegate { + public: + // Methods for determining the state of specific command ids. + virtual bool IsCommandIdChecked(int command_id) const = 0; + virtual bool IsCommandIdEnabled(int command_id) const = 0; + virtual bool IsCommandIdVisible(int command_id) const; + + // Gets the accelerator for the specified command id. Returns true if the + // command id has a valid accelerator, false otherwise. + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) = 0; + + // Some command ids have labels and icons that change over time. + virtual bool IsItemForCommandIdDynamic(int command_id) const; + virtual string16 GetLabelForCommandId(int command_id) const; + // Gets the icon for the item with the specified id, returning true if there + // is an icon, false otherwise. + virtual bool GetIconForCommandId(int command_id, SkBitmap* icon) const; + + // Notifies the delegate that the item with the specified command id was + // visually highlighted within the menu. + virtual void CommandIdHighlighted(int command_id); + + // Performs the action associated with the specified command id. + virtual void ExecuteCommand(int command_id) = 0; + + protected: + virtual ~Delegate() {} + }; + + // The Delegate can be NULL, though if it is items can't be checked or + // disabled. + explicit SimpleMenuModel(Delegate* delegate); + virtual ~SimpleMenuModel(); + + // Methods for adding items to the model. + void AddItem(int command_id, const string16& label); + void AddItemWithStringId(int command_id, int string_id); + void AddSeparator(); + void AddCheckItem(int command_id, const string16& label); + void AddCheckItemWithStringId(int command_id, int string_id); + void AddRadioItem(int command_id, const string16& label, int group_id); + void AddRadioItemWithStringId(int command_id, int string_id, int group_id); + + // These three methods take pointers to various sub-models. These models + // should be owned by the same owner of this SimpleMenuModel. + void AddButtonItem(int command_id, ButtonMenuItemModel* model); + void AddSubMenu(int command_id, const string16& label, MenuModel* model); + void AddSubMenuWithStringId(int command_id, int string_id, MenuModel* model); + + // Methods for inserting items into the model. + void InsertItemAt(int index, int command_id, const string16& label); + void InsertItemWithStringIdAt(int index, int command_id, int string_id); + void InsertSeparatorAt(int index); + void InsertCheckItemAt(int index, int command_id, const string16& label); + void InsertCheckItemWithStringIdAt(int index, int command_id, int string_id); + void InsertRadioItemAt( + int index, int command_id, const string16& label, int group_id); + void InsertRadioItemWithStringIdAt( + int index, int command_id, int string_id, int group_id); + void InsertSubMenuAt( + int index, int command_id, const string16& label, MenuModel* model); + void InsertSubMenuWithStringIdAt( + int index, int command_id, int string_id, MenuModel* model); + + // Sets the icon for the item at |index|. + void SetIcon(int index, const SkBitmap& icon); + + // Clears all items. Note that it does not free MenuModel of submenu. + void Clear(); + + // Returns the index of the item that has the given |command_id|. Returns + // -1 if not found. + int GetIndexOfCommandId(int command_id); + + // Overridden from MenuModel: + virtual bool HasIcons() const; + virtual int GetItemCount() const; + virtual ItemType GetTypeAt(int index) const; + virtual int GetCommandIdAt(int index) const; + virtual string16 GetLabelAt(int index) const; + virtual bool IsItemDynamicAt(int index) const; + virtual bool GetAcceleratorAt(int index, + ui::Accelerator* accelerator) const; + virtual bool IsItemCheckedAt(int index) const; + virtual int GetGroupIdAt(int index) const; + virtual bool GetIconAt(int index, SkBitmap* icon) const; + virtual ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const; + virtual bool IsEnabledAt(int index) const; + virtual bool IsVisibleAt(int index) const; + virtual void HighlightChangedTo(int index); + virtual void ActivatedAt(int index); + virtual MenuModel* GetSubmenuModelAt(int index) const; + + protected: + // Some variants of this model (SystemMenuModel) relies on items to be + // inserted backwards. This is counter-intuitive for the API, so rather than + // forcing customers to insert things backwards, we return the indices + // backwards instead. That's what this method is for. By default, it just + // returns what it's passed. + virtual int FlipIndex(int index) const; + + Delegate* delegate() { return delegate_; } + + private: + struct Item; + + // Functions for inserting items into |items_|. + void AppendItem(const Item& item); + void InsertItemAtIndex(const Item& item, int index); + void ValidateItem(const Item& item); + + std::vector<Item> items_; + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(SimpleMenuModel); +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_SIMPLE_MENU_MODEL_H_ diff --git a/ui/base/models/table_model.cc b/ui/base/models/table_model.cc new file mode 100644 index 0000000..f9d9877 --- /dev/null +++ b/ui/base/models/table_model.cc @@ -0,0 +1,138 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/models/table_model.h" + +#include "app/l10n_util.h" +#include "app/l10n_util_collator.h" +#include "base/logging.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace ui { + +// TableColumn ----------------------------------------------------------------- + +TableColumn::TableColumn() + : id(0), + title(), + alignment(LEFT), + width(-1), + percent(), + min_visible_width(0), + sortable(false) { +} + +TableColumn::TableColumn(int id, const string16& title, + Alignment alignment, + int width) + : id(id), + title(title), + alignment(alignment), + width(width), + percent(0), + min_visible_width(0), + sortable(false) { +} + +TableColumn::TableColumn(int id, const string16& title, + Alignment alignment, int width, float percent) + : id(id), + title(title), + alignment(alignment), + width(width), + percent(percent), + min_visible_width(0), + sortable(false) { +} + +// It's common (but not required) to use the title's IDS_* tag as the column +// id. In this case, the provided conveniences look up the title string on +// bahalf of the caller. +TableColumn::TableColumn(int id, Alignment alignment, int width) + : id(id), + alignment(alignment), + width(width), + percent(0), + min_visible_width(0), + sortable(false) { + title = l10n_util::GetStringUTF16(id); +} + +TableColumn::TableColumn(int id, Alignment alignment, int width, float percent) + : id(id), + alignment(alignment), + width(width), + percent(percent), + min_visible_width(0), + sortable(false) { + title = l10n_util::GetStringUTF16(id); +} + +// TableModel ----------------------------------------------------------------- + +// Used for sorting. +static icu::Collator* collator = NULL; + +SkBitmap TableModel::GetIcon(int row) { + return SkBitmap(); +} + +string16 TableModel::GetTooltip(int row) { + return string16(); +} + +bool TableModel::ShouldIndent(int row) { + return false; +} + +bool TableModel::HasGroups() { + return false; +} + +TableModel::Groups TableModel::GetGroups() { + // If you override HasGroups to return true, you must override this as + // well. + NOTREACHED(); + return std::vector<Group>(); +} + +int TableModel::GetGroupID(int row) { + // If you override HasGroups to return true, you must override this as + // well. + NOTREACHED(); + return 0; +} + +int TableModel::CompareValues(int row1, int row2, int column_id) { + DCHECK(row1 >= 0 && row1 < RowCount() && + row2 >= 0 && row2 < RowCount()); + string16 value1 = GetText(row1, column_id); + string16 value2 = GetText(row2, column_id); + icu::Collator* collator = GetCollator(); + + if (collator) + return l10n_util::CompareString16WithCollator(collator, value1, value2); + + NOTREACHED(); + return 0; +} + +void TableModel::ClearCollator() { + delete collator; + collator = NULL; +} + +icu::Collator* TableModel::GetCollator() { + if (!collator) { + UErrorCode create_status = U_ZERO_ERROR; + collator = icu::Collator::createInstance(create_status); + if (!U_SUCCESS(create_status)) { + collator = NULL; + NOTREACHED(); + } + } + return collator; +} + +} // namespace ui diff --git a/ui/base/models/table_model.h b/ui/base/models/table_model.h new file mode 100644 index 0000000..b9f02c2 --- /dev/null +++ b/ui/base/models/table_model.h @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_TABLE_MODEL_H_ +#define UI_BASE_MODELS_TABLE_MODEL_H_ +#pragma once + +#include <vector> + +#include "base/string16.h" +#include "unicode/coll.h" + +class SkBitmap; + +namespace ui { + +class TableModelObserver; + +// The model driving the TableView. +class TableModel { + public: + // See HasGroups, get GetGroupID for details as to how this is used. + struct Group { + // The title text for the group. + string16 title; + + // Unique id for the group. + int id; + }; + typedef std::vector<Group> Groups; + + // Number of rows in the model. + virtual int RowCount() = 0; + + // Returns the value at a particular location in text. + virtual string16 GetText(int row, int column_id) = 0; + + // Returns the small icon (16x16) that should be displayed in the first + // column before the text. This is only used when the TableView was created + // with the ICON_AND_TEXT table type. Returns an isNull() bitmap if there is + // no bitmap. + virtual SkBitmap GetIcon(int row); + + // Returns the tooltip, if any, to show for a particular row. If there are + // multiple columns in the row, this will only be shown when hovering over + // column zero. + virtual string16 GetTooltip(int row); + + // If true, this row should be indented. + virtual bool ShouldIndent(int row); + + // Returns true if the TableView has groups. Groups provide a way to visually + // delineate the rows in a table view. When groups are enabled table view + // shows a visual separator for each group, followed by all the rows in + // the group. + // + // On win2k a visual separator is not rendered for the group headers. + virtual bool HasGroups(); + + // Returns the groups. + // This is only used if HasGroups returns true. + virtual Groups GetGroups(); + + // Returns the group id of the specified row. + // This is only used if HasGroups returns true. + virtual int GetGroupID(int row); + + // Sets the observer for the model. The TableView should NOT take ownership + // of the observer. + virtual void SetObserver(TableModelObserver* observer) = 0; + + // Compares the values in the column with id |column_id| for the two rows. + // Returns a value < 0, == 0 or > 0 as to whether the first value is + // <, == or > the second value. + // + // This implementation does a case insensitive locale specific string + // comparison. + virtual int CompareValues(int row1, int row2, int column_id); + + // Reset the collator. + void ClearCollator(); + + protected: + virtual ~TableModel() {} + + // Returns the collator used by CompareValues. + icu::Collator* GetCollator(); +}; + +// TableColumn specifies the title, alignment and size of a particular column. +struct TableColumn { + enum Alignment { + LEFT, RIGHT, CENTER + }; + + TableColumn(); + TableColumn(int id, const string16& title, + Alignment alignment, int width); + TableColumn(int id, const string16& title, + Alignment alignment, int width, float percent); + + // It's common (but not required) to use the title's IDS_* tag as the column + // id. In this case, the provided conveniences look up the title string on + // bahalf of the caller. + TableColumn(int id, Alignment alignment, int width); + TableColumn(int id, Alignment alignment, int width, float percent); + + // A unique identifier for the column. + int id; + + // The title for the column. + string16 title; + + // Alignment for the content. + Alignment alignment; + + // The size of a column may be specified in two ways: + // 1. A fixed width. Set the width field to a positive number and the + // column will be given that width, in pixels. + // 2. As a percentage of the available width. If width is -1, and percent is + // > 0, the column is given a width of + // available_width * percent / total_percent. + // 3. If the width == -1 and percent == 0, the column is autosized based on + // the width of the column header text. + // + // Sizing is done in four passes. Fixed width columns are given + // their width, percentages are applied, autosized columns are autosized, + // and finally percentages are applied again taking into account the widths + // of autosized columns. + int width; + float percent; + + // The minimum width required for all items in this column + // (including the header) + // to be visible. + int min_visible_width; + + // Is this column sortable? Default is false + bool sortable; +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_TABLE_MODEL_H_ diff --git a/ui/base/models/table_model_observer.h b/ui/base/models/table_model_observer.h new file mode 100644 index 0000000..c70e89b --- /dev/null +++ b/ui/base/models/table_model_observer.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_TABLE_MODEL_OBSERVER_H_ +#define UI_BASE_MODELS_TABLE_MODEL_OBSERVER_H_ +#pragma once + +namespace ui { + +// Observer for a TableModel. Anytime the model changes, it must notify its +// observer. +class TableModelObserver { + public: + // Invoked when the model has been completely changed. + virtual void OnModelChanged() = 0; + + // Invoked when a range of items has changed. + virtual void OnItemsChanged(int start, int length) = 0; + + // Invoked when new items are added. + virtual void OnItemsAdded(int start, int length) = 0; + + // Invoked when a range of items has been removed. + virtual void OnItemsRemoved(int start, int length) = 0; + + protected: + virtual ~TableModelObserver() {} +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_TABLE_MODEL_OBSERVER_H_ diff --git a/ui/base/models/tree_model.cc b/ui/base/models/tree_model.cc new file mode 100644 index 0000000..cb924a5 --- /dev/null +++ b/ui/base/models/tree_model.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/models/tree_model.h" + +#include "base/logging.h" + +namespace ui { + +void TreeModel::SetTitle(TreeModelNode* node, + const string16& title) { + NOTREACHED(); +} + +int TreeModel::GetIconIndex(TreeModelNode* node) { + return -1; +} + +} // namespace ui diff --git a/ui/base/models/tree_model.h b/ui/base/models/tree_model.h new file mode 100644 index 0000000..c133c0b --- /dev/null +++ b/ui/base/models/tree_model.h @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_TREE_MODEL_H_ +#define UI_BASE_MODELS_TREE_MODEL_H_ +#pragma once + +#include <vector> + +#include "base/string16.h" + +class SkBitmap; + +namespace ui { + +class TreeModel; + +// TreeModelNode -------------------------------------------------------------- + +// Type of class returned from the model. +class TreeModelNode { + public: + // Returns the title for the node. + virtual const string16& GetTitle() const = 0; + + protected: + virtual ~TreeModelNode() {} +}; + +// Observer for the TreeModel. Notified of significant events to the model. +class TreeModelObserver { + public: + // Notification that nodes were added to the specified parent. + virtual void TreeNodesAdded(TreeModel* model, + TreeModelNode* parent, + int start, + int count) = 0; + + // Notification that nodes were removed from the specified parent. + virtual void TreeNodesRemoved(TreeModel* model, + TreeModelNode* parent, + int start, + int count) = 0; + + // Notification that the contents of a node has changed. + virtual void TreeNodeChanged(TreeModel* model, TreeModelNode* node) = 0; + + protected: + virtual ~TreeModelObserver() {} +}; + +// TreeModel ------------------------------------------------------------------ + +// The model for TreeView. +class TreeModel { + public: + // Returns the root of the tree. This may or may not be shown in the tree, + // see SetRootShown for details. + virtual TreeModelNode* GetRoot() = 0; + + // Returns the number of children in the specified node. + virtual int GetChildCount(TreeModelNode* parent) = 0; + + // Returns the child node at the specified index. + virtual TreeModelNode* GetChild(TreeModelNode* parent, int index) = 0; + + // Returns the index of child node at the specified index. + virtual int IndexOfChild(TreeModelNode* parent, TreeModelNode* child) = 0; + + // Returns the parent of a node, or NULL if node is the root. + virtual TreeModelNode* GetParent(TreeModelNode* node) = 0; + + // Adds an observer of the model. + virtual void AddObserver(TreeModelObserver* observer) = 0; + + // Removes an observer of the model. + virtual void RemoveObserver(TreeModelObserver* observer) = 0; + + // Sets the title of the specified node. + // This is only invoked if the node is editable and the user edits a node. + virtual void SetTitle(TreeModelNode* node, const string16& title); + + // Returns the set of icons for the nodes in the tree. You only need override + // this if you don't want to use the default folder icons. + virtual void GetIcons(std::vector<SkBitmap>* icons) {} + + // Returns the index of the icon to use for |node|. Return -1 to use the + // default icon. The index is relative to the list of icons returned from + // GetIcons. + virtual int GetIconIndex(TreeModelNode* node); + + protected: + virtual ~TreeModel() {} +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_TREE_MODEL_H_ diff --git a/ui/base/models/tree_node_iterator.h b/ui/base/models/tree_node_iterator.h new file mode 100644 index 0000000..63074ea --- /dev/null +++ b/ui/base/models/tree_node_iterator.h @@ -0,0 +1,75 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_TREE_NODE_ITERATOR_H_ +#define UI_BASE_MODELS_TREE_NODE_ITERATOR_H_ +#pragma once + +#include <stack> + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace ui { + +// Iterator that iterates over the descendants of a node. The iteration does +// not include the node itself, only the descendants. The following illustrates +// typical usage: +// while (iterator.has_next()) { +// Node* node = iterator.Next(); +// // do something with node. +// } +template <class NodeType> +class TreeNodeIterator { + public: + explicit TreeNodeIterator(NodeType* node) { + if (node->GetChildCount() > 0) + positions_.push(Position<NodeType>(node, 0)); + } + + // Returns true if there are more descendants. + bool has_next() const { return !positions_.empty(); } + + // Returns the next descendant. + NodeType* Next() { + if (!has_next()) { + NOTREACHED(); + return NULL; + } + + NodeType* result = positions_.top().node->GetChild(positions_.top().index); + + // Make sure we don't attempt to visit result again. + positions_.top().index++; + + // Iterate over result's children. + positions_.push(Position<NodeType>(result, 0)); + + // Advance to next position. + while (!positions_.empty() && positions_.top().index >= + positions_.top().node->GetChildCount()) { + positions_.pop(); + } + + return result; + } + + private: + template <class PositionNodeType> + struct Position { + Position(PositionNodeType* node, int index) : node(node), index(index) {} + Position() : node(NULL), index(-1) {} + + PositionNodeType* node; + int index; + }; + + std::stack<Position<NodeType> > positions_; + + DISALLOW_COPY_AND_ASSIGN(TreeNodeIterator); +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_TREE_NODE_ITERATOR_H_ diff --git a/ui/base/models/tree_node_iterator_unittest.cc b/ui/base/models/tree_node_iterator_unittest.cc new file mode 100644 index 0000000..2913bd5 --- /dev/null +++ b/ui/base/models/tree_node_iterator_unittest.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" + +#include "ui/base/models/tree_node_iterator.h" +#include "ui/base/models/tree_node_model.h" + +namespace ui { + +TEST(TreeNodeIteratorTest, Test) { + TreeNodeWithValue<int> root; + root.Add(0, new TreeNodeWithValue<int>(1)); + root.Add(1, new TreeNodeWithValue<int>(2)); + TreeNodeWithValue<int>* f3 = new TreeNodeWithValue<int>(3); + root.Add(2, f3); + TreeNodeWithValue<int>* f4 = new TreeNodeWithValue<int>(4); + f3->Add(0, f4); + f4->Add(0, new TreeNodeWithValue<int>(5)); + + TreeNodeIterator<TreeNodeWithValue<int> > iterator(&root); + ASSERT_TRUE(iterator.has_next()); + ASSERT_EQ(root.GetChild(0), iterator.Next()); + + ASSERT_TRUE(iterator.has_next()); + ASSERT_EQ(root.GetChild(1), iterator.Next()); + + ASSERT_TRUE(iterator.has_next()); + ASSERT_EQ(root.GetChild(2), iterator.Next()); + + ASSERT_TRUE(iterator.has_next()); + ASSERT_EQ(f4, iterator.Next()); + + ASSERT_TRUE(iterator.has_next()); + ASSERT_EQ(f4->GetChild(0), iterator.Next()); + + ASSERT_FALSE(iterator.has_next()); +} + +} // namespace ui diff --git a/ui/base/models/tree_node_model.h b/ui/base/models/tree_node_model.h new file mode 100644 index 0000000..0c64625 --- /dev/null +++ b/ui/base/models/tree_node_model.h @@ -0,0 +1,319 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_MODELS_TREE_NODE_MODEL_H_ +#define UI_BASE_MODELS_TREE_NODE_MODEL_H_ +#pragma once + +#include <algorithm> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/observer_list.h" +#include "base/scoped_ptr.h" +#include "base/scoped_vector.h" +#include "base/stl_util-inl.h" +#include "base/string16.h" +#include "ui/base/models/tree_model.h" + +namespace ui { + +// TreeNodeModel and TreeNodes provide an implementation of TreeModel around +// TreeNodes. TreeNodes form a directed acyclic graph. +// +// TreeNodes own their children, so that deleting a node deletes all +// descendants. +// +// TreeNodes do NOT maintain a pointer back to the model. As such, if you +// are using TreeNodes with a TreeNodeModel you will need to notify the observer +// yourself any time you make any change directly to the TreeNodes. For example, +// if you directly invoke SetTitle on a node it does not notify the +// observer, you will need to do it yourself. This includes the following +// methods: SetTitle, Remove and Add. TreeNodeModel provides cover +// methods that mutate the TreeNodes and notify the observer. If you are using +// TreeNodes with a TreeNodeModel use the cover methods to save yourself the +// headache. +// +// The following example creates a TreeNode with two children and then +// creates a TreeNodeModel from it: +// +// TreeNodeWithValue<int> root = +// new TreeNodeWithValue<int>(ASCIIToUTF16("root"), 0); +// root.add(new TreeNodeWithValue<int>(ASCIIToUTF16("child 1"), 1)); +// root.add(new TreeNodeWithValue<int>(ASCIIToUTF16("child 2"), 1)); +// TreeNodeModel<TreeNodeWithValue<int>>* model = +// new TreeNodeModel<TreeNodeWithValue<int>>(root); +// +// Two variants of TreeNode are provided here: +// +// . TreeNode itself is intended for subclassing. It has one type parameter +// that corresponds to the type of the node. When subclassing use your class +// name as the type parameter, eg: +// class MyTreeNode : public TreeNode<MyTreeNode> . +// . TreeNodeWithValue is a trivial subclass of TreeNode that has one type +// type parameter: a value type that is associated with the node. +// +// Which you use depends upon the situation. If you want to subclass and add +// methods, then use TreeNode. If you don't need any extra methods and just +// want to associate a value with each node, then use TreeNodeWithValue. +// +// Regardless of which TreeNode you use, if you are using the nodes with a +// TreeView take care to notify the observer when mutating the nodes. + +template <class NodeType> +class TreeNodeModel; + +// TreeNode ------------------------------------------------------------------- + +template <class NodeType> +class TreeNode : public TreeModelNode { + public: + TreeNode() : parent_(NULL) { } + + explicit TreeNode(const string16& title) + : title_(title), parent_(NULL) {} + + virtual ~TreeNode() { + } + + // Adds the specified child node. + virtual void Add(int index, NodeType* child) { + DCHECK(child); + DCHECK_LE(0, index); + DCHECK_GE(GetChildCount(), index); + // If the node has a parent, remove it from its parent. + NodeType* node_parent = child->GetParent(); + if (node_parent) + node_parent->Remove(node_parent->IndexOfChild(child)); + child->parent_ = static_cast<NodeType*>(this); + children_->insert(children_->begin() + index, child); + } + + // Removes the node by index. This does NOT delete the specified node, it is + // up to the caller to delete it when done. + virtual NodeType* Remove(int index) { + DCHECK(index >= 0 && index < GetChildCount()); + NodeType* node = GetChild(index); + node->parent_ = NULL; + children_->erase(index + children_->begin()); + return node; + } + + // Removes all the children from this node. This does NOT delete the nodes. + void RemoveAll() { + for (size_t i = 0; i < children_->size(); ++i) + children_[i]->parent_ = NULL; + children_->clear(); + } + + // Returns the number of children. + int GetChildCount() const { + return static_cast<int>(children_->size()); + } + + // Returns the number of all nodes in teh subtree rooted at this node, + // including this node. + int GetTotalNodeCount() const { + int count = 1; // Start with one to include the node itself. + for (size_t i = 0; i < children_->size(); ++i) { + const TreeNode<NodeType>* child = children_[i]; + count += child->GetTotalNodeCount(); + } + return count; + } + + // Returns a child by index. + NodeType* GetChild(int index) { + DCHECK(index >= 0 && index < GetChildCount()); + return children_[index]; + } + const NodeType* GetChild(int index) const { + DCHECK_LE(0, index); + DCHECK_GT(GetChildCount(), index); + return children_[index]; + } + + // Returns the parent. + NodeType* GetParent() { + return parent_; + } + const NodeType* GetParent() const { + return parent_; + } + + // Returns the index of the specified child, or -1 if node is a not a child. + int IndexOfChild(const NodeType* node) const { + DCHECK(node); + typename std::vector<NodeType*>::const_iterator i = + std::find(children_->begin(), children_->end(), node); + if (i != children_->end()) + return static_cast<int>(i - children_->begin()); + return -1; + } + + // Sets the title of the node. + void SetTitle(const string16& string) { + title_ = string; + } + + // Returns the title of the node. + virtual const string16& GetTitle() const { + return title_; + } + + // Returns true if this is the root. + bool IsRoot() const { return (parent_ == NULL); } + + // Returns true if this == ancestor, or one of this nodes parents is + // ancestor. + bool HasAncestor(const NodeType* ancestor) const { + if (ancestor == this) + return true; + if (!ancestor) + return false; + return parent_ ? parent_->HasAncestor(ancestor) : false; + } + + protected: + std::vector<NodeType*>& children() { return children_.get(); } + + private: + // Title displayed in the tree. + string16 title_; + + NodeType* parent_; + + // Children. + ScopedVector<NodeType> children_; + + DISALLOW_COPY_AND_ASSIGN(TreeNode); +}; + +// TreeNodeWithValue ---------------------------------------------------------- + +template <class ValueType> +class TreeNodeWithValue : public TreeNode< TreeNodeWithValue<ValueType> > { + private: + typedef TreeNode< TreeNodeWithValue<ValueType> > ParentType; + + public: + TreeNodeWithValue() { } + + explicit TreeNodeWithValue(const ValueType& value) + : ParentType(string16()), value(value) { } + + TreeNodeWithValue(const string16& title, const ValueType& value) + : ParentType(title), value(value) { } + + ValueType value; + + private: + DISALLOW_COPY_AND_ASSIGN(TreeNodeWithValue); +}; + +// TreeNodeModel -------------------------------------------------------------- + +// TreeModel implementation intended to be used with TreeNodes. +template <class NodeType> +class TreeNodeModel : public TreeModel { + public: + // Creates a TreeNodeModel with the specified root node. The root is owned + // by the TreeNodeModel. + explicit TreeNodeModel(NodeType* root) + : root_(root) { + } + + virtual ~TreeNodeModel() {} + + // Observer methods, calls into ObserverList. + virtual void AddObserver(TreeModelObserver* observer) { + observer_list_.AddObserver(observer); + } + + virtual void RemoveObserver(TreeModelObserver* observer) { + observer_list_.RemoveObserver(observer); + } + + // TreeModel methods, all forward to the nodes. + virtual NodeType* GetRoot() { return root_.get(); } + + virtual int GetChildCount(TreeModelNode* parent) { + DCHECK(parent); + return AsNode(parent)->GetChildCount(); + } + + virtual NodeType* GetChild(TreeModelNode* parent, int index) { + DCHECK(parent); + return AsNode(parent)->GetChild(index); + } + + virtual int IndexOfChild(TreeModelNode* parent, TreeModelNode* child) { + DCHECK(parent); + return AsNode(parent)->IndexOfChild(AsNode(child)); + } + + virtual TreeModelNode* GetParent(TreeModelNode* node) { + DCHECK(node); + return AsNode(node)->GetParent(); + } + + NodeType* AsNode(TreeModelNode* model_node) { + return static_cast<NodeType*>(model_node); + } + + // Sets the title of the specified node. + virtual void SetTitle(TreeModelNode* node, + const string16& title) { + DCHECK(node); + AsNode(node)->SetTitle(title); + NotifyObserverTreeNodeChanged(node); + } + + void Add(NodeType* parent, int index, NodeType* child) { + DCHECK(parent && child); + parent->Add(index, child); + NotifyObserverTreeNodesAdded(parent, index, 1); + } + + NodeType* Remove(NodeType* parent, int index) { + DCHECK(parent); + NodeType* child = parent->Remove(index); + NotifyObserverTreeNodesRemoved(parent, index, 1); + return child; + } + + void NotifyObserverTreeNodesAdded(NodeType* parent, int start, int count) { + FOR_EACH_OBSERVER(TreeModelObserver, + observer_list_, + TreeNodesAdded(this, parent, start, count)); + } + + void NotifyObserverTreeNodesRemoved(NodeType* parent, int start, int count) { + FOR_EACH_OBSERVER(TreeModelObserver, + observer_list_, + TreeNodesRemoved(this, parent, start, count)); + } + + virtual void NotifyObserverTreeNodeChanged(TreeModelNode* node) { + FOR_EACH_OBSERVER(TreeModelObserver, + observer_list_, + TreeNodeChanged(this, node)); + } + + protected: + ObserverList<TreeModelObserver>& observer_list() { return observer_list_; } + + private: + // The observers. + ObserverList<TreeModelObserver> observer_list_; + // The root. + scoped_ptr<NodeType> root_; + + DISALLOW_COPY_AND_ASSIGN(TreeNodeModel); +}; + +} // namespace ui + +#endif // UI_BASE_MODELS_TREE_NODE_MODEL_H_ diff --git a/ui/base/models/tree_node_model_unittest.cc b/ui/base/models/tree_node_model_unittest.cc new file mode 100644 index 0000000..c4747d4 --- /dev/null +++ b/ui/base/models/tree_node_model_unittest.cc @@ -0,0 +1,295 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string16.h" +#include "base/string_number_conversions.h" +#include "base/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/models/tree_node_model.h" + +namespace ui { + +class TreeNodeModelTest : public testing::Test, public TreeModelObserver { + public: + TreeNodeModelTest() + : added_count_(0), + removed_count_(0), + changed_count_(0) {} + + void AssertObserverCount(int added_count, int removed_count, + int changed_count) { + ASSERT_EQ(added_count, added_count_); + ASSERT_EQ(removed_count, removed_count_); + ASSERT_EQ(changed_count, changed_count_); + } + + void ClearCounts() { + added_count_ = removed_count_ = changed_count_ = 0; + } + + // Begin TreeModelObserver implementation. + virtual void TreeNodesAdded(TreeModel* model, TreeModelNode* parent, + int start, int count) { + added_count_++; + } + virtual void TreeNodesRemoved(TreeModel* model, TreeModelNode* parent, + int start, int count) { + removed_count_++; + } + virtual void TreeNodeChanged(TreeModel* model, TreeModelNode* node) { + changed_count_++; + } + // End TreeModelObserver implementation. + + private: + int added_count_; + int removed_count_; + int changed_count_; + + DISALLOW_COPY_AND_ASSIGN(TreeNodeModelTest); +}; + +// Verify if the model is properly adding a new node in the tree and +// notifying the observers. +// The tree looks like this: +// root +// |-- child1 +// | |-- foo1 +// | |-- foo2 +// +-- child2 +TEST_F(TreeNodeModelTest, AddNode) { + TreeNodeWithValue<int>* root = + new TreeNodeWithValue<int>(ASCIIToUTF16("root"), 0); + TreeNodeModel<TreeNodeWithValue<int> > model(root); + model.AddObserver(this); + ClearCounts(); + + // Create the first root child. + TreeNodeWithValue<int>* child1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child 1"), 1); + model.Add(root, 0, child1); + + AssertObserverCount(1, 0, 0); + + // Add two nodes under the |child1|. + TreeNodeWithValue<int>* foo1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("foo1"), 3); + TreeNodeWithValue<int>* foo2 = + new TreeNodeWithValue<int>(ASCIIToUTF16("foo2"), 4); + child1->Add(0, foo1); + child1->Add(1, foo2); + + // Create the second root child. + TreeNodeWithValue<int>* child2 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child 2"), 2); + root->Add(1, child2); + + // Check if there is two nodes under the root. + ASSERT_EQ(2, model.GetChildCount(root)); + + // Check if there is two nodes under |child1|. + ASSERT_EQ(2, model.GetChildCount(child1)); + + // Check if there is none nodes under |child2|. + ASSERT_EQ(0, model.GetChildCount(child2)); +} + +// Verify if the model is properly removing a node from the tree +// and notifying the observers. +TEST_F(TreeNodeModelTest, RemoveNode) { + TreeNodeWithValue<int>* root = + new TreeNodeWithValue<int>(ASCIIToUTF16("root"), 0); + TreeNodeModel<TreeNodeWithValue<int> > model(root); + model.AddObserver(this); + ClearCounts(); + + // Create the first child node. + TreeNodeWithValue<int>* child1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child 1"), 1); + + // And add it to the root node. + root->Add(0, child1); + + ASSERT_EQ(1, model.GetChildCount(root)); + + // Now remove the |child1| from root and release the memory. + delete model.Remove(root, 0); + + AssertObserverCount(0, 1, 0); + + ASSERT_EQ(0, model.GetChildCount(root)); +} + +// Verify if the nodes added under the root are all deleted when calling +// RemoveAll. Note that is responsability of the caller to free the memory +// of the nodes removed after RemoveAll is called. +// The tree looks like this: +// root +// |-- child1 +// | |-- foo1 +// | |-- child0 +// | |-- child1 +// +-------|-- child2 +TEST_F(TreeNodeModelTest, RemoveAllNodes) { + TreeNodeWithValue<int>* root = + new TreeNodeWithValue<int>(ASCIIToUTF16("root"), 0); + TreeNodeModel<TreeNodeWithValue<int> > model(root); + model.AddObserver(this); + ClearCounts(); + + // Create the first child node. + TreeNodeWithValue<int>* child1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child 1"), 1); + model.Add(root, 0, child1); + + TreeNodeWithValue<int>* foo1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("foo1"), 2); + model.Add(child1, 0, foo1); + + // Add some nodes to |foo1|. + for (int i = 0; i < 3; ++i) { + model.Add(foo1, i, + new TreeNodeWithValue<int>(ASCIIToUTF16("child") + + base::IntToString16(i), i)); + } + + ASSERT_EQ(3, model.GetChildCount(foo1)); + + // Now remove all nodes from root. + root->RemoveAll(); + + // Release memory, so we don't leak. + delete child1; + + ASSERT_EQ(0, model.GetChildCount(root)); +} + +// Verify if the model returns correct indexes for the specified nodes. +// The tree looks like this: +// root +// |-- child1 +// | |-- foo1 +// +-- child2 +TEST_F(TreeNodeModelTest, IndexOfChild) { + TreeNodeWithValue<int>* root = + new TreeNodeWithValue<int>(ASCIIToUTF16("root"), 0); + TreeNodeModel<TreeNodeWithValue<int> > model(root); + model.AddObserver(this); + ClearCounts(); + + TreeNodeWithValue<int>* child1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child 1"), 1); + model.Add(root, 0, child1); + + TreeNodeWithValue<int>* child2 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child 2"), 2); + model.Add(root, 1, child2); + + TreeNodeWithValue<int>* foo1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("foo1"), 0); + model.Add(child1, 0, foo1); + + ASSERT_EQ(0, model.IndexOfChild(root, child1)); + ASSERT_EQ(1, model.IndexOfChild(root, child2)); + ASSERT_EQ(0, model.IndexOfChild(child1, foo1)); + ASSERT_EQ(-1, model.IndexOfChild(root, foo1)); +} + +// Verify whether a specified node has or not an ancestor. +// The tree looks like this: +// root +// |-- child1 +// |-- child2 +TEST_F(TreeNodeModelTest, HasAncestor) { + TreeNodeWithValue<int>* root = + new TreeNodeWithValue<int>(ASCIIToUTF16("root"), 0); + TreeNodeModel<TreeNodeWithValue<int> > model(root); + + TreeNodeWithValue<int>* child1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child 1"), 0); + model.Add(root, 0, child1); + + TreeNodeWithValue<int>* child2 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child 2"), 1); + model.Add(root, 1, child2); + + ASSERT_TRUE(root->HasAncestor(root)); + ASSERT_FALSE(root->HasAncestor(child1)); + ASSERT_TRUE(child1->HasAncestor(root)); + ASSERT_FALSE(child1->HasAncestor(child2)); + ASSERT_FALSE(child2->HasAncestor(child1)); +} + +// The tree looks like this: +// root +// |-- child1 +// | |-- child2 +// | |-- child3 +// |-- foo1 +// | |-- foo2 +// | |-- foo3 +// | |-- foo4 +// +-- bar1 +// +// The TotalNodeCount of root is: 9 +// The TotalNodeCount of child1 is: 3 +// The TotalNodeCount of bar1 is: 1 +// And so on... +// The purpose here is to verify if the function returns the total of nodes +// under the specifed node correctly. The count should include the node it self. +TEST_F(TreeNodeModelTest, GetTotalNodeCount) { + TreeNodeWithValue<int>* root = + new TreeNodeWithValue<int>(ASCIIToUTF16("root"), 0); + TreeNodeModel<TreeNodeWithValue<int> > model(root); + + TreeNodeWithValue<int>* child1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child1"), 1); + TreeNodeWithValue<int>* child2 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child2"), 2); + TreeNodeWithValue<int>* child3 = + new TreeNodeWithValue<int>(ASCIIToUTF16("child3"), 3); + TreeNodeWithValue<int>* foo1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("foo1"), 4); + TreeNodeWithValue<int>* foo2 = + new TreeNodeWithValue<int>(ASCIIToUTF16("foo2"), 5); + TreeNodeWithValue<int>* foo3 = + new TreeNodeWithValue<int>(ASCIIToUTF16("foo3"), 6); + TreeNodeWithValue<int>* foo4 = + new TreeNodeWithValue<int>(ASCIIToUTF16("foo4"), 7); + TreeNodeWithValue<int>* bar1 = + new TreeNodeWithValue<int>(ASCIIToUTF16("bar1"), 8); + + model.Add(root, 0, child1); + model.Add(child1, 0, child2); + model.Add(child2, 0, child3); + + model.Add(root, 1, foo1); + model.Add(foo1, 0, foo2); + model.Add(foo1, 1, foo4); + model.Add(foo2, 0, foo3); + + model.Add(root, 0, bar1); + + ASSERT_EQ(9, root->GetTotalNodeCount()); + ASSERT_EQ(3, child1->GetTotalNodeCount()); + ASSERT_EQ(1, bar1->GetTotalNodeCount()); + ASSERT_EQ(2, foo2->GetTotalNodeCount()); +} + +// Makes sure that we are notified when the node is renamed, +// also makes sure the node is properly renamed. +TEST_F(TreeNodeModelTest, SetTitle) { + TreeNodeWithValue<int>* root = + new TreeNodeWithValue<int>(ASCIIToUTF16("root"), 0); + TreeNodeModel<TreeNodeWithValue<int> > model(root); + model.AddObserver(this); + ClearCounts(); + + const string16 title(ASCIIToUTF16("root2")); + model.SetTitle(root, title); + AssertObserverCount(0, 0, 1); + EXPECT_EQ(title, root->GetTitle()); +} + +} // namespace ui |