Clone in a terminal:
Commit 454247c
Johan Mattsson - 2014-03-15T00:25:19+00:00
454247c954b17d64256be43906d571e102564772johan.mattsson.m@gmail.com

Draw outline of paths by tracking mouse movements This commits adds a new pen tool that lets the user draw shapes on free hand in contrast to the pencil tool.

Changed files (27)

Diff

diff --git a/icons/zoom_boundaries.png b/icons/zoom_boundaries.pngnew file mode 100644index 0000000..5e1ccb7Binary files /dev/null and b/icons/zoom_boundaries.png differdiff --git a/icons/zoom_boundries.png b/icons/zoom_boundries.pngdeleted file mode 100644index 5e1ccb7..0000000Binary files a/icons/zoom_boundries.png and /dev/null differdiff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex 0350a06..b38107a 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -494,7 +494,7 @@ class BirdFontFile { 			}  			if (iter->name == "lines") {-				parse_font_boundries (iter);+				parse_font_boundaries (iter); 			}  			if (iter->name == "grid") {@@ -807,7 +807,7 @@ class BirdFontFile { 		}		 	} 	-	private void parse_font_boundries (Xml.Node* node) {+	private void parse_font_boundaries (Xml.Node* node) { 		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { 			if (iter->name == "top_limit" && "" != iter->children->content) { 				font.top_limit = parse_double_from_node (iter);@@ -1195,7 +1195,7 @@ class BirdFontFile { 			path.points.remove_link (path.points.last ()); 		} 		-		path.update_region_boundries ();+		path.update_region_boundaries (); 		 		return path;	 	}diff --git a/libbirdfont/CircleTool.vala b/libbirdfont/CircleTool.valaindex 95a0ebc..8810dc0 100644--- a/libbirdfont/CircleTool.vala+++ b/libbirdfont/CircleTool.vala@@ -61,7 +61,7 @@ public class CircleTool : Tool { 		} 		 		if (resize_circle) {-			circle.update_region_boundries ();+			circle.update_region_boundaries (); 			diameter = circle.xmax - circle.xmin; 			cx = circle.xmin + diameter / 2; 			cy = circle.ymin + diameter / 2;@@ -77,7 +77,7 @@ public class CircleTool : Tool { 			nx = circle.xmin + diameter / 2; 			ny = circle.ymin + diameter / 2; 			-			circle.update_region_boundries ();+			circle.update_region_boundaries (); 			circle.move (cx - nx, cy - ny); 			 			last_radius = radius;diff --git a/libbirdfont/ClipTool.vala b/libbirdfont/ClipTool.valaindex 8bcebfa..8022449 100644--- a/libbirdfont/ClipTool.vala+++ b/libbirdfont/ClipTool.vala@@ -43,7 +43,7 @@ public class ClipTool : Tool { 		 		paste_paths (); 		-		g.selection_boundries (out x, out y, out w, out h);+		g.selection_boundaries (out x, out y, out w, out h); 		 		dx = g.motion_x - x - w / 2.0; 		dy = g.motion_y - y + h / 2.0;@@ -191,7 +191,7 @@ public class ClipTool : Tool { 		if (path.points.length () > 0) { 			glyph.add_path (path); 			glyph.active_paths.append (path);-			path.update_region_boundries ();+			path.update_region_boundaries (); 		} 		 		PenTool.remove_all_selected_points ();diff --git a/libbirdfont/DrawingTools.vala b/libbirdfont/DrawingTools.valaindex 44670d7..0caf232 100644--- a/libbirdfont/DrawingTools.vala+++ b/libbirdfont/DrawingTools.vala@@ -72,8 +72,11 @@ public class DrawingTools : ToolCollection  { 		Tool resize_tool = new ResizeTool ("resize"); 		draw_tools.add_tool (resize_tool); -		Tool stroke_tool = new StrokeTool ("stroke");+		Tool stroke_tool = new StrokeTool ("stroke"); // create outline from path 		draw_tools.add_tool (stroke_tool);++		Tool track_tool = new TrackTool ("track"); // create outline from path+		draw_tools.add_tool (track_tool); 		 		// quadratic Bézier points 		Tool quadratic_points = new Tool ("quadratic_points", t_("Create quadratic Bézier curves"));@@ -136,7 +139,7 @@ public class DrawingTools : ToolCollection  { 				 				p.set_tie_handle (tie); 				-				PenTool.handle_selection.path.update_region_boundries ();+				PenTool.handle_selection.path.update_region_boundaries (); 			} else { 				foreach (PointSelection ep in PenTool.selected_points) { 					tie = !ep.point.tie_handles;@@ -147,7 +150,7 @@ public class DrawingTools : ToolCollection  { 					} 				 					ep.point.set_tie_handle (tie);-					ep.path.update_region_boundries ();+					ep.path.update_region_boundaries (); 				} 			} 			@@ -171,7 +174,7 @@ public class DrawingTools : ToolCollection  { 						ep.point.set_tie_handle (false); 					} 					-					p.path.update_region_boundries ();+					p.path.update_region_boundaries (); 				} 				MainWindow.get_current_glyph ().update_view (); 			}@@ -405,12 +408,12 @@ public class DrawingTools : ToolCollection  { 		}); 		view_tools.add_tool (full_glyph); -		Tool zoom_boundries = new Tool ("zoom_boundries", t_("Zoom in on region boundries"), 'v');-		zoom_boundries.select_action.connect((self) => {+		Tool zoom_boundaries = new Tool ("zoom_boundaries", t_("Zoom in on region boundaries"), 'v');+		zoom_boundaries.select_action.connect((self) => { 			zoom_tool.store_current_view (); 			glyph_canvas.get_current_display ().zoom_max (); 		});-		view_tools.add_tool (zoom_boundries);+		view_tools.add_tool (zoom_boundaries);  		Tool zoom_bg = new Tool ("zoom_background_image", t_("Zoom in on background image")); 		zoom_bg.select_action.connect((self) => {diff --git a/libbirdfont/EditPoint.vala b/libbirdfont/EditPoint.valaindex f04a6c7..0b710cb 100644--- a/libbirdfont/EditPoint.vala+++ b/libbirdfont/EditPoint.vala@@ -344,11 +344,10 @@ public class EditPoint { 		} 				 		right_handle.angle = angle;-		left_handle.angle = angle;+		left_handle.angle = angle - PI;  		set_tie_handle (true); 		eh.move_to_coordinate (right_handle.x (), right_handle.y ());-		 	}  	public EditPoint copy () {diff --git a/libbirdfont/EditPointHandle.vala b/libbirdfont/EditPointHandle.valaindex e9eebd5..30e10f7 100644--- a/libbirdfont/EditPointHandle.vala+++ b/libbirdfont/EditPointHandle.vala@@ -57,6 +57,22 @@ public class EditPointHandle  { 		return n; 	} +	public void convert_to_line () {+		switch (type) {+			case PointType.QUADRATIC:+				type = PointType.LINE_QUADRATIC;+				break;+			case PointType.DOUBLE_CURVE:+				type = PointType.LINE_DOUBLE_CURVE;+				break;+			case PointType.CUBIC:+				type = PointType.LINE_CUBIC;+				break;+			default:+				break;+		}+	}+ 	public void set_point_type (PointType point_type) { 		type = point_type; 	}diff --git a/libbirdfont/FileTab.vala b/libbirdfont/FileTab.valaindex 39f6541..4a372c5 100644--- a/libbirdfont/FileTab.vala+++ b/libbirdfont/FileTab.vala@@ -113,8 +113,12 @@ public class FileTab : FontDisplay { 	} 	 	public override void button_release (int button, double ex, double ey) {-		int r = (int) rint ((ey - 17) / row_height) + scroll;-		int i = scroll; +		int r, i;+		+		return_if_fail (!is_null(this));+		+		r = (int) rint ((ey - 17) / row_height) + scroll;+		i = scroll;   		if (scroll == 0) { 			i += 2; // headingdiff --git a/libbirdfont/Font.vala b/libbirdfont/Font.valaindex a191302..ae3a486 100644--- a/libbirdfont/Font.vala+++ b/libbirdfont/Font.vala@@ -358,6 +358,7 @@ public class Font : GLib.Object { 		uint i = 0; 		Glyph? gl; 		Glyph g;+		 		StringBuilder sb; 		 		// FIXME: this is too slowdiff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex 33bb89c..cf429c6 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -65,7 +65,7 @@ public class Glyph : FontDisplay { 	List<Line> all_lines = new List<Line> (); 	// vertical, horzontal and grid lines 	bool show_help_lines = true; 	bool xheight_lines_visible = false;-	bool margin_boundries_visible = false;+	bool margin_boundaries_visible = false; 	 	bool unassigned = false; 	public unichar unichar_code = 0;@@ -188,7 +188,7 @@ public class Glyph : FontDisplay { 		return unassigned; 	} -	public void boundries (out double x1, out double y1, out double x2, out double y2) {+	public void boundaries (out double x1, out double y1, out double x2, out double y2) { 		if (path_list.length () == 0) { 			x1 = 0; 			y1 = 0;@@ -203,7 +203,7 @@ public class Glyph : FontDisplay { 		y2 = path_list.first ().data.ymax; 				 		foreach (Path p in path_list) {-			p.update_region_boundries ();+			p.update_region_boundaries (); 						 			if (p.xmin < x1) x1 = p.xmin; 			if (p.xmax > x2) x2 = p.xmax;@@ -212,7 +212,7 @@ public class Glyph : FontDisplay { 		} 	} 	-	public void selection_boundries (out double x, out double y, out double w, out double h) {+	public void selection_boundaries (out double x, out double y, out double w, out double h) { 		double px, py, px2, py2; 		 		px = 10000;@@ -417,7 +417,7 @@ public class Glyph : FontDisplay {  		// top to bottom 		add_line (top_margin_line);-		top_margin_line.set_visible (margin_boundries_visible);+		top_margin_line.set_visible (margin_boundaries_visible); 		 		add_line (top_line); 		top_line.set_visible (glyph_has_top || xheight_lines_visible);@@ -431,7 +431,7 @@ public class Glyph : FontDisplay { 		bottom_line.set_visible (CharDatabase.has_descender (unichar_code) || xheight_lines_visible); 		 		add_line (bottom_margin_line);-		bottom_margin_line.set_visible (margin_boundries_visible);+		bottom_margin_line.set_visible (margin_boundaries_visible); 	} 	 	bool has_top_line () {@@ -450,12 +450,12 @@ public class Glyph : FontDisplay { 	} 		 	public void set_margin_lines_visible (bool m) {-		margin_boundries_visible = m;+		margin_boundaries_visible = m; 		add_help_lines (); 	} 	 	public bool get_margin_lines_visible () {-		return margin_boundries_visible;+		return margin_boundaries_visible; 	} 	 	public void remove_empty_paths () {@@ -1009,6 +1009,16 @@ public class Glyph : FontDisplay { 		move_selected_edit_point_coordinates (selected_point, xt, yt); 	} 	+	public void redraw_segment (EditPoint a, EditPoint b) {+		double margin = 10;+		double x = Math.fmin (reverse_path_coordinate_x (a.x), reverse_path_coordinate_x (b.x)) - margin;+		double y = Math.fmin (reverse_path_coordinate_y (a.y), reverse_path_coordinate_y(b.y)) - margin;+		double w = Math.fabs (reverse_path_coordinate_x (a.x) - reverse_path_coordinate_x(b.x)) + 2 * margin;+		double h = Math.fabs (reverse_path_coordinate_y (a.y) - reverse_path_coordinate_y (b.y)) + 2 * margin;+				+		redraw_area ((int)x, (int)y, (int)w, (int)h);+	}+	 	private void redraw_last_stroke (double x, double y) { 		// redraw line, if we have more than one new point on path 		double px = 0;@@ -1096,7 +1106,7 @@ public class Glyph : FontDisplay { 	public void redraw_path_region (Path p) { 		int x, y, w, h; 			-		p.update_region_boundries ();+		p.update_region_boundaries (); 		 		x = reverse_path_coordinate_x (p.xmin); 		y = reverse_path_coordinate_x (p.xmin);@@ -1152,7 +1162,7 @@ public class Glyph : FontDisplay { 		reset_zoom ();  		foreach (var p in path_list) {-			p.update_region_boundries ();+			p.update_region_boundaries (); 			 			if (p.points.length () > 2) { 				if (p.xmin < ax) ax = p.xmin;@@ -1419,9 +1429,9 @@ public class Glyph : FontDisplay { 			((!)background_image).draw (cr, allocation, view_offset_x, view_offset_y, view_zoom); 		} -		if (unlikely (Preferences.draw_boundries)) {+		if (unlikely (Preferences.draw_boundaries)) { 			foreach (unowned Path p in path_list) {-				p.draw_boundries (cr, allocation, view_zoom);+				p.draw_boundaries (cr, allocation, view_zoom); 			} 		} 		@@ -1565,7 +1575,7 @@ public class Glyph : FontDisplay { 		 		foreach (Path p in g.path_list) { 			add_path (p);-			p.update_region_boundries ();+			p.update_region_boundaries (); 		}  		remove_lines ();@@ -1595,7 +1605,7 @@ public class Glyph : FontDisplay { 		Font font = BirdFont.get_current_font ();  		remove_empty_paths ();-		boundries (out x1, out y1, out x2, out y2);+		boundaries (out x1, out y1, out x2, out y2);  		if (x2 - x1 < 1 || y2 - y1 < 1) { // create an empty thumbnail  			img = new ImageSurface (Format.ARGB32, 100, 100);@@ -1667,7 +1677,7 @@ public class Glyph : FontDisplay { 		close_path ();  		foreach (Path p in path_list) {-			p.update_region_boundries ();+			p.update_region_boundaries (); 		}		 		 		redraw_area (0, 0, allocation.width, allocation.height);diff --git a/libbirdfont/MoveTool.vala b/libbirdfont/MoveTool.valaindex ed3f9ae..35eb95e 100644--- a/libbirdfont/MoveTool.vala+++ b/libbirdfont/MoveTool.vala@@ -147,7 +147,7 @@ class MoveTool : Tool { 		 		move_path = true; -		update_selection_boundries ();+		update_selection_boundaries (); 					 		last_x = x; 		last_y = y;@@ -158,7 +158,7 @@ class MoveTool : Tool { 			selection_y = y;	 		} 		-		update_boundries_for_selection ();	+		update_boundaries_for_selection ();	 	} 	 	static void select_group () {@@ -179,8 +179,8 @@ class MoveTool : Tool { 		} 	} 	-	public static void update_selection_boundries () {-		get_selection_box_boundries (out selection_box_center_x,+	public static void update_selection_boundaries () {+		get_selection_box_boundaries (out selection_box_center_x, 			out selection_box_center_y, out selection_box_width, 			out selection_box_height);	 	}@@ -202,7 +202,7 @@ class MoveTool : Tool { 		cr.restore (); 	} 	-	public static void get_selection_box_boundries (out double x, out double y, out double w, out double h) {+	public static void get_selection_box_boundaries (out double x, out double y, out double w, out double h) { 		double px, py, px2, py2; 		Glyph glyph = MainWindow.get_current_glyph (); 		@@ -300,10 +300,10 @@ class MoveTool : Tool { 		}		 	} 	-	public static void update_boundries_for_selection () {+	public static void update_boundaries_for_selection () { 		Glyph glyph = MainWindow.get_current_glyph (); 		foreach (Path p in glyph.active_paths) {-			p.update_region_boundries ();+			p.update_region_boundaries (); 		} 	} 	@@ -333,7 +333,7 @@ class MoveTool : Tool { 			p.reverse (); 		} -		get_selection_box_boundries (out xc2, out yc2, out w, out h); +		get_selection_box_boundaries (out xc2, out yc2, out w, out h);   		dx = -(xc2 - xc); 		dy = -(yc2 - yc);@@ -342,7 +342,7 @@ class MoveTool : Tool { 			p.move (dx, dy); 		} 		-		update_selection_boundries ();+		update_selection_boundaries (); 		 		BirdFont.get_current_font ().touch (); 	}diff --git a/libbirdfont/NativeWindow.vala b/libbirdfont/NativeWindow.valaindex f835037..e783d85 100644--- a/libbirdfont/NativeWindow.vala+++ b/libbirdfont/NativeWindow.vala@@ -1,5 +1,5 @@ /*-    Copyright (C) 2012, 2013 Johan Mattsson+    Copyright (C) 2012, 2013, 2014 Johan Mattsson      This library is free software; you can redistribute it and/or modify      it under the terms of the GNU Lesser General Public License as diff --git a/libbirdfont/OpenFontFormat/GlyfTable.vala b/libbirdfont/OpenFontFormat/GlyfTable.valaindex 8226306..0dca5b6 100644--- a/libbirdfont/OpenFontFormat/GlyfTable.vala+++ b/libbirdfont/OpenFontFormat/GlyfTable.vala@@ -476,7 +476,7 @@ public class GlyfTable : Table { 		int npoints = 0; 		 		int16 ncontours;-		int16 ixmin; // set boundries+		int16 ixmin; // set boundaries 		int16 iymin; 		int16 ixmax; 		int16 iymax;@@ -778,12 +778,12 @@ public class GlyfTable : Table { 				 				Path ps = ((!) glyph.path_list.first ()).data; 				-				ps.update_region_boundries ();+				ps.update_region_boundaries (); 				xmin = ps.xmin; 				xmax = ps.xmax;  				foreach (Path p in glyph.path_list) {-					p.update_region_boundries ();+					p.update_region_boundaries (); 					 					if (p.xmin < xmin) { 						xmin = p.xmin;diff --git a/libbirdfont/OpenFontFormat/HeadTable.vala b/libbirdfont/OpenFontFormat/HeadTable.valaindex c789984..9adfc71 100644--- a/libbirdfont/OpenFontFormat/HeadTable.vala+++ b/libbirdfont/OpenFontFormat/HeadTable.vala@@ -114,7 +114,7 @@ public class HeadTable : Table { 		xmax = dis.read_short (); 		ymax = dis.read_short (); -		printd (@"font boundries:\n");+		printd (@"font boundaries:\n"); 		printd (@"xmin: $xmin\n"); 		printd (@"ymin: $ymin\n"); 		printd (@"xmax: $xmax\n");@@ -183,7 +183,7 @@ public class HeadTable : Table { 		xmax = glyf_table.xmax; 		ymax = glyf_table.ymax; -		printd (@"font boundries:\n");+		printd (@"font boundaries:\n"); 		printd (@"xmin: $xmin\n"); 		printd (@"ymin: $ymin\n"); 		printd (@"xmax: $xmax\n");diff --git a/libbirdfont/OpenFontFormat/HmtxTable.vala b/libbirdfont/OpenFontFormat/HmtxTable.valaindex ebf9324..fa64ef3 100644--- a/libbirdfont/OpenFontFormat/HmtxTable.vala+++ b/libbirdfont/OpenFontFormat/HmtxTable.vala@@ -104,7 +104,7 @@ public class HmtxTable : Table { 		 		// advance and lsb 		foreach (Glyph g in glyf_table.glyphs) {-			g.boundries (out xmin, out ymin, out xmax, out ymax);+			g.boundaries (out xmin, out ymin, out xmax, out ymax); 			lsb = (int16) (-1 * (g.left_limit - xmin) * HeadTable.UNITS); 			advance = (int16) ((g.right_limit - g.left_limit) * HeadTable.UNITS); 			extent = (int16) (lsb + (xmax - xmin) * HeadTable.UNITS);diff --git a/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala b/libbirdfont/OpenFontFormat/OpenFontFormatReader.valaindex 85dd144..2dfb546 100644--- a/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala+++ b/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala@@ -101,7 +101,7 @@ public class OpenFontFormatReader : Object { 		directory_table.parse (font_data, this);		 	} -	public void parse_all_tables () {+	public void parse_all_tables () throws Error { 		directory_table.parse_all_tables (font_data, this); 		 		if (!directory_table.validate_tables (font_data, file)) {@@ -109,15 +109,15 @@ public class OpenFontFormatReader : Object { 		} 	} -	public void parse_kern_table () {+	public void parse_kern_table () throws Error { 		directory_table.parse_kern_table (font_data); 	} -	public void parse_cmap_table () {+	public void parse_cmap_table () throws Error { 		directory_table.parse_cmap_table (font_data); 	} -	public void parse_head_table () {+	public void parse_head_table () throws Error { 		directory_table.parse_head_table (font_data); 	} 			diff --git a/libbirdfont/OpenFontFormat/PostTable.vala b/libbirdfont/OpenFontFormat/PostTable.valaindex f5e9797..e75b54e 100644--- a/libbirdfont/OpenFontFormat/PostTable.vala+++ b/libbirdfont/OpenFontFormat/PostTable.vala@@ -1194,7 +1194,7 @@ public class PostTable : Table {  		fd.add_ulong (0); // non zero for monospaced font 		-		// mem boundries may be omitted+		// mem boundaries may be omitted 		fd.add_ulong (0); // min mem for type 42 		fd.add_ulong (0); // max mem for type 42 		diff --git a/libbirdfont/OverViewItem.vala b/libbirdfont/OverViewItem.valaindex 70fbcd3..ba93d73 100644--- a/libbirdfont/OverViewItem.vala+++ b/libbirdfont/OverViewItem.vala@@ -121,7 +121,7 @@ public class OverViewItem : GLib.Object { 		} 		 		g = ((!) gl).get_current ();-		g.boundries (out x1, out y1, out x2, out y2);+		g.boundaries (out x1, out y1, out x2, out y2);  		gx = (width - (x2 - x1)) / 2*scale; 		diff --git a/libbirdfont/Path.vala b/libbirdfont/Path.valaindex e671b4c..bca60c8 100644--- a/libbirdfont/Path.vala+++ b/libbirdfont/Path.vala@@ -29,7 +29,7 @@ public class Path { 	EditPoint? last_point = null; 	EditPoint? second_last_point = null; -	/** Region boundries */+	/** Region boundaries */ 	public double xmax = double.MIN; 	public double xmin = double.MAX; 	public double ymax = double.MIN;@@ -135,6 +135,15 @@ public class Path { 		} 	} +	public EditPoint get_last_point () {+		if (points.length () == 0) {+			warning ("No point");+			return new EditPoint ();+		}+		+		return points.last ().data;+	}+ 	public bool has_direction () { 		return direction_is_set; 	}@@ -147,7 +156,7 @@ public class Path { 		stroke = width; 	} -	public void draw_boundries  (Context cr, WidgetAllocation allocation, double view_zoom) {+	public void draw_boundaries  (Context cr, WidgetAllocation allocation, double view_zoom) { 		double x = Glyph.reverse_path_coordinate_x (xmin);  		double y = Glyph.reverse_path_coordinate_y (ymin); 		double x2 = Glyph.reverse_path_coordinate_x (xmax);@@ -601,7 +610,7 @@ public class Path { 	public void thinner (double ratio)  		requires (points.length () > 2) 	{		-		update_region_boundries ();+		update_region_boundaries (); 		 		double w = Math.fabs(xmax - xmin); 		double h = Math.fabs(ymax - ymin);@@ -838,7 +847,7 @@ public class Path { 	 	public bool is_over_boundry_precision (double x, double y, double p) { 		if (unlikely (ymin == double.MAX || ymin == 10000)) {-			update_region_boundries ();+			update_region_boundaries (); 		} 		 		return (ymin - p <= y <= ymax + p) && (xmin - p <= x <= xmax + p);@@ -846,7 +855,7 @@ public class Path { 	 	public bool is_over_boundry (double x, double y) { 		if (unlikely (ymin == double.MAX)) {-			warning ("bounding box is not calculated, run update_region_boundries first.");+			warning ("bounding box is not calculated, run update_region_boundaries first."); 		}  		return (ymin <= y <= ymax) && (xmin <= x <= xmax);@@ -856,12 +865,38 @@ public class Path { 		return !(xmax <= p.xmin || ymax <= p.ymin) || (xmin >= p.xmax || ymin >= p.ymax); 	} 	-	public void add (double x, double y) {-		add_after (x, y, points.last ());+	public EditPoint delete_last_point () {+		unowned List<EditPoint> p;+		EditPoint r;+		uint len;+		+		len = points.length ();+		if (unlikely (len == 0)) {+			warning ("No points in path.");+			return new EditPoint ();+		}+		+		p = points.last ();+		r = p.data;+		points.remove_link (p);+		+		if (len > 1) {+			r.get_prev ().data.next = null;+			+			if (r.next != null) {+				r.get_next ().data.prev = null;+			}+		}+		+		return r;+	}+	+	public EditPoint add (double x, double y) {+		return add_after (x, y, points.last ()).data; 	} -	public void add_point (EditPoint p) {-		add_point_after (p, points.last ());+	public EditPoint add_point (EditPoint p) {+		return add_point_after (p, points.last ()).data; 	}  	/** Insert a new point after @param previous_point and return a reference @@ -872,6 +907,7 @@ public class Path { 		return add_point_after (p, previous_point); 	} 	+	/** @return a list item pointing to the new point */ 	public unowned List<EditPoint> add_point_after (EditPoint p, List<EditPoint>? previous_point) { 		unowned List<EditPoint> np; 		int prev_index;@@ -931,11 +967,8 @@ public class Path { 		open = true; 		edit = true; 	}-	-	public EditPoint? get_last_point () {-		return last_point;-	} +	// FIXME: delete this: 	public EditPoint? get_second_last_point () { 		return second_last_point; 	}@@ -947,10 +980,10 @@ public class Path { 			ep.y += delta_y; 		} 		-		update_region_boundries ();+		update_region_boundaries (); 	} -	private void update_region_boundries_for_point (EditPoint p) {+	private void update_region_boundaries_for_point (EditPoint p) { 		EditPointHandle left_handle; 		EditPointHandle right_handle; 		@@ -973,11 +1006,11 @@ public class Path { 			ymin = p.y; 		} 		-		update_region_boundries_for_handle (left_handle);-		update_region_boundries_for_handle (right_handle);+		update_region_boundaries_for_handle (left_handle);+		update_region_boundaries_for_handle (right_handle); 	} -	private void update_region_boundries_for_handle (EditPointHandle h) {+	private void update_region_boundaries_for_handle (EditPointHandle h) { 		if (h.x () > xmax) { 			xmax = h.x (); 		}@@ -995,7 +1028,7 @@ public class Path { 		} 	} -	public void update_region_boundries () {+	public void update_region_boundaries () { 		xmax = -10000; 		xmin = 10000; 		ymax = -10000;@@ -1009,7 +1042,7 @@ public class Path { 		}  		foreach (EditPoint p in points) {-			update_region_boundries_for_point (p);+			update_region_boundaries_for_point (p); 		} 	} 		@@ -1709,14 +1742,14 @@ public class Path { 			cr.restore (); 	} 	-	public void print_boundries () {+	public void print_boundaries () { 		stderr.printf (@"xmax $xmax \n"); 		stderr.printf (@"xmin $xmin \n"); 		stderr.printf (@"ymax $ymax \n"); 		stderr.printf (@"ymin $ymin \n");		 	} 	-	public bool has_region_boundries () {+	public bool has_region_boundaries () { 		return !(xmax == -10000 || xmin ==  10000 || ymax == -10000 || ymin ==  10000); 	} 	@@ -1888,7 +1921,7 @@ public class Path { 		} 		 		foreach (Path path in path_list.paths) {-			path.update_region_boundries ();+			path.update_region_boundaries (); 		} 		 		return path_list;@@ -1971,8 +2004,8 @@ public class Path { 		} 		 		// add editpoints on intersections -		p0.update_region_boundries ();-		p1.update_region_boundries ();+		p0.update_region_boundaries ();+		p1.update_region_boundaries (); 		foreach (Intersection inter in il.points) { 			e = new EditPoint (); 			p0.get_closest_point_on_path (e, inter.x, inter.y);@@ -2014,7 +2047,7 @@ public class Path { 		} 		 		foreach (Path pp in path_list.paths) {-			pp.update_region_boundries ();+			pp.update_region_boundaries (); 		}  		// remove duplicate paths@@ -2241,7 +2274,7 @@ public class Path { 		path.points.first ().data.recalculate_linear_handles (); 		points.last ().data.recalculate_linear_handles (); 		-		path.points.remove_link (path.points.first ());+		// FIXME: path.points.remove_link (path.points.first ()); 		 		// copy remaining points 		foreach (EditPoint p in path.points) {@@ -2253,7 +2286,7 @@ public class Path { 			path.points.remove_link (path.points.first ()); 		} 		-		close ();+		//close (); 	}  	/** Roatate around coordinate xc, xc. */@@ -2284,7 +2317,7 @@ public class Path { 			} 		} -		update_region_boundries ();+		update_region_boundaries (); 	} 	 	public void flip_vertical () {@@ -2305,7 +2338,7 @@ public class Path { 			hl.move_to_coordinate_internal (lx, -1 * ly); 		} 		-		update_region_boundries ();+		update_region_boundaries (); 	}  	public void flip_horizontal () {@@ -2326,7 +2359,7 @@ public class Path { 			hl.move_to_coordinate_internal (-1 * lx, ly); 		} 		-		update_region_boundries ();+		update_region_boundaries (); 	}  	public void init_point_type () {diff --git a/libbirdfont/PenTool.vala b/libbirdfont/PenTool.valaindex 583b46e..37d8e1d 100644--- a/libbirdfont/PenTool.vala+++ b/libbirdfont/PenTool.vala@@ -374,7 +374,7 @@ public class PenTool : Tool { 				selected_handle.move_delta ((x - last_point_x) * precision, (y - last_point_y) * precision); 			} 			-			handle_selection.path.update_region_boundries ();+			handle_selection.path.update_region_boundaries (); 			 			// FIXME: redraw line only 			glyph.redraw_area (0, 0, glyph.allocation.width, glyph.allocation.height);@@ -413,7 +413,7 @@ public class PenTool : Tool { 					glyph.move_selected_edit_point_delta (p, (x - last_point_x) * precision, (y - last_point_y) * precision); 				} 				p.recalculate_linear_handles ();-				ps.path.update_region_boundries ();+				ps.path.update_region_boundaries (); 			} 		} 		@@ -747,7 +747,7 @@ public class PenTool : Tool { 		} 				 		foreach (Path merge in glyph.path_list) {-			// don't join path with it self here+			// don't join path with itself here 			if (path == merge) { 				continue; 			}@@ -805,7 +805,7 @@ public class PenTool : Tool { 	} 	 	/** Merge paths if ends are close. */-	private static bool is_close_to_point (EditPoint ep, double x, double y) {+	public static bool is_close_to_point (EditPoint ep, double x, double y) { 		double px, py, distance; 		 		px = Glyph.reverse_path_coordinate_x (ep.x);@@ -866,20 +866,23 @@ public class PenTool : Tool { 		cr.restore (); 	} 	-	void draw_merge_icon (Context cr) {+	public static void draw_join_icon (Context cr, double x, double y) { 		ImageSurface img;-		double x, y;-	 		return_if_fail (tie_icon != null); 		 		img = (!) tie_icon;	 			 		cr.save ();-		get_tie_position (out x, out y); 		cr.set_source_surface (img, x - img.get_width () / 2, y - img.get_height () / 2); 		cr.paint (); 		cr.restore ();		 	}++	void draw_merge_icon (Context cr) {+		double x, y;+		get_tie_position (out x, out y);+		draw_join_icon (cr, x, y);+	} 	 	/** Obtain the position where to ends meet. */ 	void get_tie_position (out double x, out double y) {@@ -1071,21 +1074,34 @@ public class PenTool : Tool { 		 		remove_all_selected_points (); 		-		new_point = glyph.add_new_edit_point (x, y);+		new_point = add_new_edit_point (x, y); 		new_point.point.set_selected (true);-		new_point.path.update_region_boundries ();  		selected_point = new_point.point; 		active_edit_point = new_point.point;	 		add_selected_point (selected_point, glyph.active_paths.last ().data);  		move_selected = true;+	}+	+	public static PointSelection add_new_edit_point (int x, int y) {+		Glyph glyph;+		PointSelection new_point;+		glyph = MainWindow.get_current_glyph (); 		+		new_point = glyph.add_new_edit_point (x, y);+		new_point.path.update_region_boundaries ();++		selected_point = new_point.point;+		active_edit_point = new_point.point;	+ 		set_point_type (selected_point); 		set_default_handle_positions ();+		+		return new_point; 	} -	void set_point_type (EditPoint p) {+	static void set_point_type (EditPoint p) { 		if (p.prev != null && p.get_prev ().data.right_handle.type == PointType.QUADRATIC) { 			p.left_handle.type = PointType.QUADRATIC; 			p.right_handle.type = PointType.LINE_QUADRATIC;@@ -1151,10 +1167,10 @@ public class PenTool : Tool { 		double x = Glyph.path_coordinate_x (event_x); 		double y = Glyph.path_coordinate_y (event_y); 		-		if (unlikely (!p.has_region_boundries ())) {+		if (unlikely (!p.has_region_boundaries ())) { 			if (p.points.length () > 0) { 				warning (@"No bounding box. $(p.points.length ())");-				p.update_region_boundries ();+				p.update_region_boundaries (); 			} 		} 		@@ -1457,7 +1473,8 @@ public class PenTool : Tool { 		ep.set_reflective_handles (false); 		 		if (ep.next == null) {-			warning ("Next is null.");+			// FIXME: add a new function for this case+			// warning ("Next is null."); 		}  		if (ep.prev == null) {diff --git a/libbirdfont/PointSelection.vala b/libbirdfont/PointSelection.valaindex 2febc86..bb44854 100644--- a/libbirdfont/PointSelection.vala+++ b/libbirdfont/PointSelection.vala@@ -17,6 +17,7 @@ using Cairo;  namespace BirdFont { +/** One selected point and its path. */ public class PointSelection : GLib.Object {  	public EditPointHandle handle;@@ -40,6 +41,12 @@ public class PointSelection : GLib.Object { 		point = new EditPoint (); 		handle = new EditPointHandle.empty (); 	}+	+	/** @return true if this point is the first point in the path. */+	public bool is_first () {+		return_val_if_fail (path.points.length () > 0, false);+		return path.points.first ().data == point;+	} }  }diff --git a/libbirdfont/Preferences.vala b/libbirdfont/Preferences.valaindex bfd6f83..efb8d2f 100644--- a/libbirdfont/Preferences.vala+++ b/libbirdfont/Preferences.vala@@ -19,7 +19,7 @@ using Gee; public class Preferences { 		 	static Gee.HashMap<string, string> data;-	public static bool draw_boundries = false;+	public static bool draw_boundaries = false;  	public Preferences () { 		data = new Gee.HashMap<string, string> ();diff --git a/libbirdfont/ResizeTool.vala b/libbirdfont/ResizeTool.valaindex 706e5f2..c830738 100644--- a/libbirdfont/ResizeTool.vala+++ b/libbirdfont/ResizeTool.vala@@ -99,8 +99,8 @@ class ResizeTool : Tool { 			}  			if (!rotate_path) {-				MoveTool.update_boundries_for_selection ();-				MoveTool.get_selection_box_boundries (out selection_box_center_x,+				MoveTool.update_boundaries_for_selection ();+				MoveTool.get_selection_box_boundaries (out selection_box_center_x, 					out selection_box_center_y, out selection_box_width, 					out selection_box_height);	 			}@@ -150,7 +150,7 @@ class ResizeTool : Tool { 			p.rotate (rotation - last_rotate, selection_box_center_x, selection_box_center_y); 		} -		MoveTool.get_selection_box_boundries (out xc2, out yc2, out w, out h); +		MoveTool.get_selection_box_boundaries (out xc2, out yc2, out w, out h);   		dx = -(xc2 - xc); 		dy = -(yc2 - yc);@@ -161,7 +161,7 @@ class ResizeTool : Tool { 		 		last_rotate = rotation; 		-		MoveTool.update_selection_boundries ();+		MoveTool.update_selection_boundaries (); 	}  	static bool is_over_rotate_handle (Path p, double x, double y) {diff --git a/libbirdfont/SpinButton.vala b/libbirdfont/SpinButton.valaindex f352d55..5123e47 100644--- a/libbirdfont/SpinButton.vala+++ b/libbirdfont/SpinButton.vala@@ -197,7 +197,7 @@ public class SpinButton : Tool { 		return (int8) v; 	} -	public void set_value (string new_value, bool check_boundries = true, bool emit_signal = true) {+	public void set_value (string new_value, bool check_boundaries = true, bool emit_signal = true) { 		string v = new_value.replace (",", ".");  		while (v.char_count () < 5) {@@ -217,12 +217,12 @@ public class SpinButton : Tool { 		centi = (int8) int.parse (v.substring (v.index_of_nth_char (3), 1)); 		milli = (int8) int.parse (v.substring (v.index_of_nth_char (4), 1)); 		-		if (check_boundries && get_value () > max / 1000.0) {+		if (check_boundaries && get_value () > max / 1000.0) { 			warning (@"Out of bounds ($new_value > $max)."); 			set_value_round (max, false); 		} -		if (check_boundries && get_value () < min / 1000.0) {+		if (check_boundaries && get_value () < min / 1000.0) { 			warning (@"Out of bounds ($new_value < $min)."); 			set_value_round (min, false); 		}@@ -232,8 +232,8 @@ public class SpinButton : Tool { 		} 	} -	public void set_value_round (double v, bool check_boundries = true, bool emit_signal = true) {-		set_value (@"$v".replace (",", "."), check_boundries, emit_signal);+	public void set_value_round (double v, bool check_boundaries = true, bool emit_signal = true) {+		set_value (@"$v".replace (",", "."), check_boundaries, emit_signal); 	} 	 	public double get_value () {diff --git a/libbirdfont/TestCases.vala b/libbirdfont/TestCases.valaindex a076528..92e5386 100644--- a/libbirdfont/TestCases.vala+++ b/libbirdfont/TestCases.vala@@ -43,7 +43,7 @@ class TestCases { 		add (test_freetype, "Freetype"); 		add (test_preview, "Preview"); 		add (test_kerning, "Kerning");-		add (test_boundries, "Boundries");+		add (test_boundaries, "Boundaries"); 	} 	 	private void add (Callback callback, string name) {@@ -449,7 +449,7 @@ class TestCases { 		p.add (10, 10); 		p.add (10, -10); 		p.add (-10, -10);-		p.update_region_boundries ();+		p.update_region_boundaries(); 		g.add_path (p); 		g.close_path (); @@ -1367,8 +1367,8 @@ class TestCases { 		} 	} 	-	private static void test_boundries () {-		Preferences.draw_boundries = true;+	private static void test_boundaries () {+		Preferences.draw_boundaries = true; 	} } diff --git a/libbirdfont/TrackTool.vala b/libbirdfont/TrackTool.valanew file mode 100644index 0000000..eaae16c--- /dev/null+++ b/libbirdfont/TrackTool.vala@@ -0,0 +1,435 @@+/*+    Copyright (C) 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library is distributed in the hope that it will be useful, but +    WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +    Lesser General Public License for more details.+*/++using Cairo;+using Math;++namespace BirdFont {++/** A tool that lets the user draw fonts with the mouse +  * instead of adding bezér points one by one.+  */+public class TrackTool : Tool {+	bool draw_free_hand = false;++	int added_points = 0;+	double last_update = 0;++	int last_x = 0;+	int last_y = 0;++	int last_timer_x = 0;+	int last_timer_y = 0;+	int update_cycles = 0;++	int join_x = -1;+	int join_y = -1;+	bool join_paths = false;++	public TrackTool (string tool_tip) {+		press_action.connect ((self, b, x, y) => {+			Glyph glyph = MainWindow.get_current_glyph ();+			Path p;+			PointSelection? ps;+			PointSelection end_point;+			+			if (b == 1) {+				draw_free_hand = true;+				+				last_x = x;+				last_y = y;+				+				glyph.store_undo_state ();+				+				if (join_paths) {+					ps = get_path_with_end_point (x, y);+					return_if_fail (ps != null);+					end_point = (!) ps;+					if (end_point.is_first ()) {+						end_point.path.reverse ();+					}+					glyph.set_active_path (end_point.path);+					add_corner (x, y);+				} else {+					p = new Path ();+					glyph.add_path (p);+					glyph.open_path ();+					PenTool.add_new_edit_point (x, y).point;+					record_new_position (x, y);+				}++				glyph.update_view ();				+				added_points = 0;+				last_update = get_current_time ();+				start_update_timer ();+			}+		});+		+		double_click_action.connect ((self, b, x, y) => {+		});++		release_action.connect ((self, b, ix, iy) => {+			if (b == 1) {+				add_endpoint_and_merge (ix, iy);+			}+		});++		move_action.connect ((self, x, y) => {+			PointSelection? open_path = get_path_with_end_point (x, y);+			PointSelection p;+			bool join;+			+			join = (open_path != null);+			+			if (join != join_paths) {+				MainWindow.get_current_glyph ().update_view ();++			}+			+			join_paths = join;+			+			if (open_path != null) {+				p = (!) open_path;+				join_x = Glyph.reverse_path_coordinate_x (p.point.x);+				join_y = Glyph.reverse_path_coordinate_y (p.point.y);+			}+			+			if (draw_free_hand) {+				record_new_position (x, y);+				convert_on_timeout ();+				last_x = x;+				last_y = y;+			}+		});+		+		draw_action.connect ((tool, cairo_context, glyph) => {+			if (join_paths) {+				PenTool.draw_join_icon (cairo_context, join_x, join_y);+			}+		});+	}++	void add_endpoint_and_merge (int x, int y) {+			Glyph glyph;+			Path p;+			PointSelection? open_path = get_path_with_end_point (x, y);+			PointSelection joined_path;+			glyph = MainWindow.get_current_glyph ();+			+			if (glyph.path_list.length () == 0) {+				warning ("No path.");+				return;+			}+			+			p = glyph.path_list.last ().data;+			draw_free_hand = false;+			+			convert_points_to_line ();++			if (join_paths && open_path != null) {+				joined_path = (!) open_path;+				 +				if (joined_path.path == p) {+					p.close (); +				} else {+					merge_paths (p, joined_path);+				}+			} else {+				add_corner (x, y);+			}++			+			if (p.points.length () == 0) {+				warning ("No point.");+				return;+			}+			+			p.points.first ().data.set_tie_handle (false);+			p.points.last ().data.set_tie_handle (false);+			+			p.create_list ();+			+			if (glyph.path_list.length () < 2) {+				warning ("Points have been deleted");+				return;+			}++			if (DrawingTools.get_selected_point_type () == PointType.QUADRATIC) {+				foreach (EditPoint e in p.points) {+						e.set_tie_handle (true);+						e.process_tied_handle ();							+				}+			}+					+			glyph.update_view ();+	}++	public static void merge_paths (Path a, PointSelection b) {+		Glyph g;+		Path merged = a.copy ();+		+		if (!b.is_first ()) {+			b.path.close ();+			b.path.reverse ();+			b.path.reopen ();+		}+		+		merged.append_path (b.path);+		+		g = MainWindow.get_current_glyph ();+		+		g.add_path (merged);+		+		g.delete_path (a);+		g.delete_path (b.path);+		+		merged.create_list ();+		merged.update_region_boundaries ();+		merged.recalculate_linear_handles ();+		merged.reopen ();+		+		if (PenTool.is_counter_path (merged)) {+			merged.force_direction (Direction.COUNTER_CLOCKWISE);+		} else {+			merged.force_direction (Direction.CLOCKWISE);+		}+	}++	PointSelection? get_path_with_end_point (int x, int y) {+		Glyph glyph = MainWindow.get_current_glyph ();+		EditPoint e;+		EditPoint current_end = new EditPoint ();++		// exclude the end point on the path we are adding points to+		if (draw_free_hand) {+			current_end = get_active_path ().get_last_point ();+		}++		foreach (Path p in glyph.path_list) {+			if (p.is_open () && p.points.length () > 2) {+				e = p.points.first ().data;+				if (PenTool.is_close_to_point (e, x, y)) {+					return new PointSelection (e, p);+				}++				e = p.points.last ().data;+				if (current_end != e && PenTool.is_close_to_point (e, x, y)) {+					return new PointSelection (e, p);				+				}+			}+		}+		+		return null;+	}+	+	void record_new_position (int x, int y) {+		Glyph glyph;+		Path p;+		EditPoint new_point;+		double px, py;+	+		glyph = MainWindow.get_current_glyph ();+		+		if (glyph.path_list.length () == 0) {+			warning ("No path.");+			return;+		}+		+		p = glyph.path_list.last ().data;+		p.reopen ();+		px = Glyph.path_coordinate_x (x);+		py = Glyph.path_coordinate_y (y);+		new_point = p.add (px, py);+		added_points++;++		PenTool.convert_point_to_line (new_point, false);+		+		if (p.points.length () > 1) {+			glyph.redraw_segment (new_point, new_point.get_prev ().data);+		}+		+		glyph.update_view ();+		+		last_x = x;+		last_y = y;+	}+	+	void start_update_timer () {+		TimeoutSource timer = new TimeoutSource (100);++		timer.set_callback (() => {+			+			if (draw_free_hand) {+				record_new_position (last_x, last_y);+				convert_on_timeout ();+			}+			+			return draw_free_hand;+		});++		timer.attach (null);+	}++	/** @returns true while the mounse pointer is moving. */+	bool is_moving (int x, int y) {+		return Path.distance (x, last_x, y, last_y) >= 1;+	}+	+	/** Add a new point if the update period has ended. */+	void convert_on_timeout () {+		if (!is_moving (last_timer_x, last_timer_y)) {+			update_cycles++;+		} else {+			last_timer_x = last_x;+			last_timer_y = last_y;+			update_cycles = 0;+		}+		+		if (update_cycles > 10) {+			convert_points_to_line ();+			last_update  = get_current_time ();+			add_corner (last_x, last_y);+			added_points = 0;+			update_cycles = 0;+		}+			+		if (added_points > 40) {+			last_update  = get_current_time ();+			convert_points_to_line ();+		}+	}+	+	void add_corner (int px, int py) {+		PointSelection p;+		delete_last_points_at (px, py);+		p = PenTool.add_new_edit_point (px, py);+		p.point.set_tie_handle (false);+		p.point.get_left_handle ().convert_to_line ();+		p.point.get_right_handle ().convert_to_line ();+		p.point.recalculate_linear_handles ();+		last_update = get_current_time ();+		MainWindow.get_current_glyph ().update_view ();+	}++	Path get_active_path () {+		Glyph glyph = MainWindow.get_current_glyph ();+		+		if (glyph.path_list.length () == 0) {+			warning ("No path.");+			return new Path ();+		}+			+		return glyph.path_list.last ().data;		+	}+	+	void delete_last_points_at (int x, int y) {+		double px, py;+		Path p;+					+		p = get_active_path ();++		if (unlikely (p.points.length () == 0)) {+			warning ("Missing point.");+			return;+		}++		px = Glyph.path_coordinate_x (x);+		py = Glyph.path_coordinate_y (y);++		while (p.points.length () > 0 && is_close (p.points.last ().data, px, py)) {+			p.delete_last_point ();+		}+	}+	+	/** @return true if the new point point is closer than a few pixels EditPoint. */+	bool is_close (EditPoint p, double x, double y) {+		Glyph glyph = MainWindow.get_current_glyph ();+		return glyph.view_zoom * Path.distance (p.x, x, p.y, y) < 5;+	}+	+	/** @return the last removed point. */+	EditPoint convert_points_to_line () {+		EditPoint ep, last_point;+		double sum_x, sum_y, sum_angle, nx, ny;+		int px, py;+		EditPoint average;+		Path p;+		Glyph glyph;+		+		if (added_points == 0) {+			warning ("No points to add.");+			return new EditPoint ();+		}+		+		glyph = MainWindow.get_current_glyph ();+		+		if (glyph.path_list.length () == 0) {+			warning ("No path.");+			return new EditPoint ();+		}+			+		p = glyph.path_list.last ().data;++		if (unlikely (p.points.length () < added_points)) {+			warning ("Missing point.");+			return new EditPoint ();+		}++		sum_x = 0;+		sum_y = 0;+		sum_angle = 0;+		+		List<EditPoint> points = new List<EditPoint> ();+		+		last_point = p.points.last ().data;+		+		for (int i = 0; i < added_points; i++) {+			ep = p.delete_last_point ();+			sum_x += ep.x;+			sum_y += ep.y;			+			points.append (ep);+		}+		+		nx = sum_x / added_points;+		ny = sum_y / added_points;++		px = Glyph.reverse_path_coordinate_x (nx);+		py = Glyph.reverse_path_coordinate_y (ny);+		average = PenTool.add_new_edit_point (px, py).point;+		average.set_tie_handle (true);+		+		if (unlikely (p.points.length () == 0)) {+			warning ("No points.");+			return new EditPoint ();+		}+		+		if (average.prev != null && average.get_prev ().data.tie_handles) {+			PenTool.convert_point_to_line (average.get_prev ().data, true);+			average.get_prev ().data.process_tied_handle ();+		}++		added_points = 0;+		last_update = get_current_time ();+		glyph.update_view ();+		+		return last_point;+	}+	+	/** @return current time in milli seconds. */+	static double get_current_time () {+		return GLib.get_real_time () / 1000.0;+	}+}++}

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112