1 /* xscreensaver, Copyright (c) 2012 Jamie Zawinski <jwz@jwz.org>
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
11 * This implements the top-level screen-saver selection list in the iOS app.
14 #ifdef USE_IPHONE // whole file
17 #import "SaverListController.h"
18 #import "SaverRunner.h"
23 #define countof(x) (sizeof((x))/sizeof((*x)))
26 @implementation SaverListController
28 - (void) titleTapped:(id) sender
30 [[UIApplication sharedApplication]
31 openURL:[NSURL URLWithString:@"http://www.jwz.org/xscreensaver/"]];
37 // Extract the version number and release date from the version string.
38 // Here's an area where I kind of wish I had "Two Problems".
40 NSArray *a = [[NSString stringWithCString: screensaver_id
41 encoding:NSASCIIStringEncoding]
42 componentsSeparatedByCharactersInSet:
44 characterSetWithCharactersInString:@" ()-"]];
45 NSString *vers = [a objectAtIndex: 3];
46 NSString *year = [a objectAtIndex: 7];
48 NSString *line1 = [@"XScreenSaver " stringByAppendingString: vers];
49 NSString *line2 = [@"\u00A9 " stringByAppendingString:
50 [year stringByAppendingString:
51 @" Jamie Zawinski <jwz@jwz.org>"]];
53 UIView *v = [[UIView alloc] initWithFrame:CGRectZero];
55 // The "go to web page" button on the right
57 UIImage *img = [UIImage imageWithContentsOfFile:
58 [[[NSBundle mainBundle] bundlePath]
59 stringByAppendingPathComponent:
60 @"iSaverRunner29t.png"]];
61 UIBarButtonItem *button = [[[UIBarButtonItem alloc]
63 style: UIBarButtonItemStylePlain
65 action: @selector(titleTapped:)]
67 button.width = img.size.width;
68 self.navigationItem.rightBarButtonItem = button;
72 UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectZero];
73 UILabel *label2 = [[UILabel alloc] initWithFrame:CGRectZero];
74 [label1 setText: line1];
75 [label2 setText: line2];
76 [label1 setBackgroundColor:[UIColor clearColor]];
77 [label2 setBackgroundColor:[UIColor clearColor]];
79 [label1 setFont: [UIFont boldSystemFontOfSize: 17]];
80 [label2 setFont: [UIFont systemFontOfSize: 12]];
84 CGRect r1 = [label1 frame];
85 CGRect r2 = [label2 frame];
88 CGRect win = [self view].frame;
89 if (win.size.width > 320) { // iPad
90 [label1 setTextAlignment: UITextAlignmentLeft];
91 [label2 setTextAlignment: UITextAlignmentRight];
92 label2.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
93 r3.size.width = win.size.width;
100 r3.size.width = 320; // force it to be flush-left
101 [label1 setTextAlignment: UITextAlignmentLeft];
102 [label2 setTextAlignment: UITextAlignmentLeft];
103 r1.origin.y = -1; // make it fit in landscape
104 r2.origin.y = r1.origin.y + r1.size.height - 2;
105 r3.size.height = r1.size.height + r2.size.height;
107 v.autoresizingMask = UIViewAutoresizingFlexibleWidth;
108 [label1 setFrame:r1];
109 [label2 setFrame:r2];
112 [v addSubview:label1];
113 [v addSubview:label2];
115 self.navigationItem.titleView = v;
119 - (id)initWithNames:(NSArray *)names descriptions:(NSDictionary *)descs;
122 if (! self) return 0;
123 [self reload:names descriptions:descs];
129 - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tv
131 int n = countof(list_by_letter);
132 NSMutableArray *a = [NSMutableArray arrayWithCapacity: n];
133 for (int i = 0; i < n; i++) {
135 s[0] = (i == 'Z'-'A'+1 ? '#' : i+'A');
137 [a addObject: [NSString stringWithCString:s
138 encoding:NSASCIIStringEncoding]];
144 - (void) reload:(NSArray *)names descriptions:(NSDictionary *)descs
147 [descriptions release];
148 descriptions = [descs retain];
150 int n = countof(list_by_letter);
151 for (int i = 0; i < n; i++) {
152 list_by_letter[i] = [[NSMutableArray alloc] init];
155 for (NSString *name in names) {
156 int index = ([name cStringUsingEncoding: NSASCIIStringEncoding])[0];
157 if (index >= 'a' && index <= 'z')
159 if (index >= 'A' && index <= 'Z')
163 [list_by_letter[index] addObject: name];
166 active_section_count = 0;
167 letter_sections = [[[NSMutableArray alloc] init] retain];
168 section_titles = [[[NSMutableArray alloc] init] retain];
169 for (int i = 0; i < n; i++) {
170 if ([list_by_letter[i] count] > 0) {
171 active_section_count++;
172 [letter_sections addObject: list_by_letter[i]];
174 [section_titles addObject: [NSString stringWithFormat: @"%c", i+'A']];
176 [section_titles addObject: @"#"];
179 [self.tableView reloadData];
183 - (NSString *)tableView:(UITableView *)tv
184 titleForHeaderInSection:(NSInteger)section
186 return [section_titles objectAtIndex: section];
189 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tv
191 return active_section_count;
195 - (NSInteger)tableView:(UITableView *)tv
196 numberOfRowsInSection:(NSInteger)section
198 return [[letter_sections objectAtIndex: section] count];
201 - (NSInteger)tableView:(UITableView *)tv
202 sectionForSectionIndexTitle:(NSString *)title
203 atIndex:(NSInteger) index
206 for (NSString *sectionTitle in section_titles) {
207 if ([sectionTitle isEqualToString: title])
215 - (UITableViewCell *)tableView:(UITableView *)tv
216 cellForRowAtIndexPath:(NSIndexPath *)ip
219 [[letter_sections objectAtIndex: [ip indexAtPosition: 0]]
220 objectAtIndex: [ip indexAtPosition: 1]];
221 NSString *desc = [descriptions objectForKey:id];
223 UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier: id];
225 cell = [[[UITableViewCell alloc]
227 ? UITableViewCellStyleSubtitle
228 : UITableViewCellStyleDefault)
231 cell.textLabel.text = id;
233 cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
235 cell.detailTextLabel.text = desc;
241 /* Selecting a row launches the saver.
243 - (void)tableView:(UITableView *)tv
244 didSelectRowAtIndexPath:(NSIndexPath *)ip
246 UITableViewCell *cell = [tv cellForRowAtIndexPath: ip];
248 (SaverRunner *) [[UIApplication sharedApplication] delegate];
250 NSAssert ([s isKindOfClass:[SaverRunner class]], @"not a SaverRunner");
251 [s loadSaver: cell.textLabel.text];
254 /* Selecting a row's Disclosure Button opens the preferences.
256 - (void)tableView:(UITableView *)tv
257 accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)ip
259 UITableViewCell *cell = [tv cellForRowAtIndexPath: ip];
261 (SaverRunner *) [[UIApplication sharedApplication] delegate];
263 NSAssert ([s isKindOfClass:[SaverRunner class]], @"not a SaverRunner");
264 [s openPreferences: cell.textLabel.text];
268 - (void) scrollTo: (NSString *) name
273 for (NSArray *a in letter_sections) {
275 for (NSString *n in a) {
276 ok = [n isEqualToString: name];
284 NSIndexPath *ip = [NSIndexPath indexPathForRow: j inSection: i];
285 [self.tableView selectRowAtIndexPath:ip
287 scrollPosition: UITableViewScrollPositionMiddle];
292 - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)o
298 /* We need this to respond to "shake" gestures
300 - (BOOL)canBecomeFirstResponder
305 - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
309 - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
314 /* Shake means load a random screen saver.
316 - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
318 NSMutableArray *a = [NSMutableArray arrayWithCapacity: 200];
319 for (NSArray *sec in letter_sections)
320 for (NSString *s in sec)
324 NSString *which = [a objectAtIndex: (random() % n)];
327 (SaverRunner *) [[UIApplication sharedApplication] delegate];
329 NSAssert ([s isKindOfClass:[SaverRunner class]], @"not a SaverRunner");
330 [self scrollTo: which];
331 [s loadSaver: which];
337 for (int i = 0; i < countof(list_by_letter); i++)
338 [list_by_letter[i] release];
339 [letter_sections release];
340 [section_titles release];
341 [descriptions release];
348 #endif // USE_IPHONE -- whole file