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

Last change on this file since 547 was 364, checked in by Nicholas Riley, 17 years ago

English.lproj/Alarms.nib: Specify alternating row coloring in the nib,
now we're 10.4+.

English.lproj/InfoPlist.strings: Updated for 1.1b6.

English.lproj/Localizable.strings: Quote alarm message in pretty
description (used in tooltip). Change voice error now it no longer
incorporates OSStatus.

English.lproj/MainMenu.nib: Add speech prefs again; turn repetitions
field into a NJRValidatingField and hook up its delegate.

Info-Pester.plist: Updated for 1.1b6.

NJRHotKey.m: Switch to new Objective-C exception style.

NJRIntervalField.[hm]: Now a subclass of NJRValidatingField.

NJRTableDelegate.m: Get rid of our own tooltip support as NSTableView
now supports them (though with a minor visual glitch on the first
tooltip).

NJRTableView.[hm]: Remove tooltip support. Remove alternating row
coloring support.

NJRValidatingField.[hm]: Contains validation sheet stuff from
NJRIntervalField.

NJRVoicePopUpButton.[hm]: Switch to NSSpeechSynthesizer.

PSAlarm.m: Quote alarm message in pretty description (used in
tooltip). Fix repeating alarms not restoring as repeating if they
didn't expire while Pester was not running. No longer set timer on
Pester 1.0 alarm import, to help make importing atomic.

PSAlarmSetController.[hm]: Use NJRValidatingField for repetitions
field. Switch to new Objective-C exception style. Fix validation
issues on in/at changing. Temporary changes to restore speech support
and allow the sound popup to be removed entirely from the nib (rather
than being dragged out of the visible area, as it was in 1.1b5).
Changes for NSSpeechSynthesizer, which uses different voice names.

PSAlarms.m: Switch to new Objective-C exception style. Fix
duplication and error handling in Pester 1.0 alarm import, making
atomic.

PSAlarmsController.m: Use new tooltip support (since it's implemented
in the delegate rather than the data source, we have to proxy it).

PSAlerts.m: Wrap initialization in exception block so we don't leak.

PSApplication.m: Switch to new Objective-C exception style.

PSMediaAlert.m: Clamp repetitions at 1..99 so the user can't type an
invalid value, then quit and have it saved.

PSSpeechAlert.[hm]: Switch to NSSpeechSynthesizer. Throw an
intelligible exception if the voice is unavailable.

PSTimer.m: Switch to new Objective-C exception style.

Pester.xcodeproj: Remove VERSION generation; rename targets to be more
understandable.

Read Me.rtfd: Updated for 1.1b6.

SUSpeaker.[hm]: Gone in switch to NSSpeechSynthesizer.

VERSION: Gone - we use agvtool for everything now.

Updates/release-notes.html: Updated for 1.1b6.

Updates/updates.xml: Updated for 1.1b6.

package-Pester.sh: Use agvtool to get version. Atomically update
file on Web server to avoid partial downloads.

File size: 9.4 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 (NSTableViewDelegate)
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- (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc row:(int)rowIndex mouseLocation:(NSPoint)mouseLocation;
246{
247 id dataSource = [aTableView dataSource];
248
249 if ([dataSource respondsToSelector: @selector(toolTipForRow:)])
250 return [dataSource toolTipForRow: rowIndex];
251
252 return nil;
253}
254
255@end
256
257@implementation NJRTableDelegate (NJRTableViewDelegate)
258
259- (void)tableView:(NSTableView *)aTableView selectRowMatchingString:(NSString *)matchString;
260{
261 // Look for a highlighted column, presuming we are sorted by that column, and search its values.
262 NSTableColumn *col = [aTableView highlightedTableColumn];
263 id dataSource = [aTableView dataSource];
264 int i, rowCount = [reorderedData count];
265 if (nil == col) return;
266 if (sortDescending) {
267 for ( i = rowCount - 1 ; i >= 0 ; i-- ) {
268 NSComparisonResult order = [matchString caseInsensitiveCompare:
269 [dataSource tableView: aTableView objectValueForTableColumn: col row: i]];
270 if (order != NSOrderedDescending) break;
271 }
272 if (i < 0) i = 0;
273 } else {
274 for ( i = 0 ; i < rowCount ; i++ ) {
275 NSComparisonResult order = [matchString caseInsensitiveCompare:
276 [dataSource tableView: aTableView objectValueForTableColumn: col row: i]];
277 if (order != NSOrderedDescending) break;
278 }
279 if (i >= rowCount) i = rowCount - 1;
280 }
281 // Now select row i -- either the one we found, or the first/last row if not found.
282 [aTableView selectRow: i byExtendingSelection: NO];
283 [aTableView scrollRowToVisible: i];
284}
285
286@end
Note: See TracBrowser for help on using the repository browser.