Home | History | Annotate | Download | only in cocoa
      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