diff options
author | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-29 20:19:49 +0000 |
---|---|---|
committer | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-29 20:19:49 +0000 |
commit | 5ec892c39b8d9213af44629a676c97912c1c856e (patch) | |
tree | 412fb7994575820355359fe82dd8dd6ea0a8e04f | |
parent | fce1fb3963292596d548990fe0783d7c01bfdd48 (diff) | |
download | chromium_src-5ec892c39b8d9213af44629a676c97912c1c856e.zip chromium_src-5ec892c39b8d9213af44629a676c97912c1c856e.tar.gz chromium_src-5ec892c39b8d9213af44629a676c97912c1c856e.tar.bz2 |
Mac: Add sort support for task manager.
Also fix a leak in test Init reported by valgrind. The problem was that the test called a rogue init, which didn't cause the window to be shown, and hence -close didn't send a -windowWillClose: notification. Also restore -windowWillClose:, it accidentally got deleted in http://codereview.chromium.org/536086
BUG=32148,30398
TEST=Click a column in the task manager. Should sort.
Review URL: http://codereview.chromium.org/2873075
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@54175 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/cocoa/task_manager_mac.h | 19 | ||||
-rw-r--r-- | chrome/browser/cocoa/task_manager_mac.mm | 92 | ||||
-rw-r--r-- | chrome/browser/cocoa/task_manager_mac_unittest.mm | 63 | ||||
-rw-r--r-- | chrome/browser/task_manager.h | 2 |
4 files changed, 150 insertions, 26 deletions
diff --git a/chrome/browser/cocoa/task_manager_mac.h b/chrome/browser/cocoa/task_manager_mac.h index 282cdb1..6d57f82 100644 --- a/chrome/browser/cocoa/task_manager_mac.h +++ b/chrome/browser/cocoa/task_manager_mac.h @@ -7,6 +7,8 @@ #pragma once #import <Cocoa/Cocoa.h> +#include <vector> + #include "base/scoped_nsobject.h" #include "chrome/browser/cocoa/table_row_nsimage_cache.h" #include "chrome/browser/task_manager.h" @@ -25,6 +27,13 @@ class TaskManagerMac; TaskManagerModel* model_; // weak scoped_nsobject<WindowSizeAutosaver> size_saver_; + + // Contains a permutation of [0..|model_->ResourceCount() - 1|]. Used to + // implement sorting. + std::vector<int> indexShuffle_; + + // Descriptor of the current sort column. + scoped_nsobject<NSSortDescriptor> currentSortDescriptor_; } // Creates and shows the task manager's window. @@ -43,11 +52,15 @@ class TaskManagerMac; - (void)selectDoubleClickedTab:(id)sender; @end +@interface TaskManagerWindowController (TestingAPI) +- (NSTableView*)tableView; +@end + // This class listens to task changed events sent by chrome. class TaskManagerMac : public TaskManagerModelObserver, public TableRowNSImageCache::Table { public: - TaskManagerMac(); + TaskManagerMac(TaskManager* task_manager); virtual ~TaskManagerMac(); // TaskManagerModelObserver @@ -74,9 +87,11 @@ class TaskManagerMac : public TaskManagerModelObserver, // Lazily converts the image at the given row and caches it in |icon_cache_|. NSImage* GetImageForRow(int row); + // Returns the cocoa object. Used for testing. + TaskManagerWindowController* cocoa_controller() { return window_controller_; } private: // The task manager. - TaskManager* const task_manager_; // weak + TaskManager* const task_manager_; // weak // Our model. TaskManagerModel* const model_; // weak diff --git a/chrome/browser/cocoa/task_manager_mac.mm b/chrome/browser/cocoa/task_manager_mac.mm index b0a70a6..f081dbb 100644 --- a/chrome/browser/cocoa/task_manager_mac.mm +++ b/chrome/browser/cocoa/task_manager_mac.mm @@ -15,17 +15,15 @@ #include "chrome/common/pref_names.h" #include "grit/generated_resources.h" -// TODO(thakis): Column sort comparator -// TODO(thakis): Clicking column header doesn't sort -// TODO(thakis): Default sort column +namespace { // Width of "a" and most other letters/digits in "small" table views. -static const int kCharWidth = 6; +const int kCharWidth = 6; // Some of the strings below have spaces at the end or are missing letters, to // make the columns look nicer, and to take potentially longer localized strings // into account. -static const struct ColumnWidth { +const struct ColumnWidth { int columnId; int minWidth; int maxWidth; // If this is -1, 1.5*minColumWidth is used as max width. @@ -54,6 +52,28 @@ static const struct ColumnWidth { arraysize("15 ") * kCharWidth, -1 }, }; +class SortHelper { + public: + SortHelper(TaskManagerModel* model, NSSortDescriptor* column) + : sort_column_([[column key] intValue]), + ascending_([column ascending]), + model_(model) {} + + bool operator()(int a, int b) { + int cmp_result = model_->CompareValues(a, b, sort_column_ ); + if (!ascending_) + cmp_result = -cmp_result; + // TODO(thakis): Do grouping like on GTK. + return cmp_result < 0; + } + private: + int sort_column_; + bool ascending_; + TaskManagerModel* model_; // weak; +}; + +} // namespace + @interface TaskManagerWindowController (Private) - (NSTableColumn*)addColumnWithId:(int)columnId visible:(BOOL)isVisible; - (void)setUpTableColumns; @@ -83,12 +103,22 @@ static const struct ColumnWidth { path:prefs::kTaskManagerWindowPlacement state:kSaveWindowRect]); } - [[self window] makeKeyAndOrderFront:self]; + [self showWindow:self]; } return self; } +- (void)sortShuffleArray { + indexShuffle_.resize(model_->ResourceCount()); + for (size_t i = 0; i < indexShuffle_.size(); ++i) + indexShuffle_[i] = i; + + std::sort(indexShuffle_.begin(), indexShuffle_.end(), + SortHelper(model_, currentSortDescriptor_.get())); +} + - (void)reloadData { + [self sortShuffleArray]; [tableView_ reloadData]; [self adjustSelectionAndEndProcessButton]; } @@ -113,6 +143,10 @@ static const struct ColumnWidth { taskManager_->ActivateProcess(row); } +- (NSTableView*)tableView { + return tableView_; +} + - (void)awakeFromNib { [self setUpTableColumns]; [self setUpTableHeaderContextMenu]; @@ -141,6 +175,14 @@ static const struct ColumnWidth { [column.get() setHidden:!isVisible]; [column.get() setEditable:NO]; + // The page column should by default be sorted ascending. + BOOL ascending = columnId == IDS_TASK_MANAGER_PAGE_COLUMN; + + scoped_nsobject<NSSortDescriptor> sortDescriptor([[NSSortDescriptor alloc] + initWithKey:[NSString stringWithFormat:@"%d", columnId] + ascending:ascending]); + [column.get() setSortDescriptorPrototype:sortDescriptor.get()]; + // Default values, only used in release builds if nobody notices the DCHECK // during development when adding new columns. int minWidth = 200, maxWidth = 400; @@ -181,6 +223,10 @@ static const struct ColumnWidth { [nameCell.get() setFont:[[nameColumn dataCell] font]]; [nameColumn setDataCell:nameCell.get()]; + // Initially, sort on the tab name. + [tableView_ setSortDescriptors: + [NSArray arrayWithObject:[nameColumn sortDescriptorPrototype]]]; + [self addColumnWithId:IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN visible:YES]; [self addColumnWithId:IDS_TASK_MANAGER_SHARED_MEM_COLUMN visible:NO]; [self addColumnWithId:IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN visible:NO]; @@ -260,6 +306,14 @@ static const struct ColumnWidth { [self adjustSelectionAndEndProcessButton]; } +- (void)windowWillClose:(NSNotification*)notification { + if (taskManagerObserver_) { + taskManagerObserver_->WindowWasClosed(); + taskManagerObserver_ = nil; + } + [self autorelease]; +} + @end @implementation TaskManagerWindowController (NSTableDataSource) @@ -270,6 +324,8 @@ static const struct ColumnWidth { } - (NSString*)modelTextForRow:(int)row column:(int)columnId { + DCHECK_LT(static_cast<size_t>(row), indexShuffle_.size()); + row = indexShuffle_[row]; switch (columnId) { case IDS_TASK_MANAGER_PAGE_COLUMN: // Process return base::SysWideToNSString(model_->GetResourceTitle(row)); @@ -362,14 +418,24 @@ static const struct ColumnWidth { return cell; } +- (void) tableView:(NSTableView*)tableView + sortDescriptorsDidChange:(NSArray*)oldDescriptors { + NSArray* newDescriptors = [tableView sortDescriptors]; + if ([newDescriptors count] < 1) + return; + + currentSortDescriptor_.reset([[newDescriptors objectAtIndex:0] retain]); + [self reloadData]; // Sorts. +} + @end //////////////////////////////////////////////////////////////////////////////// // TaskManagerMac implementation: -TaskManagerMac::TaskManagerMac() - : task_manager_(TaskManager::GetInstance()), - model_(TaskManager::GetInstance()->model()), +TaskManagerMac::TaskManagerMac(TaskManager* task_manager) + : task_manager_(task_manager), + model_(task_manager->model()), icon_cache_(this) { window_controller_ = [[TaskManagerWindowController alloc] initWithTaskManagerObserver:this]; @@ -380,7 +446,11 @@ TaskManagerMac::TaskManagerMac() TaskManagerMac* TaskManagerMac::instance_ = NULL; TaskManagerMac::~TaskManagerMac() { - task_manager_->OnWindowClosed(); + if (this == instance_) { + // Do not do this when running in unit tests: |StartUpdating()| never got + // called in that case. + task_manager_->OnWindowClosed(); + } model_->RemoveObserver(this); } @@ -426,7 +496,7 @@ void TaskManagerMac::Show() { [[instance_->window_controller_ window] makeKeyAndOrderFront:instance_->window_controller_]; } else { - instance_ = new TaskManagerMac; + instance_ = new TaskManagerMac(TaskManager::GetInstance()); instance_->model_->StartUpdating(); } } diff --git a/chrome/browser/cocoa/task_manager_mac_unittest.mm b/chrome/browser/cocoa/task_manager_mac_unittest.mm index 45a2ab2..e9a7efb 100644 --- a/chrome/browser/cocoa/task_manager_mac_unittest.mm +++ b/chrome/browser/cocoa/task_manager_mac_unittest.mm @@ -5,33 +5,70 @@ #import <Cocoa/Cocoa.h> #include "base/scoped_nsobject.h" +#include "base/utf_string_conversions.h" #import "chrome/browser/cocoa/task_manager_mac.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" +#include "grit/generated_resources.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" namespace { -class TaskManagerWindowControllerTest : public CocoaTest { +class TestResource : public TaskManager::Resource { public: - virtual void SetUp() { - CocoaTest::SetUp(); - controller_ = [[TaskManagerWindowController alloc] init]; + TestResource(const string16& title) : title_(title) {} + virtual std::wstring GetTitle() const { return UTF16ToWide(title_); } + virtual SkBitmap GetIcon() const { return SkBitmap(); } + virtual base::ProcessHandle GetProcess() const { + return base::GetCurrentProcessHandle(); } + virtual bool SupportNetworkUsage() const { return false; } + virtual void SetSupportNetworkUsage() { NOTREACHED(); } + virtual void Refresh() {} + string16 title_; +}; - virtual void TearDown() { - [controller_ close]; - CocoaTest::TearDown(); - } +} // namespace - TaskManagerWindowController *controller_; +class TaskManagerWindowControllerTest : public CocoaTest { }; -// Test creation, to ensure nothing leaks or crashes +// Test creation, to ensure nothing leaks or crashes. TEST_F(TaskManagerWindowControllerTest, Init) { + TaskManager task_manager; + TaskManagerMac* bridge(new TaskManagerMac(&task_manager)); + TaskManagerWindowController* controller = bridge->cocoa_controller(); + + // Releases the controller, which in turn deletes |bridge|. + [controller close]; } -// TODO(thakis): Add tests for more methods as they become implemented -// (TaskManager::Show() etc). +TEST_F(TaskManagerWindowControllerTest, Sort) { + TaskManager task_manager; -} // namespace + TestResource resource1(UTF8ToUTF16("zzz")); + TestResource resource2(UTF8ToUTF16("zza")); + + task_manager.AddResource(&resource1); + task_manager.AddResource(&resource2); // Will be in the same group. + + TaskManagerMac* bridge(new TaskManagerMac(&task_manager)); + TaskManagerWindowController* controller = bridge->cocoa_controller(); + NSTableView* table = [controller tableView]; + ASSERT_EQ(2, [controller numberOfRowsInTableView:table]); + + // Test that table is sorted on title. + NSTableColumn* title_column = [table tableColumnWithIdentifier: + [NSNumber numberWithInt:IDS_TASK_MANAGER_PAGE_COLUMN]]; + NSCell* cell; + cell = [controller tableView:table dataCellForTableColumn:title_column row:0]; + EXPECT_TRUE([@"zza" isEqualToString:[cell title]]); + cell = [controller tableView:table dataCellForTableColumn:title_column row:1]; + EXPECT_TRUE([@"zzz" isEqualToString:[cell title]]); + + // Releases the controller, which in turn deletes |bridge|. + [controller close]; + + task_manager.RemoveResource(&resource1); + task_manager.RemoveResource(&resource2); +} diff --git a/chrome/browser/task_manager.h b/chrome/browser/task_manager.h index b67faed..2865a46 100644 --- a/chrome/browser/task_manager.h +++ b/chrome/browser/task_manager.h @@ -152,6 +152,8 @@ class TaskManager { FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, Basic); FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, Resources); FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, RefreshCalled); + FRIEND_TEST_ALL_PREFIXES(TaskManagerWindowControllerTest, Init); + FRIEND_TEST_ALL_PREFIXES(TaskManagerWindowControllerTest, Sort); // Obtain an instance via GetInstance(). TaskManager(); |