source: trunk/Cocoa/Pester/Source/NJRTableDelegate.m@ 51

Last change on this file since 51 was 51, checked in by Nicholas Riley, 21 years ago

Alarms.nib: Removed horizontal scroll bar. Turned on grid. Set delegate to NJRTableDelegate instead of PSAlarmSetController.

NJRTableDelegate: In general, made functional (was previously unused). Fixed MyCompanyName. Changed ORDER_BY_CONTEXT to use key-value coding instead of assuming data consists of a dictionary of dictionaries. Added sorting support (reorderedData, replaces oData) with autosave support for sort context. Added _positionTypeSelectDisplay, which adjusts position and justification of type select display control based on the current sort column. Added support for reverse sorting in type select string. Use table data source instead of sorted data so text matches as displayed (this will break with non-text cells...).

NJRTableView: Adapted from iTableView (Jaguar table alternate table background color), TableTester (most everything else) and NJROutlineView (keyDown, moveToBeginning/EndOfDocument). Support for type selection, delete shortcut for row deletion, and iTunes-alike background colors and frame.

NSCharacterSet-NJRExtensions: Moved _typeSelectSet from NJROutlineView as it's now shared with NJRTableView. Still need to factor NJROutlineView as embedded in HostLauncher some day.

PSAlarm: Reorganized, renamed and categorized methods. Added time accessor for the benefit of sorting. Renamed compare: to compareDate: for clarity. Added compareMessage:, though it's currently unused. Renamed cancel to cancelTimer for clarity.

PSAlarmSetController: More fun with initial first responder on window show/hide; still need to work around bug properly (subclass NSComboBox?) and fix it for real. As is, works for OS X 10.1.

PSAlarms: Added alarms accessor, returning alarm array. Fixed memory leak on successful alarm removal (oops). Added removeAlarms:, needed with sorted alarm list.

PSAlarmsController: Set window resize increment. Changes to table delegate methods to use reordered alarm list. Register for NSTableViewSelectionDidChangeNotification now we're no longer the table view delegate. Fixed autoselection in alarmsChanged by using data reordering support in NJRTableView. Implement NJRTableViewDataSource to permit deletion from table view.

Pester.pbproj: Added new files.

Read Me.rtfd: Added TableTester/iTableView acknowledgements. Updated release notes.

File size: 8.9 KB
Line 
1//
2// NJRTableDelegate.m
3// Pester
4//
5// Created by Nicholas Riley on Sun Oct 27 2002.
6// Copyright (c) 2002 Nicholas Riley. All rights reserved.
7//
8
9#import "NJRTableDelegate.h"
10#import "NSTableView-NJRExtensions.h"
11
12#pragma mark sorting support
13
14typedef struct { NSString *key; BOOL descending; } SortContext;
15
16// Sort array of itemNums, by looking up the itemNum in the dictionary of objects.
17// based on code of Ondra Cada <ocs@ocs.cz> on cocoa-dev list
18
19int ORDER_BY_CONTEXT(id left, id right, void *ctxt) {
20 SortContext *context = (SortContext *)ctxt;
21 int order = 0;
22 id key = context->key;
23 if (0 != key) {
24 id first, second; // the actual objects to compare
25
26 if (context->descending) {
27 first = [right valueForKey: key];
28 second = [left valueForKey: key];
29 } else {
30 first = [left valueForKey: key];
31 second = [right valueForKey: key];
32 }
33
34 if ([first respondsToSelector: @selector(caseInsensitiveCompare:)]) {
35 order = [first caseInsensitiveCompare:second];
36 } else { // sort numbers or dates
37 order = [(NSNumber *)first compare:second];
38 }
39 }
40 return order;
41}
42
43@interface NJRTableDelegate (Private)
44
45- (void)_positionTypeSelectDisplay;
46- (void)_sortByColumn:(NSTableColumn *)inTableColumn;
47
48@end
49
50@implementation NJRTableDelegate
51
52#pragma mark initialize-release
53
54- (void)awakeFromNib;
55{
56 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_positionTypeSelectDisplay) name: NSViewFrameDidChangeNotification object: tableView];
57}
58
59- (void)dealloc
60{
61 [[NSNotificationCenter defaultCenter] removeObserver: self];
62 [sortingColumn release];
63 [sortingKey release];
64 [reorderedData release];
65 [super dealloc];
66}
67
68#pragma mark accessing
69
70- (void)setSortingColumn:(NSTableColumn *)inNewValue;
71{
72 [inNewValue retain];
73 [sortingColumn release];
74 sortingColumn = inNewValue;
75}
76
77- (void)setSortingKey:(NSString *)inNewValue;
78{
79 [inNewValue retain];
80 [sortingKey release];
81 sortingKey = inNewValue;
82}
83
84#pragma mark sorting
85
86- (NSString *)_sortContextDefaultKey;
87{
88 NSString *autosaveName = [tableView autosaveName];
89 if (autosaveName != nil)
90 return [NSString stringWithFormat: @"NJRTableDelegate SortContext %@", autosaveName];
91 else
92 return nil;
93}
94
95- (void)_sortData;
96{
97 SortContext ctxt = { sortingKey, sortDescending };
98 NSString *sortContextKey = [self _sortContextDefaultKey];
99
100 if (sortContextKey != nil) {
101 [[NSUserDefaults standardUserDefaults] setObject:
102 [NSDictionary dictionaryWithObjectsAndKeys: sortingKey, @"sortingKey", [NSNumber numberWithBool: sortDescending], @"sortDescending", nil]
103 forKey: [self _sortContextDefaultKey]];
104 }
105
106 // sort the NSMutableArray
107 [reorderedData sortUsingFunction: ORDER_BY_CONTEXT context: &ctxt];
108 [tableView reloadData];
109}
110
111- (void)_sortByColumn:(NSTableColumn *)inTableColumn;
112{
113 NSSet *oldSelection = [self selectedItems];
114 if (sortingColumn == inTableColumn) {
115 // User clicked same column, change sort order
116 sortDescending = !sortDescending;
117 // Possible optimization: Don't actually re-sort if you just change the sorting direction; instead, just display either the nth item or the (count-1-n)th item depending on ascending/descending.)
118 } else {
119 // User clicked new column, change old/new column headers, save new sorting column, and re-sort the array.
120 if (sortingColumn != nil) {
121 [tableView setIndicatorImage: nil inTableColumn: sortingColumn];
122 sortDescending = NO; // on initial sort, preserve previous sort order
123 }
124 [self setSortingKey: [inTableColumn identifier]];
125 [self setSortingColumn: inTableColumn];
126 [tableView setHighlightedTableColumn: inTableColumn];
127 }
128 [tableView setIndicatorImage: (sortDescending ? [NSTableView descendingSortIndicator] : [NSTableView ascendingSortIndicator]) inTableColumn: inTableColumn];
129 [self _positionTypeSelectDisplay];
130 // Actually sort the data
131 [self _sortData];
132 [self selectItems: oldSelection];
133}
134
135- (void)_initialSortData;
136{
137 NSString *sortContextKey = [self _sortContextDefaultKey];
138 NSDictionary *sortContext;
139 NSString *key;
140 NSTableColumn *column;
141
142 if (sortContextKey == nil) goto noContext;
143 if ( (sortContext = [[NSUserDefaults standardUserDefaults] dictionaryForKey: sortContextKey]) == nil) goto noContext;
144 if ( (key = [sortContext objectForKey: @"sortingKey"]) == nil) goto noContext;
145 if ( (column = [tableView tableColumnWithIdentifier: key]) == nil) goto noContext;
146 sortDescending = [[sortContext objectForKey: @"sortDescending"] boolValue];
147 [self _sortByColumn: column];
148 return;
149
150noContext:
151 sortDescending = NO;
152 [self _sortByColumn: [[tableView tableColumns] objectAtIndex: 0]];
153}
154
155- (NSMutableArray *)reorderedDataForData:(NSArray *)data;
156{
157 if (reorderedData == nil) {
158 reorderedData = [data mutableCopy];
159 [self _initialSortData];
160 } else {
161 NSSet *oldSelection = [self selectedItems];
162 [reorderedData release]; reorderedData = nil;
163 reorderedData = [data mutableCopy];
164 [self _sortData];
165 [self selectItems: oldSelection];
166 }
167 return reorderedData;
168}
169
170#pragma mark type selection
171
172- (void)_positionTypeSelectDisplay;
173{
174 [tableView resetTypeSelect]; // avoid extraneous matching
175 if ([tableView typeSelectDisplay] != nil && sortingColumn != nil) {
176 NSControl *typeSelectControl = [tableView typeSelectDisplay];
177 if ([typeSelectControl isKindOfClass: [NSControl class]]) {
178 NSView *superview = [typeSelectControl superview];
179 NSRect columnRect = [superview convertRect: [tableView rectOfColumn: [tableView columnWithIdentifier: sortingKey]] fromView: tableView];
180 // XXX support horizontal scroll bar/clipping (not for Pester, but eventually)
181 NSRect tableScrollFrame = [[tableView enclosingScrollView] frame];
182 NSRect selectFrame = [typeSelectControl frame];
183 [superview setNeedsDisplayInRect: selectFrame]; // fix artifacts caused by moving view
184 selectFrame.origin.x = columnRect.origin.x;
185 selectFrame.size.width = columnRect.size.width;
186 [typeSelectControl setAlignment: [[sortingColumn dataCell] alignment]];
187 [typeSelectControl setFrame: selectFrame];
188 }
189 }
190}
191
192#pragma mark saving/restoring selection
193
194- (NSSet *)selectedItems;
195{
196 NSMutableSet *result = [NSMutableSet set];
197 NSEnumerator *e = [tableView selectedRowEnumerator];
198 NSNumber *rowNum;
199
200 while ( (rowNum = [e nextObject]) != nil) {
201 id item = [reorderedData objectAtIndex: [rowNum intValue]];
202 [result addObject: item];
203 }
204 return result;
205}
206
207- (void)selectItems:(NSSet *)inSelectedItems;
208{
209 NSEnumerator *e = [inSelectedItems objectEnumerator];
210 id item;
211 int savedLastRow = 0;
212
213 [tableView deselectAll: nil];
214
215 while ( (item = [e nextObject]) != nil ) {
216 int row = [reorderedData indexOfObjectIdenticalTo: item];
217 if (row != NSNotFound) {
218 [tableView selectRow: row byExtendingSelection: YES];
219 savedLastRow = row;
220 }
221 }
222 [tableView scrollRowToVisible: savedLastRow];
223}
224
225@end
226
227@implementation NJRTableDelegate (NJRTableViewDelegate)
228
229- (void)tableView:(NSTableView *)aTableView didClickTableColumn:(NSTableColumn *)inTableColumn
230{
231 [[tableView window] makeFirstResponder: aTableView];
232 [self _sortByColumn: inTableColumn];
233}
234
235- (void)tableViewColumnDidResize:(NSNotification *)notification;
236{
237 [self _positionTypeSelectDisplay];
238}
239
240- (void)tableViewColumnDidMove:(NSNotification *)notification;
241{
242 [self _positionTypeSelectDisplay];
243}
244
245- (void)tableView:(NSTableView *)aTableView selectRowMatchingString:(NSString *)matchString;
246{
247 // Look for a highlighted column, presuming we are sorted by that column, and search its values.
248 NSTableColumn *col = [aTableView highlightedTableColumn];
249 id dataSource = [aTableView dataSource];
250 int i, rowCount = [reorderedData count];
251 if (nil == col) return;
252 if (sortDescending) {
253 for ( i = rowCount - 1 ; i >= 0 ; i-- ) {
254 NSComparisonResult order = [matchString caseInsensitiveCompare:
255 [dataSource tableView: aTableView objectValueForTableColumn: col row: i]];
256 if (order != NSOrderedDescending) break;
257 }
258 if (i < 0) i = 0;
259 } else {
260 for ( i = 0 ; i < rowCount ; i++ ) {
261 NSComparisonResult order = [matchString caseInsensitiveCompare:
262 [dataSource tableView: aTableView objectValueForTableColumn: col row: i]];
263 if (order != NSOrderedDescending) break;
264 }
265 if (i >= rowCount) i = rowCount - 1;
266 }
267 // Now select row i -- either the one we found, or the first/last row if not found.
268 [aTableView selectRow: i byExtendingSelection: NO];
269 [aTableView scrollRowToVisible: i];
270}
271
272@end
Note: See TracBrowser for help on using the repository browser.