removed my proj files... get generated for each user locally.
[zxing.git] / iphone / Classes / DecoderViewController.m
index 8345a43..26da41d 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"
 
+// Michael Jurewitz, Dec 16, 2009 6:32 PM writes:
+// https://devforums.apple.com/message/149553
+// Notice Regarding UIGetScreenImage()
+// After carefully considering the issue, Apple is now allowing applications to
+// use the function UIGetScreenImage() to programmatically capture the current
+// screen contents.
+// Note that a future release of iPhone OS may provide a public API equivalent
+// of this functionality.  At such time, all applications using
+// UIGetScreenImage() will be required to adopt the public API.
+CGImageRef MyCGImageCopyScreenContents(void) {
+   extern CGImageRef UIGetScreenImage(void);
+   return UIGetScreenImage(); /* already retained */
+}
+
+@interface DecoderViewController()
+- (void)takeScreenshot;
+@end
 
 @implementation DecoderViewController
 
 @synthesize actionBarItem;
 
 @synthesize messageView;
-@synthesize resultView;
+@synthesize messageTextView;
+@synthesize messageHelpButton;
 @synthesize imageView;
+@synthesize picker;
 @synthesize toolbar;
 
 @synthesize decoder;
 @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";
-    
+  if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
+    // Initialization code
+    self.title =
+        NSLocalizedString(@"DecoderViewController AppTitle", @"Barcode Scanner");
+
     Decoder *d = [[Decoder alloc] init];
     self.decoder = d;
     d.delegate = self;
     [d release];
-       }
-       return self;
+    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;
-  mView.autoresizingMask = UIViewAutoresizingFlexibleHeight | 
-                           UIViewAutoresizingFlexibleWidth |
-                           UIViewAutoresizingFlexibleLeftMargin |
-                           UIViewAutoresizingFlexibleRightMargin |
-                           UIViewAutoresizingFlexibleTopMargin |
-                           UIViewAutoresizingFlexibleBottomMargin;
+
+  CGRect messageViewFrame = imageView.frame;
+  UIView *mView = [[UIView alloc] initWithFrame:messageViewFrame];
+  mView.backgroundColor = [UIColor darkGrayColor];
+  mView.alpha = 0.9;
+  mView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
+  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:@"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"];
-    
+    UIImagePickerController* aPicker =
+        [[[UIImagePickerController alloc] init] autorelease];
+    aPicker.sourceType = sourceType;
+    aPicker.delegate = self;
+    self.picker = aPicker;
+
+    // [[NSUserDefaults standardUserDefaults] boolForKey:@"allowEditing"];
+    BOOL isCamera = (sourceType == UIImagePickerControllerSourceTypeCamera);
+    if ([picker respondsToSelector:@selector(setAllowsEditing:)]) {
+      // not in 3.0
+      [picker setAllowsEditing:!isCamera];
+    }
+    if (isCamera) {
+      if ([picker respondsToSelector:@selector(setShowsCameraControls:)]) {
+        [picker setShowsCameraControls:NO];
+        UIButton *cancelButton =
+          [UIButton buttonWithType:UIButtonTypeRoundedRect];
+        NSString *cancelString =
+          NSLocalizedString(@"DecoderViewController cancel button title", @"");
+        CGFloat height = [UIFont systemFontSize];
+        CGSize size =
+          [cancelString sizeWithFont:[UIFont systemFontOfSize:height]];
+        [cancelButton setTitle:cancelString forState:UIControlStateNormal];
+        CGRect appFrame = [[UIScreen mainScreen] bounds];
+        static const int kMargin = 10;
+        static const int kInternalXMargin = 10;
+        static const int kInternalYMargin = 10;
+        CGRect frame = CGRectMake(kMargin,
+          appFrame.size.height - (height + 2*kInternalYMargin + kMargin),
+          2*kInternalXMargin + size.width,
+          height + 2*kInternalYMargin);
+        [cancelButton setFrame:frame];
+        [cancelButton addTarget:self
+                         action:@selector(cancel:)
+               forControlEvents:UIControlEventTouchUpInside];
+        picker.cameraOverlayView = cancelButton;
+        // The camera takes quite a while to start up. Hence the 2 second delay.
+        [self performSelector:@selector(takeScreenshot)
+                   withObject:nil
+                   afterDelay:2.0];
+      }
+    }
+
     // Picker is displayed asynchronously.
     [self presentModalViewController:picker animated:YES];
   } else {
 - (IBAction)pickAndDecode:(id) sender {
   UIImagePickerControllerSourceType sourceType;
   int i = [sender tag];
-  
+
   switch (i) {
     case 0: sourceType = UIImagePickerControllerSourceTypeCamera; break;
     case 1: sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; break;
 
 
 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
-       // Return YES for supported orientations
-       return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
+  // Return YES for supported orientations
+  return (interfaceOrientation == UIInterfaceOrientationPortrait);
 }
 
 
 - (void)didReceiveMemoryWarning {
-       [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
-       // Release anything that's not essential, such as cached data
+  [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
+  // 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];
-  
-       [super dealloc];
+  [picker release];
+  [actions release];
+  [resultPointViews release];
+
+  [super dealloc];
 }
 
-- (void)showMessage:(NSString *)message {
-  NSLog(message);
-  self.messageView.text = message;
-  [self.messageView sizeToFit];
+- (void)showMessage:(NSString *)message helpButton:(BOOL)showHelpButton {
+#ifdef DEBUG
+  NSLog(@"Showing message '%@' %@ help Button", message, showHelpButton ? @"with" : @"without");
+#endif
+
+  CGSize imageMaxSize = imageView.bounds.size;
+  if (showHelpButton) {
+    imageMaxSize.width -= messageHelpButton.frame.size.width;
+  }
+  CGSize size = [message sizeWithFont:messageTextView.font constrainedToSize:imageMaxSize 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.imageView setImage:image];
-  [self showMessage:[NSString stringWithFormat:@"Decoding image (%.0fx%.0f) ...", image.size.width, image.size.height]];
+- (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset{
+  [self clearImageView];
+  [self.imageView setImage:subset];
+  [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 
+- (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.picker = nil;
   [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];
-  [self updateToolbar];
+- (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
+  if (self.picker && UIImagePickerControllerSourceTypeCamera == self.picker.sourceType) {
+    // If we are using the camera, and the user hasn't manually cancelled,
+    // take another snapshot and try to decode it.
+    [self takeScreenshot];
+  } else {
+    [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;
+    }
+  }
+}
+
+- (void)cancel:(id)sender {
+  self.picker = nil;
+}
+
+- (void)takeScreenshot {
+  if (picker) {
+    CGImageRef cgScreen = MyCGImageCopyScreenContents();
+    if (cgScreen) {
+      CGRect croppedFrame = CGRectMake(0, 0, CGImageGetWidth(cgScreen),
+          CGImageGetHeight(cgScreen) - (10+toolbar.bounds.size.height));
+      CGImageRef cgCropped = CGImageCreateWithImageInRect(cgScreen, croppedFrame);
+      if (cgCropped) {
+        UIImage *screenshot = [UIImage imageWithCGImage:cgCropped];
+        CGImageRelease(cgCropped);
+        [self.decoder decodeImage:screenshot];
+      }
+      CGImageRelease(cgScreen);
+    }
+  }
+}
+
 // UIImagePickerControllerDelegate methods
 
-- (void)imagePickerController:(UIImagePickerController *)picker
-        didFinishPickingImage:(UIImage *)image
-                  editingInfo:(NSDictionary *)editingInfo
-{
-  NSLog(@"picked image size = (%f, %f)", image.size.width, image.size.height);
-  if (editingInfo) {
-    UIImage *originalImage = [editingInfo objectForKey:UIImagePickerControllerOriginalImage];
+- (void)imagePickerController:(UIImagePickerController *)aPicker
+didFinishPickingMediaWithInfo:(NSDictionary *)info {
+  UIImage *imageToDecode =
+    [info objectForKey:UIImagePickerControllerEditedImage];
+  if (!imageToDecode) {
+    imageToDecode = [info objectForKey:UIImagePickerControllerOriginalImage];
+  }
+  CGSize size = [imageToDecode size];
+  CGRect cropRect = CGRectMake(0.0, 0.0, size.width, size.height);
+  
+#ifdef DEBUG
+  NSLog(@"picked image size = (%f, %f)", size.width, size.height);
+#endif
+  NSString *systemVersion = [[UIDevice currentDevice] systemVersion];
+
+  NSValue *cropRectValue = [info objectForKey:UIImagePickerControllerCropRect];
+  if (cropRectValue) {
+    UIImage *originalImage = [info 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];
+#endif
+       cropRect = [cropRectValue CGRectValue];
+#ifdef DEBUG
       NSLog(@"crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
+#endif
+      if (([picker sourceType] == UIImagePickerControllerSourceTypeSavedPhotosAlbum) &&
+          [@"2.1" isEqualToString:systemVersion]) {
+        // adjust crop rect to work around bug in iPhone OS 2.1 when selecting from the photo roll
+        cropRect.origin.x *= 2.5;
+        cropRect.origin.y *= 2.5;
+        cropRect.size.width *= 2.5;
+        cropRect.size.height *= 2.5;
+#ifdef DEBUG
+        NSLog(@"2.1-adjusted crop rect = (%f, %f) x (%f, %f)", CGRectGetMinX(cropRect), CGRectGetMinY(cropRect), CGRectGetWidth(cropRect), CGRectGetHeight(cropRect));
+#endif
+      }
+
+      imageToDecode = originalImage;
     }
   }
-  
-  [[picker parentViewController] dismissModalViewControllerAnimated:YES];
-  [image retain];
-  [picker release];
-  [self.decoder decodeImage:image];
-  [image release];
-  [self updateToolbar];
+
+  [imageToDecode retain];
+  self.picker = nil;
+  [self.decoder decodeImage:imageToDecode cropRect:cropRect];
+  [imageToDecode release];
 }
 
-- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
-{
-  [picker dismissModalViewControllerAnimated:YES];
-  [picker release];
-  [self updateToolbar];
+
+- (void)imagePickerControllerDidCancel:(UIImagePickerController *)aPicker {
+  self.picker = nil;
+}
+
+- (void)setPicker:(UIImagePickerController *)aPicker {
+  if (picker != aPicker) {
+    [picker dismissModalViewControllerAnimated:YES];
+    picker = [aPicker retain];
+    [self updateToolbar];
+  }
 }
 
-- (void)navigationController:(UINavigationController *)navigationController 
-       didShowViewController:(UIViewController *)viewController 
+- (void)navigationController:(UINavigationController *)navigationController
+       didShowViewController:(UIViewController *)viewController
                     animated:(BOOL)animated {
   // no-op
 }
 
-- (void)navigationController:(UINavigationController *)navigationController 
-      willShowViewController:(UIViewController *)viewController 
+- (void)navigationController:(UINavigationController *)navigationController
+      willShowViewController:(UIViewController *)viewController
                     animated:(BOOL)animated {
   // no-op
 }
 
+- (void)performAction:(ResultAction *)action {
+  [action performActionWithController:self shouldConfirm:NO];
+}
+
+- (void)confirmAndPerformAction:(ResultAction *)action {
+  [action performActionWithController:self shouldConfirm:YES];
+}
+
 
 - (IBAction)performResultAction:(id)sender {
   if (self.result == nil) {
     NSLog(@"no result to perform an action on!");
     return;
   }
-  
+
   if (self.actions == nil || self.actions.count == 0) {
     NSLog(@"result has no actions to perform!");
     return;
   }
-  
+
   if (self.actions.count == 1) {
     ResultAction *action = [self.actions lastObject];
+#ifdef DEBUG
     NSLog(@"Result has the single action, (%@)  '%@', performing it",
           NSStringFromClass([action class]), [action title]);
-    [action performSelector:@selector(performActionWithController:) 
-                 withObject:self 
+#endif
+    [self performSelector:@selector(confirmAndPerformAction:)
+                 withObject:action
                  afterDelay:0.0];
   } else {
+#ifdef DEBUG
     NSLog(@"Result has multiple actions, popping up an action sheet");
+#endif
     UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithFrame:self.view.bounds];
-        
+
     for (ResultAction *action in self.actions) {
       [actionSheet addButtonWithTitle:[action title]];
     }
-    
-    int cancelIndex = [actionSheet addButtonWithTitle:@"Cancel"];
+
+    int cancelIndex = [actionSheet addButtonWithTitle:NSLocalizedString(@"DecoderViewController cancel button title", @"Cancel")];
     actionSheet.cancelButtonIndex = cancelIndex;
-    
+
     actionSheet.delegate = self;
-    
+
     [actionSheet showFromToolbar:self.toolbar];
   }
 }
   if (buttonIndex < self.actions.count) {
     int actionIndex = buttonIndex;
     ResultAction *action = [self.actions objectAtIndex:actionIndex];
-    [action performSelector:@selector(performActionWithController:) 
-                 withObject:self 
+    [self performSelector:@selector(performAction:)
+                 withObject:action
                  afterDelay:0.0];
   }
 }
 }
 
 - (void)showScan:(Scan *)scan {
-  [[self navigationController] popToViewController:self animated:YES];
+  [self clearImageView];
   [self presentResultForString:scan.text];
+  [[self navigationController] popToViewController:self animated:YES];
 }
 
 @end