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