Blame mono/Assistance/InputManager.cs

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