summaryrefslogtreecommitdiffstats
path: root/apple_remote/source/MultiClickRemoteBehavior.m
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /apple_remote/source/MultiClickRemoteBehavior.m
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'apple_remote/source/MultiClickRemoteBehavior.m')
-rw-r--r--apple_remote/source/MultiClickRemoteBehavior.m216
1 files changed, 216 insertions, 0 deletions
diff --git a/apple_remote/source/MultiClickRemoteBehavior.m b/apple_remote/source/MultiClickRemoteBehavior.m
new file mode 100644
index 000000000..f69f13a98
--- /dev/null
+++ b/apple_remote/source/MultiClickRemoteBehavior.m
@@ -0,0 +1,216 @@
+/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*****************************************************************************
+ * MultiClickRemoteBehavior.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org
+ * by Eric Bachard on 11.08.2008 under the same License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import "MultiClickRemoteBehavior.h"
+
+static const NSTimeInterval DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE = 0.35;
+static const NSTimeInterval HOLD_RECOGNITION_TIME_INTERVAL = 0.4;
+
+@implementation MultiClickRemoteBehavior
+
+- (id) init {
+ if ( (self = [super init]) ) {
+ maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE;
+ }
+ return self;
+}
+
+// Delegates are not retained!
+// http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/chapter_6_section_4.html
+// Delegating objects do not (and should not) retain their delegates.
+// However, clients of delegating objects (applications, usually) are responsible for ensuring that their delegates are around
+// to receive delegation messages. To do this, they may have to retain the delegate.
+- (void) setDelegate: (id) _delegate {
+ if ( _delegate && ( [_delegate respondsToSelector:@selector(remoteButton:pressedDown:clickCount:)] == NO )) return; // return what ?
+
+ delegate = _delegate;
+}
+- (id) delegate {
+ return delegate;
+}
+
+- (BOOL) simulateHoldEvent {
+ return simulateHoldEvents;
+}
+- (void) setSimulateHoldEvent: (BOOL) value {
+ simulateHoldEvents = value;
+}
+
+- (BOOL) simulatesHoldForButtonIdentifier: (RemoteControlEventIdentifier) identifier remoteControl: (RemoteControl*) remoteControl {
+ // we do that check only for the normal button identifiers as we would check for hold support for hold events instead
+ if (identifier > (1 << EVENT_TO_HOLD_EVENT_OFFSET)) return NO;
+
+ return [self simulateHoldEvent] && [remoteControl sendsEventForButtonIdentifier: (identifier << EVENT_TO_HOLD_EVENT_OFFSET)]==NO;
+}
+
+- (BOOL) clickCountingEnabled {
+ return clickCountEnabledButtons != 0;
+}
+- (void) setClickCountingEnabled: (BOOL) value {
+ if (value) {
+ [self setClickCountEnabledButtons: kRemoteButtonPlus | kRemoteButtonMinus | kRemoteButtonPlay | kRemoteButtonLeft | kRemoteButtonRight | kRemoteButtonMenu | kMetallicRemote2009ButtonPlay | kMetallicRemote2009ButtonMiddlePlay];
+ } else {
+ [self setClickCountEnabledButtons: 0];
+ }
+}
+
+- (unsigned int) clickCountEnabledButtons {
+ return clickCountEnabledButtons;
+}
+- (void) setClickCountEnabledButtons: (unsigned int)value {
+ clickCountEnabledButtons = value;
+}
+
+- (NSTimeInterval) maximumClickCountTimeDifference {
+ return maxClickTimeDifference;
+}
+- (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff {
+ maxClickTimeDifference = timeDiff;
+}
+
+- (void) sendSimulatedHoldEvent: (id) time {
+ BOOL startSimulateHold = NO;
+ RemoteControlEventIdentifier event = lastHoldEvent;
+ @synchronized(self) {
+ startSimulateHold = (lastHoldEvent>0 && lastHoldEventTime == [time doubleValue]);
+ }
+ if (startSimulateHold) {
+ lastEventSimulatedHold = YES;
+ event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
+ [delegate remoteButton:event pressedDown: YES clickCount: 1];
+ }
+}
+
+- (void) executeClickCountEvent: (NSArray*) values {
+ RemoteControlEventIdentifier event = [[values objectAtIndex: 0] unsignedIntValue];
+ NSTimeInterval eventTimePoint = [[values objectAtIndex: 1] doubleValue];
+
+ BOOL finishedClicking = NO;
+ int finalClickCount = eventClickCount;
+
+ @synchronized(self) {
+ finishedClicking = (event != lastClickCountEvent || eventTimePoint == lastClickCountEventTime);
+ if (finishedClicking) {
+ eventClickCount = 0;
+ lastClickCountEvent = 0;
+ lastClickCountEventTime = 0;
+ }
+ }
+
+ if (finishedClicking) {
+ [delegate remoteButton:event pressedDown: YES clickCount:finalClickCount];
+ // trigger a button release event, too
+ [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:0.1]];
+ [delegate remoteButton:event pressedDown: NO clickCount:finalClickCount];
+ }
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown remoteControl: (RemoteControl*) remoteControl {
+ if (!delegate) return;
+
+ BOOL clickCountingForEvent = ([self clickCountEnabledButtons] & event) == event;
+
+ if ([self simulatesHoldForButtonIdentifier: event remoteControl: remoteControl] && lastClickCountEvent==0) {
+ if (pressedDown) {
+ // wait to see if it is a hold
+ lastHoldEvent = event;
+ lastHoldEventTime = [NSDate timeIntervalSinceReferenceDate];
+ [self performSelector:@selector(sendSimulatedHoldEvent:)
+ withObject:[NSNumber numberWithDouble:lastHoldEventTime]
+ afterDelay:HOLD_RECOGNITION_TIME_INTERVAL];
+ return;
+ } else {
+ if (lastEventSimulatedHold) {
+ // it was a hold
+ // send an event for "hold release"
+ event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
+ lastHoldEvent = 0;
+ lastEventSimulatedHold = NO;
+
+ [delegate remoteButton:event pressedDown: pressedDown clickCount:1];
+ return;
+ } else {
+ RemoteControlEventIdentifier previousEvent = lastHoldEvent;
+ @synchronized(self) {
+ lastHoldEvent = 0;
+ }
+
+ // in case click counting is enabled we have to setup the state for that, too
+ if (clickCountingForEvent) {
+ lastClickCountEvent = previousEvent;
+ lastClickCountEventTime = lastHoldEventTime;
+ NSNumber* eventNumber;
+ NSNumber* timeNumber;
+ eventClickCount = 1;
+ timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
+ eventNumber= [NSNumber numberWithUnsignedInt:previousEvent];
+ NSTimeInterval diffTime = maxClickTimeDifference-([NSDate timeIntervalSinceReferenceDate]-lastHoldEventTime);
+ [self performSelector: @selector(executeClickCountEvent:)
+ withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
+ afterDelay: diffTime];
+ // we do not return here because we are still in the press-release event
+ // that will be consumed below
+ } else {
+ // trigger the pressed down event that we consumed first
+ [delegate remoteButton:event pressedDown: YES clickCount:1];
+ }
+ }
+ }
+ }
+
+ if (clickCountingForEvent) {
+ if (pressedDown == NO) return;
+
+ NSNumber* eventNumber;
+ NSNumber* timeNumber;
+ @synchronized(self) {
+ lastClickCountEventTime = [NSDate timeIntervalSinceReferenceDate];
+ if (lastClickCountEvent == event) {
+ eventClickCount = eventClickCount + 1;
+ } else {
+ eventClickCount = 1;
+ }
+ lastClickCountEvent = event;
+ timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
+ eventNumber= [NSNumber numberWithUnsignedInt:event];
+ }
+ [self performSelector: @selector(executeClickCountEvent:)
+ withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
+ afterDelay: maxClickTimeDifference];
+ } else {
+ [delegate remoteButton:event pressedDown: pressedDown clickCount:1];
+ }
+
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */