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

Last change on this file since 355 was 53, checked in by Nicholas Riley, 19 years ago

Updated for Pester 1.1a5 (very limited release).

Pester 1.1a4 was never released.

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.