public
Description: An iPhone application to query and cache results from the online Lexin Swedish-English dictionary for immigrants
Homepage:
Clone URL: git://github.com/octover/lexikon.git
Click here to lend your support to: lexikon and make a donation at www.pledgie.com !
octover (author)
Mon Feb 16 02:56:17 -0800 2009
commit  843fb66036afdea8981c7ede625aae4b44ff9551
tree    09e00ebcba988ba99c295e0caa9c9218f6d4fcf5
parent  99ae845836eb73a35e3af66cb10cc11633b351cc
lexikon / MainViewController.m
100644 498 lines (411 sloc) 19.355 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
//
// MainViewController.m
// Lexikon
//
// Created by Caleb Jaffa
// Copyright 2008-2009 Caleb Jaffa, MIT licensed
//
 
#import "MainViewController.h"
#import "Word.h"
#import "LexikonAppDelegate.h"
#import "DetailViewController.h"
#import "AboutViewController.h"
#import "SearchSuggestionsController.h"
 
@implementation MainViewController
 
@synthesize tableView, indexLetters, mySearchBar, suggestionsController;
 
- (void)awakeFromNib {
  // we need to get at properties of our application delegate
  LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
  
  // make it so the index is always displayed
  tableView.sectionIndexMinimumDisplayRowCount = 1;
  [self changeIndexLetters: appDelegate.swedishToEnglish];
  
  searching = NO; // keep track if we are in the middle of a search or not
}
 
- (void)dealloc {
  [mySearchBar release];
  [detailViewController release];
  [indexLetters release];
  [tableView release];
  [suggestionsController release];
  [super dealloc];
}
 
- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
  // Release anything that's not essential, such as cached data
}
 
- (void)viewWillAppear:(BOOL)animated {
  if (searching) {
    [mySearchBar becomeFirstResponder];
  }
  
  [self shiftOverlaysOff:NO];
  
  // update the app delegate that we are no longer viewing a specific word
  LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
  appDelegate.currentWord = nil;
  
  [super viewWillAppear:animated];
}
 
- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];
  [self shiftOverlaysOff:YES];
}
 
- (void)changeIndexLetters:(BOOL) swedish {
  // first time through set it up
  if(self.indexLetters == nil) {
    self.indexLetters = [NSMutableArray arrayWithObjects: @"{search}", @"A", @"B", @"C", @"D", @"E", @"F", @"G", @"H", @"I", @"J", @"K", @"L", @"M", @"N", @"O", @"P", @"Q", @"R", @"S", @"T", @"U", @"V", @"W", @"X", @"Y", @"Z", nil];
  }
  
  if(swedish) {
    [self.indexLetters addObject: @"Å"];
    [self.indexLetters addObject: @"Ä"];
    [self.indexLetters addObject: @"Ö"];
    languageSwitcherButton.title = NSLocalizedString(@"Swe to Eng", @"Language toggle button Swedish to English");
  }
  else {
    [self.indexLetters removeObjectsInArray: [NSArray arrayWithObjects: @"Å", @"Ä", @"Ö", nil]];
    languageSwitcherButton.title = NSLocalizedString(@"Eng to Swe", @"Lanuage toggle button English to Swedish");
  }
}
 
- (IBAction)switchLanguage:(id)sender {
  LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
  
  BOOL swedish = [appDelegate toggleSwedishToEnglish];
  
  [self changeIndexLetters: swedish];
  
  // if we are searching we might need to update the suggestions/filter list
  if (searching) {
    [self searchBar:mySearchBar textDidChange:mySearchBar.text];
  }
  
  [self.tableView reloadData];
}
 
- (IBAction)showAbout:(id)sender {
  AboutViewController *aboutViewController = [[AboutViewController alloc] initWithNibName:@"AboutView" bundle:nil];
  [[self navigationController] pushViewController:aboutViewController animated: YES];
  self.navigationController.navigationBarHidden = NO;
  [aboutViewController release];
}
 
 
#pragma mark TableView Datasource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  if(section == 0) {
    return 1; // return 1 for section 0 all other will be dynamic
  }
  else {
    // we need to get at properties in our app delegate
    LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSString *sectionLetter = [self.indexLetters objectAtIndex:section];
    NSMutableArray *wordsForSection = [appDelegate.currentWords objectForKey:sectionLetter];
    
    return [wordsForSection count];
  }
}
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  return [self.indexLetters count];
}
 
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
  return self.indexLetters;
}
 
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
  // return nil for the searchBar (1st section)
  if(section == 0) {
    return nil;
  }
  else {
    // NSLog(@"section # %d", section);
    LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
    
    // if there are no words for a letter return a blank string so the UI isn't cluttered
    NSString *sectionLetter = [self.indexLetters objectAtIndex:section];
    NSMutableArray *wordsForSection = [appDelegate.currentWords objectForKey:sectionLetter];
    if(wordsForSection == nil || [wordsForSection count] == 0) {
      return @"";
    }
    else {
      return sectionLetter;
    }
  }
}
 
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  // searchBar cell gets its own identifier
  NSString *identifier = (indexPath.section == 0) ? @"search" : @"cell";
  
  UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:identifier];
  if(cell == nil) {
    cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:identifier] autorelease];
  }
  
  //NSLog(@"Row: %i,%i ID: %@", indexPath.section, indexPath.row, identifier);
  if(indexPath.section == 0) {
    // we don't need to redo the cell contents if they already exist
    if (self.mySearchBar == nil) {
      // TODO: navigation bar or image to fill in the gap so our seach bar can be 290 pixels and not over the index
      UINavigationBar *myNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectZero];
      [myNavigationBar setTintColor: [UIColor colorWithRed:0.769 green:0.80 blue:0.824 alpha:1.0]];
      [myNavigationBar sizeToFit];
      [cell.contentView addSubview:myNavigationBar];
      [myNavigationBar release];
      
      // Now add our search bar
      UISearchBar *aSearchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
      [aSearchBar setTintColor: [UIColor colorWithRed:0.769 green:0.80 blue:0.824 alpha:1.0]];
      [aSearchBar sizeToFit];
      aSearchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
      aSearchBar.placeholder = NSLocalizedString(@"Search", @"Search placeholder");
      aSearchBar.delegate = self;
      
      aSearchBar.frame = CGRectMake(0, 0, 290, 44);
      [cell.contentView addSubview:aSearchBar];
      self.mySearchBar = [aSearchBar retain];
      [aSearchBar release];
    }
  }
  else {
    LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSString *sectionLetter = [self.indexLetters objectAtIndex:indexPath.section];
    NSMutableArray *wordsForSection = [appDelegate.currentWords objectForKey:sectionLetter];
    
    cell.text = [[wordsForSection objectAtIndex:indexPath.row] word];
  }
  
  return cell;
}
 
- (void)tableView:(UITableView *)aTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
  LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
  
  if (editingStyle == UITableViewCellEditingStyleDelete) {
    // first remove from our word list and the database
    [appDelegate removeWordAtSectionLetter:[self.indexLetters objectAtIndex:indexPath.section] index:indexPath.row];
    // remove from the tableview
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
  }
}
 
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
  if(indexPath.section == 0) {
    return NO;
  }
  else {
    return YES;
  }
}
#pragma mark TableView Delegate
 
- (void)viewWord:(Word *) word {
  if (detailViewController == nil) {
    detailViewController = [[DetailViewController alloc] init];
  }
  
  // update the app delegate with what word we are viewing
  LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
  appDelegate.currentWord = word.word;
  
  detailViewController.word = word.word;
  detailViewController.html = [[NSString stringWithContentsOfFile:[[[NSBundle mainBundle] resourcePath]
                                                                   stringByAppendingPathComponent:@"translationTemplate.html"]]
                               stringByReplacingOccurrencesOfString:@"{yield}" withString:word.translation];
  
  [self.navigationController pushViewController:detailViewController animated:YES];
}
 
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  if (indexPath.section == 0) {
    return nil;
  }
  LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
  NSMutableArray *wordsForSection = [appDelegate.currentWords objectForKey:[self.indexLetters objectAtIndex:indexPath.section]];
  Word *myWord = [wordsForSection objectAtIndex:indexPath.row];
  
  [self viewWord: myWord];
  
  return nil;
}
 
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath {
  [self hideIndex:YES];
}
 
- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath {
  [self hideIndex:NO];
}
 
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
  if(indexPath.section == 0) {
    return UITableViewCellEditingStyleNone;
  }
  else {
    return UITableViewCellEditingStyleDelete;
  }
}
 
#pragma mark UISearchBarDelegate
 
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
  if (! searching) {
    // if we pop back to this view we might already be in editing mode, only activate when needed
    searching = YES;
    [self hideIndex:YES];
  }
}
 
- (void)shiftOverlaysOff:(BOOL) shiftOff {
  // shift the cancelSearchTableCover and suggestionsController.view off by sliding off the screen but by
  // doing this manually we have more control than making these a subview of the MainViewController's tableView
  int newX = (shiftOff) ? 0-tableView.window.frame.size.width : 0;
 
  [UIView beginAnimations:nil context:nil];
  [UIView setAnimationDuration:0.35];
  
  cancelSearchTableCover.frame = CGRectMake(newX,
                                            cancelSearchTableCover.frame.origin.y,
                                            cancelSearchTableCover.frame.size.width,
                                            cancelSearchTableCover.frame.size.height);
  suggestionsController.view.frame = CGRectMake(newX,
                                                suggestionsController.view.frame.origin.y,
                                                suggestionsController.view.frame.size.width,
                                                suggestionsController.view.frame.size.height);
  
  [UIView commitAnimations];
}
 
- (void)showOverlays:(BOOL) show {
  // this handles showing and hiding the different overlays
  // show is NO hide cancelCoverButton and suggestions
  // show is YES show cancelCoverButton is mySearchBar.text.length == 0
  // or show suggestionsController.view
  tableView.scrollEnabled = ! show; // make sure the user can't scroll the table by catching an edge of the search field
 
  [UIView beginAnimations:nil context:nil];
  [UIView setAnimationDuration:0.2];
  
  if (! show) {
    cancelSearchTableCover.alpha = 0.0f;
    suggestionsController.view.hidden = YES;
  }
  else {
    if (mySearchBar.text.length > 0) {
      suggestionsController.view.hidden = NO;
    }
    else {
      suggestionsController.view.hidden = YES;
      cancelSearchTableCover.alpha = 0.8f;
    }
  }
 
  [UIView commitAnimations];
}
 
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
  if (suggestionsController == nil) {
    suggestionsController = [[SearchSuggestionsController alloc] initWithNibName:@"SearchSuggestions" bundle:nil];
    //suggestionsController.view.alpha = 1.0f;
    suggestionsController.main = self;
    suggestionsController.view.frame = CGRectMake(suggestionsController.view.frame.origin.x,
                                             suggestionsController.view.frame.origin.y+88,
                                             suggestionsController.view.frame.size.width,
                                             suggestionsController.view.frame.size.height);
    [self.navigationController.view addSubview:suggestionsController.view];
  }
  
  [self showOverlays:YES];
  
  if (searchBar.text.length > 0) {
    LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
    
    // find the filtered list of words that match and put them into the search suggestion table
    NSMutableArray *suggestions = [[NSMutableArray alloc] init];
    
    for (NSString *letter in self.indexLetters) {
      for (Word *aWord in [appDelegate.currentWords objectForKey:letter]) {
        if([aWord.word rangeOfString:searchBar.text options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch)].location != NSNotFound) {
          [suggestions addObject:aWord];
        }
      }
    }
 
    suggestionsController.suggestions = suggestions;
    [suggestions release];
    [suggestionsController.tableView reloadData];
  }
}
 
 
- (void)cancelSearching {
  [self hideIndex:NO];
  searching = NO;
  self.mySearchBar.text = @"";
  [self.mySearchBar resignFirstResponder];
  [self showOverlays:NO];
}
 
- (void) toggleButtonItem {
  UIBarButtonItem *buttonItem;
  // change the About button to a cancel button
  if (self.navigationItem.rightBarButtonItem.action == @selector(showAbout:)) {
    tableView.sectionIndexMinimumDisplayRowCount = NSIntegerMax;
    buttonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelSearching)];
  }
  else {
    tableView.sectionIndexMinimumDisplayRowCount = 1;
    buttonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"About", @"About button") style:UIBarButtonItemStylePlain target:self action:@selector(showAbout:)];
  }
  [self.navigationItem setRightBarButtonItem: buttonItem animated:YES];
  [buttonItem release];
}
 
- (void)hideIndex:(BOOL) hide {
  CGFloat factor = (hide) ? 30.0 : -30.0;
  
  if (cancelSearchTableCover == nil) {
    cancelSearchTableCover = [UIButton buttonWithType:UIButtonTypeCustom];
    [cancelSearchTableCover addTarget:self action:@selector(cancelSearching) forControlEvents:UIControlEventTouchDown];
    cancelSearchTableCover.backgroundColor = [UIColor blackColor];
    cancelSearchTableCover.alpha = 0.0f;
    cancelSearchTableCover.frame = CGRectMake(0, 88, 320, 320);
    [self.navigationController.view addSubview:cancelSearchTableCover];
  }
  
  if (searching) {
    [self toggleButtonItem];
    [self showOverlays:hide];
  }
  
  // setup the animation
  [UIView beginAnimations:nil context:nil];
  [UIView setAnimationDuration:0.2];
 
  mySearchBar.frame = CGRectMake(mySearchBar.frame.origin.x,
                                 mySearchBar.frame.origin.y,
                                 mySearchBar.frame.size.width + factor,
                                 mySearchBar.frame.size.height);
  
  // change the UISearchBar's size
  for ( UIView *view in mySearchBar.subviews) {
    if ([view isKindOfClass:NSClassFromString(@"UISearchBarTextField")]) {
      view.frame = CGRectMake(view.frame.origin.x,
                              view.frame.origin.y,
                              view.frame.size.width + factor,
                              view.frame.size.height);
    }
  }
  
  // move the index out of the way
// for ( UIView *view in tableView.subviews ) {
// if ([view isKindOfClass:NSClassFromString(@"UITableViewIndex")]) {
// view.center = CGPointMake(view.center.x + factor, view.center.y);
// }
// }
  [tableView setIndexHidden:hide animated:NO];
  [UIView commitAnimations];
}
 
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
  LexikonAppDelegate *appDelegate = (LexikonAppDelegate *)[[UIApplication sharedApplication] delegate];
  [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
  
  NSString *searchWord = [searchBar.text capitalizedString];
  NSString *translationString;
  NSString *translation;
  NSURL *translationURL;
  NSRange start, end;
  int encoding;
 
  [self cancelSearching];
 
  if(appDelegate.swedishToEnglish) {
    translationString = [[NSString stringWithFormat:@"http://lexin.nada.kth.se/Lexin/?dict=sve-eng&lang=source&word=%@", searchWord] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
    encoding = NSUTF8StringEncoding;
  }
  else {
    translationString = [[NSString stringWithFormat:@"http://lexin.nada.kth.se/cgi-bin/sve-eng?:%@", searchWord] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
    encoding = NSISOLatin1StringEncoding;
  }
 
  // clean up the special chars
  translationURL = [NSURL URLWithString:translationString];
  
  // get the html contents in a string
  NSError *myError;
  translation = [NSString stringWithContentsOfURL:translationURL encoding:encoding error:&myError];
  
  if(translation != nil) {
    start = [translation rangeOfString:@"<DL>"];
    end = [translation rangeOfString:@"</DL>" options:NSBackwardsSearch];
    if(! (start.location == NSNotFound && end.location == NSNotFound)) {
      NSRange myRange;
      myRange.location = start.location;
      myRange.length = end.location - start.location;
      translation = [translation substringWithRange: myRange];
      
      Word *newWord = [[Word alloc] init];
      newWord.word = searchWord;
      newWord.lang = (appDelegate.swedishToEnglish) ? SWE_LANGUAGE : ENG_LANGUAGE;
      newWord.translation = translation;
      
      [appDelegate addWordToDictionary:appDelegate.currentWords word:newWord andDatabase:YES];
      [self viewWord:newWord];
      [tableView reloadData];
    }
    else {
      // display error message that the word was not found
      [self searchFailed:[NSString stringWithFormat:NSLocalizedString(@"%@ not found", @"word not found"), searchWord]];
    }
  }
  else {
    // display error message alerting the user that we were not able to contact the Lexin website
    [self searchFailed:NSLocalizedString(@"Unable to reach the Lexin website", @"Error message when unable to contact Lexin")];
    //[NSString stringWithFormat:@"%@ %@", [myError localizedDescription], [myError localizedFailureReason]]];
  }
  
  // hide the activity indicator
  [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
  //[searchWord release];
  searchWord = nil;
}
 
- (void)searchFailed:(NSString *)message {
  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Search Error", @"Search Error alert title")
                                                  message:message
                                                 delegate:nil
                                        cancelButtonTitle:NSLocalizedString(@"OK", @"Alert OK button")
                                        otherButtonTitles: nil];
[alert show];
[alert release];
}
 
@end