├── .gitignore ├── README.md ├── generate_headers.sh ├── mac ├── EventDispatch.h ├── EventDispatch.mm └── mtg_mac.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ └── martijn.xcuserdatad │ └── xcschemes │ ├── MultiTouch Gestures Mac.xcscheme │ └── xcschememanagement.plist ├── nb-configuration.xml ├── pom.xml └── src └── main └── java └── com └── martijncourteaux └── multitouchgestures ├── EventDispatch.java ├── GestureAdapter.java ├── GestureListener.java ├── MultiTouchClient.java ├── MultiTouchGestureUtilities.java ├── demo ├── DemoAppleGestures.java ├── DemoCompareGestures.java └── DemoSimpleGestures.java └── event ├── GestureEvent.java ├── MagnifyGestureEvent.java ├── RotateGestureEvent.java └── ScrollGestureEvent.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | target/ 3 | *.dylib 4 | mac/mtg_mac.xcodeproj/project.xcworkspace/ 5 | mac/mtg_mac.xcodeproj/xcuserdata/ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | MultiTouch Gestures for Java 3 | ============================ 4 | 5 | This project aims to provide an easy way to enable MultiTouch touchpad gestures for Java in Swing. This project 6 | was originally started as an alternative for the AppleJavaExtensions `com.apple.eawt` package. 7 | 8 | Read this is you came here because `com.apple.eawt` is not working 9 | ------------------------------------------------------------------ 10 | 11 | You landed on this page, probably because you are unable to make `com.apple.eawt` compile 12 | on newer versions of Java (JDK 7 and higher). However after creating this project, I found that there is 13 | a workaround. You should have a look at this question on StackOverflow: 14 | [Using internal sun classes with javac](https://stackoverflow.com/questions/4065401/using-internal-sun-classes-with-javac). 15 | All the classes in the `com.apple.eawt` package are not included in the `$JAVA_HOME/lib/ct.sym` file, which makes 16 | the compilation fail with an error like: 17 | 18 | com.apple.eawt can not find package 19 | package com.apple.eawt does not exist 20 | 21 | Adding `-XDignore.symbol.file` to your compiler flags solves it. 22 | 23 | However, this project still has a purpose, I believe, since you can have smooth two finger scrolling. 24 | The Apple gesture features don't report scroll events, which are way smoother than the ones you get using 25 | a classic `MouseWheelListener`. 26 | 27 | 28 | Supported Platforms 29 | ------------------- 30 | Since my personal need for now is only OS X, this is supported. However, if anyone needs support 31 | for other platforms as well, feel free to fork and create a corresponding native source file for 32 | your platform. 33 | 34 | 35 | Building 36 | -------- 37 | 38 | Building the native library will automatically place the resuting binary in the resources folder 39 | of the Java project. The Java library will automatically extract the required native binary to 40 | a temporary file (that will be deleted after the application quits) and link against it. So, 41 | the only thing you will have to do to be able to use this is to build the native library and 42 | include the Java project as a dependency in Maven or add it manually to the classpath. 43 | 44 | ### Java 45 | This project is a Maven project made in NetBeans on the Java side of it. So just open this in NetBeans 46 | and you are basically good to go. You can compile this manually as well. 47 | 48 | ### Native Mac OS X 49 | This is an Xcode project that resides in the mac/ directory. Just open the project in Xcode and hit 50 | "Run". This will produce the native library file (ending in .dylib) on the right location. 51 | 52 | Testing 53 | ------- 54 | There is a test file provided in: 55 | 56 | src/main/java/com/martijncourteaux/multitouchgestures/demo/DemoSimpleGestures.java 57 | 58 | You can have a look at that file for a sample. 59 | 60 | You can see how this compares to the Apple Gesture support using this demo file: 61 | 62 | src/main/java/com/martijncourteaux/multitouchgestures/demo/DemoCompareGestures.java 63 | 64 | Usage 65 | ----- 66 | The usage is pretty similar to Apple's AppleJavaExtensions package from Java 6. You build your 67 | Swing frame, just as regular. Then you can do this: 68 | 69 | MultiTouchGestureUtilities.addGestureListener(comp, listener); 70 | 71 | Where `listener` is a `MultiTouchGestureListener` and `comp` is the JComponent you want to add 72 | the listener to. **When you dispose/remove a JFrame or component with a listener, you *MUST* 73 | remove the `MultiTouchGestureListener` using one of following techniques:** 74 | 75 | MultiTouchGestureUtilities.removeGestureListener(comp, listener); 76 | MultiTouchGestureUtilities.removeAllGestureListeners(comp); 77 | 78 | The `MultiTouchGestureListener` interface has these methods: 79 | 80 | public void magnify(MagnifyGestureEvent e); 81 | public void rotate(RotateGestureEvent e); 82 | public void scroll(ScrollGestureEvent e); 83 | 84 | Where each event type has these parameters: 85 | 86 | - `mouseX`: x-coordinate of the mouse in the component space. 87 | - `mouseX`: y-coordinate of the mouse in the component space. 88 | - `absMouseX`: x-coordinate of the mouse on the screen 89 | - `absMouseY`: y-coordinate of the mouse on the screen 90 | - `phase`: a `GesturePhase` enum indicating what phase the gesture is in. 91 | 92 | A `GesturePhase` is an enum containing these values: 93 | 94 | - `MOMENTUM`: Indicates this event is caused by momentum (like OS X). 95 | - `BEGIN`: The gesture just began. 96 | - `CHANGED`: The gesture updated (e.g.: rotating, zooming) 97 | - `END`: The gesture ended. 98 | - `CANCELLED`: The gesture was cancelled due to some popup or other thing. 99 | - `OTHER`: Any other reason for an event. Most of the time this will be errornous. 100 | 101 | **Remark**: On OS X, the `MOMENTUM` events come after the `END` event. 102 | 103 | Remark for OS X 104 | --------------- 105 | The built-in Java scrolling listeners are not as smooth as the one provided in this project. 106 | So I highly recommend checking it out. This gives you the native OS X scrolling experience. 107 | 108 | 109 | -------------------------------------------------------------------------------- /generate_headers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### MAC OS X ### 4 | DIR_OUT=mac/ 5 | CP="-classpath target/classes" 6 | P="com.martijncourteaux.multitouchgestures" 7 | 8 | javah -o $DIR_OUT/EventDispatch.h $CP ${P}.EventDispatch 9 | 10 | 11 | ### WINDOWS AND LINUX NOT SUPPORTED YET ### 12 | 13 | 14 | -------------------------------------------------------------------------------- /mac/EventDispatch.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_martijncourteaux_multitouchgestures_EventDispatch */ 4 | 5 | #ifndef _Included_com_martijncourteaux_multitouchgestures_EventDispatch 6 | #define _Included_com_martijncourteaux_multitouchgestures_EventDispatch 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: com_martijncourteaux_multitouchgestures_EventDispatch 12 | * Method: init 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_com_martijncourteaux_multitouchgestures_EventDispatch_init 16 | (JNIEnv *, jclass); 17 | 18 | /* 19 | * Class: com_martijncourteaux_multitouchgestures_EventDispatch 20 | * Method: start 21 | * Signature: ()V 22 | */ 23 | JNIEXPORT void JNICALL Java_com_martijncourteaux_multitouchgestures_EventDispatch_start 24 | (JNIEnv *, jclass); 25 | 26 | /* 27 | * Class: com_martijncourteaux_multitouchgestures_EventDispatch 28 | * Method: stop 29 | * Signature: ()V 30 | */ 31 | JNIEXPORT void JNICALL Java_com_martijncourteaux_multitouchgestures_EventDispatch_stop 32 | (JNIEnv *, jclass); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | #endif 38 | -------------------------------------------------------------------------------- /mac/EventDispatch.mm: -------------------------------------------------------------------------------- 1 | // 2 | // EventDispatch.cpp 3 | // OSXGestures4JavaJNI 4 | // 5 | // Created by Martijn Courteaux on 30/06/15. 6 | // Copyright © 2015 Martijn Courteaux. All rights reserved. 7 | // 8 | 9 | #include 10 | 11 | #import 12 | 13 | #include "EventDispatch.h" 14 | 15 | JNIEnv *env; 16 | jclass jc_EventDispatch; 17 | jmethodID jm_dispatchMagnifyGesture; 18 | jmethodID jm_dispatchRotateGesture; 19 | jmethodID jm_dispatchScrollWheelEvent; 20 | 21 | CFMachPortRef eventTap; 22 | CFRunLoopRef runningLoop; 23 | bool running = false; 24 | 25 | int convertCocoaPhaseToJavaPhase(NSEventPhase phase) 26 | { 27 | switch (phase) 28 | { 29 | case NSEventPhaseNone: 30 | return 0; 31 | case NSEventPhaseBegan: 32 | return 1; 33 | case NSEventPhaseChanged: 34 | return 2; 35 | case NSEventPhaseEnded: 36 | return 3; 37 | case NSEventPhaseCancelled: 38 | return 4; 39 | } 40 | return -1; 41 | } 42 | 43 | 44 | NSEventMask eventMask = 45 | NSEventMaskGesture | 46 | NSEventMaskMagnify| 47 | NSEventMaskSwipe | 48 | NSEventMaskRotate | 49 | NSScrollWheelMask; 50 | 51 | CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eventRef, void *refcon) { 52 | 53 | if (!(type > 0 && type < kCGEventTapDisabledByTimeout)) 54 | { 55 | return eventRef; 56 | } 57 | 58 | // convert the CGEventRef to an NSEvent 59 | NSEvent *event = [NSEvent eventWithCGEvent:eventRef]; 60 | 61 | // filter out events which do not match the mask 62 | if (!(eventMask & NSEventMaskFromType([event type]))) { return [event CGEvent]; } 63 | 64 | NSPoint m = [NSEvent mouseLocation]; 65 | 66 | switch ([event type]) 67 | { 68 | case NSEventTypeMagnify: 69 | { 70 | int phase = convertCocoaPhaseToJavaPhase([event phase]); 71 | env->CallStaticVoidMethod(jc_EventDispatch, jm_dispatchMagnifyGesture, (double) m.x, (double) m.y, event.magnification, phase); 72 | break; 73 | } 74 | case NSEventTypeSwipe: 75 | NSLog(@"Swipe: X = %10.6f; Y = %10.6f", event.deltaX, event.deltaY); 76 | break; 77 | case NSEventTypeRotate: 78 | { 79 | int phase = convertCocoaPhaseToJavaPhase([event phase]); 80 | env->CallStaticVoidMethod(jc_EventDispatch, jm_dispatchRotateGesture, (double) m.x, (double) m.y, event.rotation, phase); 81 | break; 82 | } 83 | case NSScrollWheel: 84 | { 85 | int phase = convertCocoaPhaseToJavaPhase([event phase]); 86 | env->CallStaticVoidMethod(jc_EventDispatch, jm_dispatchScrollWheelEvent, (double) m.x, (double) m.y, event.scrollingDeltaX, event.scrollingDeltaY, phase); 87 | break; 88 | } 89 | default: 90 | break; 91 | } 92 | 93 | return [event CGEvent]; 94 | } 95 | 96 | void JNICALL Java_com_martijncourteaux_multitouchgestures_EventDispatch_init(JNIEnv *env, jclass clazz) 97 | { 98 | printf("[NATIVE] Prepare JNI Gesture Listener.\n"); 99 | fflush(stdout); 100 | ::env = env; 101 | jc_EventDispatch = clazz; 102 | jm_dispatchMagnifyGesture = env->GetStaticMethodID(jc_EventDispatch, "dispatchMagnifyGesture", "(DDDI)V"); 103 | jm_dispatchRotateGesture = env->GetStaticMethodID(jc_EventDispatch, "dispatchRotateGesture", "(DDDI)V"); 104 | jm_dispatchScrollWheelEvent = env->GetStaticMethodID(jc_EventDispatch, "dispatchScrollWheelEvent", "(DDDDI)V"); 105 | } 106 | 107 | void JNICALL Java_com_martijncourteaux_multitouchgestures_EventDispatch_start(JNIEnv *env, jclass) 108 | { 109 | printf("[NATIVE] Starting JNI Gesture Listener Tap.\n"); 110 | fflush(stdout); 111 | if (!running) 112 | { 113 | eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, kCGEventMaskForAllEvents, eventTapCallback, nil); 114 | CFRunLoopAddSource(CFRunLoopGetCurrent(), CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0), kCFRunLoopCommonModes); 115 | CGEventTapEnable(eventTap, true); 116 | runningLoop = CFRunLoopGetCurrent(); 117 | CFRunLoopRun(); 118 | running = false; 119 | } 120 | } 121 | 122 | void JNICALL Java_com_martijncourteaux_multitouchgestures_EventDispatch_stop(JNIEnv *, jclass) 123 | { 124 | printf("[NATIVE] Stopping JNI Gesture Listener Tap.\n"); 125 | fflush(stdout); 126 | 127 | if (running) 128 | { 129 | running = false; 130 | CFRunLoopStop(runningLoop); 131 | CGEventTapEnable(eventTap, false); 132 | } 133 | } -------------------------------------------------------------------------------- /mac/mtg_mac.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1020E4DB1B4575BC008E4CB3 /* EventDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 1020E4D91B4575BC008E4CB3 /* EventDispatch.h */; }; 11 | 1020E4DC1B4575BC008E4CB3 /* EventDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1020E4DA1B4575BC008E4CB3 /* EventDispatch.mm */; }; 12 | 1097172E1B42ABDE005A0E28 /* libmtg_mac.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 109717121B42AA96005A0E28 /* libmtg_mac.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 13 | 109717321B42AFCD005A0E28 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 109717311B42AFCD005A0E28 /* Cocoa.framework */; }; 14 | 109717351B42AFE6005A0E28 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 109717331B42AFE6005A0E28 /* CoreFoundation.framework */; }; 15 | 109717361B42AFE6005A0E28 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 109717341B42AFE6005A0E28 /* Foundation.framework */; }; 16 | 109717381B42B00F005A0E28 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 109717371B42B00F005A0E28 /* AppKit.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 1097172D1B42ABD4005A0E28 /* CopyFiles */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = "$(PROJECT_DIR)/../src/main/resources/com/martijncourteaux/multitouchgestures"; 24 | dstSubfolderSpec = 0; 25 | files = ( 26 | 1097172E1B42ABDE005A0E28 /* libmtg_mac.dylib in CopyFiles */, 27 | ); 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1020E4D91B4575BC008E4CB3 /* EventDispatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventDispatch.h; sourceTree = SOURCE_ROOT; }; 34 | 1020E4DA1B4575BC008E4CB3 /* EventDispatch.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EventDispatch.mm; sourceTree = SOURCE_ROOT; }; 35 | 109717121B42AA96005A0E28 /* libmtg_mac.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libmtg_mac.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 109717311B42AFCD005A0E28 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 37 | 109717331B42AFE6005A0E28 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 38 | 109717341B42AFE6005A0E28 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 39 | 109717371B42B00F005A0E28 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 1097170F1B42AA96005A0E28 /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | 109717381B42B00F005A0E28 /* AppKit.framework in Frameworks */, 48 | 109717351B42AFE6005A0E28 /* CoreFoundation.framework in Frameworks */, 49 | 109717361B42AFE6005A0E28 /* Foundation.framework in Frameworks */, 50 | 109717321B42AFCD005A0E28 /* Cocoa.framework in Frameworks */, 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 109717091B42AA96005A0E28 = { 58 | isa = PBXGroup; 59 | children = ( 60 | 109717371B42B00F005A0E28 /* AppKit.framework */, 61 | 109717331B42AFE6005A0E28 /* CoreFoundation.framework */, 62 | 109717341B42AFE6005A0E28 /* Foundation.framework */, 63 | 109717311B42AFCD005A0E28 /* Cocoa.framework */, 64 | 109717141B42AA96005A0E28 /* src */, 65 | 109717131B42AA96005A0E28 /* Products */, 66 | ); 67 | sourceTree = ""; 68 | }; 69 | 109717131B42AA96005A0E28 /* Products */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 109717121B42AA96005A0E28 /* libmtg_mac.dylib */, 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | 109717141B42AA96005A0E28 /* src */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 1020E4D91B4575BC008E4CB3 /* EventDispatch.h */, 81 | 1020E4DA1B4575BC008E4CB3 /* EventDispatch.mm */, 82 | ); 83 | name = src; 84 | path = OSXGestures4JavaJNI; 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXHeadersBuildPhase section */ 90 | 109717101B42AA96005A0E28 /* Headers */ = { 91 | isa = PBXHeadersBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | 1020E4DB1B4575BC008E4CB3 /* EventDispatch.h in Headers */, 95 | ); 96 | runOnlyForDeploymentPostprocessing = 0; 97 | }; 98 | /* End PBXHeadersBuildPhase section */ 99 | 100 | /* Begin PBXNativeTarget section */ 101 | 109717111B42AA96005A0E28 /* mtg_mac */ = { 102 | isa = PBXNativeTarget; 103 | buildConfigurationList = 109717251B42AA96005A0E28 /* Build configuration list for PBXNativeTarget "mtg_mac" */; 104 | buildPhases = ( 105 | 1097170E1B42AA96005A0E28 /* Sources */, 106 | 1097170F1B42AA96005A0E28 /* Frameworks */, 107 | 109717101B42AA96005A0E28 /* Headers */, 108 | 1097172D1B42ABD4005A0E28 /* CopyFiles */, 109 | ); 110 | buildRules = ( 111 | ); 112 | dependencies = ( 113 | ); 114 | name = mtg_mac; 115 | productName = OSXGestures4JavaJNI; 116 | productReference = 109717121B42AA96005A0E28 /* libmtg_mac.dylib */; 117 | productType = "com.apple.product-type.library.dynamic"; 118 | }; 119 | /* End PBXNativeTarget section */ 120 | 121 | /* Begin PBXProject section */ 122 | 1097170A1B42AA96005A0E28 /* Project object */ = { 123 | isa = PBXProject; 124 | attributes = { 125 | LastUpgradeCheck = 0700; 126 | ORGANIZATIONNAME = "Martijn Courteaux"; 127 | TargetAttributes = { 128 | 109717111B42AA96005A0E28 = { 129 | CreatedOnToolsVersion = 7.0; 130 | }; 131 | }; 132 | }; 133 | buildConfigurationList = 1097170D1B42AA96005A0E28 /* Build configuration list for PBXProject "mtg_mac" */; 134 | compatibilityVersion = "Xcode 3.2"; 135 | developmentRegion = English; 136 | hasScannedForEncodings = 0; 137 | knownRegions = ( 138 | en, 139 | ); 140 | mainGroup = 109717091B42AA96005A0E28; 141 | productRefGroup = 109717131B42AA96005A0E28 /* Products */; 142 | projectDirPath = ""; 143 | projectRoot = ""; 144 | targets = ( 145 | 109717111B42AA96005A0E28 /* mtg_mac */, 146 | ); 147 | }; 148 | /* End PBXProject section */ 149 | 150 | /* Begin PBXSourcesBuildPhase section */ 151 | 1097170E1B42AA96005A0E28 /* Sources */ = { 152 | isa = PBXSourcesBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | 1020E4DC1B4575BC008E4CB3 /* EventDispatch.mm in Sources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXSourcesBuildPhase section */ 160 | 161 | /* Begin XCBuildConfiguration section */ 162 | 109717231B42AA96005A0E28 /* Debug */ = { 163 | isa = XCBuildConfiguration; 164 | buildSettings = { 165 | ALWAYS_SEARCH_USER_PATHS = NO; 166 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 167 | CLANG_CXX_LIBRARY = "libc++"; 168 | CLANG_ENABLE_MODULES = YES; 169 | CLANG_ENABLE_OBJC_ARC = YES; 170 | CLANG_WARN_BOOL_CONVERSION = YES; 171 | CLANG_WARN_CONSTANT_CONVERSION = YES; 172 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 173 | CLANG_WARN_EMPTY_BODY = YES; 174 | CLANG_WARN_ENUM_CONVERSION = YES; 175 | CLANG_WARN_INT_CONVERSION = YES; 176 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 177 | CLANG_WARN_UNREACHABLE_CODE = YES; 178 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 179 | COPY_PHASE_STRIP = NO; 180 | DEBUG_INFORMATION_FORMAT = dwarf; 181 | ENABLE_STRICT_OBJC_MSGSEND = YES; 182 | ENABLE_TESTABILITY = YES; 183 | GCC_C_LANGUAGE_STANDARD = gnu99; 184 | GCC_DYNAMIC_NO_PIC = NO; 185 | GCC_NO_COMMON_BLOCKS = YES; 186 | GCC_OPTIMIZATION_LEVEL = 0; 187 | GCC_PREPROCESSOR_DEFINITIONS = ( 188 | "DEBUG=1", 189 | "$(inherited)", 190 | ); 191 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 192 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 193 | GCC_WARN_UNDECLARED_SELECTOR = YES; 194 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 195 | GCC_WARN_UNUSED_FUNCTION = YES; 196 | GCC_WARN_UNUSED_VARIABLE = YES; 197 | MACOSX_DEPLOYMENT_TARGET = 10.11; 198 | MTL_ENABLE_DEBUG_INFO = YES; 199 | ONLY_ACTIVE_ARCH = YES; 200 | SDKROOT = macosx; 201 | }; 202 | name = Debug; 203 | }; 204 | 109717241B42AA96005A0E28 /* Release */ = { 205 | isa = XCBuildConfiguration; 206 | buildSettings = { 207 | ALWAYS_SEARCH_USER_PATHS = NO; 208 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 209 | CLANG_CXX_LIBRARY = "libc++"; 210 | CLANG_ENABLE_MODULES = YES; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_WARN_BOOL_CONVERSION = YES; 213 | CLANG_WARN_CONSTANT_CONVERSION = YES; 214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INT_CONVERSION = YES; 218 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 219 | CLANG_WARN_UNREACHABLE_CODE = YES; 220 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 221 | COPY_PHASE_STRIP = NO; 222 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 223 | ENABLE_NS_ASSERTIONS = NO; 224 | ENABLE_STRICT_OBJC_MSGSEND = YES; 225 | GCC_C_LANGUAGE_STANDARD = gnu99; 226 | GCC_NO_COMMON_BLOCKS = YES; 227 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 229 | GCC_WARN_UNDECLARED_SELECTOR = YES; 230 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 231 | GCC_WARN_UNUSED_FUNCTION = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | MACOSX_DEPLOYMENT_TARGET = 10.11; 234 | MTL_ENABLE_DEBUG_INFO = NO; 235 | SDKROOT = macosx; 236 | }; 237 | name = Release; 238 | }; 239 | 109717261B42AA96005A0E28 /* Debug */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | DYLIB_COMPATIBILITY_VERSION = 1; 243 | DYLIB_CURRENT_VERSION = 1; 244 | EXECUTABLE_PREFIX = lib; 245 | HEADER_SEARCH_PATHS = /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers; 246 | PRODUCT_NAME = mtg_mac; 247 | }; 248 | name = Debug; 249 | }; 250 | 109717271B42AA96005A0E28 /* Release */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | DYLIB_COMPATIBILITY_VERSION = 1; 254 | DYLIB_CURRENT_VERSION = 1; 255 | EXECUTABLE_PREFIX = lib; 256 | HEADER_SEARCH_PATHS = /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers; 257 | PRODUCT_NAME = mtg_mac; 258 | }; 259 | name = Release; 260 | }; 261 | /* End XCBuildConfiguration section */ 262 | 263 | /* Begin XCConfigurationList section */ 264 | 1097170D1B42AA96005A0E28 /* Build configuration list for PBXProject "mtg_mac" */ = { 265 | isa = XCConfigurationList; 266 | buildConfigurations = ( 267 | 109717231B42AA96005A0E28 /* Debug */, 268 | 109717241B42AA96005A0E28 /* Release */, 269 | ); 270 | defaultConfigurationIsVisible = 0; 271 | defaultConfigurationName = Release; 272 | }; 273 | 109717251B42AA96005A0E28 /* Build configuration list for PBXNativeTarget "mtg_mac" */ = { 274 | isa = XCConfigurationList; 275 | buildConfigurations = ( 276 | 109717261B42AA96005A0E28 /* Debug */, 277 | 109717271B42AA96005A0E28 /* Release */, 278 | ); 279 | defaultConfigurationIsVisible = 0; 280 | defaultConfigurationName = Release; 281 | }; 282 | /* End XCConfigurationList section */ 283 | }; 284 | rootObject = 1097170A1B42AA96005A0E28 /* Project object */; 285 | } 286 | -------------------------------------------------------------------------------- /mac/mtg_mac.xcodeproj/xcuserdata/martijn.xcuserdatad/xcschemes/MultiTouch Gestures Mac.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 61 | 62 | 63 | 64 | 65 | 66 | 72 | 73 | 79 | 80 | 81 | 82 | 84 | 85 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /mac/mtg_mac.xcodeproj/xcuserdata/martijn.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MultiTouch Gestures Mac.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 109717111B42AA96005A0E28 16 | 17 | primary 18 | 19 | 20 | 1097171C1B42AA96005A0E28 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | all 17 | 18 | 19 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.martijncourteaux 5 | MultiTouchGesturesJava 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | UTF-8 10 | 1.6 11 | 1.6 12 | 13 | MultiTouch Gestures Java 14 | 15 | 16 | 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-compiler-plugin 21 | 3.3 22 | 23 | 1.6 24 | 1.6 25 | 26 | -XDignore.symbol.file 27 | 28 | true 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/EventDispatch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures; 7 | 8 | import com.martijncourteaux.multitouchgestures.event.GestureEvent.Phase; 9 | import java.awt.Dimension; 10 | import java.awt.Toolkit; 11 | import java.io.File; 12 | import java.io.FileOutputStream; 13 | import java.io.InputStream; 14 | import java.io.OutputStream; 15 | import javax.swing.SwingUtilities; 16 | 17 | /** 18 | * 19 | * @author martijn 20 | */ 21 | class EventDispatch 22 | { 23 | 24 | private static final boolean supported; 25 | private static Thread gestureEventThread; 26 | 27 | private static void printNativeNotFound(UnsatisfiedLinkError err, String name) 28 | { 29 | System.err.println(err); 30 | System.err.println("Put the " + name + " file in one of these folders:"); 31 | String[] paths = System.getProperty("java.library.path").split(":"); 32 | for (String path : paths) 33 | { 34 | System.err.println("\t" + path); 35 | } 36 | System.err.println(); 37 | } 38 | 39 | private static void extractNative(String nativeName) 40 | { 41 | try 42 | { 43 | System.out.print("Extracting native library"); 44 | 45 | InputStream is = EventDispatch.class.getResourceAsStream(nativeName); 46 | if (is == null) 47 | { 48 | System.out.println(" [ERROR] Could not load library."); 49 | return; 50 | } 51 | File outFile = new File("./" + nativeName); 52 | OutputStream os = new FileOutputStream(outFile); 53 | byte[] buff = new byte[1024 * 8]; 54 | int bytesRead; 55 | while ((bytesRead = is.read(buff)) != -1) 56 | { 57 | os.write(buff, 0, bytesRead); 58 | System.out.print("."); 59 | } 60 | System.out.println(); 61 | 62 | os.flush(); 63 | os.close(); 64 | is.close(); 65 | outFile.deleteOnExit(); 66 | } catch (Exception e) 67 | { 68 | System.out.println(" [ERROR] Could not load library:"); 69 | e.printStackTrace(); 70 | } 71 | } 72 | 73 | static 74 | { 75 | boolean supp = false; 76 | String os = System.getProperty("os.name").toLowerCase(); 77 | if (os.contains("mac os x")) 78 | { 79 | try 80 | { 81 | extractNative("libmtg_mac.dylib"); 82 | System.loadLibrary("mtg_mac"); 83 | supp = true; 84 | } catch (UnsatisfiedLinkError err) 85 | { 86 | printNativeNotFound(err, "libmtg_mac.dylib"); 87 | } 88 | } else 89 | { 90 | System.out.println("[MULTITOUCH GESTURES] Only Mac OS X is supported at the moment."); 91 | } 92 | 93 | if (supp) 94 | { 95 | Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() 96 | { 97 | 98 | @Override 99 | public void run() 100 | { 101 | System.out.println("In shutdownhook. Stopping Event Tap."); 102 | EventDispatch.stop(); 103 | } 104 | })); 105 | } 106 | 107 | supported = supp; 108 | } 109 | 110 | public static native void init(); 111 | 112 | private static native void start(); 113 | 114 | public static native void stop(); 115 | 116 | public static void startInSeperateThread() 117 | { 118 | if (!supported) 119 | { 120 | return; 121 | } 122 | 123 | if (gestureEventThread != null) 124 | { 125 | if (gestureEventThread.isAlive()) 126 | { 127 | return; 128 | } 129 | } 130 | 131 | gestureEventThread = new Thread(new Runnable() 132 | { 133 | @Override 134 | public void run() 135 | { 136 | init(); 137 | start(); 138 | } 139 | }, "Gesture Event Thread"); 140 | gestureEventThread.start(); 141 | } 142 | 143 | public static void dispatchMagnifyGesture(final double mouseX, final double mouseY, final double magnification, final int phase) 144 | { 145 | SwingUtilities.invokeLater(new Runnable() 146 | { 147 | @Override 148 | public void run() 149 | { 150 | Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); 151 | MultiTouchGestureUtilities.dispatchMagnifyGesture(mouseX, d.height - mouseY, magnification, Phase.getByCode(phase)); 152 | } 153 | }); 154 | 155 | } 156 | 157 | public static void dispatchRotateGesture(final double mouseX, final double mouseY, final double rotation, final int phase) 158 | { 159 | SwingUtilities.invokeLater(new Runnable() 160 | { 161 | @Override 162 | public void run() 163 | { 164 | Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); 165 | MultiTouchGestureUtilities.dispatchRotateGesture(mouseX, d.height - mouseY, -Math.toRadians(rotation), Phase.getByCode(phase)); 166 | } 167 | }); 168 | } 169 | 170 | public static void dispatchScrollWheelEvent(final double mouseX, final double mouseY, final double deltaX, final double deltaY, final int phase) 171 | { 172 | SwingUtilities.invokeLater(new Runnable() 173 | { 174 | @Override 175 | public void run() 176 | { 177 | Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); 178 | MultiTouchGestureUtilities.dispatchScrollGesture(mouseX, d.height - mouseY, deltaX, deltaY, Phase.getByCode(phase)); 179 | } 180 | }); 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/GestureAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures; 7 | 8 | import com.martijncourteaux.multitouchgestures.event.MagnifyGestureEvent; 9 | import com.martijncourteaux.multitouchgestures.event.RotateGestureEvent; 10 | import com.martijncourteaux.multitouchgestures.event.ScrollGestureEvent; 11 | 12 | /** 13 | * 14 | * @author martijn 15 | */ 16 | public abstract class GestureAdapter implements GestureListener 17 | { 18 | 19 | @Override 20 | public void magnify(MagnifyGestureEvent e) 21 | { 22 | } 23 | 24 | @Override 25 | public void rotate(RotateGestureEvent e) 26 | { 27 | } 28 | 29 | @Override 30 | public void scroll(ScrollGestureEvent e) 31 | { 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/GestureListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures; 7 | 8 | import com.martijncourteaux.multitouchgestures.event.MagnifyGestureEvent; 9 | import com.martijncourteaux.multitouchgestures.event.RotateGestureEvent; 10 | import com.martijncourteaux.multitouchgestures.event.ScrollGestureEvent; 11 | 12 | /** 13 | * 14 | * @author martijn 15 | */ 16 | public interface GestureListener 17 | { 18 | public void magnify(MagnifyGestureEvent e); 19 | 20 | public void rotate(RotateGestureEvent e); 21 | 22 | public void scroll(ScrollGestureEvent e); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/MultiTouchClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures; 7 | 8 | import java.awt.event.MouseEvent; 9 | import java.awt.event.MouseListener; 10 | import java.awt.event.MouseMotionListener; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import javax.swing.JComponent; 14 | 15 | /** 16 | * 17 | * @author martijn 18 | */ 19 | class MultiTouchClient implements MouseMotionListener, MouseListener 20 | { 21 | 22 | private final JComponent component; 23 | private final List listeners; 24 | private boolean inside; 25 | 26 | public MultiTouchClient(JComponent component) 27 | { 28 | this.component = component; 29 | this.listeners = new ArrayList(); 30 | } 31 | 32 | public void attachListeners() 33 | { 34 | inside = false; 35 | component.addMouseListener(this); 36 | component.addMouseMotionListener(this); 37 | } 38 | 39 | public void detachListeners() 40 | { 41 | inside = false; 42 | component.removeMouseListener(this); 43 | component.removeMouseMotionListener(this); 44 | } 45 | 46 | public JComponent getComponent() 47 | { 48 | return component; 49 | } 50 | 51 | public List getListeners() 52 | { 53 | return listeners; 54 | } 55 | 56 | public boolean isInside() 57 | { 58 | return inside; 59 | } 60 | 61 | @Override 62 | public void mouseDragged(MouseEvent e) 63 | { 64 | inside = true; 65 | if (e.getX() < 0 || component.getWidth() >= e.getX()) 66 | { 67 | inside = false; 68 | } 69 | if (e.getY() < 0 || component.getHeight() >= e.getY()) 70 | { 71 | inside = false; 72 | } 73 | } 74 | 75 | @Override 76 | public void mouseMoved(MouseEvent e) 77 | { 78 | inside = true; 79 | } 80 | 81 | @Override 82 | public void mouseClicked(MouseEvent e) 83 | { 84 | inside = true; 85 | } 86 | 87 | @Override 88 | public void mousePressed(MouseEvent e) 89 | { 90 | inside = true; 91 | } 92 | 93 | @Override 94 | public void mouseReleased(MouseEvent e) 95 | { 96 | inside = true; 97 | if (e.getX() < 0 || component.getWidth() >= e.getX()) 98 | { 99 | inside = false; 100 | } 101 | if (e.getY() < 0 || component.getHeight() >= e.getY()) 102 | { 103 | inside = false; 104 | } 105 | } 106 | 107 | @Override 108 | public void mouseEntered(MouseEvent e) 109 | { 110 | inside = true; 111 | } 112 | 113 | @Override 114 | public void mouseExited(MouseEvent e) 115 | { 116 | inside = false; 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/MultiTouchGestureUtilities.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures; 7 | 8 | import com.martijncourteaux.multitouchgestures.event.GestureEvent; 9 | import com.martijncourteaux.multitouchgestures.event.MagnifyGestureEvent; 10 | import com.martijncourteaux.multitouchgestures.event.RotateGestureEvent; 11 | import com.martijncourteaux.multitouchgestures.event.ScrollGestureEvent; 12 | import java.awt.Point; 13 | import java.awt.Rectangle; 14 | import java.util.HashMap; 15 | import javax.swing.JComponent; 16 | import java.util.List; 17 | import java.util.Map; 18 | import javax.swing.SwingUtilities; 19 | 20 | /** 21 | * 22 | * @author martijn 23 | */ 24 | public class MultiTouchGestureUtilities 25 | { 26 | 27 | private final static HashMap clients = new HashMap(); 28 | private static int listenerCount = 0; 29 | 30 | public static int getListenerCount() 31 | { 32 | return listenerCount; 33 | } 34 | 35 | public static void addGestureListener(JComponent component, GestureListener listener) 36 | { 37 | if (listenerCount == 0) 38 | { 39 | EventDispatch.startInSeperateThread(); 40 | } 41 | MultiTouchClient client = clients.get(component); 42 | if (client == null) 43 | { 44 | client = new MultiTouchClient(component); 45 | client.attachListeners(); 46 | clients.put(component, client); 47 | } 48 | List list = client.getListeners(); 49 | 50 | list.add(listener); 51 | listenerCount++; 52 | } 53 | 54 | public static boolean removeGestureListener(JComponent component, GestureListener listener) 55 | { 56 | MultiTouchClient client = clients.get(component); 57 | if (client == null) 58 | { 59 | return false; 60 | } 61 | List list = client.getListeners(); 62 | if (list == null) 63 | { 64 | return false; 65 | } 66 | 67 | if (list.remove(listener)) 68 | { 69 | if (list.isEmpty()) 70 | { 71 | client.detachListeners(); 72 | clients.remove(component); 73 | } 74 | listenerCount--; 75 | if (listenerCount == 0) 76 | { 77 | EventDispatch.stop(); 78 | } 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | public static int removeAllGestureListeners(JComponent component) 85 | { 86 | MultiTouchClient client = clients.get(component); 87 | if (client == null) 88 | { 89 | return 0; 90 | } 91 | client.detachListeners(); 92 | clients.remove(component); 93 | List list = client.getListeners(); 94 | if (list == null) 95 | { 96 | return 0; 97 | } 98 | int c = list.size(); 99 | list.clear(); 100 | return c; 101 | } 102 | 103 | protected static void dispatchMagnifyGesture(double mouseX, double mouseY, double magnification, GestureEvent.Phase phase) 104 | { 105 | if (listenerCount == 0) 106 | { 107 | return; 108 | } 109 | 110 | int mXi = (int) Math.round(mouseX); 111 | int mYi = (int) Math.round(mouseY); 112 | 113 | for (Map.Entry e : clients.entrySet()) 114 | { 115 | JComponent c = e.getKey(); 116 | Rectangle r = new Rectangle(c.getLocationOnScreen(), c.getSize()); 117 | if (r.contains(mXi, mYi)) 118 | { 119 | MultiTouchClient client = e.getValue(); 120 | if (client.isInside()) 121 | { 122 | List list = client.getListeners(); 123 | 124 | Point relP = new Point(mXi, mYi); 125 | SwingUtilities.convertPointFromScreen(relP, c); 126 | { 127 | MagnifyGestureEvent me = new MagnifyGestureEvent(c, relP.getX(), relP.getY(), mouseX, mouseY, phase, magnification); 128 | 129 | for (GestureListener l : list) 130 | { 131 | l.magnify(me); 132 | } 133 | } 134 | 135 | return; 136 | } 137 | } 138 | } 139 | } 140 | 141 | protected static void dispatchRotateGesture(double mouseX, double mouseY, double rotation, GestureEvent.Phase phase) 142 | { 143 | if (listenerCount == 0) 144 | { 145 | return; 146 | } 147 | 148 | int mXi = (int) Math.round(mouseX); 149 | int mYi = (int) Math.round(mouseY); 150 | 151 | for (HashMap.Entry e : clients.entrySet()) 152 | { 153 | JComponent c = e.getKey(); 154 | Rectangle r = new Rectangle(c.getLocationOnScreen(), c.getSize()); 155 | if (r.contains(mXi, mYi)) 156 | { 157 | MultiTouchClient client = e.getValue(); 158 | if (client.isInside()) 159 | { 160 | List list = client.getListeners(); 161 | 162 | Point relP = new Point(mXi, mYi); 163 | SwingUtilities.convertPointFromScreen(relP, c); 164 | { 165 | RotateGestureEvent re = new RotateGestureEvent(c, relP.getX(), relP.getY(), mouseX, mouseY, phase, rotation); 166 | 167 | for (GestureListener l : list) 168 | { 169 | l.rotate(re); 170 | } 171 | } 172 | 173 | return; 174 | } 175 | } 176 | } 177 | } 178 | 179 | protected static void dispatchScrollGesture(double mouseX, double mouseY, double dX, double dY, GestureEvent.Phase phase) 180 | { 181 | if (listenerCount == 0) 182 | { 183 | return; 184 | } 185 | 186 | int mXi = (int) Math.round(mouseX); 187 | int mYi = (int) Math.round(mouseY); 188 | 189 | for (HashMap.Entry e : clients.entrySet()) 190 | { 191 | JComponent c = e.getKey(); 192 | Rectangle r = new Rectangle(c.getLocationOnScreen(), c.getSize()); 193 | if (r.contains(mXi, mYi)) 194 | { 195 | MultiTouchClient client = e.getValue(); 196 | if (client.isInside()) 197 | { 198 | List list = client.getListeners(); 199 | 200 | Point relP = new Point(mXi, mYi); 201 | SwingUtilities.convertPointFromScreen(relP, c); 202 | { 203 | ScrollGestureEvent se = new ScrollGestureEvent(c, relP.getX(), relP.getY(), mouseX, mouseY, phase, dX, dY); 204 | 205 | for (GestureListener l : list) 206 | { 207 | l.scroll(se); 208 | } 209 | } 210 | 211 | return; 212 | } 213 | } 214 | } 215 | } 216 | 217 | } 218 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/demo/DemoAppleGestures.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures.demo; 7 | 8 | import com.apple.eawt.event.GestureUtilities; 9 | import com.apple.eawt.event.MagnificationEvent; 10 | import com.apple.eawt.event.RotationEvent; 11 | import java.awt.BasicStroke; 12 | import java.awt.BorderLayout; 13 | import java.awt.Color; 14 | import java.awt.Dimension; 15 | import java.awt.Graphics; 16 | import java.awt.Graphics2D; 17 | import java.awt.event.MouseWheelEvent; 18 | import java.awt.event.MouseWheelListener; 19 | import java.awt.geom.Line2D; 20 | import javax.swing.JComponent; 21 | import javax.swing.JFrame; 22 | 23 | /** 24 | * 25 | * @author martijn 26 | */ 27 | public class DemoAppleGestures 28 | { 29 | 30 | private static double a = 0, l = 50; 31 | private static double x, y; 32 | 33 | public static void main(String[] args) 34 | { 35 | 36 | JFrame frame = new JFrame(); 37 | frame.setTitle("Apple Gestures Demo"); 38 | final JComponent comp = new JComponent() 39 | { 40 | 41 | @Override 42 | protected void paintComponent(Graphics gg) 43 | { 44 | super.paintComponent(gg); 45 | Graphics2D g = (Graphics2D) gg; 46 | 47 | Line2D.Double line = new Line2D.Double(getWidth() * 0.5 + x, getHeight() * 0.5 + y, getWidth() * 0.5 + Math.cos(a) * l + x, getHeight() * 0.5 + Math.sin(a) * l + y); 48 | g.setColor(Color.red); 49 | g.setStroke(new BasicStroke(5.0f)); 50 | g.draw(line); 51 | } 52 | 53 | }; 54 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 55 | frame.setLayout(new BorderLayout()); 56 | frame.add(comp, BorderLayout.CENTER); 57 | frame.setPreferredSize(new Dimension(300, 200)); 58 | 59 | frame.setLocationRelativeTo(null); 60 | frame.pack(); 61 | frame.setVisible(true); 62 | 63 | comp.addMouseWheelListener(new MouseWheelListener() 64 | { 65 | 66 | @Override 67 | public void mouseWheelMoved(MouseWheelEvent e) 68 | { 69 | if (e.isShiftDown()) 70 | { 71 | x -= 3.0f * e.getPreciseWheelRotation(); 72 | } else 73 | { 74 | y -= 3.0f * e.getPreciseWheelRotation(); 75 | } 76 | comp.repaint(); 77 | } 78 | }); 79 | 80 | GestureUtilities.addGestureListenerTo(comp, new com.apple.eawt.event.GestureAdapter() 81 | { 82 | 83 | @Override 84 | public void magnify(MagnificationEvent me) 85 | { 86 | l *= 1.0 + me.getMagnification(); 87 | comp.repaint(); 88 | } 89 | 90 | @Override 91 | public void rotate(RotationEvent re) 92 | { 93 | a -= Math.toRadians(re.getRotation()); 94 | comp.repaint(); 95 | } 96 | 97 | }); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/demo/DemoCompareGestures.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures.demo; 7 | 8 | import com.apple.eawt.event.GestureUtilities; 9 | import com.apple.eawt.event.MagnificationEvent; 10 | import com.apple.eawt.event.RotationEvent; 11 | import com.martijncourteaux.multitouchgestures.GestureAdapter; 12 | import com.martijncourteaux.multitouchgestures.MultiTouchGestureUtilities; 13 | import com.martijncourteaux.multitouchgestures.event.MagnifyGestureEvent; 14 | import com.martijncourteaux.multitouchgestures.event.RotateGestureEvent; 15 | import com.martijncourteaux.multitouchgestures.event.ScrollGestureEvent; 16 | import java.awt.BasicStroke; 17 | import java.awt.Color; 18 | import java.awt.Dimension; 19 | import java.awt.Graphics; 20 | import java.awt.Graphics2D; 21 | import java.awt.GridLayout; 22 | import java.awt.event.MouseWheelEvent; 23 | import java.awt.event.MouseWheelListener; 24 | import java.awt.geom.Line2D; 25 | import javax.swing.JComponent; 26 | import javax.swing.JFrame; 27 | 28 | /** 29 | * 30 | * @author martijn 31 | */ 32 | public class DemoCompareGestures { 33 | private static double a = 0, l = 50; 34 | private static double x, y; 35 | 36 | private static double aa = 0, al = 50; 37 | private static double ax, ay; 38 | 39 | public static void main(String args[]) { 40 | JFrame frame = new JFrame(); 41 | frame.setTitle("MultiTouch left, Apple right"); 42 | final JComponent compMT = new JComponent() { 43 | @Override 44 | protected void paintComponent(Graphics gg) { 45 | super.paintComponent(gg); 46 | Graphics2D g = (Graphics2D) gg; 47 | 48 | Line2D.Double line = new Line2D.Double(getWidth() * 0.5 + x, getHeight() * 0.5 + y, 49 | getWidth() * 0.5 + Math.cos(a) * l + x, 50 | getHeight() * 0.5 + Math.sin(a) * l + y); 51 | g.setColor(Color.red); 52 | g.setStroke(new BasicStroke(5.0f)); 53 | g.draw(line); 54 | } 55 | }; 56 | final JComponent compApple = new JComponent() { 57 | @Override 58 | protected void paintComponent(Graphics gg) { 59 | super.paintComponent(gg); 60 | Graphics2D g = (Graphics2D) gg; 61 | 62 | Line2D.Double line = new Line2D.Double(getWidth() * 0.5 + ax, 63 | getHeight() * 0.5 + ay, getWidth() * 0.5 + Math.cos(aa) * al + ax, 64 | getHeight() * 0.5 + Math.sin(aa) * al + ay); 65 | g.setColor(Color.red); 66 | g.setStroke(new BasicStroke(5.0f)); 67 | g.draw(line); 68 | } 69 | }; 70 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 71 | frame.setLayout(new GridLayout(1, 2)); 72 | frame.add(compMT); 73 | frame.add(compApple); 74 | frame.setPreferredSize(new Dimension(300, 200)); 75 | 76 | frame.setLocationRelativeTo(null); 77 | frame.pack(); 78 | frame.setVisible(true); 79 | 80 | MultiTouchGestureUtilities.addGestureListener(compMT, new GestureAdapter() { 81 | @Override 82 | public void magnify(MagnifyGestureEvent e) { 83 | l *= 1.0 + e.getMagnification(); 84 | compMT.repaint(); 85 | } 86 | 87 | @Override 88 | public void rotate(RotateGestureEvent e) { 89 | a += e.getRotation(); 90 | compMT.repaint(); 91 | } 92 | 93 | @Override 94 | public void scroll(ScrollGestureEvent e) { 95 | x += e.getDeltaX(); 96 | y += e.getDeltaY(); 97 | compMT.repaint(); 98 | } 99 | }); 100 | 101 | compApple.addMouseWheelListener(new MouseWheelListener() { 102 | @Override 103 | public void mouseWheelMoved(MouseWheelEvent e) { 104 | if (e.isShiftDown()) { 105 | ax -= 3.0f * e.getPreciseWheelRotation(); 106 | } else { 107 | ay -= 3.0f * e.getPreciseWheelRotation(); 108 | } 109 | compApple.repaint(); 110 | } 111 | }); 112 | 113 | GestureUtilities.addGestureListenerTo(compApple, new com.apple.eawt.event.GestureAdapter() { 114 | @Override 115 | public void magnify(MagnificationEvent me) { 116 | al *= 1.0 + me.getMagnification(); 117 | compApple.repaint(); 118 | } 119 | 120 | @Override 121 | public void rotate(RotationEvent re) { 122 | aa -= Math.toRadians(re.getRotation()); 123 | compApple.repaint(); 124 | } 125 | }); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/demo/DemoSimpleGestures.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures.demo; 7 | 8 | import com.martijncourteaux.multitouchgestures.GestureAdapter; 9 | import com.martijncourteaux.multitouchgestures.MultiTouchGestureUtilities; 10 | import com.martijncourteaux.multitouchgestures.event.MagnifyGestureEvent; 11 | import com.martijncourteaux.multitouchgestures.event.RotateGestureEvent; 12 | import com.martijncourteaux.multitouchgestures.event.ScrollGestureEvent; 13 | import java.awt.BasicStroke; 14 | import java.awt.BorderLayout; 15 | import java.awt.Color; 16 | import java.awt.Dimension; 17 | import java.awt.Graphics; 18 | import java.awt.Graphics2D; 19 | import java.awt.geom.Line2D; 20 | import javax.swing.JComponent; 21 | import javax.swing.JFrame; 22 | 23 | /** 24 | * 25 | * @author martijn 26 | */ 27 | public class DemoSimpleGestures 28 | { 29 | 30 | private static double a = 0, l = 50; 31 | private static double x, y; 32 | 33 | public static void main(String[] args) 34 | { 35 | 36 | JFrame frame = new JFrame(); 37 | frame.setTitle("MultiTouch Gestures Demo"); 38 | final JComponent comp = new JComponent() 39 | { 40 | 41 | @Override 42 | protected void paintComponent(Graphics gg) 43 | { 44 | super.paintComponent(gg); 45 | Graphics2D g = (Graphics2D) gg; 46 | 47 | Line2D.Double line = new Line2D.Double(getWidth() * 0.5 + x, getHeight() * 0.5 + y, getWidth() * 0.5 + Math.cos(a) * l + x, getHeight() * 0.5 + Math.sin(a) * l + y); 48 | g.setColor(Color.red); 49 | g.setStroke(new BasicStroke(5.0f)); 50 | g.draw(line); 51 | } 52 | 53 | }; 54 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 55 | frame.setLayout(new BorderLayout()); 56 | frame.add(comp, BorderLayout.CENTER); 57 | frame.setPreferredSize(new Dimension(300, 200)); 58 | 59 | frame.setLocationRelativeTo(null); 60 | frame.pack(); 61 | frame.setVisible(true); 62 | 63 | MultiTouchGestureUtilities.addGestureListener(comp, new GestureAdapter() 64 | { 65 | 66 | @Override 67 | public void magnify(MagnifyGestureEvent e) 68 | { 69 | l *= 1.0 + e.getMagnification(); 70 | comp.repaint(); 71 | } 72 | 73 | @Override 74 | public void rotate(RotateGestureEvent e) 75 | { 76 | a += e.getRotation(); 77 | comp.repaint(); 78 | } 79 | 80 | @Override 81 | public void scroll(ScrollGestureEvent e) 82 | { 83 | x += e.getDeltaX(); 84 | y += e.getDeltaY(); 85 | comp.repaint(); 86 | } 87 | 88 | }); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/event/GestureEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures.event; 7 | 8 | import javax.swing.JComponent; 9 | 10 | /** 11 | * 12 | * @author martijn 13 | */ 14 | public class GestureEvent 15 | { 16 | 17 | public enum Phase 18 | { 19 | MOMENTUM(0), BEGIN(1), CHANGED(2), END(3), CANCELLED(4), OTHER(-1); 20 | 21 | private final int code; 22 | 23 | private Phase(int code) 24 | { 25 | this.code = code; 26 | } 27 | 28 | public int getCode() 29 | { 30 | return code; 31 | } 32 | 33 | public static Phase getByCode(int code) 34 | { 35 | for (Phase p : values()) 36 | { 37 | if (p.getCode() == code) return p; 38 | } 39 | return OTHER; 40 | } 41 | } 42 | 43 | private final JComponent source; 44 | private final double mouseX, mouseY; 45 | private final double absMouseX, absMouseY; 46 | private final Phase phase; 47 | 48 | public GestureEvent(JComponent source, double mouseX, double mouseY, double absMouseX, double absMouseY, Phase phase) 49 | { 50 | this.source = source; 51 | this.mouseX = mouseX; 52 | this.mouseY = mouseY; 53 | this.absMouseX = absMouseX; 54 | this.absMouseY = absMouseY; 55 | this.phase = phase; 56 | } 57 | 58 | public JComponent getSource() 59 | { 60 | return source; 61 | } 62 | 63 | public double getMouseX() 64 | { 65 | return mouseX; 66 | } 67 | 68 | public double getMouseY() 69 | { 70 | return mouseY; 71 | } 72 | 73 | public double getAbsMouseX() 74 | { 75 | return absMouseX; 76 | } 77 | 78 | public double getAbsMouseY() 79 | { 80 | return absMouseY; 81 | } 82 | 83 | public Phase getPhase() 84 | { 85 | return phase; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/event/MagnifyGestureEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures.event; 7 | 8 | import javax.swing.JComponent; 9 | 10 | /** 11 | * 12 | * @author martijn 13 | */ 14 | public class MagnifyGestureEvent extends GestureEvent 15 | { 16 | private final double magnification; 17 | 18 | public MagnifyGestureEvent(JComponent source, double mouseX, double mouseY, double absMouseX, double absMouseY, Phase phase, double magnification) 19 | { 20 | super(source, mouseX, mouseY, absMouseX, absMouseY, phase); 21 | this.magnification = magnification; 22 | } 23 | 24 | public double getMagnification() 25 | { 26 | return magnification; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/event/RotateGestureEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures.event; 7 | 8 | import javax.swing.JComponent; 9 | 10 | /** 11 | * 12 | * @author martijn 13 | */ 14 | public class RotateGestureEvent extends GestureEvent 15 | { 16 | 17 | private final double rotation; 18 | 19 | public RotateGestureEvent(JComponent source, double mouseX, double mouseY, double absMouseX, double absMouseY, Phase phase, double rotation) 20 | { 21 | super(source, mouseX, mouseY, absMouseX, absMouseY, phase); 22 | this.rotation = rotation; 23 | } 24 | 25 | public double getRotation() 26 | { 27 | return rotation; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/martijncourteaux/multitouchgestures/event/ScrollGestureEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.martijncourteaux.multitouchgestures.event; 7 | 8 | import javax.swing.JComponent; 9 | 10 | /** 11 | * 12 | * @author martijn 13 | */ 14 | public class ScrollGestureEvent extends GestureEvent 15 | { 16 | private final double dX, dY; 17 | 18 | public ScrollGestureEvent(JComponent source, double mouseX, double mouseY, double absMouseX, double absMouseY, Phase phase, double dX, double dY) 19 | { 20 | super(source, mouseX, mouseY, absMouseX, absMouseY, phase); 21 | this.dX = dX; 22 | this.dY = dY; 23 | } 24 | 25 | public double getDeltaX() 26 | { 27 | return dX; 28 | } 29 | 30 | public double getDeltaY() 31 | { 32 | return dY; 33 | } 34 | 35 | } 36 | --------------------------------------------------------------------------------