summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-14 15:49:40 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-14 15:49:40 +0000
commit44cbd9e3734527f73a83f8a864be0bb5ccae0a7a (patch)
treea997fb0565558d63e0eab62b631ef984de3e9596 /ui
parent0c1c047d641a599ffffa280ab50d564cedb3e436 (diff)
downloadchromium_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.h89
-rw-r--r--ui/base/models/accelerator_cocoa.h59
-rw-r--r--ui/base/models/accelerator_gtk.h59
-rw-r--r--ui/base/models/button_menu_item_model.cc135
-rw-r--r--ui/base/models/button_menu_item_model.h113
-rw-r--r--ui/base/models/combobox_model.h27
-rw-r--r--ui/base/models/menu_model.cc45
-rw-r--r--ui/base/models/menu_model.h125
-rw-r--r--ui/base/models/simple_menu_model.cc314
-rw-r--r--ui/base/models/simple_menu_model.h146
-rw-r--r--ui/base/models/table_model.cc138
-rw-r--r--ui/base/models/table_model.h145
-rw-r--r--ui/base/models/table_model_observer.h33
-rw-r--r--ui/base/models/tree_model.cc20
-rw-r--r--ui/base/models/tree_model.h99
-rw-r--r--ui/base/models/tree_node_iterator.h75
-rw-r--r--ui/base/models/tree_node_iterator_unittest.cc41
-rw-r--r--ui/base/models/tree_node_model.h319
-rw-r--r--ui/base/models/tree_node_model_unittest.cc295
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