1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import "chrome/browser/ui/cocoa/table_model_array_controller.h" 6 7 #include "base/logging.h" 8 #include "base/sys_string_conversions.h" 9 #include "chrome/browser/remove_rows_table_model.h" 10 #include "ui/base/models/table_model.h" 11 12 @interface TableModelArrayController () 13 14 - (NSUInteger)offsetForGroupID:(int)groupID; 15 - (NSUInteger)offsetForGroupID:(int)groupID startingOffset:(NSUInteger)offset; 16 - (NSIndexSet*)controllerRowsForModelRowsInRange:(NSRange)range; 17 - (void)setModelRows:(RemoveRowsTableModel::Rows*)modelRows 18 fromControllerRows:(NSIndexSet*)rows; 19 - (void)modelDidChange; 20 - (void)modelDidAddItemsInRange:(NSRange)range; 21 - (void)modelDidRemoveItemsInRange:(NSRange)range; 22 - (NSDictionary*)columnValuesForRow:(NSInteger)row; 23 24 @end 25 26 // Observer for a RemoveRowsTableModel. 27 class RemoveRowsObserverBridge : public ui::TableModelObserver { 28 public: 29 RemoveRowsObserverBridge(TableModelArrayController* controller) 30 : controller_(controller) {} 31 virtual ~RemoveRowsObserverBridge() {} 32 33 // ui::TableModelObserver methods 34 virtual void OnModelChanged(); 35 virtual void OnItemsChanged(int start, int length); 36 virtual void OnItemsAdded(int start, int length); 37 virtual void OnItemsRemoved(int start, int length); 38 39 private: 40 TableModelArrayController* controller_; // weak 41 }; 42 43 void RemoveRowsObserverBridge::OnModelChanged() { 44 [controller_ modelDidChange]; 45 } 46 47 void RemoveRowsObserverBridge::OnItemsChanged(int start, int length) { 48 OnItemsRemoved(start, length); 49 OnItemsAdded(start, length); 50 } 51 52 void RemoveRowsObserverBridge::OnItemsAdded(int start, int length) { 53 [controller_ modelDidAddItemsInRange:NSMakeRange(start, length)]; 54 } 55 56 void RemoveRowsObserverBridge::OnItemsRemoved(int start, int length) { 57 [controller_ modelDidRemoveItemsInRange:NSMakeRange(start, length)]; 58 } 59 60 @implementation TableModelArrayController 61 62 static NSString* const kIsGroupRow = @"_is_group_row"; 63 static NSString* const kGroupID = @"_group_id"; 64 65 - (void)bindToTableModel:(RemoveRowsTableModel*)model 66 withColumns:(NSDictionary*)columns 67 groupTitleColumn:(NSString*)groupTitleColumn { 68 model_ = model; 69 tableObserver_.reset(new RemoveRowsObserverBridge(self)); 70 columns_.reset([columns copy]); 71 groupTitle_.reset([groupTitleColumn copy]); 72 model_->SetObserver(tableObserver_.get()); 73 [self modelDidChange]; 74 } 75 76 - (void)modelDidChange { 77 NSIndexSet* indexes = [NSIndexSet indexSetWithIndexesInRange: 78 NSMakeRange(0, [[self arrangedObjects] count])]; 79 [self removeObjectsAtArrangedObjectIndexes:indexes]; 80 if (model_->HasGroups()) { 81 const ui::TableModel::Groups& groups = model_->GetGroups(); 82 DCHECK(groupTitle_.get()); 83 for (ui::TableModel::Groups::const_iterator it = groups.begin(); 84 it != groups.end(); ++it) { 85 NSDictionary* group = [NSDictionary dictionaryWithObjectsAndKeys: 86 base::SysUTF16ToNSString(it->title), groupTitle_.get(), 87 [NSNumber numberWithBool:YES], kIsGroupRow, 88 nil]; 89 [self addObject:group]; 90 } 91 } 92 [self modelDidAddItemsInRange:NSMakeRange(0, model_->RowCount())]; 93 } 94 95 - (NSUInteger)offsetForGroupID:(int)groupID startingOffset:(NSUInteger)offset { 96 const ui::TableModel::Groups& groups = model_->GetGroups(); 97 DCHECK_GT(offset, 0u); 98 for (NSUInteger i = offset - 1; i < groups.size(); ++i) { 99 if (groups[i].id == groupID) 100 return i + 1; 101 } 102 NOTREACHED(); 103 return NSNotFound; 104 } 105 106 - (NSUInteger)offsetForGroupID:(int)groupID { 107 return [self offsetForGroupID:groupID startingOffset:1]; 108 } 109 110 - (int)groupIDForControllerRow:(NSUInteger)row { 111 NSDictionary* values = [[self arrangedObjects] objectAtIndex:row]; 112 return [[values objectForKey:kGroupID] intValue]; 113 } 114 115 - (void)setModelRows:(RemoveRowsTableModel::Rows*)modelRows 116 fromControllerRows:(NSIndexSet*)rows { 117 if ([rows count] == 0) 118 return; 119 120 if (!model_->HasGroups()) { 121 for (NSUInteger i = [rows firstIndex]; 122 i != NSNotFound; 123 i = [rows indexGreaterThanIndex:i]) { 124 modelRows->insert(i); 125 } 126 return; 127 } 128 129 NSUInteger offset = 1; 130 for (NSUInteger i = [rows firstIndex]; 131 i != NSNotFound; 132 i = [rows indexGreaterThanIndex:i]) { 133 int group = [self groupIDForControllerRow:i]; 134 offset = [self offsetForGroupID:group startingOffset:offset]; 135 modelRows->insert(i - offset); 136 } 137 } 138 139 - (NSIndexSet*)controllerRowsForModelRowsInRange:(NSRange)range { 140 if (!model_->HasGroups()) 141 return [NSIndexSet indexSetWithIndexesInRange:range]; 142 NSMutableIndexSet* indexes = [NSMutableIndexSet indexSet]; 143 NSUInteger offset = 1; 144 for (NSUInteger i = range.location; i < NSMaxRange(range); ++i) { 145 int group = model_->GetGroupID(i); 146 offset = [self offsetForGroupID:group startingOffset:offset]; 147 [indexes addIndex:i + offset]; 148 } 149 return indexes; 150 } 151 152 - (void)modelDidAddItemsInRange:(NSRange)range { 153 if (range.length == 0) 154 return; 155 NSMutableArray* rows = [NSMutableArray arrayWithCapacity:range.length]; 156 for (NSUInteger i = range.location; i < NSMaxRange(range); ++i) 157 [rows addObject:[self columnValuesForRow:i]]; 158 NSIndexSet* indexes = [self controllerRowsForModelRowsInRange:range]; 159 [self insertObjects:rows atArrangedObjectIndexes:indexes]; 160 } 161 162 - (void)modelDidRemoveItemsInRange:(NSRange)range { 163 if (range.length == 0) 164 return; 165 NSMutableIndexSet* indexes = 166 [NSMutableIndexSet indexSetWithIndexesInRange:range]; 167 if (model_->HasGroups()) { 168 // When this method is called, the model has already removed items, so 169 // accessing items in the model from |range.location| on may not be possible 170 // anymore. Therefore we use the item right before that, if it exists. 171 NSUInteger offset = 0; 172 if (range.location > 0) { 173 int last_group = model_->GetGroupID(range.location - 1); 174 offset = [self offsetForGroupID:last_group]; 175 } 176 [indexes shiftIndexesStartingAtIndex:0 by:offset]; 177 for (NSUInteger row = range.location + offset; 178 row < NSMaxRange(range) + offset; 179 ++row) { 180 if ([self tableView:nil isGroupRow:row]) { 181 // Skip over group rows. 182 [indexes shiftIndexesStartingAtIndex:row by:1]; 183 offset++; 184 } 185 } 186 } 187 [self removeObjectsAtArrangedObjectIndexes:indexes]; 188 } 189 190 - (NSDictionary*)columnValuesForRow:(NSInteger)row { 191 NSMutableDictionary* dict = [NSMutableDictionary dictionary]; 192 if (model_->HasGroups()) { 193 [dict setObject:[NSNumber numberWithInt:model_->GetGroupID(row)] 194 forKey:kGroupID]; 195 } 196 for (NSString* identifier in columns_.get()) { 197 int column_id = [[columns_ objectForKey:identifier] intValue]; 198 string16 text = model_->GetText(row, column_id); 199 [dict setObject:base::SysUTF16ToNSString(text) forKey:identifier]; 200 } 201 return dict; 202 } 203 204 #pragma mark Overridden from NSArrayController 205 206 - (BOOL)canRemove { 207 if (!model_) 208 return NO; 209 RemoveRowsTableModel::Rows rows; 210 [self setModelRows:&rows fromControllerRows:[self selectionIndexes]]; 211 return model_->CanRemoveRows(rows); 212 } 213 214 - (IBAction)remove:(id)sender { 215 RemoveRowsTableModel::Rows rows; 216 [self setModelRows:&rows fromControllerRows:[self selectionIndexes]]; 217 model_->RemoveRows(rows); 218 } 219 220 #pragma mark NSTableView delegate methods 221 222 - (BOOL)tableView:(NSTableView*)tableView isGroupRow:(NSInteger)row { 223 NSDictionary* values = [[self arrangedObjects] objectAtIndex:row]; 224 return [[values objectForKey:kIsGroupRow] boolValue]; 225 } 226 227 - (NSIndexSet*)tableView:(NSTableView*)tableView 228 selectionIndexesForProposedSelection:(NSIndexSet*)proposedIndexes { 229 NSMutableIndexSet* indexes = [proposedIndexes mutableCopy]; 230 for (NSUInteger i = [proposedIndexes firstIndex]; 231 i != NSNotFound; 232 i = [proposedIndexes indexGreaterThanIndex:i]) { 233 if ([self tableView:tableView isGroupRow:i]) { 234 [indexes removeIndex:i]; 235 NSUInteger row = i + 1; 236 while (row < [[self arrangedObjects] count] && 237 ![self tableView:tableView isGroupRow:row]) 238 [indexes addIndex:row++]; 239 } 240 } 241 return indexes; 242 } 243 244 #pragma mark Actions 245 246 - (IBAction)removeAll:(id)sender { 247 model_->RemoveAll(); 248 } 249 250 @end 251 252