Blame mono/Assistance/InputManager.cs

61bedf
using System;
61bedf
using System.Collections.Generic;
61bedf
61bedf
namespace Assistance {
589f9a
	public class InputManager: Track.IOwner {
0f2bf8
		public static readonly Drawing.Pen penPreview = new Drawing.Pen("Dark Green", 3.0, 0.25);
720280
		public static readonly double levelAlpha = 0.8;
61bedf
61bedf
		public class TrackHandler: Track.Handler {
61bedf
			public readonly List<int> keys = new List<int>();</int></int>
c3ebff
			public TrackHandler(InputManager owner, Track original, int keysCount = 0):
589f9a
				base(owner, original)
c3ebff
				{ for(int i = 0; i < keysCount; ++i) keys.Add(0); }
61bedf
		}
61bedf
		
61bedf
		public class KeyPoint {
61bedf
			public class Holder: IDisposable {
61bedf
				public readonly KeyPoint keyPoint;
1d3aae
				private bool holded = false;
1d3aae
				
61bedf
				public Holder(KeyPoint keyPoint)
1d3aae
					{ this.keyPoint = keyPoint; reuse(); }
1d3aae
1d3aae
				public bool available
1d3aae
					{ get { return keyPoint.available; } }
1d3aae
				public bool isHolded
1d3aae
					{ get { return holded; } }
1d3aae
				public bool reuse() {
1d3aae
					if (!holded) ++keyPoint.refCount;
1d3aae
					holded = true;
1d3aae
					return keyPoint.available;
1d3aae
				}
1d3aae
				public void release() {
1d3aae
					if (holded) --keyPoint.refCount;
1d3aae
					holded = false;
1d3aae
				}
1d3aae
					
61bedf
				public void Dispose()
61bedf
					{ Dispose(true); GC.SuppressFinalize(this); }
61bedf
				protected virtual void Dispose(bool disposing)
1d3aae
					{  release(); }
61bedf
				~Holder()
61bedf
					{ Dispose(false); }
61bedf
			}
1d3aae
			
61bedf
			private int refCount = 0;
1d3aae
			public bool available = true;
1d3aae
			
61bedf
			public Holder hold()
61bedf
				{ return new Holder(this); }
61bedf
			public bool isFree
61bedf
				{ get { return refCount <= 0; } }
61bedf
		}
61bedf
		
589f9a
		public interface IModifier: Track.IOwner {
589f9a
			void activate();
589f9a
			void modify(List<track> tracks, KeyPoint keyPoint, List<track> outTracks);
0f2bf8
			void draw(Cairo.Context context, List<track> tracks, List<point> hovers);</point>
589f9a
			void deactivate();
589f9a
		}
589f9a
589f9a
		public class Modifier: IModifier {
c3ebff
			public virtual void activate() { }
589f9a
			public virtual void modify(Track track, KeyPoint keyPoint, List<track> outTracks) { }
c3ebff
			public virtual void modify(List<track> tracks, KeyPoint keyPoint, List<track> outTracks)
c3ebff
				{ foreach(Track track in tracks) modify(track, keyPoint, outTracks); }
0f2bf8
			public virtual void drawHover(Cairo.Context context, Point hover) { }
0f2bf8
			public virtual void drawTrack(Cairo.Context context, Track track) { }
0f2bf8
			public virtual void draw(Cairo.Context context, List<track> tracks, List<point> hovers) {</point>
0f2bf8
				foreach(Track track in tracks) drawTrack(context, track);
0f2bf8
				foreach(Point hover in hovers) drawHover(context, hover);
0f2bf8
			}
c3ebff
			public virtual void deactivate() { }
c3ebff
		}
c3ebff
61bedf
		
61bedf
		public readonly Workarea workarea;
61bedf
		
61bedf
		private readonly InputState state = new InputState();
b9e5e0
		
b9e5e0
		private bool wantActive;
b9e5e0
		private bool actualActive;
61bedf
		private Tool tool;
589f9a
		private readonly List<imodifier> modifiers = new List<imodifier>();</imodifier></imodifier>
61bedf
b9e5e0
		private readonly List<list<track>> tracks = new List<list<track>>() { new List<track>() };</list<track></list<track>
61bedf
		private readonly List<keypoint> keyPoints = new List<keypoint>();</keypoint></keypoint>
61bedf
		private int keyPointsSent;
61bedf
c3ebff
b9e5e0
		public InputManager(Workarea workarea)
61bedf
			{ this.workarea = workarea; }
61bedf
61bedf
		private void paintRollbackTo(int keyIndex, List<track> subTracks) {
61bedf
			if (keyIndex >= keyPoints.Count)
61bedf
				return;
0f2bf8
61bedf
			int level = keyIndex + 1;
61bedf
			if (level <= keyPointsSent) {
589f9a
				if (level < keyPointsSent)
61bedf
					tool.paintPop(keyPointsSent - level);
61bedf
				tool.paintCancel();
61bedf
				keyPointsSent = level;
61bedf
			}
61bedf
			
61bedf
			foreach(Track track in subTracks) {
61bedf
				TrackHandler handler = (TrackHandler)track.handler;
61bedf
				handler.keys.RemoveRange(level, keyPoints.Count - level);
589f9a
				int cnt = handler.keys[keyIndex];
61bedf
				track.wayPointsRemoved = 0;
589f9a
				track.wayPointsAdded = track.points.Count - cnt;
61bedf
			}
1d3aae
			for(int i = level; i < keyPoints.Count; ++i) keyPoints[i].available = false;
61bedf
			keyPoints.RemoveRange(level, keyPoints.Count - level);
61bedf
		}
61bedf
		
589f9a
		private void paintApply(int count, List<track> subTracks) {
61bedf
			if (count <= 0)
61bedf
				return;
0f2bf8
				
1d3aae
			int level = keyPoints.Count - count;
0f2bf8
			bool resend = true;
589f9a
			
589f9a
			if (level < keyPointsSent) {
61bedf
				// apply
720280
				int applied = tool.paintApply(keyPointsSent - level);
720280
				applied = Math.Max(0, Math.Min(keyPointsSent - level, applied));
720280
				keyPointsSent -= applied;
0f2bf8
				if (keyPointsSent == level) resend = false;
589f9a
			}
0f2bf8
			
589f9a
			if (level < keyPointsSent) {
61bedf
				// rollback
61bedf
				tool.paintPop(keyPointsSent - level);
589f9a
				keyPointsSent = level;
61bedf
			}
61bedf
589f9a
			// remove keypoints
0f2bf8
			foreach(Track track in subTracks) {
0f2bf8
				TrackHandler handler = (TrackHandler)track.handler;
0f2bf8
				if (resend) {
0f2bf8
					track.wayPointsRemoved = 0;
0f2bf8
					track.wayPointsAdded = track.points.Count - handler.keys[keyPointsSent];
0f2bf8
				}
0f2bf8
				handler.keys.RemoveRange(level, handler.keys.Count - level);
0f2bf8
			}
1d3aae
			for(int i = level; i < keyPoints.Count; ++i) keyPoints[i].available = false;
61bedf
			keyPoints.RemoveRange(level, keyPoints.Count - level);
61bedf
		}
61bedf
		
61bedf
		private void paintTracks() {
61bedf
			bool allFinished = true;
b9e5e0
			foreach(Track track in tracks[0])
589f9a
				if (!track.isFinished())
61bedf
					{ allFinished = false; break; }
61bedf
61bedf
			while(true) {
61bedf
				// run modifiers
61bedf
				KeyPoint newKeyPoint = new KeyPoint();
b9e5e0
				for(int i = 0; i < modifiers.Count; ++i) {
b9e5e0
					tracks[i+1].Clear();
b9e5e0
					modifiers[i].modify(tracks[i], newKeyPoint, tracks[i+1]);
c3ebff
				}
b9e5e0
				List<track> subTracks = tracks[modifiers.Count];
c3ebff
				
c3ebff
				// create handlers	
c3ebff
				foreach(Track track in subTracks)
c3ebff
					if (track.handler == null)
c3ebff
						track.handler = new TrackHandler(this, track, keyPoints.Count);
61bedf
	
61bedf
				if (keyPoints.Count > 0) {
61bedf
					// rollback
61bedf
					int rollbackIndex = keyPoints.Count;
61bedf
					foreach(Track track in subTracks) {
61bedf
						if (track.wayPointsRemoved > 0) {
0f2bf8
							int count = track.points.Count - track.wayPointsAdded;
61bedf
							TrackHandler handler = (TrackHandler)track.handler;
61bedf
							while(rollbackIndex > 0 && (rollbackIndex >= keyPoints.Count || handler.keys[rollbackIndex] > count))
61bedf
								--rollbackIndex;
61bedf
						}
61bedf
					}
61bedf
					paintRollbackTo(rollbackIndex, subTracks);
61bedf
	
61bedf
					// apply
61bedf
					int applyCount = 0;
61bedf
					while(applyCount < keyPoints.Count && keyPoints[keyPoints.Count - applyCount - 1].isFree)
61bedf
						++applyCount;
61bedf
					paintApply(applyCount, subTracks);
61bedf
				}
61bedf
				
61bedf
				// send to tool
720280
				if (keyPointsSent == keyPoints.Count && subTracks.Count > 0)
61bedf
					tool.paintTracks(subTracks);
0f2bf8
				foreach(Track track in subTracks) {
0f2bf8
					track.wayPointsRemoved = 0;
0f2bf8
					track.wayPointsAdded = 0;
0f2bf8
				}
61bedf
				
61bedf
				// is paint finished?
61bedf
				if (newKeyPoint.isFree) {
61bedf
					if (allFinished) {
61bedf
						paintApply(keyPoints.Count, subTracks);
b9e5e0
						foreach(List<track> ts in tracks)
b9e5e0
							ts.Clear();
61bedf
					}
61bedf
					break;
61bedf
				}
61bedf
				
61bedf
				// create key point
61bedf
				if (tool.paintPush()) ++keyPointsSent;
61bedf
				keyPoints.Add(newKeyPoint);
61bedf
				foreach(Track track in subTracks)
61bedf
					((TrackHandler)track.handler).keys.Add(track.points.Count);
61bedf
			}
0f2bf8
61bedf
		}
0f2bf8
0f2bf8
		private long getDeviceId(Gdk.Device device)
0f2bf8
			{ return device == null ? 0 : device.Handle.ToInt64(); }
0f2bf8
				
61bedf
		private int trackCompare(Track track, Gdk.Device device, long touchId) {
0f2bf8
			if (getDeviceId(track.device) < getDeviceId(device)) return -1;
0f2bf8
			if (getDeviceId(track.device) > getDeviceId(device)) return 1;
61bedf
			if (track.touchId < touchId) return -1;
61bedf
			if (track.touchId > touchId) return 1;
61bedf
			return 0;
61bedf
		}
61bedf
		
61bedf
		private Track createTrack(int index, Gdk.Device device, long touchId, long ticks) {
61bedf
			Track track = new Track(
61bedf
				device,
61bedf
				touchId,
61bedf
				state.keyHistoryHolder(ticks),
61bedf
				state.buttonHistoryHolder(device, ticks) );
b9e5e0
			tracks[0].Insert(index, track);
61bedf
			return track;
61bedf
		}
61bedf
		
61bedf
		private Track getTrack(Gdk.Device device, long touchId, long ticks) {
0f2bf8
			if (tracks[0].Count == 0)
0f2bf8
				return createTrack(0, device, touchId, ticks);
61bedf
			int cmp;
61bedf
			
61bedf
			int a = 0;
b9e5e0
			cmp = trackCompare(tracks[0][a], device, touchId);
b9e5e0
			if (cmp == 0) return tracks[0][a];
61bedf
			if (cmp < 0) return createTrack(a, device, touchId, ticks);
61bedf
			
0f2bf8
			int b = tracks[0].Count - 1;
b9e5e0
			cmp = trackCompare(tracks[0][b], device, touchId);
b9e5e0
			if (cmp == 0) return tracks[0][b];
61bedf
			if (cmp > 0) return createTrack(b+1, device, touchId, ticks);
61bedf
			
61bedf
			// binary search: tracks[a] < tracks[c] < tracks[b]
61bedf
			while(true) {
61bedf
				int c = (a + b)/2;
61bedf
				if (a == c) break;
b9e5e0
				cmp = trackCompare(tracks[0][c], device, touchId);
61bedf
				if (cmp < 0) b = c; else
61bedf
					if (cmp > 0) a = c; else
b9e5e0
						return tracks[0][c];
61bedf
			}
61bedf
			return createTrack(b, device, touchId, ticks);
61bedf
		}
61bedf
		
61bedf
		private void addTrackPoint(Track track, Track.Point point, double time, bool final) {
61bedf
			// fix time
61bedf
			if (track.points.Count > 0)
61bedf
				time = Math.Max(time, track.getLast().time + Timer.step);
61bedf
			
61bedf
			// calc length
61bedf
			double length = track.points.Count > 0
1cdf17
			              ? (point.position - track.getLast().point.position).len() + track.getLast().length
61bedf
			              : 0.0;
61bedf
			
61bedf
			// add
61bedf
			track.points.Add( new Track.WayPoint(
61bedf
				point,
61bedf
				new Track.Point(),
61bedf
				(double)track.points.Count,
61bedf
				time,
61bedf
				length,
61bedf
				track.points.Count,
61bedf
				final ));
61bedf
			++track.wayPointsAdded;
61bedf
		}
61bedf
		
61bedf
		private void touchTracks(bool finish = false) {
b9e5e0
			foreach(Track track in tracks[0])
61bedf
				if (!track.isFinished() && track.points.Count > 0)
61bedf
					addTrackPoint(track, track.getLast().point, track.getLast().time, finish);
61bedf
			paintTracks();
61bedf
		}
61bedf
		
b9e5e0
		private void actualActivate() {
b9e5e0
			bool wasActive = actualActive;
b9e5e0
			actualActive = wantActive && tool != null;
b9e5e0
			if (wasActive == actualActive) return;
b9e5e0
			
b9e5e0
			if (actualActive) {
b9e5e0
				foreach(IModifier modifier in modifiers)
b9e5e0
					modifier.activate();
b9e5e0
				tool.activate();
b9e5e0
			} else {
b9e5e0
				touchTracks(true);
b9e5e0
				tool.deactivate();
b9e5e0
				foreach(IModifier modifier in modifiers)
b9e5e0
					modifier.deactivate();
b9e5e0
			}
b9e5e0
		}
b9e5e0
		
b9e5e0
		public bool isActive()
b9e5e0
			{ return actualActive; }
b9e5e0
		public void activate()
b9e5e0
			{ wantActive = true; actualActivate(); }
b9e5e0
		public void deactivate()
b9e5e0
			{ wantActive = false; actualActivate(); }
b9e5e0
		public void finishTracks()
b9e5e0
			{ if (isActive()) touchTracks(true); }
b9e5e0
			
b9e5e0
		public List<track> getInputTracks()
b9e5e0
			{ return tracks[0]; }
b9e5e0
		public List<track> getOutputTracks()
b9e5e0
			{ return tracks[modifiers.Count]; }
61bedf
		
61bedf
		
b9e5e0
		public void trackEvent(Gdk.Device device, long touchId, Track.Point point, bool final, long ticks) {
b9e5e0
			if (isActive()) {
589f9a
				Track track = getTrack(device, touchId, ticks);
589f9a
				if (!track.isFinished()) {
61bedf
					double time = (double)(ticks - track.keyHistory.ticks)*Timer.step - track.keyHistory.timeOffset;
61bedf
					addTrackPoint(track, point, time, final);
61bedf
					paintTracks();
61bedf
				}
61bedf
			}
61bedf
		}
61bedf
61bedf
		public void keyEvent(bool press, Gdk.Key key, long ticks) {
61bedf
			state.keyEvent(press, key, ticks);
b9e5e0
			if (isActive()) {
61bedf
				tool.keyEvent(press, key, state);
61bedf
				touchTracks();
61bedf
			}
61bedf
		}
61bedf
		
61bedf
		public void buttonEvent(bool press, Gdk.Device device, uint button, long ticks) {
61bedf
			state.buttonEvent(press, device, button, ticks);
b9e5e0
			if (isActive()) {
61bedf
				tool.buttonEvent(press, device, button, state);
61bedf
				touchTracks();
61bedf
			}
61bedf
		}
61bedf
	
61bedf
		public Tool getTool()
61bedf
			{ return tool; }
61bedf
		
61bedf
		public void setTool(Tool tool) {
0f2bf8
			if (this.tool != tool) {
b9e5e0
				if (actualActive) {
61bedf
					finishTracks();
61bedf
					this.tool.deactivate();
61bedf
				}
61bedf
				
61bedf
				this.tool = tool;
61bedf
				
b9e5e0
				if (actualActive) {
b9e5e0
					if (this.tool != null)
b9e5e0
						this.tool.activate();
b9e5e0
					else
b9e5e0
						actualActivate();
b9e5e0
				}
61bedf
			}
61bedf
		}
61bedf
		
61bedf
		public int getModifiersCount()
61bedf
			{ return modifiers.Count; }
589f9a
		public IModifier getModifier(int index)
61bedf
			{ return modifiers[index]; }
61bedf
589f9a
		public void insertModifier(int index, IModifier modifier) {
b9e5e0
			if (actualActive)
b9e5e0
				finishTracks();
61bedf
			modifiers.Insert(index, modifier);
b9e5e0
			tracks.Insert(index+1, new List<track>());
b9e5e0
			if (actualActive)
b9e5e0
				modifier.activate();
61bedf
		}
589f9a
		public void addModifier(IModifier modifier)
61bedf
			{ insertModifier(getModifiersCount(), modifier); }
61bedf
		
61bedf
		public void removeModifier(int index) {
b9e5e0
			if (actualActive) {
b9e5e0
				finishTracks();
b9e5e0
				modifiers[index].deactivate();
b9e5e0
			}
61bedf
			modifiers.RemoveAt(index);
b9e5e0
			tracks.RemoveAt(index+1);
61bedf
		}
589f9a
		public void removeModifier(IModifier modifier) {
61bedf
			for(int i = 0; i < getModifiersCount(); ++i)
61bedf
				if (getModifier(i) == modifier)
61bedf
					{ removeModifier(i); break; }
61bedf
		}
61bedf
		public void clearModifiers() {
61bedf
			while(getModifiersCount() > 0)
b9e5e0
				removeModifier(getModifiersCount() - 1);
61bedf
		}
61bedf
	
61bedf
		
0f2bf8
		public void draw(Cairo.Context context, List<point> hovers) {</point>
0f2bf8
			if (!isActive()) return;
0f2bf8
0f2bf8
			// paint tool
0f2bf8
			tool.draw(context);
0f2bf8
			
61bedf
			// paint not sent sub-tracks
b9e5e0
			if (keyPointsSent < keyPoints.Count) {
61bedf
				context.Save();
61bedf
				penPreview.apply(context);
b9e5e0
				foreach(Track track in getOutputTracks()) {
61bedf
					TrackHandler handler = (TrackHandler)track.handler;
0f2bf8
					int start = handler.keys[keyPointsSent] - 1;
0f2bf8
					if (start < 0) start = 0;
61bedf
					if (start < track.points.Count) {
720280
						Drawing.Color color = penPreview.color;
720280
						int level = keyPointsSent;
720280
						
720280
						color.apply(context);
61bedf
						context.MoveTo(track.points[start].point.position.x, track.points[start].point.position.y);
720280
						for(int i = start + 1; i < track.points.Count; ++i) {
720280
							while(level < handler.keys.Count && handler.keys[level] <= i) {
720280
								context.Stroke();
720280
								context.MoveTo(track.points[i-1].point.position.x, track.points[i-1].point.position.y);
720280
								color.a *= levelAlpha;
720280
								color.apply(context);
720280
								++level;
720280
							}
720280
							context.LineTo(track.points[i].point.position.x, track.points[i].point.position.y);
720280
						}
61bedf
					}
61bedf
				}
61bedf
				context.Stroke();
61bedf
				context.Restore();
61bedf
			}
b9e5e0
			
b9e5e0
			// paint modifiers
b9e5e0
			for(int i = 0; i < modifiers.Count; ++i)
0f2bf8
				modifiers[i].draw(context, tracks[i], hovers);
61bedf
		}
61bedf
	}
61bedf
}
61bedf