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