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 "tab_view_picker_table.h"
      6 
      7 #include "base/logging.h"
      8 
      9 @interface TabViewPickerTable (Private)
     10 // If a heading is shown, the indices between the tab items and the table rows
     11 // are shifted by one. These functions convert between tab indices and table
     12 // indices.
     13 - (NSInteger)tabIndexFromTableIndex:(NSInteger)tableIndex;
     14 - (NSInteger)tableIndexFromTabIndex:(NSInteger)tabIndex;
     15 
     16 // Returns if |item| is the item shown as heading. If |heading_| is nil, this
     17 // always returns |NO|.
     18 - (BOOL)isHeadingItem:(id)item;
     19 
     20 // Reloads the outline view and sets the selection to the row corresponding to
     21 // the currently selected tab.
     22 - (void)reloadDataWhileKeepingCurrentTabSelected;
     23 @end
     24 
     25 @implementation TabViewPickerTable
     26 
     27 - (id)initWithFrame:(NSRect)frame {
     28   if ((self = [super initWithFrame:frame])) {
     29     [self setDelegate:self];
     30     [self setDataSource:self];
     31   }
     32   return self;
     33 }
     34 
     35 - (id)initWithCoder:(NSCoder*)coder {
     36   if ((self = [super initWithCoder:coder])) {
     37     [self setDelegate:self];
     38     [self setDataSource:self];
     39   }
     40   return self;
     41 }
     42 
     43 - (void)awakeFromNib {
     44   DCHECK(tabView_);
     45   DCHECK_EQ([self delegate], self);
     46   DCHECK_EQ([self dataSource], self);
     47   DCHECK(![self allowsEmptySelection]);
     48   DCHECK(![self allowsMultipleSelection]);
     49 
     50   // Suppress the "Selection changed" message that's sent while the table is
     51   // being built for the first time (this causes a selection change to index 0
     52   // and back to the prior index).
     53   id oldTabViewDelegate = [tabView_ delegate];
     54   [tabView_ setDelegate:nil];
     55 
     56   [self reloadDataWhileKeepingCurrentTabSelected];
     57 
     58   oldTabViewDelegate_ = oldTabViewDelegate;
     59   [tabView_ setDelegate:self];
     60 }
     61 
     62 - (NSString*)heading {
     63   return heading_.get();
     64 }
     65 
     66 - (void)setHeading:(NSString*)str {
     67   heading_.reset([str copy]);
     68   [self reloadDataWhileKeepingCurrentTabSelected];
     69 }
     70 
     71 - (void)reloadDataWhileKeepingCurrentTabSelected {
     72   NSInteger index =
     73       [tabView_ indexOfTabViewItem:[tabView_ selectedTabViewItem]];
     74   [self reloadData];
     75   if (heading_)
     76     [self expandItem:[self outlineView:self child:0 ofItem:nil]];
     77   NSIndexSet* indexSet =
     78       [NSIndexSet indexSetWithIndex:[self tableIndexFromTabIndex:index]];
     79   [self selectRowIndexes:indexSet byExtendingSelection:NO];
     80 }
     81 
     82 // NSTabViewDelegate methods.
     83 - (void)         tabView:(NSTabView*)tabView
     84     didSelectTabViewItem:(NSTabViewItem*)tabViewItem {
     85   DCHECK_EQ(tabView_, tabView);
     86   NSInteger index =
     87       [tabView_ indexOfTabViewItem:[tabView_ selectedTabViewItem]];
     88   NSIndexSet* indexSet =
     89       [NSIndexSet indexSetWithIndex:[self tableIndexFromTabIndex:index]];
     90   [self selectRowIndexes:indexSet byExtendingSelection:NO];
     91   if ([oldTabViewDelegate_
     92           respondsToSelector:@selector(tabView:didSelectTabViewItem:)]) {
     93     [oldTabViewDelegate_ tabView:tabView didSelectTabViewItem:tabViewItem];
     94   }
     95 }
     96 
     97 - (BOOL)            tabView:(NSTabView*)tabView
     98     shouldSelectTabViewItem:(NSTabViewItem*)tabViewItem {
     99   if ([oldTabViewDelegate_
    100           respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)]) {
    101     return [oldTabViewDelegate_ tabView:tabView
    102                 shouldSelectTabViewItem:tabViewItem];
    103   }
    104   return YES;
    105 }
    106 
    107 - (void)          tabView:(NSTabView*)tabView
    108     willSelectTabViewItem:(NSTabViewItem*)tabViewItem {
    109   if ([oldTabViewDelegate_
    110           respondsToSelector:@selector(tabView:willSelectTabViewItem:)]) {
    111     [oldTabViewDelegate_ tabView:tabView willSelectTabViewItem:tabViewItem];
    112   }
    113 }
    114 
    115 - (NSInteger)tabIndexFromTableIndex:(NSInteger)tableIndex {
    116   if (!heading_)
    117     return tableIndex;
    118   DCHECK(tableIndex > 0);
    119   return tableIndex - 1;
    120 }
    121 
    122 - (NSInteger)tableIndexFromTabIndex:(NSInteger)tabIndex {
    123   DCHECK_GE(tabIndex, 0);
    124   DCHECK_LT(tabIndex, [tabView_ numberOfTabViewItems]);
    125   if (!heading_)
    126     return tabIndex;
    127   return tabIndex + 1;
    128 }
    129 
    130 - (BOOL)isHeadingItem:(id)item {
    131   return item && item == heading_.get();
    132 }
    133 
    134 // NSOutlineViewDataSource methods.
    135 - (NSInteger)  outlineView:(NSOutlineView*)outlineView
    136     numberOfChildrenOfItem:(id)item {
    137   if (!item)
    138     return heading_ ? 1 : [tabView_ numberOfTabViewItems];
    139   return (item == heading_.get()) ? [tabView_ numberOfTabViewItems] : 0;
    140 }
    141 
    142 - (BOOL)outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item {
    143   return [self isHeadingItem:item];
    144 }
    145 
    146 - (id)outlineView:(NSOutlineView*)outlineView
    147             child:(NSInteger)index
    148            ofItem:(id)item {
    149   if (!item) {
    150     return heading_.get() ?
    151         heading_.get() : static_cast<id>([tabView_ tabViewItemAtIndex:index]);
    152   }
    153   return (item == heading_.get()) ? [tabView_ tabViewItemAtIndex:index] : nil;
    154 }
    155 
    156 - (id)            outlineView:(NSOutlineView*)outlineView
    157     objectValueForTableColumn:(NSTableColumn*)tableColumn
    158                        byItem:(id)item {
    159   if ([item isKindOfClass:[NSTabViewItem class]])
    160     return [static_cast<NSTabViewItem*>(item) label];
    161   if ([self isHeadingItem:item])
    162     return [item uppercaseString];
    163   return nil;
    164 }
    165 
    166 // NSOutlineViewDelegate methods.
    167 - (void)outlineViewSelectionDidChange:(NSNotification*)notification {
    168   int row = [self selectedRow];
    169   [tabView_ selectTabViewItemAtIndex:[self tabIndexFromTableIndex:row]];
    170 }
    171 
    172 - (BOOL)outlineView:(NSOutlineView *)sender isGroupItem:(id)item {
    173   return [self isHeadingItem:item];
    174 }
    175 
    176 - (BOOL)outlineView:(NSOutlineView*)outlineView shouldExpandItem:(id)item {
    177   return [self isHeadingItem:item];
    178 }
    179 
    180 - (BOOL)outlineView:(NSOutlineView*)outlineView shouldCollapseItem:(id)item {
    181   return NO;
    182 }
    183 
    184 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item {
    185   return ![self isHeadingItem:item];
    186 }
    187 
    188 // -outlineView:shouldShowOutlineCellForItem: is 10.6-only.
    189 - (NSRect)frameOfOutlineCellAtRow:(NSInteger)row {
    190   return NSZeroRect;
    191 }
    192 
    193 @end
    194