Added 'About' screen, localized application name
[zxing.git] / iphone / Classes / DecoderViewController.m
index 73cb075..9332484 100644 (file)
 #import "DecoderViewController.h"
 #import "Decoder.h"
 #import "NSString+HTML.h"
+#import "ResultParser.h"
 #import "ParsedResult.h"
 #import "ResultAction.h"
 
 #import "Database.h"
 #import "ArchiveController.h"
+#import "MessageViewController.h"
 #import "Scan.h"
 #import "TwoDDecoderResult.h"
 
-
 @implementation DecoderViewController
 
 @synthesize cameraBarItem;
@@ -40,7 +41,8 @@
 @synthesize actionBarItem;
 
 @synthesize messageView;
-@synthesize resultView;
+@synthesize messageTextView;
+@synthesize messageHelpButton;
 @synthesize imageView;
 @synthesize toolbar;
 
 @synthesize result;
 @synthesize actions;
 
+@synthesize resultPointViews;
+
 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
                // Initialization code
-    self.title = @"ZXing";
+    self.title = NSLocalizedString(@"DecoderViewController AppTitle", @"Barcode Scanner");
     
     Decoder *d = [[Decoder alloc] init];
     self.decoder = d;
     d.delegate = self;
     [d release];
+    resultPointViews = [[NSMutableArray alloc] init];
        }
        return self;
 }
 
+- (void) messageReady:(id)sender {
+  MessageViewController *messageController = sender;
+  [[self navigationController] pushViewController:messageController animated:true];
+  [messageController release];
+}
+
+- (void) messageFailed:(id)sender {
+  MessageViewController *messageController = sender;
+  NSLog(@"Failed to load message!");
+  [messageController release];
+}
+
+- (void) showHints:(id)sender {
+  NSLog(@"Showing Hints!");
+  
+  MessageViewController *hintsController = 
+  [[MessageViewController alloc] initWithMessageFilename:@"Hints"
+                                                  target:self 
+                                               onSuccess:@selector(messageReady:) 
+                                               onFailure:@selector(messageFailed:)];
+  hintsController.title = NSLocalizedString(@"DecoderViewController Hints MessageViewController title", @"Hints");
+  hintsController.view;
+}
+
+- (void) showAbout:(id)sender {
+  NSLog(@"Showing About!");
+  
+  MessageViewController *aboutController = 
+  [[MessageViewController alloc] initWithMessageFilename:@"About"
+                                                  target:self 
+                                               onSuccess:@selector(messageReady:) 
+                                               onFailure:@selector(messageFailed:)];
+  aboutController.title = NSLocalizedString(@"DecoderViewController About MessageViewController title", @"About");
+  aboutController.view;
+}
+
+  
+#define HELP_BUTTON_WIDTH (44.0)
+#define HELP_BUTTON_HEIGHT (55.0)
+
+
+#define FONT_NAME @"TimesNewRomanPSMT"
+#define FONT_SIZE 16.0
+
+- (void) reset {
+  self.result = nil;
+  [self clearImageView];
+  [self updateToolbar];
+  [self showMessage:NSLocalizedString(@"DecoderViewController take or choose picture", @"Please take or choose a picture containing a barcode") helpButton:YES];
+}
 
 // Implement loadView if you want to create a view hierarchy programmatically
 - (void)loadView {
   [super loadView];
   
-  CGRect mViewFrame = self.resultView.bounds;
-  UITextView *mView = [[UITextView alloc] initWithFrame:mViewFrame];
-  mView.backgroundColor = [UIColor yellowColor];
-  mView.alpha = 0.95;
-  mView.editable = false;
-  mView.scrollEnabled = true;
+  CGRect messageViewFrame = imageView.frame;
+  UIView *mView = [[UIView alloc] initWithFrame:messageViewFrame];
+  mView.backgroundColor = [UIColor darkGrayColor];
+  mView.alpha = 0.9;
   mView.autoresizingMask = UIViewAutoresizingFlexibleHeight | 
-                           UIViewAutoresizingFlexibleWidth |
-                           UIViewAutoresizingFlexibleLeftMargin |
-                           UIViewAutoresizingFlexibleRightMargin |
-                           UIViewAutoresizingFlexibleTopMargin |
-                           UIViewAutoresizingFlexibleBottomMargin;
+  UIViewAutoresizingFlexibleWidth |
+  UIViewAutoresizingFlexibleTopMargin;
+  
+  UITextView *mTextView = [[UITextView alloc] initWithFrame:messageViewFrame];
+  mTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight | 
+  UIViewAutoresizingFlexibleWidth;
+  mTextView.editable = false;
+  mTextView.scrollEnabled = true;
+  mTextView.font = [UIFont fontWithName:FONT_NAME size:FONT_SIZE];
+  mTextView.textColor = [UIColor whiteColor];
+  mTextView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.0];
+  mTextView.textAlignment = UITextAlignmentLeft;
+  mTextView.alpha = 1.0;
+  [mView addSubview:mTextView];
+
+  UIButton *mHelpButton = [[UIButton buttonWithType:UIButtonTypeInfoLight] retain];
+  mHelpButton.frame = CGRectMake(messageViewFrame.size.width - HELP_BUTTON_WIDTH, 0.0, HELP_BUTTON_WIDTH, HELP_BUTTON_HEIGHT);
+  
+  mHelpButton.backgroundColor = [UIColor clearColor];
+  [mHelpButton setUserInteractionEnabled:YES];
+  [mHelpButton addTarget:self action:@selector(showHints:) forControlEvents:UIControlEventTouchUpInside];
+
+  self.messageHelpButton = mHelpButton;
+  [mHelpButton release];
+  
+  self.messageTextView = mTextView;
+  [mTextView release];
+  
   self.messageView = mView;
   [mView release];
   
-  [self.resultView addSubview:self.messageView];
-  [self updateToolbar];
-  [self showMessage:NSLocalizedString(@"Please take or choose a picture containing a barcode", @"")];
+  [self.view addSubview:self.messageView];
+  
+  // add the 'About' button at the top-right of the navigation bar
+  UIBarButtonItem *aboutButton = 
+  [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"DecoderViewController about button title", @"About") 
+                                   style:UIBarButtonItemStyleBordered
+                                  target:self 
+                                  action:@selector(showAbout:)];
+  self.navigationItem.rightBarButtonItem = aboutButton;
+  [aboutButton release];
+  
+  [self reset];
 }
 
 - (void) updateToolbar {
 }
 
 
+- (void)clearImageView {
+  imageView.image = nil;
+  for (UIView *view in resultPointViews) {
+    [view removeFromSuperview];
+  }
+  [resultPointViews removeAllObjects];
+}
+
 - (void)pickAndDecodeFromSource:(UIImagePickerControllerSourceType) sourceType {
-  self.result = nil;
+  [self reset];
+  \
   // Create the Image Picker
   if ([UIImagePickerController isSourceTypeAvailable:sourceType]) {
     UIImagePickerController* picker = [[UIImagePickerController alloc] init];
     picker.sourceType = sourceType;
     picker.delegate = self;
-    picker.allowsImageEditing = [[NSUserDefaults standardUserDefaults] 
-                                 boolForKey:@"allowEditing"];
+    picker.allowsImageEditing = YES; // [[NSUserDefaults standardUserDefaults] boolForKey:@"allowEditing"];
     
     // Picker is displayed asynchronously.
     [self presentModalViewController:picker animated:YES];
        // Release anything that's not essential, such as cached data
 }
 
-
 - (void)dealloc {
   [decoder release];
+  [self clearImageView];
   [imageView release];
   [actionBarItem release];
   [cameraBarItem release];
   [savedPhotosBarItem release];
   [archiveBarItem release];
   [toolbar release];
+  [actions dealloc];
+  [resultPointViews dealloc];
   
        [super dealloc];
 }
 
-- (void)showMessage:(NSString *)message {
+- (void)showMessage:(NSString *)message helpButton:(BOOL)showHelpButton {
 #ifdef DEBUG
-  NSLog(@"Showing message '%@'", message);
+  NSLog(@"Showing message '%@' %@ help Button", message, showHelpButton ? @"with" : @"without");
 #endif
-  self.messageView.text = message;
-  [self.messageView sizeToFit];
+  
+  CGSize maxSize = imageView.bounds.size;
+  if (showHelpButton) {
+    maxSize.width -= messageHelpButton.frame.size.width;
+  }
+  CGSize size = [message sizeWithFont:messageTextView.font constrainedToSize:maxSize lineBreakMode:UILineBreakModeWordWrap];
+  float height = 20.0 + fmin(100.0, size.height);
+  if (showHelpButton) {
+    height = fmax(HELP_BUTTON_HEIGHT, height);
+  }
+
+  CGRect messageFrame = imageView.bounds;
+  messageFrame.origin.y = CGRectGetMaxY(messageFrame) - height;
+  messageFrame.size.height = height;
+  [self.messageView setFrame:messageFrame];
+  messageView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
+  CGRect messageViewBounds = [messageView bounds];
+
+  self.messageTextView.text = message;
+  messageTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+  if (showHelpButton) {
+    CGRect textViewFrame;
+    CGRect helpButtonFrame;
+    
+    CGRectDivide(messageViewBounds, &helpButtonFrame, &textViewFrame, HELP_BUTTON_WIDTH, CGRectMaxXEdge);
+    [self.messageTextView setFrame:textViewFrame];
+    
+    [messageHelpButton setFrame:helpButtonFrame];
+    messageHelpButton.alpha = 1.0;
+    messageHelpButton.enabled = YES;
+    messageHelpButton.autoresizingMask = 
+      UIViewAutoresizingFlexibleLeftMargin | 
+      UIViewAutoresizingFlexibleTopMargin;
+    [messageView addSubview:messageHelpButton];
+  } else {
+    [messageHelpButton removeFromSuperview];
+    messageHelpButton.alpha = 0.0;
+    messageHelpButton.enabled = NO;
+
+    [self.messageTextView setFrame:messageViewBounds];
+  }
 }
 
 // DecoderDelegate methods
 
 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image {
+  [self clearImageView];
   [self.imageView setImage:image];
-  [self showMessage:[NSString stringWithFormat:NSLocalizedString(@"Decoding image (%.0fx%.0f) ...", @"shown while image is decoding"), image.size.width, image.size.height]];
+  [self showMessage:[NSString stringWithFormat:NSLocalizedString(@"DecoderViewController MessageWhileDecodingWithDimensions", @"Decoding image (%.0fx%.0f) ..."), image.size.width, image.size.height]
+     helpButton:NO];
 }
 
 - (void)decoder:(Decoder *)decoder 
   decodingImage:(UIImage *)image 
     usingSubset:(UIImage *)subset
        progress:(NSString *)message {
+  [self clearImageView];
   [self.imageView setImage:subset];
-  [self showMessage:message];
+  [self showMessage:message helpButton:NO];
 }
 
 - (void)presentResultForString:(NSString *)resultString {
-  self.result = [ParsedResult parsedResultForString:resultString];
-  [self showMessage:[self.result stringForDisplay]];
+  self.result = [ResultParser parsedResultForString:resultString];
+  [self showMessage:[self.result stringForDisplay] helpButton:NO];
   self.actions = self.result.actions;
 #ifdef DEBUG
   NSLog(@"result has %d actions", actions ? 0 : actions.count);
 #endif
   [self updateToolbar];
-}  
+} 
+
+- (void)presentResultPoints:(NSArray *)resultPoints 
+                   forImage:(UIImage *)image
+                usingSubset:(UIImage *)subset {
+  // simply add the points to the image view
+  imageView.image = subset;
+  for (NSValue *pointValue in resultPoints) {
+    [imageView addResultPoint:[pointValue CGPointValue]];
+  }
+}
 
-- (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image withResult:(TwoDDecoderResult *)twoDResult {
+- (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)twoDResult {
   [self presentResultForString:twoDResult.text];
   
+  [self presentResultPoints:twoDResult.points forImage:image usingSubset:subset];
+  
   // save the scan to the shared database
   [[Database sharedDatabase] addScanWithText:twoDResult.text];
   
   [self performResultAction:self];
 }
 
-- (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image reason:(NSString *)reason {
-  [self showMessage:reason];
+- (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
+  [self showMessage:reason helpButton:YES];
   [self updateToolbar];
 }
 
 
+- (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
+  [super willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
+  
+  if (imageView.image) {
+    /*
+    CGRect viewBounds = imageView.bounds;
+    CGSize imageSize = imageView.image.size;
+    float scale = fmin(viewBounds.size.width / imageSize.width,
+                       viewBounds.size.height / imageSize.height);
+    float xOffset = (viewBounds.size.width - scale * imageSize.width) / 2.0;
+    float yOffset = (viewBounds.size.height - scale * imageSize.height) / 2.0;
+     */
+    
+    for (UIView *view in resultPointViews) {
+      view.alpha = 0.0;
+    }
+  }  
+}
+
+- (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration {
+  [super willAnimateSecondHalfOfRotationFromInterfaceOrientation:fromInterfaceOrientation duration:duration];
+
+  if (imageView.image) {
+    /*
+    CGRect viewBounds = imageView.bounds;
+    CGSize imageSize = imageView.image.size;
+    float scale = fmin(viewBounds.size.width / imageSize.width,
+                       viewBounds.size.height / imageSize.height);
+    float xOffset = (viewBounds.size.width - scale * imageSize.width) / 2.0;
+    float yOffset = (viewBounds.size.height - scale * imageSize.height) / 2.0;
+     */
+    
+    for (UIView *view in resultPointViews) {
+      view.alpha = 1.0;
+    }
+  }  
+}
+
 // UIImagePickerControllerDelegate methods
 
 - (void)imagePickerController:(UIImagePickerController *)picker
         didFinishPickingImage:(UIImage *)image
                   editingInfo:(NSDictionary *)editingInfo
 {
+  UIImage *imageToDecode = image;
 #ifdef DEBUG
   NSLog(@"picked image size = (%f, %f)", image.size.width, image.size.height);
+#endif
   if (editingInfo) {
     UIImage *originalImage = [editingInfo objectForKey:UIImagePickerControllerOriginalImage];
     if (originalImage) {
+#ifdef DEBUG
       NSLog(@"original image size = (%f, %f)", originalImage.size.width, originalImage.size.height);
-    }
-    NSValue *cropRectValue = [editingInfo objectForKey:UIImagePickerControllerCropRect];
-    if (cropRectValue) {
-      CGRect cropRect = [cropRectValue CGRectValue];
-      NSLog(@"crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
+#endif
+      NSValue *cropRectValue = [editingInfo objectForKey:UIImagePickerControllerCropRect];
+      if (cropRectValue) {
+        CGRect cropRect = [cropRectValue CGRectValue];
+#ifdef DEBUG
+        NSLog(@"crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
+#endif
+        UIGraphicsBeginImageContext(cropRect.size);
+        
+        [originalImage drawAtPoint:CGPointMake(-CGRectGetMinX(cropRect),
+                                               -CGRectGetMinY(cropRect))];
+        
+        imageToDecode = UIGraphicsGetImageFromCurrentImageContext();
+        UIGraphicsEndImageContext();
+      }
     }
   }
-#endif
   
   [[picker parentViewController] dismissModalViewControllerAnimated:YES];
-  [image retain];
+  [imageToDecode retain];
   [picker release];
-  [self.decoder decodeImage:image];
-  [image release];
+  [self.decoder decodeImage:imageToDecode];
+  [imageToDecode release];
   [self updateToolbar];
 }
 
       [actionSheet addButtonWithTitle:[action title]];
     }
     
-    int cancelIndex = [actionSheet addButtonWithTitle:NSLocalizedString(@"Cancel", @"")];
+    int cancelIndex = [actionSheet addButtonWithTitle:NSLocalizedString(@"DecoderViewController cancel button title", @"Cancel")];
     actionSheet.cancelButtonIndex = cancelIndex;
     
     actionSheet.delegate = self;
 }
 
 - (void)showScan:(Scan *)scan {
-  [self.imageView setImage:nil];
+  [self clearImageView];
   [self presentResultForString:scan.text];
   [[self navigationController] popToViewController:self animated:YES];
 }