From http://www.jwz.org/xscreensaver/xscreensaver-5.17.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 #import "SaverRunner.h"
19 #import "version.h"
20
21 #undef countof
22 #define countof(x) (sizeof((x))/sizeof((*x)))
23
24
25 @implementation SaverListController
26
27 - (void) titleTapped:(id) sender
28 {
29   [[UIApplication sharedApplication]
30     openURL:[NSURL URLWithString:@"http://www.jwz.org/xscreensaver/"]];
31 }
32
33
34 - (void)makeTitleBar
35 {
36   // Extract the version number and release date from the version string.
37   // Here's an area where I kind of wish I had "Two Problems".
38
39   NSArray *a = [[NSString stringWithCString: screensaver_id
40                           encoding:NSASCIIStringEncoding]
41                  componentsSeparatedByCharactersInSet:
42                    [NSCharacterSet
43                      characterSetWithCharactersInString:@" ()-"]];
44   NSString *vers = [a objectAtIndex: 3];
45   NSString *year = [a objectAtIndex: 7];
46
47   NSString *line1 = [@"XScreenSaver " stringByAppendingString: vers];
48   NSString *line2 = [@"\u00A9 " stringByAppendingString:
49                         [year stringByAppendingString:
50                                 @" Jamie Zawinski <jwz@jwz.org>"]];
51
52   UIView *v = [[UIView alloc] initWithFrame:CGRectZero];
53
54   // The "go to web page" button on the right
55
56   UIImage *img = [UIImage imageWithContentsOfFile:
57                             [[[NSBundle mainBundle] bundlePath]
58                               stringByAppendingPathComponent:
59                                 @"iSaverRunner29t.png"]];
60   UIBarButtonItem *button = [[[UIBarButtonItem alloc]
61                                initWithImage: img
62                                style: UIBarButtonItemStylePlain
63                                target: self
64                                action: @selector(titleTapped:)]
65                               autorelease];
66   button.width = img.size.width;
67   self.navigationItem.rightBarButtonItem = button;
68
69   // The title bar
70
71   UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectZero];
72   UILabel *label2 = [[UILabel alloc] initWithFrame:CGRectZero];
73   [label1 setText: line1];
74   [label2 setText: line2];
75   [label1 setBackgroundColor:[UIColor clearColor]];
76   [label2 setBackgroundColor:[UIColor clearColor]];
77
78   [label1 setFont: [UIFont boldSystemFontOfSize: 17]];
79   [label2 setFont: [UIFont systemFontOfSize: 12]];
80   [label1 sizeToFit];
81   [label2 sizeToFit];
82
83   CGRect r1 = [label1 frame];
84   CGRect r2 = [label2 frame];
85   CGRect r3 = r2;
86
87   CGRect win = [self view].frame;
88   if (win.size.width > 320) {                                   // iPad
89     [label1 setTextAlignment: UITextAlignmentLeft];
90     [label2 setTextAlignment: UITextAlignmentRight];
91     label2.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
92     r3.size.width = win.size.width;
93     r1 = r3;
94     r1.origin.x   += 6;
95     r1.size.width -= 12;
96     r2 = r1;
97
98   } else {                                                      // iPhone
99     r3.size.width = 320; // force it to be flush-left
100     [label1 setTextAlignment: UITextAlignmentLeft];
101     [label2 setTextAlignment: UITextAlignmentLeft];
102     r1.origin.y = -1;    // make it fit in landscape
103     r2.origin.y = r1.origin.y + r1.size.height - 2;
104     r3.size.height = r1.size.height + r2.size.height;
105   }
106   v.autoresizingMask = UIViewAutoresizingFlexibleWidth;
107   [label1 setFrame:r1];
108   [label2 setFrame:r2];
109   [v setFrame:r3];
110
111   [v addSubview:label1];
112   [v addSubview:label2];
113
114   self.navigationItem.titleView = v;
115 }
116
117
118 - (id)initWithNames:(NSArray *)names descriptions:(NSDictionary *)descs;
119 {
120   self = [self init];
121   if (! self) return 0;
122   [self reload:names descriptions:descs];
123   [self makeTitleBar];
124   return self;
125 }
126
127
128 - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tv
129 {
130   int n = countof(list_by_letter);
131   NSMutableArray *a = [NSMutableArray arrayWithCapacity: n];
132   for (int i = 0; i < n; i++) {
133     char s[2];
134     s[0] = (i == 'Z'-'A'+1 ? '#' : i+'A');
135     s[1] = 0;
136     [a addObject: [NSString stringWithCString:s
137                             encoding:NSASCIIStringEncoding]];
138   }
139   return a;
140 }
141
142
143 - (void) reload:(NSArray *)names descriptions:(NSDictionary *)descs
144 {
145   if (descriptions)
146     [descriptions release];
147   descriptions = [descs retain];
148
149   int n = countof(list_by_letter);
150   for (int i = 0; i < n; i++) {
151     list_by_letter[i] = [[NSMutableArray alloc] init];
152   }
153
154   for (NSString *name in names) {
155     int index = ([name cStringUsingEncoding: NSASCIIStringEncoding])[0];
156     if (index >= 'a' && index <= 'z')
157       index -= 'a'-'A';
158     if (index >= 'A' && index <= 'Z')
159       index -= 'A';
160     else
161       index = n-1;
162     [list_by_letter[index] addObject: name];
163   }
164
165   active_section_count = 0;
166   letter_sections = [[[NSMutableArray alloc] init] retain];
167   section_titles = [[[NSMutableArray alloc] init] retain];
168   for (int i = 0; i < n; i++) {
169     if ([list_by_letter[i] count] > 0) {
170       active_section_count++;
171       [letter_sections addObject: list_by_letter[i]];
172       if (i <= 'Z'-'A')
173         [section_titles addObject: [NSString stringWithFormat: @"%c", i+'A']];
174       else
175         [section_titles addObject: @"#"];
176     }
177   }
178   [self.tableView reloadData];
179 }
180
181
182 - (NSString *)tableView:(UITableView *)tv
183               titleForHeaderInSection:(NSInteger)section
184 {
185   return [section_titles objectAtIndex: section];
186 }
187
188 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tv
189 {
190   return active_section_count;
191 }
192
193
194 - (NSInteger)tableView:(UITableView *)tv
195                        numberOfRowsInSection:(NSInteger)section
196 {
197   return [[letter_sections objectAtIndex: section] count];
198 }
199
200 - (NSInteger)tableView:(UITableView *)tv
201              sectionForSectionIndexTitle:(NSString *)title
202                atIndex:(NSInteger) index
203 {
204   int i = 0;
205   for (NSString *sectionTitle in section_titles) {
206     if ([sectionTitle isEqualToString: title])
207       return i;
208     i++;
209   }
210   return -1;
211 }
212
213
214 - (UITableViewCell *)tableView:(UITableView *)tv
215                      cellForRowAtIndexPath:(NSIndexPath *)ip
216 {
217   NSString *id =
218     [[letter_sections objectAtIndex: [ip indexAtPosition: 0]]
219       objectAtIndex: [ip indexAtPosition: 1]];
220   NSString *desc = [descriptions objectForKey:id];
221
222   UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier: id];
223   if (!cell) {
224     cell = [[[UITableViewCell alloc]
225               initWithStyle: (desc
226                               ? UITableViewCellStyleSubtitle
227                               : UITableViewCellStyleDefault)
228               reuseIdentifier: id]
229              autorelease];
230     cell.textLabel.text = id;
231
232     cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
233     if (desc)
234       cell.detailTextLabel.text = desc;
235   }
236   return cell;
237 }
238
239
240 /* Selecting a row launches the saver.
241  */
242 - (void)tableView:(UITableView *)tv
243         didSelectRowAtIndexPath:(NSIndexPath *)ip
244 {
245   UITableViewCell *cell = [tv cellForRowAtIndexPath: ip];
246   SaverRunner *s = 
247     (SaverRunner *) [[UIApplication sharedApplication] delegate];
248   if (! s) return;
249   if (! [s isKindOfClass:[SaverRunner class]])
250     abort();
251   [s loadSaver: cell.textLabel.text];
252 }
253
254 /* Selecting a row's Disclosure Button opens the preferences.
255  */
256 - (void)tableView:(UITableView *)tv
257         accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)ip
258 {
259   UITableViewCell *cell = [tv cellForRowAtIndexPath: ip];
260   SaverRunner *s = 
261     (SaverRunner *) [[UIApplication sharedApplication] delegate];
262   if (! s) return;
263   if (! [s isKindOfClass:[SaverRunner class]])
264     abort();
265   [s openPreferences: cell.textLabel.text];
266 }
267
268
269 - (void) scrollTo: (NSString *) name
270 {
271   int i = 0;
272   int j = 0;
273   Bool ok = NO;
274   for (NSArray *a in letter_sections) {
275     j = 0;
276     for (NSString *n in a) {
277       ok = [n isEqualToString: name];
278       if (ok) goto DONE;
279       j++;
280     }
281     i++;
282   }
283  DONE:
284   if (ok) {
285     NSIndexPath *ip = [NSIndexPath indexPathForRow: j inSection: i];
286     [self.tableView selectRowAtIndexPath:ip
287                     animated:NO
288                     scrollPosition: UITableViewScrollPositionMiddle];
289   }
290 }
291
292
293 - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)o
294 {
295   return YES;
296 }
297
298
299 - (void)dealloc
300 {
301   for (int i = 0; i < countof(list_by_letter); i++)
302     [list_by_letter[i] release];
303   [letter_sections release];
304   [section_titles release];
305   [descriptions release];
306   [super dealloc];
307 }
308
309 @end
310
311
312 #endif // USE_IPHONE -- whole file