From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / OSX / SaverListController.m
1 /* xscreensaver, Copyright (c) 2012 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * This implements the top-level screen-saver selection list in the iOS app.
12  */
13
14 #ifdef USE_IPHONE  // whole file
15
16
17 #import "SaverListController.h"
18
19 #undef countof
20 #define countof(x) (sizeof((x))/sizeof((*x)))
21
22
23 @implementation SaverListController
24
25 - (id)initWithNames:(NSArray *)names descriptions:(NSDictionary *)descs;
26 {
27   self = [self init];
28   if (! self) return 0;
29   [self reload:names descriptions:descs];
30   return self;
31 }
32
33
34 - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tv
35 {
36   int n = countof(list_by_letter);
37   NSMutableArray *a = [NSMutableArray arrayWithCapacity: n];
38   for (int i = 0; i < n; i++) {
39     char s[2];
40     s[0] = (i == 'Z'-'A'+1 ? '#' : i+'A');
41     s[1] = 0;
42     [a addObject: [NSString stringWithCString:s
43                             encoding:NSASCIIStringEncoding]];
44   }
45   return a;
46 }
47
48
49 - (void) reload:(NSArray *)names descriptions:(NSDictionary *)descs
50 {
51   if (descriptions)
52     [descriptions release];
53   descriptions = [descs retain];
54
55   int n = countof(list_by_letter);
56   for (int i = 0; i < n; i++) {
57     list_by_letter[i] = [[NSMutableArray alloc] init];
58   }
59
60   for (NSString *name in names) {
61     int index = ([name cStringUsingEncoding: NSASCIIStringEncoding])[0];
62     if (index >= 'a' && index <= 'z')
63       index -= 'a'-'A';
64     if (index >= 'A' && index <= 'Z')
65       index -= 'A';
66     else
67       index = n-1;
68     [list_by_letter[index] addObject: name];
69   }
70
71   active_section_count = 0;
72   letter_sections = [[[NSMutableArray alloc] init] retain];
73   section_titles = [[[NSMutableArray alloc] init] retain];
74   for (int i = 0; i < n; i++) {
75     if ([list_by_letter[i] count] > 0) {
76       active_section_count++;
77       [letter_sections addObject: list_by_letter[i]];
78       if (i <= 'Z'-'A')
79         [section_titles addObject: [NSString stringWithFormat: @"%c", i+'A']];
80       else
81         [section_titles addObject: @"#"];
82     }
83   }
84   [self.tableView reloadData];
85 }
86
87
88 - (NSString *)tableView:(UITableView *)tv
89               titleForHeaderInSection:(NSInteger)section
90 {
91   return [section_titles objectAtIndex: section];
92 }
93
94 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tv
95 {
96   return active_section_count;
97 }
98
99
100 - (NSInteger)tableView:(UITableView *)tv
101                        numberOfRowsInSection:(NSInteger)section
102 {
103   return [[letter_sections objectAtIndex: section] count];
104 }
105
106 - (NSInteger)tableView:(UITableView *)tv
107              sectionForSectionIndexTitle:(NSString *)title
108                atIndex:(NSInteger) index
109 {
110   int i = 0;
111   for (NSString *sectionTitle in section_titles) {
112     if ([sectionTitle isEqualToString: title])
113       return i;
114     i++;
115   }
116   return -1;
117 }
118
119
120 - (UITableViewCell *)tableView:(UITableView *)tv
121                      cellForRowAtIndexPath:(NSIndexPath *)ip
122 {
123   NSString *id =
124     [[letter_sections objectAtIndex: [ip indexAtPosition: 0]]
125       objectAtIndex: [ip indexAtPosition: 1]];
126   NSString *desc = [descriptions objectForKey:id];
127
128   UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier: id];
129   if (!cell) {
130     cell = [[[UITableViewCell alloc]
131               initWithStyle: (desc
132                               ? UITableViewCellStyleSubtitle
133                               : UITableViewCellStyleDefault)
134               reuseIdentifier: id]
135              autorelease];
136     cell.textLabel.text = id;
137     if (desc)
138       cell.detailTextLabel.text = desc;
139   }
140   return cell;
141 }
142
143
144 - (void)tapTimer:(NSTimer *)t
145 {
146   [last_tap release];
147   last_tap = 0;
148   tap_count = 0;
149   tap_timer = 0;
150 }
151
152
153 - (void)tableView:(UITableView *)tv
154     didSelectRowAtIndexPath:(NSIndexPath *)ip
155 {
156   UITableViewCell *cell = [tv cellForRowAtIndexPath: ip];
157   selected = cell.textLabel.text;
158   [self.navigationItem.leftBarButtonItem  setEnabled: !!selected];
159   [self.navigationItem.rightBarButtonItem setEnabled: !!selected];
160
161   if (tap_count == 0) {                                 // First tap
162     tap_count = 1;
163     last_tap = [[ip copy] retain];
164     tap_timer = [NSTimer scheduledTimerWithTimeInterval: 0.3
165                          target:self 
166                          selector:@selector(tapTimer:)
167                          userInfo:nil
168                          repeats:NO];
169
170   } else if (tap_count == 1 && tap_timer &&             // Second tap
171              [ip isEqual:last_tap]) {
172     [tap_timer invalidate];
173     [last_tap release];
174     last_tap = 0;
175     tap_timer = 0;
176     tap_count = 0;
177  
178     // Press the leftmost button in the button-bar.
179     UIBarButtonItem *b = self.navigationItem.leftBarButtonItem;
180     [[b target] performSelector: [b action] withObject: cell];
181
182   } else if (! [ip isEqual:last_tap]) {                 // Tap on a new row
183     if (tap_timer) [tap_timer invalidate];
184     tap_timer = 0;
185     tap_count = 0;
186   }
187 }
188
189
190 /* We can't select a row immediately after creation, but selecting it
191    a little while later works (presumably after redisplay has happened)
192    so do it on a timer.
193  */
194 - (void) scrollToCB: (NSTimer *) timer
195 {
196   NSString *name = [timer userInfo];
197
198   int i = 0;
199   int j = 0;
200   Bool ok = NO;
201   for (NSArray *a in letter_sections) {
202     j = 0;
203     for (NSString *n in a) {
204       ok = [n isEqualToString: name];
205       if (ok) goto DONE;
206       j++;
207     }
208     i++;
209   }
210  DONE:
211   if (ok) {
212     NSIndexPath *ip = [NSIndexPath indexPathForRow: j inSection: i];
213     [self.tableView selectRowAtIndexPath:ip
214                     animated:NO
215                     scrollPosition: UITableViewScrollPositionMiddle];
216     [self tableView:self.tableView didSelectRowAtIndexPath:ip];
217   }
218 }
219
220
221 - (void) scrollTo: (NSString *) name
222 {
223   [NSTimer scheduledTimerWithTimeInterval: 0
224            target:self
225            selector:@selector(scrollToCB:)
226            userInfo:name
227            repeats:NO];
228 }
229
230
231 - (void)viewWillAppear:(BOOL)animated 
232 {
233   /* Hitting the back button and returing to this view deselects,
234      and we can't re-select it from here, so again, do it once
235      we return to the event loop.
236    */
237   if (selected)
238     [NSTimer scheduledTimerWithTimeInterval: 0
239              target:self
240              selector:@selector(scrollToCB:)
241              userInfo:selected
242              repeats:NO];
243   [super viewWillAppear:animated];
244 }
245
246
247 - (NSString *) selected
248 {
249   return selected;
250 }
251
252
253 - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)o
254 {
255   return YES;
256 }
257
258
259 - (void)dealloc
260 {
261   for (int i = 0; i < countof(list_by_letter); i++)
262     [list_by_letter[i] release];
263   [letter_sections release];
264   [section_titles release];
265   [descriptions release];
266   [super dealloc];
267 }
268
269 @end
270
271
272 #endif // USE_IPHONE -- whole file