Blame client/iOS/Views/TouchPointerView.m

Packit 1fb8d4
/*
Packit 1fb8d4
 RDP Touch Pointer View 
Packit 1fb8d4
 
Packit 1fb8d4
 Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
Packit 1fb8d4
 
Packit 1fb8d4
 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. 
Packit 1fb8d4
 If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#import "TouchPointerView.h"
Packit 1fb8d4
#import "Utils.h"
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
#define RESET_DEFAULT_POINTER_IMAGE_DELAY 0.15
Packit 1fb8d4
Packit 1fb8d4
#define POINTER_ACTION_CURSOR   0
Packit 1fb8d4
#define POINTER_ACTION_CLOSE    3
Packit 1fb8d4
#define POINTER_ACTION_RCLICK   2
Packit 1fb8d4
#define POINTER_ACTION_LCLICK   4
Packit 1fb8d4
#define POINTER_ACTION_MOVE     4
Packit 1fb8d4
#define POINTER_ACTION_SCROLL   5
Packit 1fb8d4
#define POINTER_ACTION_KEYBOARD 7
Packit 1fb8d4
#define POINTER_ACTION_EXTKEYBOARD 8
Packit 1fb8d4
#define POINTER_ACTION_RESET 6
Packit 1fb8d4
Packit 1fb8d4
@interface TouchPointerView (Private)
Packit 1fb8d4
- (void)setCurrentPointerImage:(UIImage*)image;
Packit 1fb8d4
- (void)displayPointerActionImage:(UIImage*)image;
Packit 1fb8d4
- (BOOL)pointInsidePointer:(CGPoint)point;
Packit 1fb8d4
- (BOOL)pointInsidePointerArea:(int)area point:(CGPoint)point;
Packit 1fb8d4
- (CGPoint)getCursorPosition;
Packit 1fb8d4
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
Packit 1fb8d4
- (void)handleSingleTap:(UITapGestureRecognizer*)gesture;
Packit 1fb8d4
- (void)handlerForGesture:(UIGestureRecognizer*)gesture sendClick:(BOOL)sendClick;
Packit 1fb8d4
@end
Packit 1fb8d4
Packit 1fb8d4
@implementation TouchPointerView
Packit 1fb8d4
Packit 1fb8d4
@synthesize delegate = _delegate;
Packit 1fb8d4
Packit 1fb8d4
- (void)awakeFromNib
Packit 1fb8d4
{
Packit 1fb8d4
    [super awakeFromNib];
Packit 1fb8d4
    
Packit 1fb8d4
    // set content mode when rotating (keep aspect ratio)
Packit 1fb8d4
    [self setContentMode:UIViewContentModeTopLeft];
Packit 1fb8d4
    
Packit 1fb8d4
    // load touchPointerImage
Packit 1fb8d4
    _default_pointer_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"touch_pointer_default" ofType:@"png"]] retain];
Packit 1fb8d4
    _active_pointer_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"touch_pointer_active" ofType:@"png"]] retain];
Packit 1fb8d4
    _lclick_pointer_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"touch_pointer_lclick" ofType:@"png"]] retain];
Packit 1fb8d4
    _rclick_pointer_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"touch_pointer_rclick" ofType:@"png"]] retain];
Packit 1fb8d4
    _scroll_pointer_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"touch_pointer_scroll" ofType:@"png"]] retain];
Packit 1fb8d4
    _extkeyboard_pointer_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"touch_pointer_ext_keyboard" ofType:@"png"]] retain];
Packit 1fb8d4
    _keyboard_pointer_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"touch_pointer_keyboard" ofType:@"png"]] retain];
Packit 1fb8d4
    _reset_pointer_img = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"touch_pointer_reset" ofType:@"png"]] retain];        
Packit 1fb8d4
    _cur_pointer_img = _default_pointer_img;
Packit 1fb8d4
    _pointer_transformation = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
Packit 1fb8d4
    
Packit 1fb8d4
    // init flags
Packit 1fb8d4
    _pointer_moving = NO;
Packit 1fb8d4
    _pointer_scrolling = NO;
Packit 1fb8d4
    
Packit 1fb8d4
    // create areas array
Packit 1fb8d4
    int i, j;
Packit 1fb8d4
    CGFloat area_width = [_cur_pointer_img size].width / 3.0f;
Packit 1fb8d4
    CGFloat area_height = [_cur_pointer_img size].height / 3.0f;
Packit 1fb8d4
    for(i = 0; i < 3; i++)
Packit 1fb8d4
    {
Packit 1fb8d4
        for(j = 0; j < 3; j++)
Packit 1fb8d4
        {
Packit 1fb8d4
            _pointer_areas[j + i * 3] = CGRectMake(j * area_width, i * area_height, area_width, area_height);
Packit 1fb8d4
        }
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    // init gesture recognizers
Packit 1fb8d4
    UITapGestureRecognizer* singleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)] autorelease];
Packit 1fb8d4
    [singleTapRecognizer setNumberOfTouchesRequired:1];
Packit 1fb8d4
    [singleTapRecognizer setNumberOfTapsRequired:1];
Packit 1fb8d4
    
Packit 1fb8d4
    UILongPressGestureRecognizer* dragDropRecognizer = [[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleDragDrop:)] autorelease];
Packit 1fb8d4
    dragDropRecognizer.minimumPressDuration = 0.4;
Packit 1fb8d4
    //        dragDropRecognizer.allowableMovement = 1000.0;        
Packit 1fb8d4
    
Packit 1fb8d4
    UILongPressGestureRecognizer* pointerMoveScrollRecognizer = [[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePointerMoveScroll:)] autorelease];
Packit 1fb8d4
    pointerMoveScrollRecognizer.minimumPressDuration = 0.15;
Packit 1fb8d4
    pointerMoveScrollRecognizer.allowableMovement = 1000.0;
Packit 1fb8d4
    [pointerMoveScrollRecognizer requireGestureRecognizerToFail:dragDropRecognizer];
Packit 1fb8d4
    
Packit 1fb8d4
    [self addGestureRecognizer:singleTapRecognizer];
Packit 1fb8d4
    [self addGestureRecognizer:dragDropRecognizer];
Packit 1fb8d4
    [self addGestureRecognizer:pointerMoveScrollRecognizer];    
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
- (void)dealloc
Packit 1fb8d4
{
Packit 1fb8d4
    [super dealloc];
Packit 1fb8d4
    [_default_pointer_img autorelease];
Packit 1fb8d4
    [_active_pointer_img autorelease];
Packit 1fb8d4
    [_lclick_pointer_img autorelease];
Packit 1fb8d4
    [_rclick_pointer_img autorelease];
Packit 1fb8d4
    [_scroll_pointer_img autorelease];
Packit 1fb8d4
    [_extkeyboard_pointer_img autorelease];
Packit 1fb8d4
    [_keyboard_pointer_img autorelease];
Packit 1fb8d4
    [_reset_pointer_img autorelease];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#pragma mark - Public interface
Packit 1fb8d4
Packit 1fb8d4
// positions the pointer on screen if it got offscreen after an orentation change
Packit 1fb8d4
-(void)ensurePointerIsVisible
Packit 1fb8d4
{
Packit 1fb8d4
    CGRect bounds = [self bounds];
Packit 1fb8d4
    if(_pointer_transformation.tx > (bounds.size.width - _cur_pointer_img.size.width))
Packit 1fb8d4
        _pointer_transformation.tx = bounds.size.width - _cur_pointer_img.size.width;
Packit 1fb8d4
    if(_pointer_transformation.ty > (bounds.size.height - _cur_pointer_img.size.height))
Packit 1fb8d4
        _pointer_transformation.ty = bounds.size.height - _cur_pointer_img.size.height;
Packit 1fb8d4
    [self setNeedsDisplay];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
// show/hides the touch pointer
Packit 1fb8d4
-(void)setHidden:(BOOL)hidden
Packit 1fb8d4
{  
Packit 1fb8d4
    [super setHidden:hidden];
Packit 1fb8d4
Packit 1fb8d4
    // if shown center pointer in view
Packit 1fb8d4
    if(!hidden)
Packit 1fb8d4
    {
Packit 1fb8d4
        _pointer_transformation = CGAffineTransformMakeTranslation(([self bounds].size.width - [_cur_pointer_img size].width) / 2, 
Packit 1fb8d4
                                                                  ([self bounds].size.height - [_cur_pointer_img size].height) / 2);
Packit 1fb8d4
       [self setNeedsDisplay];
Packit 1fb8d4
    }    
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(UIEdgeInsets)getEdgeInsets
Packit 1fb8d4
{
Packit 1fb8d4
    return UIEdgeInsetsMake(0, 0, [_cur_pointer_img size].width, [_cur_pointer_img size].height);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
- (CGPoint)getPointerPosition
Packit 1fb8d4
{
Packit 1fb8d4
    return CGPointMake(_pointer_transformation.tx, _pointer_transformation.ty);        
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
- (int)getPointerWidth
Packit 1fb8d4
{
Packit 1fb8d4
    return [_cur_pointer_img size].width;    
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
- (int)getPointerHeight
Packit 1fb8d4
{
Packit 1fb8d4
    return [_cur_pointer_img size].height;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
@end
Packit 1fb8d4
Packit 1fb8d4
@implementation TouchPointerView (Private)
Packit 1fb8d4
Packit 1fb8d4
- (void)setCurrentPointerImage:(UIImage*)image
Packit 1fb8d4
{
Packit 1fb8d4
    _cur_pointer_img = image;
Packit 1fb8d4
    [self setNeedsDisplay];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
- (void)displayPointerActionImage:(UIImage*)image
Packit 1fb8d4
{
Packit 1fb8d4
    [self setCurrentPointerImage:image];
Packit 1fb8d4
    [self performSelector:@selector(setCurrentPointerImage:) withObject:_default_pointer_img afterDelay:RESET_DEFAULT_POINTER_IMAGE_DELAY];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
// Only override drawRect: if you perform custom drawing.
Packit 1fb8d4
// An empty implementation adversely affects performance during animation.
Packit 1fb8d4
- (void)drawRect:(CGRect)rect
Packit 1fb8d4
{
Packit 1fb8d4
    // Drawing code
Packit 1fb8d4
    CGContextRef context = UIGraphicsGetCurrentContext();
Packit 1fb8d4
    CGContextSaveGState(context);
Packit 1fb8d4
    CGContextConcatCTM(context, _pointer_transformation);
Packit 1fb8d4
    CGContextDrawImage(context, CGRectMake(0, 0, [_cur_pointer_img size].width, [_cur_pointer_img size].height), [_cur_pointer_img CGImage]);
Packit 1fb8d4
    CGContextRestoreGState(context);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
// helper that returns YES if the given point is within the pointer
Packit 1fb8d4
-(BOOL) pointInsidePointer:(CGPoint)point
Packit 1fb8d4
{
Packit 1fb8d4
    CGRect rec = CGRectMake(0, 0, [_cur_pointer_img size].width, [_cur_pointer_img size].height);
Packit 1fb8d4
    return CGRectContainsPoint(CGRectApplyAffineTransform(rec, _pointer_transformation), point);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
// helper that returns YES if the given point is within the given pointer area
Packit 1fb8d4
-(BOOL) pointInsidePointerArea:(int)area point:(CGPoint)point
Packit 1fb8d4
{
Packit 1fb8d4
    CGRect rec = _pointer_areas[area];
Packit 1fb8d4
    return CGRectContainsPoint(CGRectApplyAffineTransform(rec, _pointer_transformation), point);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
// returns the position of the cursor
Packit 1fb8d4
-(CGPoint) getCursorPosition
Packit 1fb8d4
{
Packit 1fb8d4
    CGRect transPointerArea = CGRectApplyAffineTransform(_pointer_areas[POINTER_ACTION_CURSOR], _pointer_transformation);
Packit 1fb8d4
    return CGPointMake(CGRectGetMidX(transPointerArea), CGRectGetMidY(transPointerArea));
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
// this filters events - if the pointer was clicked the scrollview won't get any events
Packit 1fb8d4
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
Packit 1fb8d4
{
Packit 1fb8d4
    return [self pointInsidePointer:point];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#pragma mark - Action handlers
Packit 1fb8d4
Packit 1fb8d4
// handles single tap gestures, returns YES if the event was handled by the pointer, NO otherwise
Packit 1fb8d4
-(void) handleSingleTap:(UITapGestureRecognizer*)gesture
Packit 1fb8d4
{
Packit 1fb8d4
    // get touch position within our view
Packit 1fb8d4
    CGPoint touchPos = [gesture locationInView:self];
Packit 1fb8d4
    
Packit 1fb8d4
    // look if pointer was in one of our action areas
Packit 1fb8d4
    if([self pointInsidePointerArea:POINTER_ACTION_CLOSE point:touchPos])
Packit 1fb8d4
        [[self delegate] touchPointerClose];
Packit 1fb8d4
    else if([self pointInsidePointerArea:POINTER_ACTION_LCLICK point:touchPos])
Packit 1fb8d4
    {
Packit 1fb8d4
        [self displayPointerActionImage:_lclick_pointer_img];
Packit 1fb8d4
        [[self delegate] touchPointerLeftClick:[self getCursorPosition] down:YES];        
Packit 1fb8d4
        [[self delegate] touchPointerLeftClick:[self getCursorPosition] down:NO];        
Packit 1fb8d4
    }
Packit 1fb8d4
    else if([self pointInsidePointerArea:POINTER_ACTION_RCLICK point:touchPos])
Packit 1fb8d4
    {
Packit 1fb8d4
        [self displayPointerActionImage:_rclick_pointer_img];
Packit 1fb8d4
        [[self delegate] touchPointerRightClick:[self getCursorPosition] down:YES];        
Packit 1fb8d4
        [[self delegate] touchPointerRightClick:[self getCursorPosition] down:NO];        
Packit 1fb8d4
    }
Packit 1fb8d4
    else if([self pointInsidePointerArea:POINTER_ACTION_KEYBOARD point:touchPos])
Packit 1fb8d4
    {
Packit 1fb8d4
        [self displayPointerActionImage:_keyboard_pointer_img];
Packit 1fb8d4
        [[self delegate] touchPointerToggleKeyboard];
Packit 1fb8d4
    }
Packit 1fb8d4
    else if([self pointInsidePointerArea:POINTER_ACTION_EXTKEYBOARD point:touchPos])
Packit 1fb8d4
    {
Packit 1fb8d4
        [self displayPointerActionImage:_extkeyboard_pointer_img];
Packit 1fb8d4
        [[self delegate] touchPointerToggleExtendedKeyboard];
Packit 1fb8d4
    }
Packit 1fb8d4
    else if([self pointInsidePointerArea:POINTER_ACTION_RESET point:touchPos])
Packit 1fb8d4
    {
Packit 1fb8d4
        [self displayPointerActionImage:_reset_pointer_img];
Packit 1fb8d4
        [[self delegate] touchPointerResetSessionView];
Packit 1fb8d4
    }
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(void) handlerForGesture:(UIGestureRecognizer*)gesture sendClick:(BOOL)sendClick
Packit 1fb8d4
{
Packit 1fb8d4
    if([gesture state] == UIGestureRecognizerStateBegan)
Packit 1fb8d4
    {
Packit 1fb8d4
        CGPoint touchPos = [gesture locationInView:self];
Packit 1fb8d4
        if([self pointInsidePointerArea:POINTER_ACTION_LCLICK point:touchPos])
Packit 1fb8d4
        {
Packit 1fb8d4
            _prev_touch_location = touchPos;
Packit 1fb8d4
            _pointer_moving = YES;
Packit 1fb8d4
            if(sendClick == YES)
Packit 1fb8d4
            {
Packit 1fb8d4
                [[self delegate] touchPointerLeftClick:[self getCursorPosition] down:YES];        
Packit 1fb8d4
                [self setCurrentPointerImage:_active_pointer_img];                
Packit 1fb8d4
            }   
Packit 1fb8d4
        }
Packit 1fb8d4
        else if([self pointInsidePointerArea:POINTER_ACTION_SCROLL point:touchPos])
Packit 1fb8d4
        {
Packit 1fb8d4
            [self setCurrentPointerImage:_scroll_pointer_img];                
Packit 1fb8d4
            _prev_touch_location = touchPos;
Packit 1fb8d4
            _pointer_scrolling = YES;
Packit 1fb8d4
        }
Packit 1fb8d4
    }
Packit 1fb8d4
    else if([gesture state] == UIGestureRecognizerStateChanged)
Packit 1fb8d4
    {
Packit 1fb8d4
        if(_pointer_moving)
Packit 1fb8d4
        {
Packit 1fb8d4
            CGPoint touchPos = [gesture locationInView:self];
Packit 1fb8d4
            _pointer_transformation = CGAffineTransformTranslate(_pointer_transformation, touchPos.x - _prev_touch_location.x, touchPos.y - _prev_touch_location.y);                                    
Packit 1fb8d4
            [[self delegate] touchPointerMove:[self getCursorPosition]];
Packit 1fb8d4
            _prev_touch_location = touchPos;
Packit 1fb8d4
            [self setNeedsDisplay];
Packit 1fb8d4
        }
Packit 1fb8d4
        else if(_pointer_scrolling)
Packit 1fb8d4
        {
Packit 1fb8d4
            CGPoint touchPos = [gesture locationInView:self];
Packit 1fb8d4
            float delta = touchPos.y - _prev_touch_location.y;
Packit 1fb8d4
            if(delta > GetScrollGestureDelta())
Packit 1fb8d4
            {
Packit 1fb8d4
                [[self delegate] touchPointerScrollDown:YES];
Packit 1fb8d4
                _prev_touch_location = touchPos;
Packit 1fb8d4
            }
Packit 1fb8d4
            else if(delta < -GetScrollGestureDelta())
Packit 1fb8d4
            {
Packit 1fb8d4
                [[self delegate] touchPointerScrollDown:NO];
Packit 1fb8d4
                _prev_touch_location = touchPos;
Packit 1fb8d4
            }
Packit 1fb8d4
        }
Packit 1fb8d4
    }
Packit 1fb8d4
    else if([gesture state] == UIGestureRecognizerStateEnded)
Packit 1fb8d4
    {
Packit 1fb8d4
        if(_pointer_moving)
Packit 1fb8d4
        {
Packit 1fb8d4
            if(sendClick == YES)
Packit 1fb8d4
                [[self delegate] touchPointerLeftClick:[self getCursorPosition] down:NO];        
Packit 1fb8d4
            _pointer_moving = NO;
Packit 1fb8d4
            [self setCurrentPointerImage:_default_pointer_img];
Packit 1fb8d4
        }
Packit 1fb8d4
Packit 1fb8d4
        if(_pointer_scrolling)
Packit 1fb8d4
        {
Packit 1fb8d4
            [self setCurrentPointerImage:_default_pointer_img];
Packit 1fb8d4
            _pointer_scrolling = NO;            
Packit 1fb8d4
        }
Packit 1fb8d4
    }    
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
// handles long press gestures
Packit 1fb8d4
-(void) handleDragDrop:(UILongPressGestureRecognizer*)gesture
Packit 1fb8d4
{
Packit 1fb8d4
    [self handlerForGesture:gesture sendClick:YES];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(void) handlePointerMoveScroll:(UILongPressGestureRecognizer*)gesture
Packit 1fb8d4
{
Packit 1fb8d4
    [self handlerForGesture:gesture sendClick:NO];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
@end