001/* [{ 002Copyright 2007-2011 Nicolas Carranza <nicarran at gmail.com> 003 004This file is part of jpen. 005 006jpen is free software: you can redistribute it and/or modify 007it under the terms of the GNU Lesser General Public License as published by 008the Free Software Foundation, either version 3 of the License, 009or (at your option) any later version. 010 011jpen is distributed in the hope that it will be useful, 012but WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014GNU Lesser General Public License for more details. 015 016You should have received a copy of the GNU Lesser General Public License 017along with jpen. If not, see <http://www.gnu.org/licenses/>. 018}] */ 019package jpen.owner.awt; 020 021import java.awt.Component; 022import java.awt.Dialog; 023import java.awt.event.MouseEvent; 024import java.awt.event.MouseMotionListener; 025import java.awt.Window; 026import java.lang.ref.WeakReference; 027import java.util.Arrays; 028import java.util.Collection; 029import javax.swing.SwingUtilities; 030import jpen.internal.ActiveWindowProperty; 031import jpen.owner.AbstractPenOwner; 032import jpen.owner.PenClip; 033import jpen.PenProvider; 034import jpen.provider.osx.CocoaProvider; 035import jpen.provider.system.SystemProvider; 036import jpen.provider.wintab.WintabProvider; 037import jpen.provider.xinput.XinputProvider; 038 039public abstract class ComponentPenOwner 040 extends AbstractPenOwner{ 041 042 private final ComponentPenClip componentPenClip=new ComponentPenClip(this); 043 044 public abstract Component getActiveComponent(); 045 046 //@Override 047 public final Collection<PenProvider.Constructor> getPenProviderConstructors(){ 048 return Arrays.asList( 049 new PenProvider.Constructor[]{ 050 new SystemProvider.Constructor(), 051 new XinputProvider.Constructor(), 052 new WintabProvider.Constructor(), 053 new CocoaProvider.Constructor(), 054 } 055 ); 056 } 057 058 //@Override 059 public final PenClip getPenClip(){ 060 return componentPenClip; 061 } 062 063 protected final Unpauser unpauser=new Unpauser(); 064 065 protected final class Unpauser 066 implements MouseMotionListener{ 067 068 private volatile boolean enabled; 069 private WeakReference<Component> myActiveComponentRef; 070 071 public synchronized void enable(){ 072 if(enabled) 073 return; 074 Component myActiveComponent=getActiveComponent(); 075 myActiveComponentRef=new WeakReference<Component>(myActiveComponent); 076 myActiveComponent.addMouseMotionListener(unpauser); // unpauses only when mouse motion is detected. 077 enabled=true; 078 } 079 080 synchronized void disable(){ 081 if(!enabled) 082 return; 083 Component myActiveComponent=myActiveComponentRef.get(); 084 if(myActiveComponent!=null){ 085 myActiveComponent.removeMouseMotionListener(unpauser); 086 myActiveComponent=null; 087 } 088 enabled=false; 089 } 090 091 //@Override 092 public void mouseMoved(MouseEvent ev){ 093 //L.fine("v"); 094 unpause(); 095 } 096 097 //@Override 098 public void mouseDragged(MouseEvent ev){ 099 } 100 101 void unpause(){ 102 synchronized(penManagerHandle.getPenSchedulerLock()){ 103 if(enabled){ 104 activeWindowPL.setEnabled(true); 105 penManagerHandle.setPenManagerPaused(false); 106 disable(); 107 } 108 } 109 } 110 } 111 112 private final ActiveWindowPL activeWindowPL=new ActiveWindowPL(); 113 114 private class ActiveWindowPL 115 implements ActiveWindowProperty.Listener{ 116 117 private boolean enabled; 118 private ActiveWindowProperty activeWindowP; 119 120 void setEnabled(boolean enabled){ 121 if(activeWindowP==null) 122 activeWindowP=new ActiveWindowProperty(this); 123 this.enabled=enabled; 124 } 125 126 //@Override 127 public void activeWindowChanged(Window activeWindow){ 128 if(!enabled) 129 return; 130 synchronized(getPenSchedulerLock(activeWindow)){ 131 if(activeWindow==null){ 132 // if there is no active window on this application, on MS Windows the mouse stops sending events. 133 pauseAMoment(); 134 return; 135 } 136 Window componentWindow=SwingUtilities.getWindowAncestor(getActiveComponent()); 137 if(componentWindow==null) 138 return; 139 if(activeWindow!=componentWindow && 140 activeWindow instanceof Dialog){ 141 // A modal dialog stops sending events from other windows when shown... then here we honor this behavior 142 Dialog activeDialog=(Dialog)activeWindow; 143 if(activeDialog.isModal()) 144 pauseAMoment(); 145 } 146 } 147 } 148 149 private void pauseAMoment(){ 150 pause(); 151 unpauser.enable(); 152 } 153 } 154 155 protected final void pause(){ 156 unpauser.disable(); 157 activeWindowPL.setEnabled(false); 158 penManagerHandle.setPenManagerPaused(true); 159 } 160 161 @Override 162 protected void draggingOutDisengaged(){ 163 pause(); 164 } 165 166 /** 167 Checks if the given {@link Component} holds the {@link Component#getTreeLock()} before actually getting and returning the {@link jpen.owner.PenOwner.PenManagerHandle#getPenSchedulerLock()}. Prefer using this method instead of {@link jpen.owner.PenOwner.PenManagerHandle#getPenSchedulerLock()} to be shure you aren't causing deadlocks because the {@link ComponentPenClip} methods hold the {@link Component#getTreeLock()}. 168 */ 169 protected Object getPenSchedulerLock(Component component){ 170 if(component!=null && Thread.currentThread().holdsLock(component.getTreeLock())) 171 throw new AssertionError("tryed to hold penSchedulerLock while holding Component's treeLock"); 172 return penManagerHandle.getPenSchedulerLock(); 173 } 174}