Branch libreoffice-5-0-4
[LibreOffice.git] / apple_remote / source / MultiClickRemoteBehavior.m
blob0972a5ae99d633d20890f16a9d67f4f0ba169914
1 /* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*****************************************************************************
3  * MultiClickRemoteBehavior.m
4  * RemoteControlWrapper
5  *
6  * Created by Martin Kahr on 11.03.06 under a MIT-style license.
7  * Copyright (c) 2006 martinkahr.com. All rights reserved.
8  *
9  * Code modified and adapted to OpenOffice.org
10  * by Eric Bachard on 11.08.2008 under the same License
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28  * THE SOFTWARE.
29  *
30  *****************************************************************************/
32 #import "MultiClickRemoteBehavior.h"
34 const NSTimeInterval DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE = 0.35;
35 const NSTimeInterval HOLD_RECOGNITION_TIME_INTERVAL = 0.4;
37 @implementation MultiClickRemoteBehavior
39 - (id) init {
40         if ( (self = [super init]) ) {
41                 maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE;
42         }
43         return self;
46 // Delegates are not retained!
47 // http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/chapter_6_section_4.html
48 // Delegating objects do not (and should not) retain their delegates.
49 // However, clients of delegating objects (applications, usually) are responsible for ensuring that their delegates are around
50 // to receive delegation messages. To do this, they may have to retain the delegate.
51 - (void) setDelegate: (id) _delegate {
52         if ( _delegate && ( [_delegate respondsToSelector:@selector(remoteButton:pressedDown:clickCount:)] == NO )) return; // return what ?
54         delegate = _delegate;
56 - (id) delegate {
57         return delegate;
60 - (BOOL) simulateHoldEvent {
61         return simulateHoldEvents;
63 - (void) setSimulateHoldEvent: (BOOL) value {
64         simulateHoldEvents = value;
67 - (BOOL) simulatesHoldForButtonIdentifier: (RemoteControlEventIdentifier) identifier remoteControl: (RemoteControl*) remoteControl {
68         // we do that check only for the normal button identifiers as we would check for hold support for hold events instead
69         if (identifier > (1 << EVENT_TO_HOLD_EVENT_OFFSET)) return NO;
71         return [self simulateHoldEvent] && [remoteControl sendsEventForButtonIdentifier: (identifier << EVENT_TO_HOLD_EVENT_OFFSET)]==NO;
74 - (BOOL) clickCountingEnabled {
75         return clickCountEnabledButtons != 0;
77 - (void) setClickCountingEnabled: (BOOL) value {
78         if (value) {
79        [self setClickCountEnabledButtons: kRemoteButtonPlus | kRemoteButtonMinus | kRemoteButtonPlay | kRemoteButtonLeft | kRemoteButtonRight | kRemoteButtonMenu | kMetallicRemote2009ButtonPlay | kMetallicRemote2009ButtonMiddlePlay];
80         } else {
81                 [self setClickCountEnabledButtons: 0];
82         }
85 - (unsigned int) clickCountEnabledButtons {
86         return clickCountEnabledButtons;
88 - (void) setClickCountEnabledButtons: (unsigned int)value {
89         clickCountEnabledButtons = value;
92 - (NSTimeInterval) maximumClickCountTimeDifference {
93         return maxClickTimeDifference;
95 - (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff {
96         maxClickTimeDifference = timeDiff;
99 - (void) sendSimulatedHoldEvent: (id) time {
100         BOOL startSimulateHold = NO;
101         RemoteControlEventIdentifier event = lastHoldEvent;
102         @synchronized(self) {
103                 startSimulateHold = (lastHoldEvent>0 && lastHoldEventTime == [time doubleValue]);
104         }
105         if (startSimulateHold) {
106                 lastEventSimulatedHold = YES;
107                 event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
108                 [delegate remoteButton:event pressedDown: YES clickCount: 1];
109         }
112 - (void) executeClickCountEvent: (NSArray*) values {
113         RemoteControlEventIdentifier event = [[values objectAtIndex: 0] unsignedIntValue];
114         NSTimeInterval eventTimePoint = [[values objectAtIndex: 1] doubleValue];
116         BOOL finishedClicking = NO;
117         int finalClickCount = eventClickCount;
119         @synchronized(self) {
120                 finishedClicking = (event != lastClickCountEvent || eventTimePoint == lastClickCountEventTime);
121                 if (finishedClicking) {
122                         eventClickCount = 0;
123                         lastClickCountEvent = 0;
124                         lastClickCountEventTime = 0;
125                 }
126         }
128         if (finishedClicking) {
129                 [delegate remoteButton:event pressedDown: YES clickCount:finalClickCount];
130                 // trigger a button release event, too
131                 [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:0.1]];
132                 [delegate remoteButton:event pressedDown: NO clickCount:finalClickCount];
133         }
136 - (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown remoteControl: (RemoteControl*) remoteControl {
137         if (!delegate)  return;
139         BOOL clickCountingForEvent = ([self clickCountEnabledButtons] & event) == event;
141         if ([self simulatesHoldForButtonIdentifier: event remoteControl: remoteControl] && lastClickCountEvent==0) {
142                 if (pressedDown) {
143                         // wait to see if it is a hold
144                         lastHoldEvent = event;
145                         lastHoldEventTime = [NSDate timeIntervalSinceReferenceDate];
146                         [self performSelector:@selector(sendSimulatedHoldEvent:)
147                                            withObject:[NSNumber numberWithDouble:lastHoldEventTime]
148                                            afterDelay:HOLD_RECOGNITION_TIME_INTERVAL];
149                         return;
150                 } else {
151                         if (lastEventSimulatedHold) {
152                                 // it was a hold
153                                 // send an event for "hold release"
154                                 event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
155                                 lastHoldEvent = 0;
156                                 lastEventSimulatedHold = NO;
158                                 [delegate remoteButton:event pressedDown: pressedDown clickCount:1];
159                                 return;
160                         } else {
161                                 RemoteControlEventIdentifier previousEvent = lastHoldEvent;
162                                 @synchronized(self) {
163                                         lastHoldEvent = 0;
164                                 }
166                                 // in case click counting is enabled we have to setup the state for that, too
167                                 if (clickCountingForEvent) {
168                                         lastClickCountEvent = previousEvent;
169                                         lastClickCountEventTime = lastHoldEventTime;
170                                         NSNumber* eventNumber;
171                                         NSNumber* timeNumber;
172                                         eventClickCount = 1;
173                                         timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
174                                         eventNumber= [NSNumber numberWithUnsignedInt:previousEvent];
175                                         NSTimeInterval diffTime = maxClickTimeDifference-([NSDate timeIntervalSinceReferenceDate]-lastHoldEventTime);
176                                         [self performSelector: @selector(executeClickCountEvent:)
177                                                            withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
178                                                            afterDelay: diffTime];
179                                         // we do not return here because we are still in the press-release event
180                                         // that will be consumed below
181                                 } else {
182                                         // trigger the pressed down event that we consumed first
183                                         [delegate remoteButton:event pressedDown: YES clickCount:1];
184                                 }
185                         }
186                 }
187         }
189         if (clickCountingForEvent) {
190                 if (pressedDown == NO) return;
192                 NSNumber* eventNumber;
193                 NSNumber* timeNumber;
194                 @synchronized(self) {
195                         lastClickCountEventTime = [NSDate timeIntervalSinceReferenceDate];
196                         if (lastClickCountEvent == event) {
197                                 eventClickCount = eventClickCount + 1;
198                         } else {
199                                 eventClickCount = 1;
200                         }
201                         lastClickCountEvent = event;
202                         timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
203                         eventNumber= [NSNumber numberWithUnsignedInt:event];
204                 }
205                 [self performSelector: @selector(executeClickCountEvent:)
206                                    withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
207                                    afterDelay: maxClickTimeDifference];
208         } else {
209                 [delegate remoteButton:event pressedDown: pressedDown clickCount:1];
210         }
214 @end
216 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */