Add to Gambit REPL some functions to send SMS and take pictures (this functionnality...
[gambit-c.git] / examples / iOS / ViewController.m
blobdcd8d30ac7ba073d97d2c874a764d4e480329d9a
1 //
2 //  ViewController.m
3 //
4 //  Created by Marc Feeley on 11-03-06.
5 //  Copyright 2011-2012 Université de Montréal. All rights reserved.
6 //
8 // Note: some of this code comes from the KeyboardAccessory
9 // sample application written by Apple.
11 #import <UIKit/UIKit.h>
12 #import <AudioToolbox/AudioToolbox.h>
13 #import "ViewController.h"
16 @implementation ViewController
18 @synthesize segmCtrl, webView0, webView1, webView2, webView3, textView0, textView1, imageView0, imageView1, cancelButton, accessoryView, toolbar, keyboardSounds, timer, queuedActions, locationManager;
20 //-----------------------------------------------------------------------------
22 // Gambit setup/cleanup.
26  * ___VERSION must match the version number of the Gambit header file.
27  */
29 #define ___VERSION 406004
30 #include "gambit.h"
33  * Define SCHEME_LIBRARY_LINKER as the name of the Scheme library
34  * prefixed with "____20_" and suffixed with "__".  This is the
35  * function that initializes the Scheme library.
36  */
38 #define SCHEME_LIBRARY_LINKER ____20_program__
40 ___BEGIN_C_LINKAGE
41 extern ___mod_or_lnk SCHEME_LIBRARY_LINKER (___global_state_struct*);
42 ___END_C_LINKAGE
45 extern char **main_argv;
46 ___UCS_2 ucs2_gambcdir[1024];
48 void gambit_setup()
50   /*
51    * Setup the Scheme library by calling "___setup" with appropriate
52    * parameters.  The call to "___setup_params_reset" sets all
53    * parameters to their default setting.
54    */
56   int debug_settings = ___DEBUG_SETTINGS_INITIAL;
57   ___UCS_2STRING *ucs2_argv;
58   int last_dir_sep;
59   int i;
61   if (___NONNULLCHARSTRINGLIST_to_NONNULLUCS_2STRINGLIST
62         (main_argv,
63          &ucs2_argv)
64       != ___FIX(___NO_ERR))
65     exit(1);
67   last_dir_sep = 0;
68   i = 0;
70   while (ucs2_argv[0][i] != '\0')
71     {
72       if (ucs2_argv[0][i] == '/')
73         last_dir_sep = i;
74       i++;
75     }
77   for (i=0; i<last_dir_sep; i++)
78     ucs2_gambcdir[i] = ucs2_argv[0][i];
80   ucs2_gambcdir[i] = '\0';
82   // Set debugging settings so that all threads with uncaught
83   // exceptions start a REPL.
85   debug_settings =
86     (debug_settings
87      & ~___DEBUG_SETTINGS_UNCAUGHT_MASK)
88     | (___DEBUG_SETTINGS_UNCAUGHT_ALL
89        << ___DEBUG_SETTINGS_UNCAUGHT_SHIFT);
91   debug_settings =
92     (debug_settings
93      & ~___DEBUG_SETTINGS_ERROR_MASK)
94     | (___DEBUG_SETTINGS_ERROR_REPL
95        << ___DEBUG_SETTINGS_ERROR_SHIFT);
97   ___setup_params_struct setup_params;
99   ___setup_params_reset (&setup_params);
101   setup_params.version        = ___VERSION;
102   setup_params.linker         = SCHEME_LIBRARY_LINKER;
103   setup_params.argv           = ucs2_argv;
104   setup_params.gambcdir       = ucs2_gambcdir;
105   setup_params.debug_settings = debug_settings;
107   ___setup (&setup_params);
109   ___disable_heartbeat_interrupts ();
113 void gambit_cleanup()
115   ___cleanup ();
119 //-----------------------------------------------------------------------------
122 static ViewController *theViewController = nil;
124 - (void)viewDidLoad {
126   [super viewDidLoad];
128   webViews[0] = webView0;
129   webViews[1] = webView1;
130   webViews[2] = webView2;
131   webViews[3] = webView3;
133   textViews[0] = textView0;
134   textViews[1] = textView1;
136   imageViews[0] = imageView0;
137   imageViews[1] = imageView1;
139   theViewController = self;
141   set_textView_font(0, @"Courier-Bold", 16);
143   segmCtrl.selectedSegmentIndex = UISegmentedControlNoSegment;
145   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
146   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
148   timer = nil;
150   queuedActions = [[NSMutableArray alloc] init];
152   locationManager = nil;
154   gambit_setup();
156   [self heartbeat_tick];
160 - (void)viewDidUnload {
162   [super viewDidUnload];
164   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
165   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
167   segmCtrl = nil;
169   webView0 = nil;
170   webView1 = nil;
171   webView2 = nil;
172   webView3 = nil;
174   textView0 = nil;
175   textView1 = nil;
177   imageView0 = nil;
178   imageView1 = nil;
180   cancelButton = nil;
182   accessoryView = nil;
183   toolbar = nil;
185   theViewController = nil;
187   gambit_cleanup();
191 - (void)viewWillAppear:(BOOL)animated {
193   [super viewWillAppear:animated];
197 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
199   return YES;
203 - (void)didReceiveMemoryWarning {
205   [super didReceiveMemoryWarning];
209 #pragma mark -
210 #pragma mark Text view delegate methods
212 - (BOOL)textViewShouldBeginEditing:(UITextView *)aTextView {
214   if (textViews[0].inputAccessoryView == nil)
215     {
216       [[NSBundle mainBundle] loadNibNamed:@"AccessoryView" owner:self options:nil];
218       textViews[0].inputAccessoryView = accessoryView;
220       accessoryView = nil;
222       show_textView(0);
223     }
225   return YES;
229 - (BOOL)textViewShouldEndEditing:(UITextView *)aTextView {
231   [aTextView resignFirstResponder];
232   return YES;
236 #pragma mark -
237 #pragma mark Responding to keyboard events
239 - (void)keyboardWillShow:(NSNotification *)notification {
241   /*
242     Reduce the size of the text view so that it's not obscured by the keyboard.
243     Animate the resize so that it's in sync with the appearance of the keyboard.
244   */
246   NSDictionary *userInfo = [notification userInfo];
248   // Get the origin of the keyboard when it's displayed.
249   NSValue* aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
251   // Get the top of the keyboard as the y coordinate of its origin in
252   // self's view's coordinate system. The bottom of the text view's
253   // frame should align with the top of the keyboard's final position.
254   CGRect keyboardRect = [aValue CGRectValue];
255   keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
257   CGFloat keyboardTop = keyboardRect.origin.y;
258   CGRect newTextViewFrame = self.view.bounds;
259   newTextViewFrame.size.height = keyboardTop - self.view.bounds.origin.y;
261   // Get the duration of the animation.
262   NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
263   NSTimeInterval animationDuration;
264   [animationDurationValue getValue:&animationDuration];
266   // Animate the resize of the text view's frame in sync with the
267   // keyboard's appearance.
268   [UIView beginAnimations:nil context:NULL];
269   [UIView setAnimationDuration:animationDuration];
271   textViews[0].frame = newTextViewFrame;
273   [UIView commitAnimations];
277 - (void)keyboardWillHide:(NSNotification *)notification {
279   NSDictionary* userInfo = [notification userInfo];
281   /*
282     Restore the size of the text view (fill self's view).  Animate the
283     resize so that it's in sync with the disappearance of the
284     keyboard.
285   */
287   NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
288   NSTimeInterval animationDuration;
289   [animationDurationValue getValue:&animationDuration];
291   [UIView beginAnimations:nil context:NULL];
292   [UIView setAnimationDuration:animationDuration];
294   textViews[0].frame = self.view.bounds;
296   [UIView commitAnimations];
300 #include "intf.h"
303 void set_navigation(int n) {
305   ViewController *vc = theViewController;
306   if (vc != nil)
307     {
308       [vc->segmCtrl removeTarget:vc action:@selector(navigation_changed:) forControlEvents:UIControlEventValueChanged];
309       vc->segmCtrl.selectedSegmentIndex = n;
310       [vc->segmCtrl addTarget:vc action:@selector(navigation_changed:) forControlEvents:UIControlEventValueChanged];
311     }
315 - (void)queue_action:(void(^)())action {
317   [queuedActions addObject:[action copy]];
321 - (void)queue_action_asap:(void(^)())action {
323   [self queue_action:action];
325   [self schedule_next_heartbeat_tick:0.0];
329 - (void)send_event:(NSString*)name {
331   [self queue_action_asap:^{ send_event(name); }];
335 - (void)send_key:(NSString*)name {
337   [self queue_action_asap:^{ send_key(name); }];
341 - (void)heartbeat_tick {
343   [self queue_action:^{ [self schedule_next_heartbeat_tick:heartbeat()]; }];
345   ___enable_heartbeat_interrupts ();
347   while ([queuedActions count] > 0)
348     {
349       void (^action)(void) = [queuedActions objectAtIndex:0];
350       [queuedActions removeObjectAtIndex:0];
352       ___ON_THROW(
353         {
354           action();
355         },
356         exit(0);
357       );
358     }
360   ___disable_heartbeat_interrupts ();
364 - (void)schedule_next_heartbeat_tick:(double)interval {
366   if (interval >= 0)
367     {
368       if (timer != nil)
369         {
370           [timer invalidate];
371           [timer release];
372         }
374       timer = [[NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(heartbeat_tick) userInfo:nil repeats:NO] retain];
375     }
379 - (void)app_become_active {
381   theViewController.keyboardSounds = -1; // delay check of user preferences
383   [self send_event:@"app-become-active"];
387 void show_cancelButton() {
389   ViewController *vc = theViewController;
390   if (vc != nil)
391     {
392       vc->cancelButton.hidden = NO;
393     }
397 void hide_cancelButton() {
399   ViewController *vc = theViewController;
400   if (vc != nil)
401     {
402       vc->cancelButton.hidden = YES;
403     }
407 void show_webView(int view) {
409   ViewController *vc = theViewController;
410   if (vc != nil)
411     {
412       int i;
414       for (int i=0; i<NB_WEBVIEWS; i++)
415         if (i != view)
416           {
417             [vc->webViews[i] resignFirstResponder];
418             vc->webViews[i].hidden = YES;
419           }
421       for (int i=0; i<NB_TEXTVIEWS; i++)
422         {
423           [vc->textViews[i] resignFirstResponder];
424           vc->textViews[i].hidden = YES;
425         }
427       for (int i=0; i<NB_IMAGEVIEWS; i++)
428         {
429           [vc->imageViews[i] resignFirstResponder];
430           vc->imageViews[i].hidden = YES;
431         }
433       [vc->webViews[view] becomeFirstResponder];
434       vc->webViews[view].hidden = NO;
435     }
439 void show_textView(int view) {
441   ViewController *vc = theViewController;
442   if (vc != nil)
443     {
444       int i;
446       for (int i=0; i<NB_WEBVIEWS; i++)
447         {
448           [vc->webViews[i] resignFirstResponder];
449           vc->webViews[i].hidden = YES;
450         }
452       for (int i=0; i<NB_TEXTVIEWS; i++)
453         if (i != view)
454           {
455             [vc->textViews[i] resignFirstResponder];
456             vc->textViews[i].hidden = YES;
457           }
459       for (int i=0; i<NB_IMAGEVIEWS; i++)
460         {
461           [vc->imageViews[i] resignFirstResponder];
462           vc->imageViews[i].hidden = YES;
463         }
465       [vc->textViews[view] becomeFirstResponder];
466       vc->textViews[view].hidden = NO;
467     }
471 void show_imageView(int view) {
473   ViewController *vc = theViewController;
474   if (vc != nil)
475     {
476       int i;
478       for (int i=0; i<NB_WEBVIEWS; i++)
479         {
480           [vc->webViews[i] resignFirstResponder];
481           vc->webViews[i].hidden = YES;
482         }
484       for (int i=0; i<NB_TEXTVIEWS; i++)
485         {
486           [vc->textViews[i] resignFirstResponder];
487           vc->textViews[i].hidden = YES;
488         }
490       for (int i=0; i<NB_IMAGEVIEWS; i++)
491         if (i != view)
492           {
493             [vc->imageViews[i] resignFirstResponder];
494             vc->imageViews[i].hidden = YES;
495           }
497       [vc->imageViews[view] becomeFirstResponder];
498       vc->imageViews[view].hidden = NO;
499     }
503 void set_textView_font(int view, NSString *name, int size) {
505   ViewController *vc = theViewController;
506   if (vc != nil)
507     {
508       vc->textViews[view].font = [UIFont fontWithName:name size:size];
509     }
513 void set_textView_content(int view, NSString *str) {
515   ViewController *vc = theViewController;
516   if (vc != nil)
517     {
518       vc->textViews[view].text = str;
519     }
523 NSString *get_textView_content(int view) {
525   ViewController *vc = theViewController;
526   if (vc != nil)
527     {
528       return vc->textViews[view].text;
529     }
531   return @"";
535 void add_to_textView(int view, NSString *str) {
537   ViewController *vc = theViewController;
538   if (vc != nil)
539     {
540       NSMutableString *new_text = [vc->textViews[view].text mutableCopy];
541       [new_text appendString:str];
542       vc->textViews[view].text = new_text;
543       [new_text release];
544     }
548 void add_output_to_textView(int view, NSString *str) {
550   add_to_textView(view, [str stringByReplacingOccurrencesOfString:@" " withString:@"\u2007"]);
554 void add_input_to_textView(int view, NSString *str) {
556   ViewController *vc = theViewController;
557   if (vc != nil)
558     {
559       NSMutableString *new_text = [vc->textViews[view].text mutableCopy];
560       NSRange selectedRange = vc->textViews[view].selectedRange;
562       [new_text replaceCharactersInRange:selectedRange withString:str];
564       vc->textViews[view].text = new_text;
565       [new_text release];
566     }
570 void set_webView_content(int view, NSString *str, NSString *base_url_path, BOOL enable_scaling, NSString *mime_type) {
572   ViewController *vc = theViewController;
573   if (vc != nil)
574     {
575       [vc->webViews[view]
576           loadData:[str dataUsingEncoding:NSUnicodeStringEncoding]
577           MIMEType:mime_type
578           textEncodingName:@"UTF-8"
579           baseURL:[NSURL fileURLWithPath:(base_url_path != nil) ? base_url_path : [[NSBundle mainBundle] bundlePath]]
580       ];
581       vc->webViews[0].scalesPageToFit = enable_scaling;
582     }
586 void set_webView_content_from_file(int view, NSString *path, NSString *base_url_path, BOOL enable_scaling, NSString *mime_type) {
588   ViewController *vc = theViewController;
589   if (vc != nil)
590     {
591       NSData *data = [NSData dataWithContentsOfFile:path];
592       if (data != nil)
593         {
594           [vc->webViews[view]
595               loadData:data
596               MIMEType:mime_type
597               textEncodingName:@"UTF-8"
598               baseURL:[NSURL fileURLWithPath:(base_url_path != nil) ? base_url_path : [[NSBundle mainBundle] bundlePath]]
599            ];
600           vc->webViews[view].scalesPageToFit = enable_scaling;
601         }
602     }
606 NSString *eval_js_in_webView(int view, NSString *script) {
608   ViewController *vc = theViewController;
609   if (vc != nil)
610     {
611       return [vc->webViews[view] stringByEvaluatingJavaScriptFromString:script];
612     }
614   return nil;
618 void open_URL(NSString *url) {
620   [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
624 BOOL send_SMS(NSString *recipient, NSString *message) {
626   ViewController *vc = theViewController;
627   if (vc != nil)
628     {
629       if ([MFMessageComposeViewController canSendText])
630         {
631           MFMessageComposeViewController *controller = [[[MFMessageComposeViewController alloc] init] autorelease];
633           controller.body = message;
634           controller.recipients = [NSArray arrayWithObjects:recipient,nil];
635           controller.messageComposeDelegate = vc;
636           [vc presentModalViewController:controller animated:YES];
638           return YES;
639         }
640     }
642   return NO;
646 - (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
648   NSString *event;
650   [self dismissModalViewControllerAnimated:YES];
652   switch (result) {
654   default:
655   case MessageComposeResultFailed:
656     event = @"SMS-failed";
657     break;
659   case MessageComposeResultCancelled:
660     event = @"SMS-cancelled";
661     break;
663   case MessageComposeResultSent:
664     event = @"SMS-sent";
665     break;
666   }
668   [self send_event:event];
672 BOOL pick_image() {
674   ViewController *vc = theViewController;
675   if (vc != nil)
676     {
677       if ([UIImagePickerController isCameraDeviceAvailable: UIImagePickerControllerCameraDeviceRear] ||
678           [UIImagePickerController isCameraDeviceAvailable: UIImagePickerControllerCameraDeviceFront])
679         {
680           UIImagePickerController *controller = [[[UIImagePickerController alloc] init] autorelease];
682           controller.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType: UIImagePickerControllerSourceTypeCamera];
684           controller.sourceType = UIImagePickerControllerSourceTypeCamera;
686           controller.delegate = vc;
688           [vc presentModalViewController:controller animated:YES];
690           return YES;
691         }
692     }
694   return NO;
698 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
700   [picker dismissModalViewControllerAnimated:YES];
702   [self send_event:@"pick-image:"];
706 void set_idle_timer(BOOL enable) {
708   [UIApplication sharedApplication].idleTimerDisabled = !enable;
712 void set_toolbar_alpha(double alpha) {
714   ViewController *vc = theViewController;
715   if (vc != nil)
716     {
717       [vc->toolbar setAlpha: alpha];
718     }
721 void segm_ctrl_set_title(int segment, NSString *title) {
723   ViewController *vc = theViewController;
724   if (vc != nil)
725     {
726       [vc->segmCtrl setTitle:title forSegmentAtIndex:segment];
727     }
731 void segm_ctrl_insert(int segment, NSString *title) {
733   ViewController *vc = theViewController;
734   if (vc != nil)
735     {
736       [vc->segmCtrl insertSegmentWithTitle:title atIndex:segment animated:false];
737     }
741 void segm_ctrl_remove(int segment) {
743   ViewController *vc = theViewController;
744   if (vc != nil)
745     {
746       [vc->segmCtrl removeSegmentAtIndex:segment animated:false];
747     }
751 void segm_ctrl_remove_all() {
753   ViewController *vc = theViewController;
754   if (vc != nil)
755     {
756       [vc->segmCtrl removeAllSegments];
757     }
761 void set_pref(NSString *key, NSString *value) {
763   NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
764   [prefs setObject:value forKey:key];
765   [prefs synchronize];
769 NSString *get_pref(NSString *key) {
771   NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
772   return [prefs stringForKey:key];
776 void set_pasteboard(NSString *value) {
778   UIPasteboard *pb = [UIPasteboard generalPasteboard];
779   pb.string = value;
783 NSString *get_pasteboard() {
785   UIPasteboard *pb = [UIPasteboard generalPasteboard];
786   return pb.string;
790 NSString *get_documents_dir() {
792   NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
793   return [paths objectAtIndex:0];
797 void popup_alert(NSString *title, NSString *msg, NSString *cancel_button, NSString *accept_button) {
799   UIAlertView *alert = [[UIAlertView alloc]
800                         initWithTitle: title
801                         message: msg
802                         delegate: theViewController
803                         cancelButtonTitle: cancel_button
804                         otherButtonTitles: accept_button, nil];
805   [alert show];
806   [alert release];
810 // Called when an alertview button is touched
811 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
813   NSString *event;
815   switch (buttonIndex) {
817   case 1:
818     event = @"popup-alert-accept";
819     break;
821   default:
822   case 0:
823     event = @"popup-alert-cancel";
824     break;
825   }
827   [self send_event:event];
831 #pragma mark -
832 #pragma mark Toolbar action
834 - (IBAction)navigation_changed:(id)sender {
835   int n = segmCtrl.selectedSegmentIndex;
836   if (n >= 0)
837     [self send_event:[NSString stringWithFormat:@"NAV%d", n]];
841 #pragma mark -
842 #pragma mark Cancel button action
844 - (IBAction)touch_up_cancel:(id)sender {
845   [self send_event:@"cancel"];
849 - (BOOL)textView:(UITextView *)textView2 shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
851   if ([text hasSuffix:@"\n"])
852     {
853       unichar c;
854       int end = [textViews[0].text length];
856       int line_start = range.location+range.length;
858       while (line_start > 0 &&
859              (c = [textViews[0].text characterAtIndex:line_start-1]) != '\n' &&
860              c != 0x2007) // non breaking space
861         line_start--;
863       int line_end = range.location+range.length;
865       while (line_end < end &&
866              [textViews[0].text characterAtIndex:line_end] != '\n')
867         line_end++;
869       if (line_start == line_end)
870         {
871           [textViews[0] resignFirstResponder]; // Hide the keyboard after "return" key is pressed on empty line
872         }
873       else
874         {
875           NSString *line = [textViews[0].text substringWithRange:NSMakeRange(line_start, line_end-line_start)];
877           if (line_end == end)
878             {
879               add_to_textView(0, text);
881               [self queue_action_asap:^{
882                   send_input([line stringByAppendingString:text]);
883               }];
884             }
885           else
886             add_to_textView(0, line);
887         }
889       return NO;
890     }
892   return YES;
896 - (IBAction)touch_up_Char:(id)sender withString:(NSString *)aString {
898   add_input_to_textView(0, aString);
902 #pragma mark -
903 #pragma mark Accessory view action
905 - (IBAction)touch_down:(id)sender {
907   if (keyboardSounds != 0)
908     {
909       if (keyboardSounds == -1) // delayed check of user preferences?
910         {
911           Boolean exists_and_valid;
912           keyboardSounds =
913             CFPreferencesGetAppBooleanValue(CFSTR("keyboard"),
914                                             CFSTR("/var/mobile/Library/Preferences/com.apple.preferences.sounds"),
915                                             &exists_and_valid);
916           if (!exists_and_valid)
917             keyboardSounds = true; // by default turn on keyboard clicks
918         }
920       if (keyboardSounds != 0)
921         AudioServicesPlaySystemSound(1104); // keyboard "tock" sound
922     }
926 #pragma mark -
927 #pragma mark Accessory view action
929 - (IBAction)touch_up_F1:(id)sender {
930   [self send_key:@"F1"];
934 #pragma mark -
935 #pragma mark Accessory view action
937 - (IBAction)touch_up_F2:(id)sender {
938   [self send_key:@"F2"];
942 #pragma mark -
943 #pragma mark Accessory view action
945 - (IBAction)touch_up_F3:(id)sender {
946   [self send_key:@"F3"];
950 #pragma mark -
951 #pragma mark Accessory view action
953 - (IBAction)touch_up_F4:(id)sender {
954   [self send_key:@"F4"];
958 #pragma mark -
959 #pragma mark Accessory view action
961 - (IBAction)touch_up_F5:(id)sender {
962   [self send_key:@"F5"];
966 #pragma mark -
967 #pragma mark Accessory view action
969 - (IBAction)touch_up_F6:(id)sender {
970   [self send_key:@"F6"];
974 #pragma mark -
975 #pragma mark Accessory view action
977 - (IBAction)touch_up_F7:(id)sender {
978   [self send_key:@"F7"];
982 #pragma mark -
983 #pragma mark Accessory view action
985 - (IBAction)touch_up_F8:(id)sender {
986   [self send_key:@"F8"];
990 #pragma mark -
991 #pragma mark Accessory view action
993 - (IBAction)touch_up_F9:(id)sender {
994   [self send_key:@"F9"];
998 #pragma mark -
999 #pragma mark Accessory view action
1001 - (IBAction)touch_up_F10:(id)sender {
1002   [self send_key:@"F10"];
1006 #pragma mark -
1007 #pragma mark Accessory view action
1009 - (IBAction)touch_up_F11:(id)sender {
1010   [self send_key:@"F11"];
1014 #pragma mark -
1015 #pragma mark Accessory view action
1017 - (IBAction)touch_up_F12:(id)sender {
1018   [self send_key:@"F12"];
1022 #pragma mark -
1023 #pragma mark Accessory view action
1025 - (IBAction)touch_up_F13:(id)sender {
1026   [self send_key:@"F13"];
1030 #pragma mark -
1031 #pragma mark Accessory view action
1033 - (IBAction)touch_up_SHARP:(id)sender {
1034   [self send_key:@"#"];
1038 #pragma mark -
1039 #pragma mark Accessory view action
1041 - (IBAction)touch_up_DQUOTE:(id)sender {
1042   [self send_key:@"\""];
1046 #pragma mark -
1047 #pragma mark Accessory view action
1049 - (IBAction)touch_up_QUOTE:(id)sender {
1050   [self send_key:@"'"];
1054 #pragma mark -
1055 #pragma mark Accessory view action
1057 - (IBAction)touch_up_COMMA:(id)sender {
1058   [self send_key:@","];
1062 #pragma mark -
1063 #pragma mark Accessory view action
1065 - (IBAction)touch_up_PLUS:(id)sender {
1066   [self send_key:@"+"];
1070 #pragma mark -
1071 #pragma mark Accessory view action
1073 - (IBAction)touch_up_MINUS:(id)sender {
1074   [self send_key:@"-"];
1078 #pragma mark -
1079 #pragma mark Accessory view action
1081 - (IBAction)touch_up_STAR:(id)sender {
1082   [self send_key:@"*"];
1086 #pragma mark -
1087 #pragma mark Accessory view action
1089 - (IBAction)touch_up_SLASH:(id)sender {
1090   [self send_key:@"/"];
1094 #pragma mark -
1095 #pragma mark Accessory view action
1097 - (IBAction)touch_up_LPAREN:(id)sender {
1098   [self send_key:@"("];
1102 #pragma mark -
1103 #pragma mark Accessory view action
1105 - (IBAction)touch_up_RPAREN:(id)sender {
1106   [self send_key:@")"];
1110 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
1112   NSString *url = [[request URL] relativeString];
1114   if ([url hasPrefix:@"event:"])
1115     {
1116       [self send_event:url];
1118       return NO;
1119     }
1121   return YES;
1125 void setup_location_updates(double desired_accuracy, double distance_filter)
1127   ViewController *vc = theViewController;
1128   if (vc != nil)
1129     {
1130       if (vc->locationManager == nil)
1131         {
1132           vc->locationManager = [[CLLocationManager alloc] init];
1133           vc->locationManager.delegate = vc;
1134         }
1136       if (desired_accuracy < 0.0)
1137         [vc->locationManager stopUpdatingLocation];
1138       else
1139         {
1140           if (desired_accuracy == 0.0)
1141             vc->locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
1142           else
1143             vc->locationManager.desiredAccuracy = desired_accuracy;
1145           vc->locationManager.distanceFilter = kCLDistanceFilterNone;
1147           if (distance_filter < 0.0)
1148             [vc->locationManager startMonitoringSignificantLocationChanges];
1149           else
1150             {
1151               if (distance_filter > 0.0)
1152                 vc->locationManager.distanceFilter = distance_filter;
1153               [vc->locationManager startUpdatingLocation];
1154             }
1155         }
1156     }
1160 // Delegate method from the CLLocationManagerDelegate protocol.
1162 - (void)locationManager:(CLLocationManager *)manager
1163     didUpdateToLocation:(CLLocation *)newLocation
1164     fromLocation:(CLLocation *)oldLocation
1166     NSString *event = [NSString stringWithFormat:@"location-update:%+.9f %+.9f %+.1f %+.1f %+.1f %+.1f %+.1f %+.6f",
1167                                 newLocation.coordinate.latitude,
1168                                 newLocation.coordinate.longitude,
1169                                 newLocation.horizontalAccuracy,
1170                                 newLocation.altitude,
1171                                 newLocation.verticalAccuracy,
1172                                 newLocation.course,
1173                                 newLocation.speed,
1174                                 [newLocation.timestamp timeIntervalSince1970]];
1175     [self send_event:event];
1178 #pragma mark -
1179 #pragma mark Memory management
1181 - (void)dealloc {
1183   int i;
1185   [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:nil];
1187   [segmCtrl release];
1189   for (i=0; i<NB_WEBVIEWS; i++)
1190     [webViews[i] release];
1192   for (i=0; i<NB_TEXTVIEWS; i++)
1193     [textViews[i] release];
1195   for (i=0; i<NB_IMAGEVIEWS; i++)
1196     [imageViews[i] release];
1198   [cancelButton release];
1200   [super dealloc];
1204 @end