Clone in a terminal:
Commit b0488ce
Johan Mattsson - 2012-12-15T12:07:17+00:00
b0488ceddd1defed94a24d31b79484bf0432a7c1johan.mattsson.m@gmail.com

new directory structure

Changed files (141)

Diff

diff --git a/birdfont-export/BirdfontExport.vala b/birdfont-export/BirdfontExport.valanew file mode 100644index 0000000..c434bd8--- /dev/null+++ b/birdfont-export/BirdfontExport.vala@@ -0,0 +1,23 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Birdfont;++/** Stand alone command line utility for exporting fonts. */+public static int main (string[] arg) {				+	return run_export (arg);+}diff --git a/birdfont-export/wscript b/birdfont-export/wscriptnew file mode 100644index 0000000..78f0f36--- /dev/null+++ b/birdfont-export/wscript@@ -0,0 +1,23 @@+def build (bld):++	bld (+		rule = "valac -C ./libbirdfont/birdfont.vapi ../birdfont-export/BirdfontExport.vala -X ../build/libbirdfont/libbirdfont.so -X ./birdfont.h --pkg gtk+-2.0 --pkg libxml-2.0 --pkg gdk-2.0 --pkg webkit-1.0",+		depends = 'libbirdfont',+		target = '../BirdfontExport.c',+		name = 'birdfont_export'+	)++	bld (+		rule = 'gcc  ../build/BirdfontExport.c -I../build/ -L  ../build/libbirdfont -l birdfont -o ${TGT} \+				$(pkg-config --cflags --libs glib-2.0) \+				$(pkg-config --cflags --libs libxml-2.0) \+				$(pkg-config --cflags --libs gio-2.0) \+				$(pkg-config --cflags --libs libsoup-2.4) \+				$(pkg-config --cflags --libs gtk+-2.0) \+				$(pkg-config --cflags --libs webkit-1.0)',+		source  = ['../BirdfontExport.c'],+		target  = 'birdfont-export',+		depends = 'birdfont_export'+	)+	+	bld.install_files('${PREFIX}/bin/', ['../build/birdfont-export/birdfont-export'], chmod=755)diff --git a/birdfont/GtkWindow.vala b/birdfont/GtkWindow.valanew file mode 100644index 0000000..4d44788--- /dev/null+++ b/birdfont/GtkWindow.vala@@ -0,0 +1,808 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Gtk;+using Gdk;+using Supplement;+using WebKit;++namespace Supplement {++public class GtkWindow : Gtk.Window, NativeWindow {++	HBox list_box;+	HBox canvas_box;+	+	WebView html_canvas;+	ScrolledWindow html_box;++	VBox tab_box;++	static DrawingArea margin_bottom;+	static DrawingArea margin_right;+	+	GlyphCanvasArea glyph_canvas_area;+	+	static List<uint> key_pressed = new List<uint> ();+	+	public GtkWindow (string title) {+		// set_title (title);+	}+	+	public void init () {+		margin_bottom = new DrawingArea ();+		margin_right = new DrawingArea ();+	+		margin_bottom.set_size_request (0, 0);+		margin_right.set_size_request (0, 0);+		+		delete_event.connect (quit);+		+		set_size_and_position ();+		+		html_canvas = new WebView ();+		WebKit.set_cache_model (CacheModel.DOCUMENT_VIEWER);+		html_canvas.get_settings ().enable_default_context_menu = false;+				+		html_canvas.title_changed.connect ((p, s) => {+			webkit_callback (s);+		});+		+		html_box = new ScrolledWindow (null, null);+		html_box.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);+		html_box.add (html_canvas);+		html_canvas.set_editable (true);+		+		MainWindow.get_tab_bar ().signal_tab_selected.connect ((f, tab) => {+			bool n;+			File layout_dir;+			File layout_uri;+			string uri = "";+			FontDisplay fd = tab.get_display ();+			+			MainWindow.glyph_canvas.set_current_glyph (fd);+			n = fd.is_html_canvas ();+			+			if (n) {+				layout_dir = FontDisplay.find_layout_dir ();+				uri = fd.get_uri ();+				+				if (uri == "") {+					layout_uri = layout_dir.get_child (fd.get_html_file ());+					uri = fd.path_to_uri ((!) layout_uri.get_path ());+				}+		+				if (fd.get_html () == "") {+					+					if (fd.get_name () == "Preview") {+						// hack: force webkit to ignore cache in preview					+						html_box.set_visible (false);+						glyph_canvas_area.set_visible (true);+												+						try {+							Preview preview = (Preview) fd;+							DataInputStream dis = new DataInputStream (preview.get_html_file ().read ());+							string? line;+							StringBuilder sb = new StringBuilder ();+							uint rid = Random.next_int ();+							Font font = Supplement.get_current_font ();+							+							File preview_directory = Supplement.get_preview_directory ();+							+							File f_ttf = font.get_folder ().get_child (@"$(font.get_name ()).ttf");+							File f_eot = font.get_folder ().get_child (@"$(font.get_name ()).eot");+							File f_svg = font.get_folder ().get_child (@"$(font.get_name ()).svg");++							if (f_ttf.query_exists ()) {+								f_ttf.delete ();+							}+								+							if (f_eot.query_exists ()) {+								f_eot.delete ();+							}+							+							if (f_svg.query_exists ()) {+								f_svg.delete ();+							}+							+							ExportTool.export_ttf_font ();							+							ExportTool.export_svg_font ();+							+							File r_ttf = preview_directory.get_child (@"$(font.get_name ())$rid.ttf");+							File r_svg = preview_directory.get_child (@"$(font.get_name ())$rid.svg");+							+							if (Supplement.win32) {+								f_ttf.copy (r_ttf, FileCopyFlags.NONE);+							}+							+							f_svg.copy (r_svg, FileCopyFlags.NONE);++							while ((line = dis.read_line (null)) != null) {+								line = ((!) line).replace (@"$(font.get_name ()).ttf", @"$(FontDisplay.path_to_uri ((!) f_ttf.get_path ()))?$rid");+								line = ((!) line).replace (@"$(font.get_name ()).eot", @"$(FontDisplay.path_to_uri ((!) f_eot.get_path ()))?$rid");+								line = ((!) line).replace (@"$(font.get_name ()).svg", @"$(FontDisplay.path_to_uri ((!) f_svg.get_path ()))?$rid");+								sb.append ((!) line);+							}+					+							html_canvas.load_html_string (sb.str, uri);							+						} catch (Error e) {+							warning (e.message);+							warning ("Failed to load html into canvas.");+						}+						+					} else {+						// normal way to load a uri for all other pages+						html_canvas.load_uri (uri);+						html_canvas.reload_bypass_cache ();			+					}+						+				} else {+					html_canvas.load_html_string (fd.get_html (), uri);+				}+				+				html_box.set_visible (n);+				glyph_canvas_area.set_visible (!n);+			} else {+				html_box.set_visible (false);+				glyph_canvas_area.set_visible (true);+			}+		});++		// Hide this canvas when window is realized and flip canvas +		// visibility in tab selection signal.+		html_canvas.expose_event.connect ((t, e) => {+			glyph_canvas_area.set_visible (false);+			return false;+		});+				+		MainWindow.tabs.add_unique_tab (MainWindow.menu_tab, 60, true);+		+		MainWindow.tabs.select_tab_name ("Menu");++		glyph_canvas_area = new GlyphCanvasArea (MainWindow.glyph_canvas);++		canvas_box = new HBox (false, 0);+		canvas_box.pack_start (glyph_canvas_area, true, true, 0);+		canvas_box.pack_start (html_box, true, true, 0);+		+		tab_box = new VBox (false, 0);+		tab_box.pack_start (new TabbarCanvas (MainWindow.tabs), false, false, 0);	+		+		tab_box.pack_start (canvas_box, true, true, 0);++		tab_box.pack_start (new TooltipCanvas (MainWindow.tool_tip), false, false, 0);+		tab_box.pack_start (margin_bottom, false, false, 0);+		+		list_box = new HBox (false, 0);+		list_box.pack_start (tab_box, true, true, 0);+		list_box.pack_start (new ToolboxCanvas (MainWindow.tools), false, false, 0);+		list_box.pack_start (margin_right, false, false, 0);++		VBox vbox = new VBox (false, 0);+		+		vbox.pack_start(create_menu (), false, false, 0);+		vbox.pack_start(list_box, true, true, 0);++		add (vbox);+		+		try {+			set_icon_from_file ((!) Icons.find_icon ("window_icon.png").get_path ());+		} catch (GLib.Error e) {+			warning (e.message);+		}++		key_press_event.connect ((t, event) => {+			MainWindow window = MainWindow.get_singleton ();+			FontDisplay fd = window.glyph_canvas.current_display;+			+			if (fd is Glyph) {+				window.tools.key_press (event.keyval);+			}+			+			window.glyph_canvas.key_press (event.keyval);+			return false;+		});+		+		key_release_event.connect ((t, event) => {+			MainWindow window = MainWindow.get_singleton ();+			if (window.glyph_canvas is Glyph) {+				window.glyph_canvas.key_release (event.keyval);+			}+			return false;+		});+		+		show_all ();+	}++	MenuBar create_menu () {+		MenuBar menubar = new MenuBar ();+		Gtk.Menu file_menu = new Gtk.Menu ();+		Gtk.Menu edit_menu = new Gtk.Menu ();+		Gtk.Menu tab_menu = new Gtk.Menu ();+		Gtk.Menu tool_menu = new Gtk.Menu ();++		AccelGroup accel_group = new Gtk.AccelGroup();+		add_accel_group (accel_group);+		+		// File+		Gtk.MenuItem new_item = new Gtk.MenuItem.with_mnemonic (_("_New"));+		file_menu.append (new_item);+		new_item.activate.connect (() => { MenuTab.new_file (); });+		new_item.add_accelerator ("activate", accel_group, 'N', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem load_item = new Gtk.MenuItem.with_mnemonic (_("_Open"));+		file_menu.append (load_item);+		load_item.activate.connect (() => { MenuTab.load (); });+		load_item.add_accelerator ("activate", accel_group, 'O', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem save_item = new Gtk.MenuItem.with_mnemonic (_("_Save"));+		file_menu.append (save_item);+		save_item.activate.connect (() => { MenuTab.save (); });+		save_item.add_accelerator ("activate", accel_group, 'S', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem save_as_item = new Gtk.MenuItem.with_mnemonic (_("Save _as"));+		file_menu.append (save_as_item);+		save_as_item.activate.connect (() => { MenuTab.save_as (); });+				+		Gtk.MenuItem export_item = new Gtk.MenuItem.with_mnemonic (_("_Export"));+		file_menu.append (export_item);+		export_item.activate.connect (() => { ExportTool.export_all (); });+		export_item.add_accelerator ("activate", accel_group, 'E', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem preview_item = new Gtk.MenuItem.with_mnemonic(_("_Preview"));+		file_menu.append (preview_item);+		preview_item.activate.connect (() => { MenuTab.preview (); });+		preview_item.add_accelerator ("activate", accel_group, 'P', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem kerning_item = new Gtk.MenuItem.with_mnemonic (_("_Kerning"));+		file_menu.append (kerning_item);+		kerning_item.activate.connect (() => { MenuTab.show_kerning_context (); });+		kerning_item.add_accelerator ("activate", accel_group, 'K', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem quit_item = new Gtk.MenuItem.with_mnemonic (_("_Quit"));+		file_menu.append (quit_item);+		quit_item.activate.connect (() => { quit(); });++		// Edit+		Gtk.MenuItem undo_item = new Gtk.MenuItem.with_mnemonic (_("_Undo"));+		edit_menu.append (undo_item);+		undo_item.activate.connect (() => { MainWindow.get_current_display ().undo (); });	+		undo_item.add_accelerator ("activate", accel_group, 'Z', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);+		+		Gtk.MenuItem copy_item = new Gtk.MenuItem.with_mnemonic (_("_Copy"));+		edit_menu.append (copy_item);+		copy_item.activate.connect (() => { ClipTool.copy (); });		+		copy_item.add_accelerator ("activate", accel_group, 'C', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem paste_item = new Gtk.MenuItem.with_mnemonic (_("_Paste"));+		edit_menu.append (paste_item);+		paste_item.activate.connect (() => { ClipTool.paste (); });	+		paste_item.add_accelerator ("activate", accel_group, 'V', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);+		+		// Tab+		Gtk.MenuItem next_tab_item = new Gtk.MenuItem.with_mnemonic (_("_Next tab"));+		tab_menu.append (next_tab_item);+		next_tab_item.activate.connect (() => { +			TabBar tb = MainWindow.get_tab_bar ();+			int n = tb.get_selected () + 1;+			+			if (!(0 <= n < tb.get_length ())) {+				return;+			}+			+			tb.select_tab (n);+		});	++		Gtk.MenuItem prevoius_tab_item = new Gtk.MenuItem.with_mnemonic (_("_Previous tab"));+		tab_menu.append (prevoius_tab_item);+		prevoius_tab_item.activate.connect (() => { +			TabBar tb = MainWindow.get_tab_bar ();+			int n = tb.get_selected () - 1;++			if (!(0 <= n < tb.get_length ())) {+				return;+			}+			+			tb.select_tab (n);+		});					++		Gtk.MenuItem close_tab_item = new Gtk.MenuItem.with_mnemonic (_("_Close tab"));+		tab_menu.append (close_tab_item);+		close_tab_item.activate.connect (() => { +			TabBar tb = MainWindow.get_tab_bar ();+			int n = tb.get_selected ();++			if (!(0 <= n < tb.get_length ())) {+				return;+			}+			+			tb.close_tab (n);+		});	+		close_tab_item.add_accelerator ("activate", accel_group, 'W', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		// Tool+		Gtk.MenuItem pen_item = new Gtk.MenuItem.with_mnemonic (_("_Create path"));+		tool_menu.append (pen_item);+		pen_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("pen_tool");+		});+		pen_item.add_accelerator ("activate", accel_group, ',', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem zoom_item = new Gtk.MenuItem.with_mnemonic (_("_Zoom"));+		tool_menu.append (zoom_item);+		zoom_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("zoom_tool");+		});++		Gtk.MenuItem counter_item = new Gtk.MenuItem.with_mnemonic (_("_Create counter path"));+		tool_menu.append (counter_item);+		counter_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("cut");+		});+		counter_item.add_accelerator ("activate", accel_group, 'U', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem move_item = new Gtk.MenuItem.with_mnemonic (_("_Move"));+		tool_menu.append (move_item);+		move_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("move");+		});+		move_item.add_accelerator ("activate", accel_group, 'M', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem full_unicode_item = new Gtk.MenuItem.with_mnemonic (_("Show _full unicode characters set"));+		tool_menu.append (full_unicode_item);+		full_unicode_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("utf_8");+		});+		full_unicode_item.add_accelerator ("activate", accel_group, 'F', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem default_charset_item = new Gtk.MenuItem.with_mnemonic (_("Show de_fault characters set"));+		tool_menu.append (default_charset_item);+		default_charset_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("custom_character_set");+		});+		default_charset_item.add_accelerator ("activate", accel_group, 'R', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem avalilable_characters_item = new Gtk.MenuItem.with_mnemonic (_("Show characters in font"));+		tool_menu.append (avalilable_characters_item);+		avalilable_characters_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("available_characters");+		});+		avalilable_characters_item.add_accelerator ("activate", accel_group, 'D', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem add_grid_item = new Gtk.MenuItem.with_mnemonic (_("Add new _grid item"));+		tool_menu.append (add_grid_item);+		add_grid_item.activate.connect (() => { +			MainWindow.get_toolbox ().add_new_grid ();+		});++		Gtk.MenuItem remove_grid_item = new Gtk.MenuItem.with_mnemonic (_("Remove gr_id item"));+		tool_menu.append (remove_grid_item);+		remove_grid_item.activate.connect (() => { +			MainWindow.get_toolbox ().remove_current_grid ();+		});+		+		Gtk.MenuItem zoom_in_item = new Gtk.MenuItem.with_mnemonic (_("_Zoom in"));+		tool_menu.append (zoom_in_item);+		zoom_in_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("zoom_in");+		});+		zoom_in_item.add_accelerator ("activate", accel_group, '+', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem zoom_out_item = new Gtk.MenuItem.with_mnemonic (_("Zoom _out"));+		tool_menu.append (zoom_out_item);+		zoom_out_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("zoom_out");+		});+		zoom_out_item.add_accelerator ("activate", accel_group, '-', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		Gtk.MenuItem zoom_1_1_item = new Gtk.MenuItem.with_mnemonic (_("Zoom to _scale 1:1"));+		tool_menu.append (zoom_1_1_item);+		zoom_1_1_item.activate.connect (() => { +			Toolbox.select_tool_by_name ("zoom_1_1");+		});+		zoom_1_1_item.add_accelerator ("activate", accel_group, '0', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++		// Add menus+		Gtk.MenuItem file_launcher = new Gtk.MenuItem.with_mnemonic (_("_File"));+		file_launcher.set_submenu (file_menu);++		Gtk.MenuItem edit_launcher = new Gtk.MenuItem.with_mnemonic (_("_Edit"));+		edit_launcher.set_submenu (edit_menu);++		Gtk.MenuItem tab_launcher = new Gtk.MenuItem.with_mnemonic (_("_Tab"));+		tab_launcher.set_submenu (tab_menu);++		Gtk.MenuItem tool_launcher = new Gtk.MenuItem.with_mnemonic (_("T_ool"));+		tool_launcher.set_submenu (tool_menu);+						+		menubar.append (file_launcher);+		menubar.append (edit_launcher);+		menubar.append (tab_launcher);+		menubar.append (tool_launcher);+		+		return menubar;	+	}++	internal void toggle_expanded_margin_bottom () {+		int w, h;+		margin_bottom.get_size_request (out w, out h);+		+		if (h == 1) h = 2; +		else h = 1;+		+		margin_bottom.set_size_request (w, h);+	}+	+	internal void toggle_expanded_margin_right () {	+		int w, h;+		margin_right.get_size_request (out w, out h);++		if (w == 1) w = 2; +		else w = 1;++		margin_right.set_size_request (w, h);+	}++	public static void hide_cursor () {+		/*+		Pixmap pixmap = new Pixmap (null, 1, 1, 1);+		Color color = { 0, 0, 0, 0 };+		Cursor cursor = new Cursor.from_pixmap (pixmap, pixmap, color, color, 0, 0);+		*/+		+		// Fixa: But why?+		// (Supplement.exe:1300): Gdk-CRITICAL **: gdk_window_set_cursor: assertion `GDK_IS_WINDOW (window)' failed		+		// singleton.frame.set_cursor (cursor);+	}++	public void update_window_size () {+		int w, h;+		get_size (out w, out h);+		+		Preferences.set ("window_width", @"$w");+		Preferences.set ("window_height", @"$h");+	}+		+	private void set_size_and_position () {+		int w = Preferences.get_window_width ();+		int h = Preferences.get_window_height ();+		+		set_default_size (w, h);+		// move (10, 240);+	}+	+	public bool quit () {+		bool added;+		SaveDialog s = new SaveDialog ();+		+		if (Supplement.get_current_font ().is_modified ()) {+			added = MainWindow.get_tab_bar ().add_unique_tab (s, 50);+		} else {+			added = false;+		}+		+		if (!added) {+			Supplement.get_current_font ().save_backup ();+			Gtk.main_quit ();+		}+		+		s.finished.connect (() => {+			Supplement.get_current_font ().delete_backup ();+			Gtk.main_quit ();+		});+		+		return true;+	}+	+	public string? file_chooser (string title) {+		string? fn = null;+		FileChooserDialog file_chooser = new FileChooserDialog (title, this, FileChooserAction.SAVE, Stock.CANCEL, ResponseType.CANCEL, Stock.SAVE, ResponseType.ACCEPT);+		Font font = Supplement.get_current_font ();+		+		try {+			file_chooser.set_current_folder_file (font.get_folder ());+		} catch (GLib.Error e) {+			stderr.printf (e.message);+		}+		+		if (file_chooser.run () == ResponseType.ACCEPT) {	+			MainWindow.get_glyph_canvas ().redraw ();+			fn = file_chooser.get_filename ();+		}++		file_chooser.destroy ();+		+		return fn;+	}	+}++class TabbarCanvas : DrawingArea {+	TabBar tabbar;+	+	public TabbarCanvas (TabBar tb) {		+		tabbar = tb;++		set_extension_events (ExtensionMode.CURSOR | EventMask.POINTER_MOTION_MASK);+		add_events (EventMask.BUTTON_PRESS_MASK | EventMask.POINTER_MOTION_MASK | EventMask.LEAVE_NOTIFY_MASK);+	  +		motion_notify_event.connect ((t, e)=> {+			Gtk.Allocation alloc;+			tabbar.motion (e.x, e.y);+			get_allocation (out alloc);+			queue_draw_area (0, 0, alloc.width, alloc.height);+			return true;+		});	+				+		button_press_event.connect ((t, e)=> {+			Gtk.Allocation alloc;+			get_allocation (out alloc);+			tabbar.select_tab_click (e.x, e.y, alloc.width, alloc.height);+			queue_draw_area (0, 0, alloc.width, alloc.height);+			return true;+		});++		expose_event.connect ((t, e)=> {+			Context cr = cairo_create (get_window ());++			Gtk.Allocation alloc;+			get_allocation (out alloc);++			tabbar.draw (cr, alloc.width, alloc.height);+			return true;+		});+	+		tabbar.signal_tab_selected.connect ((t) => {+			Gtk.Allocation alloc;+			get_allocation (out alloc);+			queue_draw_area (0, 0, alloc.width, alloc.height);	+		});+		+		set_size_request (20, 25);+	}+	+}++class ToolboxCanvas : DrawingArea {+	Toolbox tb;+	+	public ToolboxCanvas (Toolbox toolbox) {+		tb = toolbox;+		+		tb.redraw.connect ((x, y, w, h) => {+			queue_draw_area (x, y, w, h);+		});+		+		button_press_event.connect ((se, e)=> {+			tb.press (e.button, e.x, e.y);+			return true;+		});	+				+		button_release_event.connect ((se, e)=> {+			tb.release (e.button, e.x, e.y);+			return true;+		});++		motion_notify_event.connect ((sen, e)=> {+			tb.move (e.x, e.y);+			return true;+		});+		+		expose_event.connect ((t, e)=> {+			Gtk.Allocation allocation;+			get_allocation (out allocation);+			+			Context cw = cairo_create(get_window());+			tb.allocation_width = allocation.width;+			tb.allocation_height = allocation.height;+			tb.draw (allocation.width, allocation.height, cw);+			+			return true;+		});+		+		add_events (EventMask.BUTTON_PRESS_MASK | EventMask.BUTTON_RELEASE_MASK | EventMask.POINTER_MOTION_MASK | EventMask.LEAVE_NOTIFY_MASK);++		set_size_request (160, 100);++		leave_notify_event.connect ((t, e)=> {+			tb.reset_active_tool ();+			return true;+		});+		+	}+}++public class GlyphCanvasArea : DrawingArea  {+	GlyphCanvas glyph_canvas;+	Gtk.Allocation alloc;++	/* State on linux */+	public static const int L_SHIFT = 17;+	public static const int L_CTRL = 20;+	public static const int L_SHIFT_CTRL = 21;++	/* State on windows */+	public static const int W_SHIFT = 1;+	public static const int W_CTRL = 4;+	public static const int W_SHIFT_CTRL = 5;+		+	public GlyphCanvasArea (GlyphCanvas gc) {+		int event_flags;+		+		glyph_canvas = gc;+		+		event_flags = EventMask.BUTTON_PRESS_MASK;+		event_flags |= EventMask.BUTTON_RELEASE_MASK;+		event_flags |= EventMask.POINTER_MOTION_MASK;+		event_flags |= EventMask.LEAVE_NOTIFY_MASK;+		event_flags |= EventMask.SCROLL_MASK;+		+		add_events (event_flags);++		glyph_canvas.signal_redraw_area.connect ((x, y, w, h) => {+			queue_draw_area ((int)x, (int)y, (int)w, (int)h);+		});++		expose_event.connect ((t, e)=> {		+			Gtk.Allocation allocation;+			get_allocation (out allocation);+			+			glyph_canvas.allocation.width = allocation.width;+			glyph_canvas.allocation.height = allocation.height;+			glyph_canvas.allocation.y = allocation.x;+			glyph_canvas.allocation.y = allocation.y;+				+			if (unlikely (allocation != alloc && alloc.width != 0)) {+				// Set size of glyph widget to an even number and notify +				// set new allocation for glyph+				bool ug = false;+				+				if (allocation.height % 2 != 0) {+					MainWindow.native_window.toggle_expanded_margin_bottom ();+					ug = true;+				}+				+				if (allocation.width % 2 != 0) {+					MainWindow.native_window.toggle_expanded_margin_right ();+					ug = true;+				}					+				+				if (ug) {+					//redraw_area (1, 1, 2, 2);+				} else if (unlikely (allocation.width % 2 != 0 || allocation.height % 2 != 0)) {+					warning (@"\nGlyph canvas is not divisible by two.\nWidth: $(allocation.width)\nHeight: $(allocation.height)");+				}+				+				Supplement.current_glyph.resized ();+			}+			+			alloc = allocation;+			+			Context cw = cairo_create (get_window());+			+			Surface s = new Surface.similar (cw.get_target (), Cairo.Content.COLOR_ALPHA, allocation.width, allocation.height);+			Context c = new Context (s); ++			glyph_canvas.current_display.draw (glyph_canvas.allocation, c);++			cw.save ();+			cw.set_source_surface (c.get_target (), 0, 0);+			cw.paint ();+			cw.restore ();+			+			return true;+		});++		button_press_event.connect ((t, e)=> {+			set_modifier ((int) e.state);+			+			if (e.type == EventType.BUTTON_PRESS) {+				glyph_canvas.current_display.button_press (e.button, e.x, e.y);	+			} else if (e.type == EventType.2BUTTON_PRESS) {+				glyph_canvas.current_display.double_click (e.button, e.x, e.y);+			}+				+			return true;+		});+		+		button_release_event.connect ((t, e)=> {+			set_modifier ((int) e.state);+			glyph_canvas.current_display.button_release ((int) e.button, e.x, e.y);+			return true;+		});+		+		motion_notify_event.connect ((t, e)=> {+			glyph_canvas.current_display.motion_notify (e.x, e.y);		+			return true;+		});+		+		scroll_event.connect ((t, e)=> {+			set_modifier ((int) e.state);+			+			if (e.direction == Gdk.ScrollDirection.UP) {+				glyph_canvas.current_display.scroll_wheel_up (e.x, e.y);+			} else if (e.direction == Gdk.ScrollDirection.DOWN) {+				glyph_canvas.current_display.scroll_wheel_down (e.x, e.y);+			}+			+			glyph_canvas.current_display.button_release (2, e.x, e.y);+			return true;+		});		+	}++	static void set_modifier (int k) {+		switch (k) {+			case L_SHIFT:+				KeyBindings.set_modifier (SHIFT);+				break;+			case L_CTRL:+				KeyBindings.set_modifier (CTRL);+				break;+			case L_SHIFT_CTRL:+				KeyBindings.set_modifier (SHIFT | CTRL);+				break;+			case W_SHIFT:+				KeyBindings.set_modifier (SHIFT);+				break;+			case W_CTRL:+				KeyBindings.set_modifier (CTRL);+				break;+			case W_SHIFT_CTRL:+				KeyBindings.set_modifier (SHIFT | CTRL);+				break;++			default:+				KeyBindings.set_modifier (NONE);+				break;+		}		+	}+}++public class TooltipCanvas : DrawingArea {+	TooltipArea tooltip_area;+	+	public TooltipCanvas (TooltipArea ta) {+		tooltip_area = ta;++		expose_event.connect ((t, e)=> {+				Allocation allocation = {0, 0, 0, 0};+				Gtk.Allocation alloc;+				Context cr = cairo_create (get_window ());+				+				get_allocation (out alloc);++				allocation.width = alloc.width;+				allocation.height = alloc.height;+				allocation.y = alloc.x;+				allocation.y = alloc.y;+				+				tooltip_area.draw (cr, allocation);++				return true;+		});+		+		tooltip_area.redraw.connect (() => {+			Gtk.Allocation alloc;+			get_allocation (out alloc);+			queue_draw_area (0, 0, alloc.width, alloc.height);+		});+		+		set_size_request (10, 20);+	}+}++}diff --git a/birdfont/Main.vala b/birdfont/Main.valanew file mode 100644index 0000000..09cbc58--- /dev/null+++ b/birdfont/Main.vala@@ -0,0 +1,53 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Birdfont;+using Supplement;++const string GETTEXT_PACKAGE = "birdfont"; ++void parse_gtk_rc () {+	File f = FontDisplay.find_file ("layout", "birdfont.rc");+	Gtk.rc_parse ((!) f.get_path ());+}++public static int main (string[] arg) {+	GtkWindow native_window;+	MainWindow window;+	string file;+	Supplement.Supplement supplement = new Supplement.Supplement ();+	supplement.init (arg);+	+	Gtk.init (ref arg);+	parse_gtk_rc ();++	native_window = new GtkWindow ("birdfont");	+	window = new MainWindow ();+	+	window.set_native (native_window);+	native_window.init ();++	file = supplement.args.get_file ();+	if (file != "") {+		window.menu_tab.load_font (file);+	}+	+	Gtk.main ();++	return 0;+}+diff --git a/birdfont/wscript b/birdfont/wscriptnew file mode 100644index 0000000..89ce384--- /dev/null+++ b/birdfont/wscript@@ -0,0 +1,26 @@+def build (bld):	+	bld (+		rule = 'valac -C ./libbirdfont/birdfont.vapi ../birdfont/Main.vala ../birdfont/GtkWindow.vala -X ../build/libbirdfont/libbirdfont.so -X ./birdfont.h --pkg gtk+-2.0 --pkg libxml-2.0 --pkg gdk-2.0 --pkg webkit-1.0',+		name = 'main_c',+		source  = ['../birdfont/Main.vala', '../birdfont/GtkWindow.vala'],+		target = '../Main.c',+		depends = 'libbirdfont'+	)++	bld (+		rule = """gcc ../build/Main.c ../build/GtkWindow.c \+				-D 'GETTEXT_PACKAGE="birdfont"' \+				-I ../build/ -I ../build/libbirdfont/ -L ../build/libbirdfont -l birdfont \+				-o ${TGT} \+				$(pkg-config --cflags --libs glib-2.0) \+				$(pkg-config --cflags --libs libxml-2.0) \+				$(pkg-config --cflags --libs gio-2.0) \+				$(pkg-config --cflags --libs libsoup-2.4) \+				$(pkg-config --cflags --libs gtk+-2.0) \+				$(pkg-config --cflags --libs webkit-1.0)""",+		source  = ['../Main.c'],+		target  = 'birdfont',+		depends = 'main_c'+	)++	bld.install_files('${PREFIX}/bin/', ['../build/birdfont/birdfont'], chmod=755)diff --git a/configure b/configureindex 33a0757..1489cb3 100755--- a/configure+++ b/configure@@ -3,4 +3,4 @@ cd waf-* && \ ./configure && \ cp ./waf ../ && \ cd .. && \-./waf configure+./waf configure $*diff --git a/icons/zoom_1_1.png b/icons/zoom_1_1.pngindex ff8d1e2..51c9eb9 100644Binary files a/icons/zoom_1_1.png and b/icons/zoom_1_1.png differdiff --git a/libbirdfont/Allocation.vala b/libbirdfont/Allocation.valanew file mode 100644index 0000000..efcc36d--- /dev/null+++ b/libbirdfont/Allocation.vala@@ -0,0 +1,27 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++public struct Allocation {+	public int width;+	public int height;+	public int x;+	public int y;+}++}diff --git a/libbirdfont/Argument.vala b/libbirdfont/Argument.valanew file mode 100644index 0000000..623a50b--- /dev/null+++ b/libbirdfont/Argument.vala@@ -0,0 +1,241 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {+	+public class Argument : GLib.Object {+	+	List<string> args;+	+	public Argument (string line) {+		args = new List<string> ();+		set_argument (line);+	}+	+	public Argument.command_line (string[] arg) {	+		args = new List<string> ();++		foreach (string a in arg) {+			args.append (a);+		}+	}+	+	/** returns 0 if valid or index the bad parameter. */ +	public int validate () {+		string prev = "";+		int i = 0;+		foreach (string a in args) {+			+			// program name+			if (i == 0) {+				prev = a;+				i++;+				continue;+			}+			+			// file name+			if (i == 1 && !a.has_prefix ("-")) {+				prev = a;+				i++;+				continue;+			}++			// a single character, like -t+			if (!a.has_prefix ("--") && a.has_prefix ("-")) {+				a = expand_param (a);+			}+			+			// valid parameter+			if (a == "--exit" || a == "--slow" || a == "--help" || a == "--test" || a == "--fatal-warning" || a == "--show-coordinates") {+				prev = a;+				i++;+				continue;+			} else if (a.has_prefix ("--")) {+				return i;+			}+			+			// not argument to parameter+			if (!(prev == "--test")) {+				return i;+			}+			+			prev = a;+			i++;+		}+		+		return 0;+	}+	+	/** Return the font file parameter. */+	public string get_file () {+		string f = "";+		+		if (args.length () >= 2) {+			f = args.nth (1).data;+		}++		if (f.has_prefix ("-")) {+			return "";+		}+		+		return f;+	}+	+	public void print_all () {+		print (@"$(args.length ()) arguments:\n");+		+		foreach (string p in args) {+			print (@"$p\n");+		}+	}+	+	public bool has_argument (string param) {+		return (get_argument (param) != null);+	}+	+	/** Get commandline argument. */+	public string? get_argument (string param) {+		int i = 0;+		string? n;+		string p;++		if (param.substring (0, 1) != "-") {+			warning (@"parameters must begin with \"-\" got $param");+			return null;+		}++		foreach (string s in args) {++			// this is content not a parameter +			if (s.substring (0, 1) != "-") continue;++			// we might need to expand -t to test fo instance+			if (s.substring (0, 2) != "--") {+				p = expand_param (s);+			} else {				+				p = s;+			}+			+			if (param == p) {+				if (i + 2 >= args.length ()) {+					return "";+				}+				+				n = args.nth (i + 2).data;+				if (n == null) {+					return "";+				}+				+				if (args.nth (i + 2).data.substring (0, 1) == "-") {+					return "";+				}+				+				return args.nth (i + 2).data;+			}+			+			i++;+		}+		+		return null;+	}++	private void print_padded (string cmd, string desc) {+		int l = 25 - cmd.char_count ();++		stdout.printf (cmd);+		+		for (int i = 0; i < l; i++) {+				stdout.printf (" ");+		}+		+		stdout.printf (desc);+		stdout.printf ("\n");+	}++	/** Return full command line parameter for the abbrevation.+	 * -t becomes --test.+	 */+	private string expand_param (string? param) {+		if (param == null) return "";+		var p = (!) param;+		+		if (p.length == 0) return "";+		if (p.get_char (0) != '-') return "";+		if (p.char_count () != 2) return "";+		+		switch (p.get_char (1)) {+			case 'c':+				return "--show-coordinates";+			case 'e': +				return "--exit";+			case 'f': +				return "--fatal-warning";+			case 'h': +				return "--help";+			case 's': +				return "--slow";+			case 't': +				return "--test";+		}+		+		return "";+	}++	private void set_argument (string arg) {+		int i = 0;+		int a;+		string n;+		+		if (arg.char_count () <= 1) {+			return;+		}+		+		do {+			a = arg.index_of (" ", i + 1);+			n = arg.substring (i, a - i);+			+			if (n.index_of ("\"") == 0) {+				a = arg.index_of ("\"", i + 1);+				n = arg.substring (i, a - i + 1);+			}+					+			args.append (n);+			+			i += n.char_count () + 1;+		} while (i < arg.char_count ());+	}++	public void print_help () +		requires (args.length () > 0)+	{+		stdout.printf (_("Usage") + ": ");+		stdout.printf (args.nth (0).data);+		stdout.printf (" [" + _("FILE") + "] [" + _("OPTION") + " ...]\n");++		print_padded ("-c, --show-coordinates", _("show coordinate in glyph view"));+		print_padded ("-e, --exit", _("exit if a test case failes"));+		print_padded ("-f, --fatal-warning", _("treat warnings as fatal"));+		print_padded ("-h, --help", _("show this message"));+		print_padded ("-s, --slow", _("sleep between each command in test suite"));+		print_padded ("-t, --test [TEST]", _("run test case"));+		+		stdout.printf ("\n");+	}++}+	+}diff --git a/libbirdfont/BackgroundSelection.vala b/libbirdfont/BackgroundSelection.valanew file mode 100644index 0000000..84f5bf5--- /dev/null+++ b/libbirdfont/BackgroundSelection.vala@@ -0,0 +1,302 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Gdk;++namespace Supplement {++class BackgroundSelection : FontDisplay {++	int active_box = -1;+	ImageSurface add_icon;+	List <string> background_images = new List <string> ();+	Allocation allocation;+	Glyph glyph;+	+	public BackgroundSelection () {+		ImageSurface? i;+		glyph = MainWindow.get_current_glyph ();+		+		i = Icons.get_icon ("add_background_image.png");+		return_if_fail (i != null);+		add_icon = (!) i;+	}++	public override string get_name () {+		return "Backgrounds";+	}++	public override void draw (Allocation allocation, Context cr) {		+		double x, y, zoom;+		int box_index = 0;+		Font font;++		this.allocation = allocation;++		// bg color+		cr.save ();+		cr.set_source_rgba (255/255.0, 255/255.0, 255/255.0, 1);+		cr.rectangle (0, 0, allocation.width, allocation.height);+		cr.fill ();+		cr.restore ();+		+		draw_box (box_index, 20, 20, cr);+		cr.set_source_surface (add_icon, 62, 62);+		cr.paint ();+			+		cr.save ();+		x = 160;+		y = 20;+		zoom = 0.04;+		+		font = Supplement.get_current_font ();+		foreach (string file in background_images) {+			box_index++;++			if (x > allocation.width - 140) {+				x = 20;+				y += 140;+			}++			draw_box (box_index, x, y, cr);+			draw_thumbnail (file, x, y, cr, box_index);+			+			x += 140;	+		}+		cr.restore ();+			+	}+	+	private ImageSurface create_thumbnail (string file) throws GLib.Error {+		Pixbuf pixbuf;+		+		Font font = Supplement.get_current_font ();+		File folder = font.get_backgrounds_folder ();+		string fn = Checksum.compute_for_string (ChecksumType.SHA1, file);+		File original = File.new_for_path (file);+		File thumbnail_path = folder.get_child (@"thumbnail");+		File png_image = thumbnail_path.get_child (@"$(fn).png");+		+		if (!thumbnail_path.query_exists ()) {+			DirUtils.create ((!) thumbnail_path.get_path (), 0xFFFFFF);+		}+		+		if (!png_image.query_exists ()) {+			pixbuf = new Pixbuf.from_file_at_scale ((!) original.get_path (), 100, 100, true);+			if (!pixbuf.save ((!) png_image.get_path (), "png")) {+				warning (@"can't save $((!) png_image.get_path ())");+			}+		}+		+		return new ImageSurface.from_png ((!) png_image.get_path ());+	}+	+	private bool draw_thumbnail (string file, double x, double y, Context cr, int box_index) {+		ImageSurface img;+	+		try {+			img = create_thumbnail (file);+			+			if (img.status () == Cairo.Status.SUCCESS) {+				cr.set_source_surface (img, x + 10, y + 10);+				cr.paint ();+			}+		} catch (GLib.Error e) {+			warning (e.message);+			return false;+		}+		+		return true;	+	}+	+	private void draw_box (int index, double x, double y, Context cr) {+		cr.save ();+		cr.set_line_join (LineJoin.ROUND);+		cr.set_line_width (7);+		+		if (index == active_box) {+			cr.set_source_rgba (203/255.0, 220/255.0, 249/255.0, 1);+		} else {+			cr.set_source_rgba (183/255.0, 200/255.0, 223/255.0, 1);+		}+		+		cr.rectangle (x, y, 120, 120);+		cr.fill_preserve ();+		cr.stroke ();+		+		cr.restore ();		+	}+	+	public override void button_release (int button, double ex, double ey) {+		motion (ex, ey);+		+		if (active_box == 0) {+			add_image ();+		} else {+			select_image ();+		}+	}++	private void add_image () {+		string? file;++		file = MainWindow.file_chooser ("Add image");++		if (file != null) {+			Supplement.get_current_font ().add_background_image ((!) file);+		}+		+		MainWindow.get_glyph_canvas ().redraw ();				+		+		collect_background_images ();+	}++	private void select_image () {+		Font font;+		string file;+		GlyphBackgroundImage bg;+		TabBar tb;+		Tool zoom_background;++		font = Supplement.get_current_font ();+				+		if (!(0 <= active_box - 1 < background_images.length ())) {+			return;+		}+		+		file = background_images.nth (active_box - 1).data; // Fixa: check bounds+		bg = new GlyphBackgroundImage (file);+		tb = MainWindow.get_tab_bar ();+		+		bg.reset_scale (glyph);+		+		glyph.set_background_visible (true);+		+		MainWindow.get_current_glyph ().set_background_image (bg);+		tb.select_tab_name (MainWindow.get_current_glyph ().get_name ());++		zoom_background = MainWindow.get_tool ("zoom_background_image");+		zoom_background.select_action (zoom_background);++		MainWindow.get_toolbox ().select_tool_by_name ("cut_background");+	}++	private void add_unique_background (string file) {+		foreach (var f in background_images) {+			if (f == file) {+				return;+			}+		}+		+		background_images.prepend (file);+	}++	public override void selected_canvas () {+		Font font = Supplement.get_current_font ();+		File folder = font.get_backgrounds_folder ();+		+		if (!folder.query_exists ()) {+			DirUtils.create ((!) folder.get_path (), 0xFFFFFF);+		}+		+		collect_background_images ();+	}++	bool is_image (string file_name) {+		return file_name.index_of (".png") != -1 +			|| file_name.index_of (".jpg") != -1+			|| file_name.index_of (".jpeg") != -1;+	}++	void collect_background_images () {+		Font font = Supplement.get_current_font ();+		File directory = font.get_folder ();+		FileEnumerator enumerator;+		FileInfo? file_info;+		FileInfo fi;+		File img_file;++		while (background_images.length () > 0) {+			background_images.delete_link (background_images.first ());+		}+		+		foreach (string file in font.background_images) {+			add_unique_background (file);+		}+				+		try {+			enumerator = directory.enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, 0);+			+			while ((file_info = enumerator.next_file ()) != null) {+				fi = (!) file_info;+				+				if (is_image (fi.get_name ())) {+					img_file = directory.get_child (fi.get_name ());					+					add_unique_background ((!)img_file.get_path ());+				}+				+			}+		} catch (GLib.Error e) {+			warning (e.message);+		}+	}+	+	public override void key_press (uint e) {+	}++	public override void key_release (uint e) {+	}+	+	public override void motion_notify (double x, double y) {+		motion (x, y);+	}+	+	public void motion (double x, double y) {+		int active;+		+		active = (int) (x / 140); // column+		active += (int) (y / 140) * (int) (allocation.width / 140.0); // row+		+		if (active != active_box) {+			MainWindow.get_glyph_canvas ().redraw ();+			active_box = active;+		}+	}++	public override void button_press (uint button, double ex, double ey) {+		motion (ex, ey);+	}++	public override void store_current_view () {+	}+	+	public override void restore_last_view () {+	}++	public override void next_view () {+	}+	+	public override void scroll_wheel_up (double x, double y) {+	}++	public override void scroll_wheel_down (double x, double y) {+	}++}++}diff --git a/libbirdfont/BackgroundTool.vala b/libbirdfont/BackgroundTool.valanew file mode 100644index 0000000..2e505e9--- /dev/null+++ b/libbirdfont/BackgroundTool.vala@@ -0,0 +1,158 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++class BackgroundTool : Tool {++	double begin_x = 0;+	double begin_y = 0;++	public double img_offset_x = 0;+	public double img_offset_y = 0;++	public double img_width = 0;+	public double img_height = 0;+	+	public double img_scale_x = 0;++	public static double top_limit;+	public static double bottom_limit;+		+	bool move_bg;+	bool resize_bg; // DEL+	+	public BackgroundTool (string name) {+		base (name, _("Move, resize and rotate background image"));+		+		top_limit = 0;+		bottom_limit = 0;++		select_action.connect((self) => {+		});++		deselect_action.connect ((self) => {+		});+		+		press_action.connect((self, b, x, y) => {+			Glyph g = MainWindow.get_current_glyph ();+			GlyphBackgroundImage? bg = g.get_background_image ();+			GlyphBackgroundImage background;+			+			if (bg == null) {+				return;+			}+			+			background = (!) bg;+			+			g.store_undo_state ();+			+			background.handler_press (x, y);+			+			begin_x = x;+			begin_y = y;+			+			img_offset_x = background.img_offset_x;+			img_offset_y = background.img_offset_y;+			+			img_scale_x = background.img_scale_x;+			+			img_width = background.get_img ().get_width () * background.img_scale_x;+			img_height = background.get_img ().get_height () * background.img_scale_y;+			+			move_bg = true;+		});++		release_action.connect((self, b, x, y) => {+			Glyph g = MainWindow.get_current_glyph ();+			GlyphBackgroundImage? bg = g.get_background_image ();+			+			if (bg == null) {+				return;+			}+			+			img_offset_x = ((!)bg).img_offset_x;+			img_offset_y = ((!)bg).img_offset_y;+			+			((!)bg).handler_release (x, y);+			+			move_bg = false;+			resize_bg = false;+		});++		move_action.connect((self, x, y) => {			+			move (x, y);+		});+		+		draw_action.connect ((self, cairo_context, glyph) => {+			Glyph g = MainWindow.get_current_glyph ();+			GlyphBackgroundImage? background_image = g.get_background_image ();+			if (background_image == null) return;+			+			((!) background_image).draw_handle (cairo_context, glyph);+		});+	}+		+	void move (double x, double y) {+		Glyph g = MainWindow.get_current_glyph ();+		GlyphBackgroundImage? background_image = g.get_background_image ();+		GlyphBackgroundImage bg = (!) background_image;+		+		double xscale, yscale, dx, dy;++		if (background_image == null) return;+		 		+		bg.handler_move (x, y);+	+		if (!(move_bg || resize_bg)) return;+		+		dx = x - begin_x;+		dy = y - begin_y;+		+		dx *= 1/g.view_zoom;+		dy *= 1/g.view_zoom;+	+		dx *= PenTool.precision;+		dy *= PenTool.precision;+		+		if (bg.selected_handle == 2) {+			bg.set_img_rotation_from_coordinate (x, y);+		}+		+		if (bg.selected_handle == 1) {+			xscale = img_scale_x * ((img_width - dx) / img_width);	+			yscale = xscale;++			bg.set_img_scale (xscale, yscale);+			bg.set_img_offset (this.img_offset_x + dx, this.img_offset_y);+		} +		+		if (move_bg && bg.selected_handle <= 0) {+			bg.set_img_offset (this.img_offset_x + dx, this.img_offset_y + dy);+		}++		MainWindow.get_glyph_canvas ().redraw ();+	}+	+	public override bool test () {+		return false;+	}++}++}+diff --git a/libbirdfont/Birdfont.vala b/libbirdfont/Birdfont.valanew file mode 100644index 0000000..60c15b5--- /dev/null+++ b/libbirdfont/Birdfont.vala@@ -0,0 +1,169 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/+using Supplement;++namespace Birdfont {++static void print_export_help (string[] arg) {+	stdout.printf (_("Usage:"));+	stdout.printf (arg[0]);+	stdout.printf (" [OPTION ...] FILE\n");+	stdout.printf ("-h, --help                      " + _("print this message\n"));+	stdout.printf ("-o, --output [DIRECTORY]        " + _("write files to this directory\n"));+	stdout.printf ("-s, --svg                       " + _("write svg file\n"));+	stdout.printf ("-t, --ttf                       " + _("write ttf and eot files\n"));+	stdout.printf ("\n");+}++public static int run_export (string[] arg) {+	string output_directory = ".";+	string file_name = "";+	bool specific_formats = false;	+	bool write_ttf = false;+	bool write_svg = false;+	Supplement.Supplement supplement = new Supplement.Supplement ();+	+	File directory;+		+	if (arg.length < 2) {+		print_export_help (arg);+	}+	+	for (int i = 1; i < arg.length; i++) {++		if (arg[i] == "-f" || arg[i] == "--fatal-warnings") {+			supplement.fatal_wanings = true;+			return 0;+		}++		if (arg[i] == "-h" || arg[i] == "--help") {+			print_export_help (arg);+			return 0;+		}+		+		if ((arg[i] == "-o" || arg[i] == "--output") && i + 1 < arg.length) {+			output_directory = arg[i + 1];+			i++;+			continue;+		}++		if (arg[i] == "-s" || arg[i] == "--svg") {+			write_svg = true;+			specific_formats = true;+			continue;+		}+		+		if (arg[i] == "-t" || arg[i] == "--ttf") {+			write_ttf = true;+			specific_formats = true;+			continue;+		}+		+		if (arg[i].has_prefix ("-")) {+			print_export_help (arg);+			return 1;+		}+		+		if (!arg[i].has_prefix ("-")) {+			file_name = arg[i];+						+			if (i != arg.length - 1) {+				print_export_help (arg);+				return 1;+			}+			+			break;+		}+	}++	if (supplement.fatal_wanings) {+		LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;+		Log.set_handler (null, levels, supplement.fatal_warning);		+	}++	Preferences preferences = new Preferences ();+	preferences.load ();+		+	supplement.args = new Argument ("");+	supplement.current_font = new Font ();+	supplement.current_glyph = new Glyph ("");+	+	if (!supplement.get_current_font ().load (file_name, false)) {+		stderr.printf (@"Failed to load font $file_name.\n");+		+		if (!file_name.has_suffix (".ffi")) {+			stderr.printf (@"Is it a .ffi file?\n");+		}+		+		return 1;+	}++	directory = File.new_for_path (output_directory);+	+	if (!directory.query_exists ()) {+		stderr.printf (_("Can't find output directory") + @"$((!)directory.get_path ())\n");+		return 1;+	}++	if (!specific_formats || write_svg) {+		print (_("Writing") + @" $(supplement.current_font.get_name ()).svg to $output_directory\n");+		ExportTool.export_svg_font_path (File.new_for_path (output_directory));+	}++	if (!specific_formats || write_ttf) {+		print (_("Writing") + @" $(supplement.current_font.get_name ()).ttf to $output_directory\n");+		ExportTool.export_ttf_font_path (File.new_for_path (output_directory), false);+	}+	+	return 0;+}++public static string wine_to_unix_path (string exec_path) {+	bool drive_c, drive_z;+	int i;+	string p, q;++	p = exec_path;+	p = p.replace ("\\", "/");+	+	drive_c = exec_path.index_of ("C:") == 0;+	drive_z = exec_path.index_of ("Z:") == 0;+	+	i = p.index_of (":");+	+	if (i != -1) {+		p = p.substring (i + 2);+	}++	if (drive_c) {+		q = @"/home/$(Environment.get_user_name ())/.wine/drive_c/" + p;+		+		if (File.new_for_path (q).query_exists ()) {+			return q;+		} else {+			return p;+		}+	}+	+	if (drive_z) {+		return ("/" + p).dup ();+	}++	return exec_path.dup ();+}++}diff --git a/libbirdfont/CharDatabase.vala b/libbirdfont/CharDatabase.valanew file mode 100644index 0000000..54b71ce--- /dev/null+++ b/libbirdfont/CharDatabase.vala@@ -0,0 +1,62 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++class CharDatabase {++	public CharDatabase () {+	}++	public static bool has_ascender (unichar c) {+		if (!c.islower()) return true;+		+		// todo: their derrivatives+		switch (c) {+			case 'b': return true;+			case 'd': return true;+			case 'f': return true;+			case 'h': return true;+			case 'i': return true;+			case 'j': return true;+			case 'k': return true;+			case 'l': return true;	+		}+		+		if ('à' <= c <= 'å') return true;+		if ('è' <= c <= 'ö') return true;+		if ('ù' <= c <= 'ă') return true;+		if ('ć' <= c <= 'ė') return true;++		return false;+	}++	public static bool has_descender (unichar c) {+		// todo: their derrivatives+		switch (c) {+			case 'g': return true;+			case 'j': return true;+			case 'p': return true;+			case 'q': return true;+		}+		+		return false;		+	}+	+}++}diff --git a/libbirdfont/ClipTool.vala b/libbirdfont/ClipTool.valanew file mode 100644index 0000000..0472373--- /dev/null+++ b/libbirdfont/ClipTool.vala@@ -0,0 +1,102 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++public class ClipTool : Tool {++	static Glyph? glyph = null; +	static List<Path> path = new List<Path> ();++	public static void copy () {+		Font font = Supplement.get_current_font ();+		OverView overview  = MainWindow.get_overview ();+		FontDisplay fd = MainWindow.get_current_display ();+		string gc;+		Glyph g = MainWindow.get_current_glyph ();++		glyph = null;++		while (path.length () > 0) {+			path.remove_link (path.first ());+		}+					+		if (fd is OverView) {+			gc = overview.get_selected_char ();+			glyph = font.get_glyph (gc);+		} +		+		if (fd is Glyph) {+			foreach (Path p in g.active_paths) {+				path.append (p.copy ());+			}++			if (path.length () == 0) {+				glyph = g.copy ();+			}+		}++	}++	public static void paste () {+		Font font = Supplement.get_current_font ();+		OverView overview  = MainWindow.get_overview ();+		FontDisplay fd = MainWindow.get_current_display ();+		string gc;+		Glyph? destination = null;+		unichar new_char;+		Path inserted;+		+		if (fd is OverView) {+			gc = overview.get_selected_char ();+			destination = font.get_glyph (gc);+		+			if (destination == null) {+				new_char = gc.get_char (0);+				destination = new Glyph (gc, new_char) ;+				font.add_glyph ((!) destination);+			}+		}+		+		if (fd is Glyph) {+			destination = (Glyph) fd;++			if (destination == glyph) {+				return;+			}+			+			((!)destination).store_undo_state ();+			+			if (glyph != null) {+				foreach (Path p in ((!)glyph).path_list) {+					inserted = p.copy ();+					((!)destination).add_path (inserted);+				}+			}++			foreach (Path p in path) {+				inserted = p.copy ();+				((!)destination).add_path (inserted);+			}+			+			((!)destination).update_view ();+		}+	}++}++}diff --git a/libbirdfont/Context.vala b/libbirdfont/Context.valanew file mode 100644index 0000000..1061488--- /dev/null+++ b/libbirdfont/Context.vala@@ -0,0 +1,335 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++/** Kerning context. */+public class ContextDisplay : FontDisplay {++	List <Word> row;+	int active_handle = -1;+	int selected_handle = -1;+	+	double begin_handle_x = 0;+	double begin_handle_y = 0;+	+	double last_handle_x = 0;+	+	public ContextDisplay () {+		Word w = new Word ();+		row = new List <Word> ();+		row.append (w);+	}++	public override string get_name () {+		return "Kerning";+	}+	+	public override void draw (Allocation allocation, Context cr) {+		Glyph? g;+		Glyph glyph;+		double x, y, w, kern, alpha;+		double x2;+		int i, wi;+		string prev;+		+		i = 0;+		+		// bg color+		cr.save ();+		cr.set_source_rgba (1, 1, 1, 1);+		cr.rectangle (0, 0, allocation.width, allocation.height);+		cr.fill ();+		cr.restore ();+	+		alpha = 1;+		y = 100;+		x = 20;+		w = 0;+		prev = "";+		+		foreach (Word word in row) {+			wi = 0;+			+			foreach (string s in word.glyph) {				+				g = (!) Supplement.get_current_font ().get_glyph (s);	+				+				if (s == "") {+					continue;+				}+				+				kern = (prev != "") ? get_kerning (prev, s) : 0;+				+				// draw glyph				+				if (g == null) {+					w = 50;+					alpha = 1;+				} else {+					alpha = 0;+					glyph = (!) g;+					Svg.draw_svg_path (cr, glyph.get_svg_data (), x + kern, y);+					w = glyph.get_width ();+				}++				// handle+				if (active_handle == i) {+					//x2 = x + kern;+					x2 = x + kern / 2.0;+					+					cr.save ();+					cr.set_source_rgba (153/255.0, 153/255.0, 173/255.0, 1);+					cr.move_to (x2 - 5, y + 20);+					cr.line_to (x2 + 0, y + 20 - 5);+					cr.line_to (x2 + 5, y + 20);+					cr.fill ();+					+					cr.set_font_size (10);+					cr.show_text (s);+					cr.restore ();+				}	+				+				x += w + kern;+	+				// caption+				if (g == null || ((!)g).is_empty ()) {+					cr.save ();+					cr.set_source_rgba (153/255.0, 153/255.0, 153/255.0, alpha);+					cr.move_to (x - w / 2.0 - 5, y + 20);+					cr.set_font_size (10);+					cr.show_text (s);+					cr.restore ();+				}+							+				prev = s;+				+				wi++;+				i++;+			}+			+			if (y > allocation.height && i > 10) {+				row.remove (word);+			}+						+			y += MainWindow.get_current_glyph ().get_height () + 20;+			x = 20;			+		}+	}+	+	private void set_kerning (int handle, double val) {+		string a, b;+		Font font;+		int wi = 0;+		double kern;+		+		font = Supplement.get_current_font ();++		font.touch ();++		a = "";+		b = "";++		foreach (Word word in row) {+			foreach (string s in word.glyph) {+				+				if (s == "") {+					continue;+				}+				+				b = s;+				+				if (handle == wi) {+					kern = font.get_kerning (a, b) + val;+					font.set_kerning (a, b, kern);+				}+				+				wi++;+				+				a = b;+			}+		}+		+		+	}+	+	private double get_kerning (string a, string b) {+		Font font = Supplement.get_current_font ();+		return font.get_kerning (a, b);+	}+	+	public override void selected_canvas () {+		Glyph g;+		Word w;+		unowned List<Word>? r;+		unowned List<string>? rw;+		StringBuilder s = new StringBuilder ();+		bool append_char = false;+		+		KeyBindings.singleton.set_require_modifier (true);+		+		g = MainWindow.get_current_glyph ();+		s.append_unichar (g.get_unichar ());++		r = row;+		return_if_fail (r != null);++		if (row.length () == 0) {+			append_char = true;+		}+		+		if (row.length () > 0) {+			rw = row.first ().data.glyph;+			return_if_fail (rw != null);+			append_char = (row.first ().data.glyph.data.index_of (s.str) == -1);+		}+		+		if (append_char) {+			w = new Word ();+			row.append (w);+			w.glyph.prepend (s.str);+		}					+	}+	+	public override void key_press (uint keyval) {+		unichar c = (unichar) keyval;+		Font f = Supplement.get_current_font ();+		string name;+		+		if (KeyBindings.singleton.modifier == NONE || KeyBindings.singleton.modifier == SHIFT) {+					+			if (keyval == Key.BACK_SPACE && row.length () > 0) {	+				row.first ().data.glyph.remove_link (row.first ().data.glyph.last ());+			}+			+			if (row.length () == 0 || c == Key.ENTER) {+				row.prepend (new Word ());+			}+			+			if (!is_modifier_key (c) && c.validate ()) {+				name = f.get_name_for_character (c);+				row.first ().data.glyph.append (name);+			}+		}+		+		MainWindow.get_glyph_canvas ().redraw ();+	}+	+	public override void motion_notify (double ex, double ey) {+		double k, y;+		+		if (selected_handle == -1) {+			set_active_handle (ex, ey);+		} else {+			y = 1;+			+			if (Math.fabs (ey - begin_handle_y) > 20) {+				y = ((Math.fabs (ey - begin_handle_y) / 100) + 1);+			}+			+			k = (ex - last_handle_x) / y; // y-axis is variable precision+			set_kerning (selected_handle, k);+			MainWindow.get_glyph_canvas ().redraw ();+		}+		+		last_handle_x = ex;+	}+	+	public void set_active_handle (double ex, double ey) {+		double y = 100;+		double x = 20;+		double w = 0;+		double d, kern;+		double min = double.MAX;+		int i = 0;+		int row_index = 0;+		int col_index = 0;+		Glyph? g;+		Glyph glyph = new Glyph.no_lines ("");+		+		string prev = "";+		string gl_name = "";+		+		foreach (Word word in row) {+			col_index = 0;+			foreach (string s in word.glyph) {+				+				// draw glyph+				g = (!) Supplement.get_current_font ().get_glyph (s);	+				kern = (prev != "") ? get_kerning (prev, s) : 0;+				+				if (g == null) {+					w = 50;	+				} else {+					glyph = (!) g;+					w = glyph.get_width ();+					+				}+				+				d = Math.pow (x + kern - ex, 2) + Math.pow (y - ey, 2);+				+				if (d < min) {+					min = d;+					gl_name = glyph.get_name ();+					+					if (active_handle != i - row_index) {+						active_handle = i - row_index;+						MainWindow.get_glyph_canvas ().redraw ();+					}++					if (col_index == word.glyph.length () || col_index == 0) {+						active_handle = -1;+					}+				}+				+				prev = s;+				x += w + kern;+				i++;+				col_index++;+			}+			+			row_index++;+			y += MainWindow.get_current_glyph ().get_height () + 20;+			x = 20;+		}+	}+	+	public override void button_release (int button, double ex, double ey) {+		set_active_handle (ex, ey);+		selected_handle = -1;+	}+	+	public override void button_press (uint button, double ex, double ey) {+		set_active_handle (ex, ey);+		selected_handle = active_handle;+		begin_handle_x = ex;+		begin_handle_y = ey;+		last_handle_x = ex;+	}++}++class Word {+	public List <string> glyph;+	+	public Word () {+		glyph = new List <string> ();+		glyph.append ("");+	}+}++}diff --git a/libbirdfont/CutBackgroundTool.vala b/libbirdfont/CutBackgroundTool.valanew file mode 100644index 0000000..db17ddd--- /dev/null+++ b/libbirdfont/CutBackgroundTool.vala@@ -0,0 +1,246 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Math;++namespace Supplement {++class CutBackgroundTool : Tool {++	double x1 = 0;+	double y1 = 0;+	double x2 = 0;+	double y2 = 0;+	+	bool is_visible = false;+	bool is_set = false;+	bool is_done = false;+		+	public signal void new_image (GlyphBackgroundImage file);+	+	public CutBackgroundTool (string name) {+		base (name, _("Crop background image"));++		select_action.connect((self) => {+		});++		deselect_action.connect ((self) => {+		});+		+		press_action.connect((self, b, x, y) => {				+			if (!is_over_rectangle (x, y) || !is_visible) {+				x1 = x;+				y1 = y;+	+				x2 = x;+				y2 = y;+				+				is_visible = true;+				is_set = false;+				is_done = false;+			}+			+			if (is_set && is_visible && is_over_rectangle (x, y)) {+				do_cut ();+				is_set = false;+				is_visible = false;+				is_done = true;+				+				x1 = 0;+				y1 = 0;+				x2 = 0;+				y2 = 0;+			}+		});++		release_action.connect((self, b, x, y) => {+			if (!is_set && !is_done) {+				x2 = x;+				y2 = y;+				is_set = true;+			}+			+			is_done = false;+		});++		move_action.connect((self, x, y) => {+			if (is_visible && !is_set) {+				x2 = x;+				y2 = y;+				+				MainWindow.get_glyph_canvas ().redraw ();+			}+		});+		+		draw_action.connect ((self, cr, glyph) => {+			if (is_visible) {+				cr.save ();+				cr.set_line_width (2.0);+				cr.set_source_rgba (0, 0, 1, 0.3);+				cr.rectangle (fmin (x1, x2), fmin (y1, y2), fabs (x1 - x2), fabs (y1 - y2));+				cr.stroke ();+				cr.restore ();+			}+		});++		new_image.connect ((file) => {+			Glyph glyph = MainWindow.get_current_glyph ();+			TabBar tb = MainWindow.get_tab_bar ();+			+			glyph.store_undo_state ();+			+			glyph.set_background_image (file);+			tb.select_tab_name (glyph.get_name ());+			+			glyph.set_background_visible (true);+		});++	}++	bool is_over_rectangle (double x, double y) {+		return fmin (x1, x2) + 1 < x < fmax (x1, x2) - 1 && fmin (y1, y2) + 1 < y < fmax (y1, y2) - 1;+	}++	double get_width () {+		return fabs (x1 - x2);+	}++	double get_height () {+		return fabs (y1 - y2);+	}++	void do_cut () {+		double x, y;+		int h, w;+		double wc, hc;+		+		Glyph g = MainWindow.get_current_glyph ();+		GlyphBackgroundImage? b = g.get_background_image ();+		GlyphBackgroundImage bg = (!) b;+		+		ImageSurface img;+		+		Surface sr;+		Context cr;+		+		double tx, ty, vx, vy;	+		if (b == null) {+			return;+		}++		// Add margin+		Surface sg = new Surface.similar (bg.get_img (), bg.get_img ().get_content (), bg.size_margin, bg.size_margin);+		Context cg = new Context (sg);+		+		wc = bg.get_margin_width ();+		hc = bg.get_margin_height ();+		+		cg.set_source_rgba (1, 1, 1, 1);+		cg.rectangle (0, 0, bg.size_margin, bg.size_margin);+		cg.fill ();+		+		cg.set_source_surface (bg.get_img (), wc, hc);+		cg.paint ();++		// find start+		tx = bg.img_offset_x - g.view_offset_x;+		ty = bg.img_offset_y - g.view_offset_y;+				+		ty *= g.view_zoom;+		tx *= g.view_zoom;++		vx = g.path_coordinate_x (tx) - g.path_coordinate_x (fmin (x1, x2));			+		vy = g.path_coordinate_y (ty) - g.path_coordinate_y (fmin (y1, y2));+		+		x = (int) (vx / bg.img_scale_x);+		y = (int) (-vy / bg.img_scale_y);++		// do the cut+		img = bg.get_img ();++		w = (int) (get_width () / g.view_zoom);+		h = (int) (get_height () / g.view_zoom);++		sr = new Surface.similar (sg, img.get_content (), w, h);+		cr = new Context (sr);++		cr.set_source_rgba (1, 1, 1, 1);+		cr.rectangle (0, 0, w, h);+		cr.fill ();+			+		cr.scale (bg.img_scale_x, bg.img_scale_y);+		+		cr.set_source_surface (sg, x, y);+		cr.paint ();+		+		save_img (sr, g);+		+		cr.restore ();+	}++	void save_img (Surface sr, Glyph g) {+		GlyphBackgroundImage newbg;+		Font f = Supplement.get_current_font ();+		File img_dir;+		File img_file;+		File img_file_next;+		string fn;+		double wc, hc;+		+		img_dir =  f.get_backgrounds_folder ().get_child ("parts");++		if (!img_dir.query_exists ()) {+			DirUtils.create ((!) img_dir.get_path (), 0xFFFFFF);+		}+	+		img_file = img_dir.get_child (@"NEW_BACKGROUND.png");+		fn = (!) img_file.get_path ();+		+		sr.write_to_png (fn);+		+		newbg = new GlyphBackgroundImage (fn);+		+		fn = newbg.get_sha1 () + ".png";++		img_file_next = img_dir.get_child (fn);+		+		try {+			if (img_file_next.query_exists ()) {+				img_file_next.delete ();+			}++			img_file.set_display_name (fn);+		} catch (GLib.Error e) {+			warning (e.message);+			return;+		}+		+		newbg = new GlyphBackgroundImage ((!) f.get_backgrounds_folder ().get_child ("parts").get_child (fn).get_path ());+			+		// set position for the new background+		wc = newbg.get_margin_width ();+		hc = newbg.get_margin_height ();+		+		newbg.img_x = g.path_coordinate_x (fmin (x1, x2)) - wc - 0.5;+		newbg.img_y = g.path_coordinate_y (fmin (y1, y2)) + hc + 0.5;++		new_image (newbg);+	}+}++}diff --git a/libbirdfont/CutTool.vala b/libbirdfont/CutTool.valanew file mode 100644index 0000000..40aab82--- /dev/null+++ b/libbirdfont/CutTool.vala@@ -0,0 +1,61 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++class CutTool : Tool {++	PenTool pen;+	+	public CutTool (string n) {+		base (n, "Create counter path", 'u', CTRL);+		+		select_action.connect((self) => {+			pen = (PenTool) MainWindow.get_tool ("pen_tool");+		});+		+		press_action.connect((self, b, x, y) => {+			pen.press_action (pen, b, x, y);+		});++		release_action.connect((self, b, x, y) => {+			pen.release_action (pen, b, x, y);+		});+		+		move_action.connect ((self, x, y)	 => {+			pen.move_action (pen, x, y);+		});+	}++	public static void force_direction () {+		Glyph g = MainWindow.get_current_glyph ();+		Tool current_tool = MainWindow.get_toolbox ().get_current_tool ();+		+		foreach (Path p in g.active_paths) {+			if (p.is_open () && !p.has_direction ()) {+				if (current_tool is PenTool) {+					p.force_direction (Direction.CLOCKWISE);+				} else if (current_tool is CutTool) {+					p.force_direction (Direction.COUNTER_CLOCKWISE);+				}				+			}+		}+	}+	+}++}diff --git a/libbirdfont/DropMenu.vala b/libbirdfont/DropMenu.valanew file mode 100644index 0000000..f7387c9--- /dev/null+++ b/libbirdfont/DropMenu.vala@@ -0,0 +1,201 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++public enum MenuDirection {+	DROP_DOWN,+	POP_UP;+}++public class DropMenu : GLib.Object {++	public delegate void Selected (MenuAction self);+	public signal void selected (DropMenu self);+	+	double x = -1;+	double y = -1;+	bool menu_visible = false;++	List <MenuAction> actions = new List <MenuAction> ();++	const int item_height = 25;+	+	MenuDirection direction = MenuDirection.DROP_DOWN;+	+	ImageSurface? icon = null;+	+	public DropMenu (string icon_file = "") {+		if (icon_file != "") {+			icon = Icons.get_icon (@"$icon_file.png");+		}+	}+	+	public void deselect_all () {+		foreach (MenuAction m in actions) {+			m.set_selected (false);+		}+	}+	+	public void set_direction (MenuDirection d) {+		direction = d;+	}+	+	public void close () {+		menu_visible = false;+	}+	+	public MenuAction add_item (string label) {+		MenuAction m = new MenuAction (label);+		add_menu_item (m);+		return m;+	}+	+	public void add_menu_item (MenuAction m) {+		m.parent = this;+		actions.append (m);+	}+		+	public bool is_over_icon (double px, double py) {+		if (x == -1 || y == -1) {+			return false;+		}+		+		return x - 5 < px < x + 12 + 5 && y - 5 < py < y + 12 + 5;+	}++	public bool menu_item_action (double px, double py) {+		MenuAction? action;++		if (menu_visible) {+			action = get_menu_action_at (px, py);+			+			if (action != null) {+				((!) action).action ((!)action);+				selected (this);+				menu_visible = false;+				return true;+			}+		}+		+		return false;+	}+	+	public bool menu_icon_action (double px, double py) {		+		menu_visible = is_over_icon (px, py);+		return menu_visible;+	}+	+	MenuAction? get_menu_action_at (double px, double py) {+		double n = 0;+		double ix, iy;+		+		foreach (MenuAction item in actions) {+			ix = x - 100 + 12;+			+			if (direction == MenuDirection.DROP_DOWN) {+				iy = y + 12 + n * item_height;+			} else {+				iy = y - 24 - n * item_height;+			}+	+			if (ix <= px <= ix + 100 && iy <= py <= iy + item_height) {+				return item;+			}+			+			n++;			+		}++		return null;+	}+	+	public void set_position (double px, double py) {+		x = px;+		y = py;+	}+	+	public void draw_menu (Context cr) {+		double ix, iy;+		int n;+		+		if (likely (!menu_visible)) {+			return;+		}+		+		cr.save ();+		cr.set_source_rgba (122/255.0, 150/255.0, 169/255.0, 1);+		cr.set_line_join (LineJoin.ROUND);+		cr.set_line_width (12);+			+		if (direction == MenuDirection.DROP_DOWN)+			cr.rectangle (x - 100 + 18, y + 18, 88, actions.length () * item_height - 12);+		else+			cr.rectangle (x - 100 + 18, y + 6 - actions.length () * item_height, 88, actions.length () * item_height - 12);+			+		cr.fill_preserve ();+		cr.stroke ();+		cr.restore ();+		+		cr.save ();+		+		n = 0;+		foreach (MenuAction item in actions) {+			+			if (direction == MenuDirection.DROP_DOWN)+				iy = y + 33 + n * item_height - 5;+			else +				iy = y - 8 - n * item_height;+			+			ix = x - 100 + 20;+			+			item.draw (ix, iy, cr);+			n++;+		}+		+		cr.restore ();+	}+	+	public void draw_icon (Context cr) {+		double alpha = 1;+		ImageSurface i = (!) icon;+		+		if (!menu_visible) {+			alpha = 0;+		}+		+		cr.save ();+		+		cr.set_source_rgba (122/255.0, 150/255.0, 169/255.0, alpha);+		+		cr.rectangle (x, y, 12, 12);+		cr.fill_preserve ();+		cr.stroke ();+		+		if (likely (icon != null && i.status () == Cairo.Status.SUCCESS)) {+			cr.set_source_surface (i, x, y + (12 - i.get_height ()) / 2.0);+			cr.paint ();+		} else {+			warning ("Failed to load icon.");+		}+		+		cr.restore ();+	}+}++}diff --git a/libbirdfont/EditPoint.vala b/libbirdfont/EditPoint.valanew file mode 100644index 0000000..24ae2d0--- /dev/null+++ b/libbirdfont/EditPoint.vala@@ -0,0 +1,343 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Math;++namespace Supplement {++public enum PointType {+	NONE,+	NORMAL,+	LINE,+	CURVE,+	DOUBLE_CURVE,+	END,+	FLOATING+}++public class EditPoint {+	+	public double x;+	public double y;+	public PointType type;+	+	public double r = 1;+	public double g = 0;+	public double b = 0;+	public double a = 1;+	+	public unowned List<EditPoint>? prev = null;+	public unowned List<EditPoint>? next = null;++	public bool active = false;+	public bool selected = false;+		+	public int selected_handle = 0;+	+	public EditPointHandle right_handle;+	public EditPointHandle left_handle;+	+	public bool tie_handles = false;+	+	public EditPoint (double nx = 0, double ny = 0, PointType nt = PointType.LINE) {+		x = nx;+		y = ny;+		type = nt;+		active = false;+		+		set_active (true);+		+		if (nt == PointType.FLOATING) {+			r = 1;+			g = 1;+			b = 0;+			a = 1;+			active = false;+		}+	+		right_handle = new EditPointHandle (this, 0, 7);+		left_handle = new EditPointHandle (this, PI, 7);++		if (unlikely (nx.is_nan () || ny.is_nan ())) {+			warning (@"Invalid point at ($nx,$ny).");+			x = 0;+			y = 0;+		}+	}++	/** Flip handles if next point on path is in the other direction. +	 *  Used to recalculate handles after new point is inserted on a path.+	 */+	public void recalculate_handles (double px, double py) {+		double dr, dl;+		EditPointHandle t;+		+		if (next == null || ((!)next).length () < 2) {+				return;+		}+		+		px = get_next ().nth (1).data.x;+		py = get_next ().nth (1).data.y;+		+		dr = Math.sqrt (Math.pow (px - right_handle.x (), 2) + Math.pow (py - right_handle.y (), 2));+		dl = Math.sqrt (Math.pow (px - left_handle.x (), 2) + Math.pow (py - left_handle.y (), 2));++		// flip handles+		if (dl < dr) {+			t = right_handle;+			right_handle = left_handle;+			left_handle = t;+		}		+	}+		+	/** Set bezier points for linear paths. */+	public void recalculate_linear_handles () {+		unowned EditPointHandle h;+		unowned EditPoint n;+		double nx, ny;++		if (prev == null && next != null) {+			prev = get_next ().last ();+		}++		// left handle+		if (prev != null) {+			n = get_prev ().data;+			h = get_left_handle ();+			+			if (h.type == PointType.LINE) {+				nx = x + ((n.x - x) / 3);+				ny = y + ((n.y - y) / 3);+			+				h.move_to_coordinate (nx, ny);+			}++			h = n.get_right_handle ();+			+			// on the other side+			h = n.get_right_handle ();+			+			if (h.type == PointType.LINE) {+				nx = n.x + ((x - n.x) / 3);+				ny = n.y + ((y - n.y) / 3);+			+				h.move_to_coordinate (nx, ny);+			}+		}++		// right handle+		if (next != null) {+			n = get_next ().data;+			h = get_right_handle ();+			+			if (h.type == PointType.LINE) {+				nx = x + ((n.x - x) / 3);+				ny = y + ((n.y - y) / 3);+				+				h.move_to_coordinate (nx, ny);+			}++			h = n.get_left_handle ();+			+			if (h.type == PointType.LINE) {+				nx = n.x + ((x - n.x) / 3);+				ny = n.y + ((y - n.y) / 3);++				h.move_to_coordinate (nx, ny);+			}+		}+	}+	+	public void set_tie_handle (bool t) {+		tie_handles = t;+	}++	public void process_tied_handle () {+		double a, b, c, length, angle;+		EditPointHandle eh;+		+		eh = right_handle;+		+		a = left_handle.x () - right_handle.x ();+		b = left_handle.y () - right_handle.y ();+		c = a * a + b * b;+		+		if (c == 0) {+			return;+		}+		+		length = sqrt (fabs (c));+		+		if (right_handle.y () < left_handle.y ()) {+			angle = acos (a / length) + PI;+		} else {+			angle = -acos (a / length) + PI;+		}+		+		left_handle.type = PointType.CURVE;+		right_handle.type = PointType.CURVE;+		+		right_handle.angle = angle;+		left_handle.angle = angle;++		set_tie_handle (true);+		eh.move_to_coordinate (right_handle.x (), right_handle.y ());+	}++	public EditPoint copy () {+		EditPoint new_point = new EditPoint ();+		+		new_point.x = x;+		new_point.y = y;+		+		new_point.type = type;+		new_point.set_color (r, g, b, a);+		+		new_point.tie_handles = tie_handles;+		+		new_point.right_handle.angle = right_handle.angle;+		new_point.right_handle.length = right_handle.length;+		new_point.right_handle.type = right_handle.type;++		new_point.left_handle.angle = left_handle.angle;+		new_point.left_handle.length = left_handle.length;		+		new_point.left_handle.type = left_handle.type;++		return new_point;+	}++	public double get_distance (double x, double y) {+		return Path.distance (this.x, x, this.y, y);+	}++	public unowned EditPointHandle get_left_handle () {+		return left_handle;+	}+	+	public unowned EditPointHandle get_right_handle () {+		return right_handle;+	}+	+	public void set_color (double r, double g, double b, double a) {+		this.r = r;+		this.g = g;+		this.b = b;+		this.a = a;+	}+	+	public unowned List<EditPoint> get_prev () {+		if (prev == null && next != null && get_next ().prev == get_next ().first ()) {+			prev = get_next ().last ();+		}+		+		if (unlikely (prev == null)) {+			warning ("EditPoint.prev is null");+		}+		+		return (!) prev;+	}++	public unowned List<EditPoint> get_next () {+		if (unlikely (next == null)) {+			warning ("EditPoint.next is null");+		}+		+		return (!) next;+	}+	+	public unowned List<EditPoint> get_list () {+		return get_next ().first ();+	}+	+	public void set_position (double tx, double ty) {+		x = tx;+		y = ty;++		if (unlikely (tx.is_nan () || ty.is_nan ())) {+			warning (@"Invalid point at ($tx,$ty).");+			x = 0;+			y = 0;+		}+	}+	+	public static void to_coordinate (ref double x, ref double y) {+		double xc, yc, xt, yt, ivz;+		Glyph g = MainWindow.get_current_glyph ();+		+		ivz = 1 / g.view_zoom;+		+		xc = (g.allocation.width / 2.0);+		yc = (g.allocation.height / 2.0);+				+		x *= ivz;+		y *= ivz;++		xt = x - xc + g.view_offset_x;+		yt = yc - y - g.view_offset_y;		+		+		x = xt;+		y = yt;+	}+	+	public bool is_selected () {+		return selected;+	}+	+	public void set_selected (bool s) {+		selected = s;+	}+	+	public bool set_active (bool active) {+		bool update = (this.active != active);+		+		if (update) {+			this.active = active;+		+			if (active) {+				r = 0.5;+				g = 0;+				b = 1;+				a = 1;+			} else {+				r = 0;+				g = 1;+				b = 0.5;+				a = 1;+			}+		}+		+		return update;+	}+	+	public int get_index () {+		int i = -1;+		+		if (next == null) return i;+	+		foreach (var d in get_next ().first ()) {+			i++;+			+			if (this == d) {+				return i;+			}+		}+		+		return i;+	}+}++}diff --git a/libbirdfont/EditPointHandle.vala b/libbirdfont/EditPointHandle.valanew file mode 100644index 0000000..c03fcc1--- /dev/null+++ b/libbirdfont/EditPointHandle.vala@@ -0,0 +1,167 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Math;++namespace Supplement {+		+public class EditPointHandle  {+	+	public double angle;+	public double length;+	+	public EditPoint parent;+	+	public PointType type;+	+	EditPoint? visual_handle = null;++	static EditPoint none = new EditPoint ();++	public bool active;++	public EditPointHandle.empty() {+		this.parent = none;+		this.angle = 0;+		this.length = 10;+		this.type = PointType.LINE;+		this.active = false;+	}+	+	public EditPointHandle (EditPoint parent, double angle, double length) {+		this.parent = parent;+		this.angle = angle;+		this.length = length;+		this.type = PointType.LINE;+		this.active = false;+	}++	public EditPointHandle copy () {+		EditPointHandle n = new EditPointHandle.empty ();+		n.angle = angle;+		n.length = length;+		n.parent = parent;+		n.type = type;+		n.active = active;+		return n;+	}++	public void set_point_type (PointType point_type) {+		type = point_type;+	}++	public double x () {+		double r = px ();+		+		if (unlikely (r <= -100000)) {+			print_position ();+			move_to (0, 0);+		}+	+		return r;+	}+	+	public double y () {+		double r = py ();+		+		if (unlikely (r <= -100000)) {+			print_position ();+			move_to (0, 0);+		}+		+		return r;+	}+	+	double px () {+		assert ((EditPoint?) parent != null);+		return cos (angle) * length + parent.x;+	}++	double py () {+		assert ((EditPoint?) parent != null);+		return sin (angle) * length + parent.y;+	}+		+	void print_position () {+			warning (@"\nEdit point handle at position $(px ()),$(py ()) is not valid.\n"+				+ @"Type: $(parent.type), "+				+ @"Index: $(parent.get_index ()) of $(parent.get_list ().length ())\n"+				+ @"Angle: $angle, Length: $length.");	+	}+	+	public EditPoint get_point () {+		EditPoint p;+		+		if (visual_handle == null) {+			visual_handle = new EditPoint (0, 0);+		}+	+		p = (!) visual_handle;+		p.x = x ();+		p.y = y ();+		+		return p;+	}+	+	public void move_to_coordinate (double x, double y) {+		double a, b, c;++		a = parent.x - x;+		b = parent.y - y;+		c = a * a + b * b;+		+		if (c == 0) {+			return;+		}+		+		length = sqrt (fabs (c));	+		+		if (c < 0) length = -length;+	+		if (y < parent.y) {+			angle = acos (a / length) + PI;+		} else {+			angle = -acos (a / length) + PI;+		}+		+		if (parent.tie_handles) {+			tie_handle ();+		}+	}++	private void tie_handle () {+		if (this == parent.get_left_handle ()) {+			parent.get_right_handle ().angle = angle - PI;+		} else {+			parent.get_left_handle ().angle = angle - PI;+		}+	}+	+	public void move_delta (double dx, double dy) {+		double px = px () + dx * Glyph.ivz ();+		double py = py () - dy * Glyph.ivz ();+		+		move_to_coordinate (px, py);+	}+	+	public void move_to (double x, double y) {+		EditPoint.to_coordinate (ref x, ref y);+		move_to_coordinate (x, y);+	}+}++}diff --git a/libbirdfont/EotWriter.vala b/libbirdfont/EotWriter.valanew file mode 100644index 0000000..2f32e05--- /dev/null+++ b/libbirdfont/EotWriter.vala@@ -0,0 +1,174 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Math;++namespace Supplement {++class EotWriter : GLib.Object {+	+	string ttf_file_name;+	string eot_file_name;+	+	public EotWriter (string ttf_file, string eot_file) {+		ttf_file_name = ttf_file;+		eot_file_name = eot_file;+	}+	+	public void write () throws Error {+		FontData fd = new FontData ();+		FontData ttf_data = new FontData ();+		OpenFontFormatReader input; +		uint32 ttf_length;+		File ttf_file;+		File eot_file;+		FileInfo file_info;+		OtfInputStream dis;+		DataOutputStream os;+		uint8* data;+		uint32 l;+		NameTable names;+		string tn;+		+		ttf_file = File.new_for_path (ttf_file_name);+		+		if (!ttf_file.query_exists ()) {+			warning (@"EotWriter: file does not exist. $((!) ttf_file.get_path ())");+			return;+		}++		eot_file = File.new_for_path (eot_file_name);+		+		if (eot_file.query_exists ()) {+			warning ("File exists in eot export.");+			return;+		}+		+		os = new DataOutputStream(eot_file.create (FileCreateFlags.REPLACE_DESTINATION));++		file_info = ttf_file.query_info ("*", FileQueryInfoFlags.NONE);+		ttf_length = (uint32) file_info.get_size ();+		 +		if (ttf_length == 0) {+			warning ("TTF file is of zero length.");+			return;+		}++		dis = new OtfInputStream (ttf_file.read ());+		ttf_data.write_table (dis, 0, ttf_length);++		// parse file to find head checksum+		input = new OpenFontFormatReader ();+		input.parse_index (ttf_file_name);+ +		names = input.directory_table.name_table;+		+		fd.add_littleendian_u32 (0); // table length+		fd.add_littleendian_u32 (ttf_length);+	+		fd.add_littleendian_u32 (0x00020001); // table format version+		fd.add_littleendian_u32 (0); // flags+		+		// panose+		for (int i = 0; i < 10; i++) {+			fd.add (0);+		}+		+		// according to specification should charset and italic be added the +		// other way around, but it does not work+				+		fd.add (1); // default charset+		fd.add (0); // italic set to regular+				+		fd.add_littleendian_u32 (400); // weight+		fd.add_littleendian_u16 (0); // 0 drm, designer must know that notified+		fd.add_littleendian_u16 (0x504C); // magic number++		// FIXA:+		// unicode ranges +		fd.add_littleendian_u32 (0);+		fd.add_littleendian_u32 (0);+		fd.add_littleendian_u32 (0);+		fd.add_littleendian_u32 (0);+		+		// FIXA:+		// code page range+		fd.add_littleendian_u32 (0);+		fd.add_littleendian_u32 (0);+		+		fd.add_littleendian_u32 (input.get_head_checksum ()); // head checksum adjustment+		+		// reserved+		fd.add_littleendian_u32 (0);+		fd.add_littleendian_u32 (0);+		fd.add_littleendian_u32 (0);+		fd.add_littleendian_u32 (0);+	+		fd.add_littleendian_u16 (0); // padding+		+		tn = names.get_name (names.FONT_NAME);+		fd.add_littleendian_u16 ((uint16) (2 * tn.char_count ())); // strlen of family name+		fd.add_str_littleendian_utf16 (tn);+		+		fd.add_littleendian_u16 (0); // padding+		+		tn = names.get_name (names.SUBFAMILY_NAME);+		fd.add_littleendian_u16 ((uint16) (2 * tn.char_count ())); // strlen of family name+		fd.add_str_littleendian_utf16 (tn);++		fd.add_littleendian_u16 (0); // padding+		+		// FIXA version or name + version+		tn = names.get_name (names.VERSION);+		fd.add_littleendian_u16 ((uint16) (2 * tn.char_count ())); // strlen of family name+		fd.add_str_littleendian_utf16 (tn);+		+		fd.add_littleendian_u16 (0); // padding+		+		tn = names.get_name (names.FULL_FONT_NAME);+		fd.add_littleendian_u16 ((uint16) (2 * tn.char_count ())); // strlen of family name+		fd.add_str_littleendian_utf16 (tn);+		+		fd.add_littleendian_u16 (0); // padding+		+		fd.add_littleendian_u16 (0); // length of root string+		// the root string goes here.+		+		// update length of this table+		fd.seek (0);+		fd.add_littleendian_u32 (fd.length () + ttf_length);+		+		l = fd.length_with_padding ();+		data = fd.table_data;+		for (int i = 0; i < l; i++) {+			os.put_byte (data[i]);+		}++		l = ttf_data.length_with_padding ();+		data = ttf_data.table_data;+		for (int i = 0; i < l; i++) {+			os.put_byte (data[i]);+		}+		+		dis.close ();+		os.close ();+		input.close ();+	}+	+}+		+}diff --git a/libbirdfont/Expander.vala b/libbirdfont/Expander.valanew file mode 100644index 0000000..7e2bd50--- /dev/null+++ b/libbirdfont/Expander.vala@@ -0,0 +1,225 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Math;++namespace Supplement {++public class Expander : GLib.Object {+	+	public double x = 7;+	public double y = 5;++	public double w = 6;+	public double h = 5;+	+	public double margin = 0;+	+	protected double rotation = 0;+	protected double opacity = 0.5;+	+	protected bool active = false;+	protected bool open = false;++	public List<Tool> tool;++	bool persist = false;+	bool unique = false;+	+	public Expander () {+	}++	/** Returns true if tools can be used with the current canvas after+	 * they have been selectes and false if they are a commands to be executed.+	 */+	public bool is_persistent () {+		return persist;+	}++	/** Returns true if all other tools in thid expander should be deselected +	 * when a tool is selected.+	 */+	public bool is_unique () {+		return unique;+	}++	public void set_persistent (bool p) {+		persist = p;+	}++	public void set_unique (bool u) {+		unique = u;+	}+	+	private void update_tool_position () {+		int i = 0;+		double xt = x + 10;+		double yt = y - 4;+		foreach (var t in tool) {+			t.set_properties (xt, yt, w, h);+			+			if (i == 3) {+				yt += 31;+				i = 0;+				xt = x + 10;+			} else {+				i++;+				xt += w + 3 + 25;+			}+		}+	}+	+	public void set_offset (double ty) {+		y = ty;+		+		if (open) {+			update_tool_position ();+		}+	}+	+	public void add_tool (Tool t) {+		tool.append (t);+		update_tool_position ();+		+		t.select_action.connect ((selected) => {+				if (!selected.new_selection) {+					if (is_persistent ()) {+						selected.set_selected (true);+					} else {+						selected.set_selected (false);+					}+				}+			+				MainWindow.get_toolbox ().redraw ((int) x, (int) y, (int) w  + 300, (int) (h + margin));+			+				if (is_unique ()) {+					foreach (var deselected in tool) {+						if (selected.get_id () != deselected.get_id ()) {+							deselected.set_selected (false);+						}+					}+				}++				if (!is_persistent ()) {+						var time = new TimeoutSource(200);+						time.set_callback(() => {+							selected.set_selected (false);+							MainWindow.get_toolbox ().redraw ((int) x, (int) y, (int) w  + 300, (int) (h + margin));+							return false;+						});+						time.attach(null);+				}++				selected.new_selection = false;+			});+	}+	+	public bool is_over (double xp, double yp) {	+		return (x - 7/2.0 <= xp <= x + w  + 7/2.0 && y - 7/2.0<= yp <= y + w + 7/2.0);  +	}+	+	public bool set_active (bool a) {+		bool r = (active != a);+		opacity = (a) ? 1 : 0;+		active = a;+		return r;+	}+	+	public bool is_open () {+		return open;+	}+	+	public virtual bool set_open (bool o) {+		bool r = (open != o);+		rotation = (o) ? Math.PI_2 : 0;+		+		if (o) {+			margin = 35 * (int)((tool.length () / 4.0) + 1) ;+			rotation = Math.PI_2;			+			if (tool.length () % 4 == 0) {+				margin -= 35;+			}+		} else {+			margin = 0;+			rotation = 0; +		}+		+		open = o;+		return r;+	}+	+	public void draw (int wd, int hd, Context cr) {+		double lx, ly;+		double ih2 = 5.4 / 2;+		double iw2 = 5.4 / 2;+		+		// box+		cr.save ();+		cr.set_line_join (LineJoin.ROUND);+		cr.set_line_width(7);+		cr.set_source_rgba (176/255.0, 211/255.0, 230/255.0, opacity);+		cr.rectangle (x, y, w, h);+		cr.stroke ();+		cr.restore ();+		+		// arrow+		cr.save ();+		+		cr.translate (x + w/2, y + h/2);+		cr.rotate (rotation);++		cr.set_line_width (1);+		cr.set_source_rgba (0, 0, 0, opacity);++		cr.new_path ();+		cr.move_to (-iw2, -ih2);+		cr.line_to (iw2, 0);	+		cr.line_to (-iw2, +ih2);++		cr.close_path();+		cr.stroke ();+		cr.restore ();+		+		// separator+		cr.save ();+		lx = x + w + 7;+		ly = y + ih2;+		if (lx < wd) {+			cr.set_line_width(1);+			cr.set_source_rgba (0, 0, 0, 0.2);+			cr.move_to (lx, ly);+			cr.line_to (wd - w - x + 4, ly);	+			cr.stroke ();+		}+		cr.restore ();++	}+	+	public void draw_content (int w, int h, Context cr) {+		if (open) {+			cr.save ();+			foreach (var t in tool) {+				t.draw (cr);+			}+			cr.restore ();+		}+	}+	+}++}diff --git a/libbirdfont/ExportTool.vala b/libbirdfont/ExportTool.valanew file mode 100644index 0000000..8d7d4d4--- /dev/null+++ b/libbirdfont/ExportTool.vala@@ -0,0 +1,538 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++public class ExportTool : Tool {+	private static string? prefered_browser = null;+	private static ExportThread export_thread;+	+	private static unowned Thread<void*> export_thread_handle;++	private static bool stop_export_thread = false;++	public ExportTool (string n) {+		base (n, _("Export glyph to svg file"), 'e', CTRL);+	+		select_action.connect((self) => {+			export_svg_font ();+			// Fixa: add export_glyph_to_svg (); as tool in toolbox+		});+		+		press_action.connect((self, b, x, y) => {+		});++		release_action.connect((self, b, x, y) => {+		});+		+		move_action.connect ((self, x, y)	 => {+		});+	}++	public static bool export_all () {+		Font font = Supplement.get_current_font ();+		bool f;+		+		if (font.get_ttf_export ()) {+			f = export_ttf_font ();+			+			if (!f) {+				warning ("Failed to export font");+				return false;+			}+		}+		+		if (font.get_svg_export ()) {+			f = export_svg_font ();++			if (!f) {+				warning ("Failed to export font");+				return false;+			}+		}+		+		return true;+	}++	public void export_glyph_to_svg () {+		Glyph glyph = MainWindow.get_current_glyph ();+		Font font = Supplement.get_current_font ();+		FontDisplay fd = MainWindow.get_current_display ();+		string glyph_svg;+		string svg_file = @"$(glyph.get_name ()).svg";+		File file = font.get_folder ().get_child (svg_file);+		DataOutputStream os;+		string name = glyph.get_name ();+		int pid = 0;+		+		if (!(fd is Glyph)) {+			return;+		}+		+		try {+			+			if (file.query_exists ()) {+				file.delete ();+			}+			+			os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));	+	+			_("Alphabet");+					+			os.put_string (+"""<?xml version="1.0" standalone="no"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >+<svg """);++			os.put_string (@"xmlns=\"http://www.w3.org/2000/svg\"\n");+			os.put_string (@"\twidth=\"$(glyph.get_width () / Glyph.SCALE)\"\n");+			os.put_string (@"\theight=\"$(glyph.get_height () / Glyph.SCALE)\"\n");+			os.put_string (@"\tid=\"glyph_$(name)\"\n");+			os.put_string (@"\tversion=\"1.1\">\n");+			+			os.put_string (@"\n");+			+			os.put_string (@"<g id=\"$(name)\">\n");+			+			foreach (Path p in glyph.path_list) {+				+				glyph_svg = Svg.to_svg_path (p, glyph);+				+				os.put_string (@"<path ");+				os.put_string (@"style=\"fill:#000000;stroke-width:0px\" ");+				os.put_string (@"d=\"$(glyph_svg)\" id=\"path_$(name)$(pid++)\" />\n");+				+			}+			+			os.put_string ("""</g>+			+</svg>""");+	+		} catch (Error e) {+			stderr.printf (@"Export \"$svg_file\" \n");+			critical (@"$(e.message)");+			status (_("Can't export") + @" $svg_file.");+		}+		+		status (_("Wrote") + @" $svg_file.");+	}++	public static void generate_html_document (string html_file, Font font) {+		File file = File.new_for_path (html_file);+		DataOutputStream os;+		string name = font.get_name ();++		try {+			os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));++os.put_string (+"""<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">+<html>+<head>++	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">+	<title>Lorem ipsum</title>+	+	<style type="text/css">++		@font-face {+			font-family: '"""); os.put_string (@"$(name)");              os.put_string ("""SVG';+			src: url('""");     os.put_string (@"$(name).svg#$(name)");  os.put_string ("""') format('svg');+		}+	""");++	os.put_string ("""+		@font-face {+			font-family: '"""); os.put_string (@"$(name)");              os.put_string ("""TTF';+			src: url('""");     os.put_string (@"$(name).ttf"); os.put_string ("""') format('truetype');+		} +		""");++	os.put_string ("""+		@font-face {+			font-family: '"""); os.put_string (@"$(name)");              os.put_string ("""EOT';+			src: url('""");     os.put_string (@"$(name).eot"); os.put_string ("""');+		} +		+		""");+		+	os.put_string ("""	+		h1 {+			font-weight:normal;+			margin: 0 0 5px 0;+			color: #afafaf;+		}++		h2 {+			font-weight:normal;+			margin: 0 0 5px 0;+			color: #afafaf;+		}+		+		h3 {+			font-weight:normal;+			margin: 0 0 5px 0;+			color: #afafaf;+		}+		+		h4 {+			font-weight:normal;+			margin: 0 0 5px 0;+			color: #afafaf;+		}+		+		p {+			margin: 0 0 5px 0;+			color: #000000;+		}+		+		body {+			margin: 30px 0 0 30px;+		}+		+		div {+			font-family: """);++			os.put_string ("'");+			os.put_string (@"$(name)");+			os.put_string ("EOT'");+			+			os.put_string (", '");+			os.put_string (@"$(name)");+			os.put_string ("SVG'");+			+			os.put_string (", '");+			os.put_string (@"$(name)");+			os.put_string ("TTF'");++			os.put_string (";");+			os.put_string ("""+			margin: 0 0 30px 0;+		}++		h1.bigger {+			font-size: 58pt;+		}++		h2.big {+			font-size: 40pt;+		}+		+		h3.small {+			font-size: 32pt;+		}++		h4.smaller {+			font-size: 18pt;+		}++		h1.percent {+			font-size: 100%;+		}		+		+		p.bigger {+			font-size: 32pt;+		}++		p.big {+			font-size: 24pt;+		}+		+		p.small {+			font-size: 18pt;+		}++		p.smaller {+			font-size: 12pt;+		}++	</style>+""");+	+	os.put_string (+"""	+</head>+<body>++<div>+	<h1 class="bigger">Lorem ipsum</h1>+	<p class="bigger">Dolor sit amet!</p>+</div>++<div>+	<h2 class="big">Hamburgerfonstiv</h2>+	<p class="big">Inspirerande</p>+</div>++<div>+	<h3 class="small">Handgloves & Mittoms</h3>+	<p class="small">Wind blows in Hauges poetry.</p>+</p>+</div>++<div>+	<h4 class="smaller">Headline 16pt</h4>+	<p class="smaller">Ett litet stycke svalhjärta.</p>	+</div>++<div>+	<h4 class="smaller">""" +  _("Alphabet") + """</h4>+	<p class="smaller">""" + _("a b c d e f g h i j k l m n o p q r s t u v w x y z") + """</p>+	<p class="smaller">""" + _("A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") + """</p>+	<p class="smaller">0 1 2 3 4 5 6 7 8 9</p>+</div>++</body>+</html>+""");++		} catch (GLib.Error e) {+			warning (e.message);+		}+	}++	public static bool export_ttf_font () {+		Font font = Supplement.get_current_font ();+		File file = font.get_folder ();+		return export_ttf_font_path (file);+	}+	+	/** Stop a running export thread.*/+	public static void stop_export () {+		lock (stop_export_thread) {+			stop_export_thread = true;+		}+	}+	+	public static bool should_stop () {+		bool r;+		lock (stop_export_thread) {+			r = stop_export_thread;+		}+		return r;+	} +	+	public static string get_birdfont_export () {+		File f;+		+		f = File.new_for_path ("birdfont-export.sh");+		if (f.query_exists ()) {+			return "sh birdfont-export.sh";+		}++		f = File.new_for_path ("../birdfont-export.sh");	+		if (f.query_exists ()) {+			return "sh ../birdfont-export.sh";+		}++		f = File.new_for_path ("birdfont-export.exe");	+		if (f.query_exists ()) {+			return (!) f.get_path ();+		}++		f = File.new_for_path (@"$PREFIX/birdfont-export");+		if (f.query_exists ()) {+			return @"$PREFIX/bin/birdfont-export";+		}++		warning ("Can't find birdfont-export.");+		+		return "birdfont-export";+	}+	+	public static bool export_ttf_font_path (File folder, bool async = true) {+		Font current_font = Supplement.get_current_font ();+		File ttf_file;+		File eot_file;+		string temp_file;+		bool done = true;+		string export_command;+		+		if (Supplement.win32) {+			VersionList.set_suppress_signal (true);+			async = false;+		}+		+		try {+			// create a copy of current font and use it in a separate +			// export thread+			temp_file = current_font.save_backup ();+			ttf_file = folder.get_child (current_font.get_name () + ".ttf");+			eot_file = folder.get_child (current_font.get_name () + ".eot");++			if (ttf_file.query_exists ()) {+				ttf_file.delete ();+			}++			if (eot_file.query_exists ()) {+				eot_file.delete ();+			}+			+			assert (!is_null (temp_file));+			assert (!is_null (ttf_file.get_path ()));+			assert (!is_null (eot_file.get_path ()));++			export_thread = new ExportThread (temp_file, (!) ttf_file.get_path (), (!) eot_file.get_path ());++			try {+				if (async) {+					export_command = @"$(get_birdfont_export ()) --ttf -o $((!) folder.get_path ()) $temp_file";+					+					try {+						Process.spawn_command_line_async (export_command);+					} catch (Error e) {+						stderr.printf (@"Failed to execute \"$export_command\" \n");+						critical (@"$(e.message)");+					}+				}+				+				if (!async) {+					export_thread.run ();+				}+			} catch (ThreadError e) {+				warning (e.message);+				done = false;+			}	+			+		} catch (Error e) {+			critical (@"$(e.message)");+			done = false;+		}++		if (Supplement.win32) {+			VersionList.set_suppress_signal (false);+		}+		+		return done;		+	}+	+	public static bool export_svg_font () {+		Font font = Supplement.get_current_font ();+		return export_svg_font_path (font.get_folder ());+	}+		+	public static bool export_svg_font_path (File folder) {+		Font font = Supplement.get_current_font ();+		string file_name = @"$(font.get_name ()).svg";+		File file;+		SvgFontFormatWriter fo;+		+		try {+			file = folder.get_child (file_name);+			+			if (file.query_exists ()) {+				file.delete ();+			}+			+			fo = new SvgFontFormatWriter ();+			fo.open (file);+			fo.write_font_file (font);+			fo.close ();+		} catch (Error e) {+			critical (@"$(e.message)");+			status (e.message);+			return false;+		}+		+		status (_ ("Wrote font files"));+		return true;+	}++	public class ExportThread : GLib.Object {+		private static string ffi;+		private static string ttf;+		private static string eot;++		public ExportThread (string ffi, string ttf, string eot) {+			this.ffi = ffi.dup ();+			this.ttf = ttf.dup ();+			this.eot = eot.dup ();+		}+		+		public void* run () {+			assert (!is_null (ffi));+			assert (!is_null (ttf));+			assert (!is_null (eot));+			+			if (!should_stop ()) { +				write_ttf ();+			}+			+			if (!should_stop ()) { +				write_eof ();+			}++			return null;+		}+		+		void write_ttf () {+			OpenFontFormatWriter fo = new OpenFontFormatWriter ();+			Font f = new Font ();+			File file = (!) File.new_for_path (ttf);++			return_if_fail (!is_null (ffi));			+			return_if_fail (!is_null (ttf));+			return_if_fail (!is_null (file));+			return_if_fail (!is_null (f));+			return_if_fail (!is_null (fo));+			+			try {+				if (!f.load (ffi, false)) {+					warning (@"Can't read $ffi");+				}+				+				fo.open (file);+				fo.write_ttf_font (f);+				fo.close ();+			} catch (Error e) {+				critical (@"$(e.message)");+			}+		}+		+		void write_eof () {+			EotWriter fo;++			return_if_fail (!is_null (this));+			return_if_fail (!is_null (ttf));+			return_if_fail (!is_null (eot));+			+			fo = new EotWriter (ttf, eot);++			return_if_fail (!is_null (fo));++			try {+				fo.write ();+			} catch (Error e) {+				warning ("EOF conversion falied.");+				critical (@"$(e.message)");+			}+		}++	}+	+	private static void status (string s) {+		TooltipArea status = MainWindow.get_tool_tip ();+		+		if (is_null (status)) {+			return;+		}+		+		status.show_text (s);+	}+}++}diff --git a/libbirdfont/Font.vala b/libbirdfont/Font.valanew file mode 100644index 0000000..f21499f--- /dev/null+++ b/libbirdfont/Font.vala@@ -0,0 +1,1439 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Xml;++namespace Supplement {++public class Font : GLib.Object {+	+	GlyphTable glyph_cache = new GlyphTable ();+	+	/** Glyphs that not are tied to a unichar value. */+	GlyphTable unassigned_glyphs = new GlyphTable ();++	List<string> glyph_names = new List<string> ();++	/** Last unassigned index */+	int next_unindexed = 0;+	+	public List <string> background_images = new List <string> ();+	public string background_scale = "1";+		+	/** Top margin */+	public double top_limit;+		+	/** Height of upper case letters. */+	public double top_position;++	/** x-height upper bearing from origo. */+	public double xheight_position;++	/** Base line coordinate from origo. */+	public double base_line;+	+	/** Descender position */+	public double bottom_position;+	+	/** Bottom margon */+	public double bottom_limit;+	+	public string? backup_file = null;+	public string? font_file = null;+	+	bool modified = false;+	+	private string name = "typeface";+	+	bool ttf_export = true;+	bool svg_export = true;++	OpenFontFormatReader otf;+	bool otf_font = false;+	+	public List<string> grid_width = new List<string> ();+	+	public Font () {+		// positions in pixels at first zoom level+		// default x-height should be 60 in 1:1+		top_limit = -66 * Glyph.SCALE;+		top_position = -54 * Glyph.SCALE;+		xheight_position = -38 * Glyph.SCALE;+		base_line = 18 * Glyph.SCALE;+		bottom_position = 38 * Glyph.SCALE;+		bottom_limit = 45 * Glyph.SCALE;+	}++	public void touch () {+		modified = true;+	}++	public bool get_ttf_export () {+		return ttf_export;+	}++	public bool get_svg_export () {+		return svg_export;+	}++	public File get_backgrounds_folder () {+		string fn = @"$(get_name ()) backgrounds";+		File f = Supplement.get_settings_directory ().get_child (fn);+		return f;+	}++	/** Retuns true if the current font has be modified */+	public bool is_modified () {+		return modified;+	}++	/** Full path to this font file. */+	public string get_path () {+		int i = 0;+		+		if (font_file != null) {+			return (!) font_file;+		}+		+		StringBuilder sb = new StringBuilder ();+		sb.append (Environment.get_home_dir ());+		sb.append (@"/$(get_name ()).ffi");+		+		File f = File.new_for_path (sb.str);++		while (f.query_exists ()) {+			sb.erase ();+			sb.append (Environment.get_home_dir ());+			sb.append (@"/$(get_name ())$(++i).ffi");+			f = File.new_for_path (sb.str);+		}+		+		return sb.str;+	}++	public string get_file_name () {+		string p = get_path ();+		int i = p.last_index_of ("/");+		+		if (i == -1) {+			i = p.last_index_of ("\\");+		}+		+		p = p.substring (i + 1);+		+		return p;+	}+		+	public File get_folder () {+		string p = get_path ();+		int i = p.last_index_of ("/");+		+		if (i == -1) {+			i = p.last_index_of ("\\");+		}+		+		p = p.substring (0, i);+		+		return File.new_for_path (p);+	}+	+	public double get_height () {+		double r = base_line - top_position;+		return (r > 0) ? r : -r;+	}+		+	public void set_name (string name) {+		this.name = name;+	}+	+	public string get_name () {+		return name;+	}++	public void print_all () {+		stdout.printf ("Assigned:\n");		+		glyph_cache.for_each((g) => {+			stdout.printf (@"$(g.get_name ())\n");+		});++		stdout.printf ("\n");+		stdout.printf ("Unssigned:\n");		+		unassigned_glyphs.for_each ((g) => {+			stdout.printf (@"$(g.get_name ())\n");+		});++	}++	public bool has_glyph (string n) {+		return get_glyph (n) != null;+	}++	public Glyph get_nonmarking_return () {+		Glyph ret;+		+		if (has_glyph ("nonmarkingreturn")) {+			return (!) get_glyph ("nonmarkingreturn");+		}+				+		ret = new Glyph ("nonmarkingreturn", '\r');+		ret.set_unassigned (false);+		ret.left_limit = 0;+		ret.right_limit = 0;+		ret.remove_empty_paths ();+		+		assert (ret.path_list.length () == 0);+		+		return ret;+	}+		+	public Glyph get_null_character () {+		Glyph n;+		+		if (has_glyph ("null")) {+			return (!) get_glyph ("null");+		}+		+		n = new Glyph ("null", '\0');+		n.set_unassigned (false);+		n.left_limit = 0;+		n.right_limit = 0;+		n.remove_empty_paths ();+		+		assert (n.path_list.length () == 0);+		+		return n;+	}+	+	public Glyph get_space () {+		Glyph n;+		+		if (has_glyph (" ")) {+			return (!) get_glyph (" ");+		}++		if (has_glyph ("space")) {+			return (!) get_glyph ("space");+		}+				+		n = new Glyph ("space", ' ');+		n.set_unassigned (false);+		n.left_limit = 0;+		n.right_limit = 27;+		n.remove_empty_paths ();+		+		assert (n.path_list.length () == 0);+		+		return n;		+	}+	+	public Glyph get_not_def_character () {+		Glyph g;++		Path p;+		Path i;+		+		if (has_glyph (".notdef")) {+			return (!) get_glyph (".notdef");+		}+		+		g = new Glyph (".notdef", 0);+		p = new Path ();+		i = new Path ();+		+		g.set_unassigned (true);+		g.left_limit = -33;+		g.right_limit = 33;+		+		p.add (-20, -top_position - 5);+		p.add (20, -top_position - 5);+		p.add (20, -base_line + 5);+		p.add (-20, -base_line + 5);+		p.close ();+		+		i.add (-15, -top_position - 10);+		i.add (15, -top_position - 10);+		i.add (15, -base_line + 10);+		i.add (-15, -base_line + 10);+		i.reverse ();+		i.close ();++		g.add_path (i);+		g.add_path (p);++		return g;+	}+		+	public void add_glyph (Glyph glyph) {+		GlyphCollection? gc = get_glyph_collection (glyph.get_name ());++		if (gc == null) {+			add_glyph_collection (new GlyphCollection (glyph));+		}+	}++	public void add_glyph_collection (GlyphCollection glyph_collection) {+		GlyphCollection? gc = get_cached_glyph_collection (glyph_collection.get_name ());+		+		if (glyph_collection.get_name () == "") {+			warning ("Refusing to insert glyph with name \"\", null character should be named null.");+			return;+		}+		+		if (gc != null) {+			warning ("glyph has already been added");+			return;+		}+		+		if (glyph_collection.get_current ().is_unassigned ()) {+			unassigned_glyphs.insert (glyph_collection);+		} else {+			glyph_cache.insert (glyph_collection);+		}++		if (!has_name (glyph_collection.get_name ())) {+			glyph_names.append (glyph_collection.get_name ());+			glyph_names.sort (strcmp);+		}+	}+	+	public string get_name_for_character (unichar c) {+		// if some glyph is already mapped to unichar, return it's name+		uint i = 0;+		Glyph? gl;+		Glyph g;+		StringBuilder sb;+		+		while ((gl = get_glyph_indice (i++)) != null) {+			g = (!) gl;+			+			if (g.unichar_code == c) {+				return g.name;+			}+		}+						+		// otherwise return some default name, possibly from unicode database+		if (c == 0) {+			return ".null".dup ();+		}+		+		sb = new StringBuilder ();+		sb.append_unichar (c);+		return sb.str;		+	}+	+	public bool has_name (string name) {+		foreach (string n in glyph_names) {+			if (n == name) {+				return true;+			}+		}+		+		return false;+	}+	+	public void delete_glyph (string glyph) {+		glyph_names.remove_all (glyph);+		glyph_cache.remove (glyph);+	}+	+	/** Obtain all versions and alterntes for this glyph. */+	public GlyphCollection? get_glyph_collection (string glyph) {+		GlyphCollection? gc = get_cached_glyph_collection (glyph);+		Glyph? g;+		+		if (gc == null && otf_font) {+			// load it from otf file if we need to+			g = otf.read_glyph (glyph);+			+			if (g != null) {+				add_glyph_callback ((!) g);+				return get_cached_glyph_collection (glyph);+			}+		}+			+		return gc;+	}++	public GlyphCollection? get_cached_glyph_collection (string glyph) {+		GlyphCollection? gc = null;+		+		gc = glyph_cache.get (glyph);+		+		if (gc == null) {+			gc = unassigned_glyphs.get (glyph);+		}+		+		return gc;+	}+	+	public Glyph? get_glyph (string glyph) {+		GlyphCollection? gc = get_glyph_collection (glyph);+		+		if (gc == null) {+			return null;+		}+		+		return ((!)gc).get_current ();+	}+	+	public Glyph? get_glyph_indice (unichar glyph_indice) {+		GlyphCollection? gc;+		string n;+		+		if (!(0 <= glyph_indice < glyph_names.length ())) {+			return null;+		}+		+		n = glyph_names.nth (glyph_indice).data;+		gc = get_glyph_collection (n);+		+		if (gc != null) {+			return ((!) gc).get_current ();+		}+		+		return null;+	}+	+	public void add_background_image (string file) {+		background_images.append (file);+	}++	public double get_kerning (string a, string b) {+		Glyph? gl = get_glyph (a);+		Glyph g;+		+		if (gl == null) {+			warning (@"glyph \"$a\" does not exist cannot obtain kerning");+			return 0;+		}+		+		g = (!) gl;+		+		return g.get_kerning (b);+	}++	public void set_kerning (string a, string b, double val) {+		Glyph? gl;+		Glyph g;+		+		gl = get_glyph (a);+		+		if (gl == null) {+			warning ("glyph is not parsed yet cannot add kerning");+			return;+		}+		+		g = (!) gl;+		g.add_kerning (b, val);+	}+		+	/** Returns path to backup file. */+	public string save_backup () {+		File dir = Supplement.get_backup_directory ();+		File? temp_file = null;+		int i = 0;++		if (backup_file == null) {+			temp_file = dir.get_child (@"current_font_$i.ffi");+			+			while (((!) temp_file).query_exists ()) {+				i++;+				temp_file = dir.get_child (@"current_font_$i.ffi");+			}+			+			backup_file = ((!) temp_file).get_path ();+		}++		assert (backup_file != null);		+		write_font_file ((!) backup_file);+		+		return (!) backup_file;+	}+	+	public bool save (string path) {+		bool r = write_font_file (path);+		+		if (r) {+			font_file = path;+		}+		+		modified = false;+		add_thumbnail ();+		Preferences.add_recent_files (get_path ());+		+		return r;+	}++	public bool write_font_file (string path) {+		try {+			File file = File.new_for_path (path);++			if (file.query_file_type (0) == FileType.DIRECTORY) {+				stderr.printf (@"Can not save font. $path is a directory.");+				return false;+			}+			+			if (file.query_exists ()) {+				file.delete ();+			}+			+			DataOutputStream os = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION));+			+			os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>""");+			os.put_string ("\n");+				+			os.put_string ("<font>\n");+			+			os.put_string ("\n");+			os.put_string (@"<name>$(get_name ())</name>\n");+			+			os.put_string ("\n");+			os.put_string (@"<ttf-export>$(ttf_export)</ttf-export>\n");++			os.put_string ("\n");+			os.put_string (@"<svg-export>$(svg_export)</svg-export>\n");+			+			os.put_string ("\n");+			os.put_string ("<lines>\n");+			+			os.put_string (@"\t<top_limit>$top_limit</top_limit>\n");+			os.put_string (@"\t<top_position>$top_position</top_position>\n");+			os.put_string (@"\t<x-height>$xheight_position</x-height>\n");+			os.put_string (@"\t<base_line>$base_line</base_line>\n");+			os.put_string (@"\t<bottom_position>$bottom_position</bottom_position>\n");+			os.put_string (@"\t<bottom_limit>$bottom_limit</bottom_limit>\n");+			+			os.put_string ("</lines>\n\n");++			foreach (string gv in grid_width) {+				os.put_string (@"<grid width=\"$(gv)\"/>\n");+			}+			+			if (GridTool.sizes.length () > 0) {+				os.put_string ("\n");+			}+			+			os.put_string (@"<background scale=\"$(background_scale)\" />\n");+			os.put_string ("\n");+			+			if (background_images.length () > 0) {+				os.put_string (@"<images>\n");+				+				foreach (string f in background_images) {+					os.put_string (@"\t<img src=\"$f\"/>\n");+				}+			+				os.put_string (@"</images>\n");+				os.put_string ("\n");+			}+			+			glyph_cache.for_each ((gc) => {+				try {+					bool selected;+					foreach (Glyph g in gc.get_version_list ().glyphs) {+						selected = (g == gc.get_current ());+						write_glyph (g, selected, os);+					}+				} catch (GLib.Error ef) {+					stderr.printf (@"Failed to save $path \n");+					stderr.printf (@"$(ef.message) \n");+				}+			});+		+			glyph_cache.for_each ((gc) => {+				try {+					Glyph glyph = gc.get_current ();+					+					foreach (Kerning k in glyph.kerning) {+						string l, r;+						Glyph? gr = get_glyph (k.glyph_right);+						Glyph glyph_right;+						+						if (gr == null) {+							warning ("kerning glyph that does not exist.");+						}+						+						glyph_right = (!) gr;+						+						l = Font.to_hex_code (glyph.unichar_code);+						r = Font.to_hex_code (glyph_right.unichar_code);+										+						os.put_string (@"<hkern left=\"U+$l\" right=\"U+$r\" kerning=\"$(k.val)\"/>\n");+					}+				} catch (GLib.Error e) {+					warning (e.message);+				}+			});++			glyph_cache.for_each ((gc) => {+				GlyphBackgroundImage bg;+				+				try {+					string data;+					+					foreach (Glyph g in gc.get_version_list ().glyphs) {++						if (g.get_background_image () != null) {+							bg = (!) g.get_background_image ();+							data = bg.get_png_base64 ();+							+							if (!bg.is_valid ()) {+								continue;+							}+							+							os.put_string (@"<background-image sha1=\"");+							os.put_string (bg.get_sha1 ());+							os.put_string ("\" ");+							os.put_string (" data=\"");+							os.put_string (data);+							os.put_string ("");+							os.put_string ("\" />\n");	+						}+					}+				} catch (GLib.Error ef) {+					stderr.printf (@"Failed to save $path \n");+					stderr.printf (@"$(ef.message) \n");+				}+			});+						+			os.put_string ("</font>");+			+		} catch (GLib.Error e) {+			stderr.printf (@"Failed to save $path \n");+			stderr.printf (@"$(e.message) \n");+			return false;+		} catch (GLib.IOError e) {+			stderr.printf (@"Failed to save $path \n");+			stderr.printf (@"$(e.message) \n");+			return false;+		}+		+		return true;+	}++	private void write_glyph (Glyph g, bool selected, DataOutputStream os) throws GLib.Error {+		os.put_string (@"<glyph unicode=\"$(to_hex (g.unichar_code))\" selected=\"$selected\" left=\"$(g.left_limit)\" right=\"$(g.right_limit)\">\n");++		foreach (var p in g.path_list) {+			if (p.points.length () == 0) {+				continue;+			}+			+			os.put_string ("\t<object>");++			foreach (var ep in p.points) {+				os.put_string (@"<point x=\"$(ep.x)\" y=\"$(ep.y)\" ");+				+				if (ep.right_handle.type == PointType.CURVE) {+					os.put_string (@"right_angle=\"$(ep.right_handle.angle)\" ");+					os.put_string (@"right_length=\"$(ep.right_handle.length)\" ");+				}+				+				if (ep.left_handle.type == PointType.CURVE) {+					os.put_string (@"left_angle=\"$(ep.left_handle.angle)\" ");+					os.put_string (@"left_length=\"$(ep.left_handle.length)\" ");						+				}+				+				if (ep.right_handle.type == PointType.CURVE || ep.left_handle.type == PointType.CURVE) {+					os.put_string (@"tie_handles=\"$(ep.tie_handles)\" ");+				}+				+				os.put_string ("/>");+				+			}+			+			os.put_string ("</object>\n");+		}+		+		GlyphBackgroundImage? bg = g.get_background_image ();+		+		if (bg != null) {+			GlyphBackgroundImage background_image = (!) bg;++			double pos_x = background_image.img_x;+			double pos_y = background_image.img_y;+			+			double scale_x = background_image.img_scale_x;+			double scale_y = background_image.img_scale_y;+			+			double rotation = background_image.img_rotation;+			+			if (background_image.is_valid ()) {+				os.put_string (@"\t<background sha1=\"$(background_image.get_sha1 ())\" x=\"$pos_x\" y=\"$pos_y\" scale_x=\"$scale_x\" scale_y=\"$scale_y\" rotation=\"$rotation\"/>\n");+			}+		}+		+		os.put_string ("</glyph>\n\n"); ++	}++	public void set_font_file (string path) {+		font_file = path;+		modified = false;+	}++	public uint length () {+		return glyph_names.length ();+	}++	public bool is_empty () {+		return (glyph_names.length () == 0);+	}++	public bool load (string path, bool recent = true) {+		bool loaded = false;+		+		try {+			otf_font = false;++			while (grid_width.length () > 0) {+				grid_width.remove_link (grid_width.first ());+			}++			while (glyph_names.length () > 0) {+				glyph_names.remove_link (glyph_names.first ());+			}+			+			glyph_cache.remove_all ();+			unassigned_glyphs.remove_all ();+			+			if (path.has_suffix (".ffi")) {+				loaded = parse_file (path);+				font_file = path;+			}+			+			if (Supplement.experimental) {+				if (path.has_suffix (".ttf")) {+					loaded = parse_otf_file (path);+					font_file = path;+				}+			}+			+			if (recent) {+				add_thumbnail ();+				Preferences.add_recent_files (get_path ());+			}+		} catch (GLib.Error e) {+			warning (e.message);+			return false;+		}+		+		return loaded;+	}++	private void add_thumbnail () {+		File f = Supplement.get_thumbnail_directory ().get_child (@"$((!) get_file_name ()).png");+		Glyph? gl = get_glyph ("a");+		Glyph g;+		ImageSurface img;+		ImageSurface img_scale;+		Context cr;+		double scale;+		+		if (gl == null) {+			gl = get_glyph_indice (4);+		}		+		+		if (gl == null) {+			gl = get_not_def_character ();+		}+		+		g = (!) gl;++		img = g.get_thumbnail ();+		scale = 70.0 / img.get_width ();+		+		if (scale > 70.0 / img.get_height ()) {+			scale = 70.0 / img.get_height ();+		}+		+		if (scale > 1) {+			scale = 1;+		}++		img_scale = new ImageSurface (Format.ARGB32, (int) (scale * img.get_width ()), (int) (scale * img.get_height ()));+		cr = new Context (img_scale);+		+		cr.scale (scale, scale);++		cr.save ();+		cr.set_source_surface (img, 0, 0);+		cr.paint ();+		cr.restore ();+		+		img_scale.write_to_png ((!) f.get_path ());+	}++	/** Callback function for loading glyph in a separate thread. */+	public void add_glyph_callback (Glyph g) {+		GlyphCollection? gcl;+		GlyphCollection gc;+		+		gcl = get_cached_glyph_collection (g.get_name ());+		+		if (gcl != null) {+			warning (@"glyph \"$(g.get_name ())\" does already exist");+		}+		+		if (g.is_unassigned ()) {+			gc = new GlyphCollection (g);+			+			if (g.name == "") {+				warning ("refusing to insert glyph without name");+				g.name = @"($(++next_unindexed))";+				return;+			}+			+			add_glyph_collection ((!) gc);+		} else if (gcl == null) {+			gc = new GlyphCollection (g);+			add_glyph_collection ((!) gc);+		} else {+			stderr.printf (@"Glyph collection does already have an entry for $(g.get_name ()) char $((uint64) g.unichar_code).\n");+		}+				+		// take xheight from appropriate lower case letter+		// xheight_position = estimate_xheight ();+	}++	public bool parse_otf_file (string path) throws GLib.Error {+		otf = new OpenFontFormatReader ();+		otf_font = true;+		+		otf.parse_index (path);+		+		foreach (string n in otf.get_all_names ()) {+			glyph_names.append (n);+		}+		+		return true;+	}+	+	public bool parse_file (string path) throws GLib.Error {+		Parser.init ();+		+		Xml.Doc* doc;+		Xml.Node* root;+		Xml.Node* node;++		// set this path as file for this font+		font_file = path;+		+		// empty cache and fill it with new glyphs from disk+		glyph_cache.remove_all ();+		unassigned_glyphs.remove_all ();++		while (background_images.length () > 0) {+			background_images.remove_link (background_images.first ());+		}++		create_background_files (path);++		doc = Parser.parse_file (path);+		root = doc->get_root_element ();+		+		if (root == null) {+			delete doc;+			return false;+		}++		node = root;+		+		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {+		+			if (iter->name == "glyph") {+				parse_glyph (iter);+			}++			if (iter->name == "lines") {+				parse_font_boundries (iter);+			}++			if (iter->name == "grid") {+				parse_grid (iter);+			}++			if (iter->name == "background") {+				parse_background (iter);+			}+			+			if (iter->name == "images") {+				parse_background_selection (iter);+			}++			if (iter->name == "name") {+				name = iter->children->content;+			}++			if (iter->name == "hkern") {+				parse_kerning (iter);+			}++			if (iter->name == "ttf-export") {+				ttf_export = bool.parse (iter->children->content);+			}			++			if (iter->name == "svg-export") {+				svg_export = bool.parse (iter->children->content);+			}+			+		}+    +		delete doc;+		Parser.cleanup ();++		return true;+	}+	+	private void create_background_files (string path) {+		Xml.Doc* doc = Parser.parse_file (path);+		Xml.Node* root;+		Xml.Node* node;+		+		root = doc->get_root_element ();+		+		if (root == null) {+			delete doc;+			return;+		}++		node = root;+		+		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {+			if (iter->name == "name") {+				name = iter->children->content;+			}+			+			if (iter->name == "background-image") {+				parse_background_image (iter);+			}+		}+    +		delete doc;+		Parser.cleanup ();+	}+	+	private void parse_kerning (Xml.Node* node) {+		string attr_name;+		string attr_content;+		string left = "";+		string right = "";+		string kern = "";++		StringBuilder b;+		+		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+			attr_name = prop->name;+			attr_content = prop->children->content;+			+			if (attr_name == "left") {+				b = new StringBuilder ();+				b.append_unichar (to_unichar (attr_content));+				left = @"$(b.str)";+			}++			if (attr_name == "right") {+				b = new StringBuilder ();+				b.append_unichar (to_unichar (attr_content));+				right = @"$(b.str)";+			}+			+			if (attr_name == "kerning") {+				kern = attr_content;+			}+		}+		+		set_kerning (left, right, double.parse (kern));+	}+	+	private void parse_background_image (Xml.Node* node) +		requires (node != null)+	{+		string attr_name;+		string attr_content;+		+		string file = "";+		string data = "";+		+		File img_dir;+		File img_file;+		FileOutputStream file_stream;+		DataOutputStream png_stream;+		+		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+			return_if_fail (!is_null (prop->name));+			return_if_fail (!is_null (prop->children));+			return_if_fail (!is_null (prop->children->content));+			+			attr_name = prop->name;+			attr_content = prop->children->content;+			+			if (attr_name == "sha1") {+				file = attr_content;+			}+			+			if (attr_name == "data") {+				data = attr_content;+			}+		}+		+		img_dir = get_backgrounds_folder ().get_child ("parts");++		if (!img_dir.query_exists ()) {+			DirUtils.create ((!) img_dir.get_path (), 0xFFFFFF);+		}+	+		img_file = img_dir.get_child (@"$(file).png");+		+		if (img_file.query_exists ()) {+			return;+		}+		+		try {+			file_stream = img_file.create (FileCreateFlags.REPLACE_DESTINATION);+			png_stream = new DataOutputStream (file_stream);++			png_stream.write (Base64.decode (data));+			png_stream.close ();	+		} catch (GLib.Error e) {+			warning (e.message);+		}+	}+	+	private void parse_background (Xml.Node* node) +		requires (node != null)+	{+		string attr_name;+		string attr_content;+				+		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+			+			return_if_fail (!is_null (prop->name));+			return_if_fail (!is_null (prop->children));+			return_if_fail (!is_null (prop->children->content));+			+			attr_name = prop->name;+			attr_content = prop->children->content;+			+			if (attr_name == "scale") {+				background_scale = attr_content;+			}+		}+	}+	+	private void parse_background_selection (Xml.Node* node) {+		string attr_name = "";+		string attr_content;+		+		return_if_fail (node != null);+				+		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {+			if (iter->name == "img") {+				for (Xml.Attr* prop = iter->properties; prop != null; prop = prop->next) {+					attr_name = prop->name;+					attr_content = prop->children->content;+					+					if (attr_name == "src") {+						background_images.append (attr_content);+					}+				}+			}+		}+	}+	+	private void parse_grid (Xml.Node* node) {+		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+			string attr_name = prop->name;+			string attr_content = prop->children->content;+			+			if (attr_name == "width") {+				grid_width.append (attr_content);+			}+		}		+	}+	+	private void parse_font_boundries (Xml.Node* node) {+		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {+			if (iter->name == "top_limit") top_limit = parse_double_from_node (iter);+			if (iter->name == "top_position") top_position = parse_double_from_node (iter);+			if (iter->name == "x-height") xheight_position = parse_double_from_node (iter);+			if (iter->name == "base_line") base_line = parse_double_from_node (iter);+			if (iter->name == "bottom_position") bottom_position = parse_double_from_node (iter);+			if (iter->name == "bottom_limit") bottom_limit = parse_double_from_node (iter);+		}			+	}+	+	private double parse_double_from_node (Xml.Node* iter) {+		double d;+		bool r = double.try_parse (iter->children->content, out d);+		+		if (unlikely (!r)) {+			string? s = iter->content;+			if (s == null) warning (@"Content is null for node $(iter->name)\n");+			else warning (@"Failed to parse double for \"$(iter->content)\"\n");+		}+		+		return (r) ? d : 0;+	}+	+	/** Parse one glyph. */+	public void parse_glyph (Xml.Node* node) {+		string name = "";+		int left = 0;+		int right = 0;+		unichar uni = 0;+		int version = 0;+		bool selected = false;+		Glyph g;+		GlyphCollection? gc;+		+		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+			string attr_name = prop->name;+			string attr_content = prop->children->content;+			StringBuilder b;+			+			if (attr_name == "unicode") {+				uni = to_unichar (attr_content);+				b = new StringBuilder ();+				b.append_unichar (uni);+				name = b.str;+			}++			if (attr_name == "left") {+				left = int.parse (attr_content);+			}+			+			if (attr_name == "right") {+				right = int.parse (attr_content);+			}+			+			if (attr_name == "version") {+				version = int.parse (attr_content);+			}+			+			if (attr_name == "selected") {+				selected = bool.parse (attr_content);+			}+		}++		g = new Glyph (name, uni);+		+		g.left_limit = left;+		g.right_limit = right;++		parse_content (g, node);+		+		// todo: use disk thread and idle add this:+		+		gc = get_glyph_collection (g.get_name ());+		+		if (g.get_name () == "") {+			warning ("No name set for glyph.");+		}+				+		if (gc == null) {+			gc = new GlyphCollection (g);+			add_glyph_collection ((!) gc);+		} else {+			((!)gc).insert_glyph (g, selected);+		}+		+	}+	+	/** Parse visual objects and paths */+	private void parse_content (Glyph g, Xml.Node* node) {+		Xml.Node* i;+		return_if_fail (node != null);+		+		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {+			if (iter->name == "object") {+				Path p = new Path ();+				+				for (i = iter->children; i != null; i = i->next) {+					if (i->name == "point") {+						parse_point (p, i);+					}					+				}++				p.close ();+				g.add_path (p);+			}+			+			if (iter->name == "background") {+				parse_background_scale (g, iter);+			}+		}+	}+	+	private void parse_background_scale (Glyph g, Xml.Node* node) {+		GlyphBackgroundImage img;+		GlyphBackgroundImage? new_img = null;+		+		string attr_name = "";+		string attr_content;+		+		File img_file = get_backgrounds_folder ().get_child ("parts");+		+		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+			attr_name = prop->name;+			attr_content = prop->children->content;+			+			if (attr_name == "sha1") {+				img_file = img_file.get_child (attr_content + ".png");++				if (!img_file.query_exists ()) {+					warning (@"Background file has not been created yet. $((!) img_file.get_path ())");+				}+				+				new_img = new GlyphBackgroundImage ((!) img_file.get_path ());+				g.set_background_image ((!) new_img);+			}+		}+		+		if (unlikely (new_img == null)) {+			warning (@"No source for image found for $attr_name in $(g.name)");+			return;+		}+	+		img = (!) new_img;+	+		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+			attr_name = prop->name;+			attr_content = prop->children->content;+							+			if (attr_name == "x") {+				img.img_x = double.parse (attr_content);+			}+			+			if (attr_name == "y") {+				img.img_y = double.parse (attr_content);+			}	++			if (attr_name == "scale_x") {+				img.img_scale_x = double.parse (attr_content);+			}++			if (attr_name == "scale_y") {+				img.img_scale_y = double.parse (attr_content);+			}+						+			if (attr_name == "rotation") {+				img.img_rotation = double.parse (attr_content);+			}+		}+		+		img.set_position(img.img_x, img.img_y);	+	}+	+	private void parse_point (Path p, Xml.Node* iter) {+		double x = 0;+		double y = 0;+		+		PointType type = PointType.LINE;+		+		double angle_right = 0;+		double angle_left = 0;+		+		double length_right = 0;+		double length_left = 0;+		+		PointType type_right = PointType.LINE;+		PointType type_left = PointType.LINE;+		+		bool tie_handles = false;+		+		EditPoint ep;+		+		for (Xml.Attr* prop = iter->properties; prop != null; prop = prop->next) {+			string attr_name = prop->name;+			string attr_content = prop->children->content;+						+			if (attr_name == "x") x = double.parse (attr_content);+			if (attr_name == "y") y = double.parse (attr_content);+			+			if (attr_name == "right_angle") {+				type = PointType.CURVE; // FIXA: delete, maybe+			}	+			+			if (attr_name == "right_angle") {+				type_right = PointType.CURVE;+			}	++			if (attr_name == "left_angle") {+				type_left = PointType.CURVE;+			}+						+			if (attr_name == "right_angle") angle_right = double.parse (attr_content);+			if (attr_name == "right_length") length_right = double.parse (attr_content);+			if (attr_name == "left_angle") angle_left = double.parse (attr_content);+			if (attr_name == "left_length") length_left = double.parse (attr_content);+			+			if (attr_name == "tie_handles") tie_handles = bool.parse (attr_content);+		}+		+		ep = new EditPoint (x, y);+		+		ep.type = type;+		+		ep.right_handle.angle = angle_right;+		ep.right_handle.length = length_right;+		ep.right_handle.type = type_right;+		+		ep.left_handle.angle = angle_left;+		ep.left_handle.length = length_left;+		ep.left_handle.type = type_left;+		+		ep.tie_handles = tie_handles;+		+		p.add_point (ep);+	}+	+	public bool restore_backup () {+		string? b = present_backup_file ();+		+		if (b == null) return false;+		+		return parse_file ((!)b);+	}+	+	private string? present_backup_file () {+		try {+			File dir = Supplement.get_settings_directory ();+			var files = dir.enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, 0);++			// What if we have more than one backup file left?+			FileInfo? file_info;+			while ((file_info = files.next_file ()) != null) {+				FileInfo fi = (!) file_info;+				if (fi.get_name ().index_of ("current_font_") != -1) {+					File f = dir.get_child (fi.get_name ());+					backup_file = f.get_path ();+					return backup_file;+				}+			}+		} catch (GLib.Error e) {+			stderr.printf (@"Failed to load backup\n");+			stderr.printf (@"$(e.message) \n");+		}+		+		return null;+	}+	+	/** Delete temporary rescue files. */+	public void delete_backup () {+		if (backup_file == null) return;++		try {+			File f = File.new_for_path ((!) backup_file);+			f.delete ();		+		} catch (GLib.Error e) {+			stderr.printf (@"Failed to delete backup\n");+			stderr.printf (@"$(e.message) \n");+		}+	}+		+	public static string to_hex (unichar ch) {+		StringBuilder s = new StringBuilder ();+		s.append ("U+");+		s.append (to_hex_code (ch));+		return s.str;+	}++	public static string to_hex_code (unichar ch) {+		StringBuilder s = new StringBuilder ();+		+		uint8 a = (uint8)(ch & 0x00000F);+		uint8 b = (uint8)((ch & 0x0000F0) >> 4 * 1);+		uint8 c = (uint8)((ch & 0x000F00) >> 4 * 2);+		uint8 d = (uint8)((ch & 0x00F000) >> 4 * 3);+		uint8 e = (uint8)((ch & 0x0F0000) >> 4 * 4);+		uint8 f = (uint8)((ch & 0xF00000) >> 4 * 5);+		+		if (e != 0 || f != 0) {+			s.append (oct_to_hex (f));+			s.append (oct_to_hex (e));+		}+		+		if (c != 0 || d != 0) {+			s.append (oct_to_hex (d));+			s.append (oct_to_hex (c));+		}+				+		s.append (oct_to_hex (b));+		s.append (oct_to_hex (a));+		+		return s.str;+	}++	public static unichar to_unichar (string unicode) {+		int index = 2;+		int i = 0;+		unichar c;+		unichar rc = 0;+		bool r;++		if (unlikely (unicode.index_of ("U+") != 0)) {+			warning ("All unicode values must begin with U+");+		}+		+		while (r = unicode.get_next_char (ref index, out c)) {+			rc <<= 4;+			rc += hex_to_oct (c);+			+			return_val_if_fail (++i <= 6, '\0');+		}++		return rc;+	}+	+	private static string oct_to_hex (uint8 o) {+		switch (o) {+			case 10: return "a";+			case 11: return "b";+			case 12: return "c";+			case 13: return "d";+			case 14: return "e";+			case 15: return "f";+		}++		return_val_if_fail (0 <= o <= 9, "-".dup ());+		+		return o.to_string ();+	}++	private static uint8 hex_to_oct (unichar o) {+		StringBuilder s = new StringBuilder ();+		s.append_unichar (o);+	+		switch (o) {+			case 'a': return 10;+			case 'b': return 11;+			case 'c': return 12;+			case 'd': return 13;+			case 'e': return 14;+			case 'f': return 15;+		}+		+		return_val_if_fail ('0' <= o <= '9', 0);+		+		return (uint8) (o - '0');+	}++}++}diff --git a/libbirdfont/FontDisplay.vala b/libbirdfont/FontDisplay.valanew file mode 100644index 0000000..820bbee--- /dev/null+++ b/libbirdfont/FontDisplay.vala@@ -0,0 +1,239 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Birdfont;++namespace Supplement {++/** Javascripts in webkit do use this callback. */+public class PropertyFunction {+	public delegate void PropertyCallback (string val);+	public PropertyCallback call;+}++public abstract class FontDisplay : GLib.Object {+	+	List<string> property = new List<string> ();+	List<PropertyFunction> call = new List<PropertyFunction> ();+	+	/** Queue redraw area */+	public signal void redraw_area (double x, double y, double w, double h);+	+	/** Name of symbol or set of symbols in display selections like the tab panel. */+	public virtual string get_name () {+		return "";+	}+	+	public virtual bool is_html_canvas () {+		return false;+	}++	public static string path_to_uri (string path) {+		string uri = path;+		string wp;+		+		// wine uri hack+		if (Supplement.win32) {+			wp = wine_to_unix_path (uri);+			+			if (find_file (wp, "").query_exists ()) {+				uri = wp;+			}++			if (uri.index_of ("\\") > -1) {+				uri = uri.replace ("\\", "/");+			}+		}++		if (uri.index_of ("/") == 0) {+			uri = @"file://$uri";+		} else {+			uri = @"file:///$uri";+		}+		+		return uri;+	}++	public virtual string get_uri () {+		return "";+	}+	+	public virtual string get_html_file () {+		return "index.html";+	}+	+	public virtual string get_html () {+		return "";+	}+	+	public virtual void draw (Allocation allocation, Context cr) {+	}+	+	public virtual void selected_canvas () {+	}+	+	public virtual void key_press (uint keyval) {+	}+	+	public virtual void key_release (uint keyval) {+	}+	+	public virtual void motion_notify (double x, double y) {+	}+	+	public virtual void button_release (int button, double x, double y) {+	}+	+	public virtual void button_press (uint button, double x, double y) {+	}++	public virtual void double_click (uint button, double ex, double ey) {+	}	++	public virtual void zoom_in () {+	}+	+	public virtual void zoom_out () {+	}+	+	public virtual void zoom_max () {+	}+	+	public virtual void zoom_min () {+	}+	+	public virtual void reset_zoom () {+	}+	+	public virtual void store_current_view () {+		warning ("store_current_view not implemented for this class.");+	}+	+	public virtual void restore_last_view () {+		warning ("restore_last_view not implemented for this class.");+	}++	public virtual void next_view () {+		warning ("next_view not implemented for this class.");+	}+	+	public virtual void scroll_wheel_up (double x, double y) {+	}+	+	public virtual void scroll_wheel_down (double x, double y) {+	}++	public virtual void undo () {+	}++	public static File find_layout_dir () {+		return find_file (null, "layout");+	}++	public void add_html_callback (string prop, PropertyFunction.PropertyCallback cb) {+		PropertyFunction pf = new PropertyFunction ();+		pf.call = cb;+		property.append (prop);+		call.append (pf);+	}+	+	public void process_property (string prop) {+		string k, v;+		int i, j;+		PropertyFunction cb;+		+		if (prop == "" || prop == "done") {+			return;+		}+		+		i = prop.index_of (":");+		+		if (i <= 0) {+			return;+		}+		+		k = prop.substring (0, i);+		v = prop.substring (i + 1);+		+		j = 0;+		+		foreach (string p in property) {+			if (p == k) {+				break;+			}+			j++;+		}+		+		if (j >= property.length ()) {+			warning (@"key  \"$k\" not found in property list, value: $v");+			return;+		}+		+		cb = call.nth (j).data;+		cb.call (v);+	}++	public static File find_file (string? dir, string name) {+		File f;+		string d = (dir == null) ? "" : (!) dir;+		+		f = get_file (Supplement.exec_path + "/", name + "/");+		if (likely (f.query_exists ())) return f;+		+		f = get_file (Supplement.exec_path + "/" + d + "/", name);+		if (likely (f.query_exists ())) return f;+		+		f = get_file ("./" + d + "/", name);+		if (likely (f.query_exists ())) return f;		++		f = get_file ("../" + d + "/", name);+		if (likely (f.query_exists ())) return f;++		f = get_file (".\\" + d + "\\", name);+		if (likely (f.query_exists ())) return f;++		f = get_file ("", name);+		if (likely (f.query_exists ())) return f;++		f = get_file (d + "\\", name);+		if (likely (f.query_exists ())) return f;++		f = get_file ("/usr/local/share/birdfont/" + d + "/", name);+		if (likely (f.query_exists ())) return f;++		f = get_file ("/usr/share/birdfont/" + d + "/", name);+		if (likely (f.query_exists ())) return f;++		f = get_file (@"$PREFIX/share/birdfont/" + d + "/", name);+		if (likely (f.query_exists ())) return f;+				+		warning (@"Did not find file $name in $d");+			+		return f;		+	}++	public static File get_file (string? path, string name) {+		StringBuilder fn = new StringBuilder ();+		string p = (path == null) ? "" : (!) path;+		fn.append (p);+		fn.append ((!) name);+		return File.new_for_path (fn.str);+	}+}++}diff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valanew file mode 100644index 0000000..2440a0b--- /dev/null+++ b/libbirdfont/Glyph.vala@@ -0,0 +1,1719 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Math;++namespace Supplement {++public class Glyph : FontDisplay {++	public static const double SCALE = 1.0;++	// Background image+	GlyphBackgroundImage? background_image = null;+	bool background_image_visible = true;+	+	// Glyph zoom level+	public double view_zoom = 0.01;+	public double view_offset_x = 0;+	public double view_offset_y = 0;+	List<ZoomView> zoom_list = new List<ZoomView> ();+	int zoom_list_index = 0;+	+	// Paths+	public List<Path> path_list;+	public List<Path> active_paths = new List<Path> ();++	// Control points+	public EditPoint? new_point_on_path = null;+	public EditPoint? flipping_point_on_path = null;+	public EditPoint? last_added_edit_point = null;+		+	// The point where edit event begun +	double pointer_begin_x = 0;+	double pointer_begin_y = 0;++	// Current pointer position+	double motion_x = 0;+	double motion_y = 0;+		+	// Zoom area+	double zoom_x1 = 0;+	double zoom_y1 = 0;+	double zoom_x2 = 0;+	double zoom_y2 = 0;+	bool zoom_area_is_visible = false;+	+	bool move_view = false;+	public double move_offset_x = 0;+	public double move_offset_y = 0;++	public Allocation allocation;+	+	public string name;++	public double left_limit;+	public double right_limit;+	+	// x-height, lsb, etc.+	public List<Line> vertical_help_lines = new List<Line> ();+	public List<Line> horizontal_help_lines = new List<Line> ();+	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 unassigned = false;+	public unichar unichar_code = 0;+	+	List<Glyph> undo_list = new List<Glyph> ();+	+	public List<Kerning> kerning = new List<Kerning> ();+	+	string glyph_sequence = "";+	bool open = true;+	+	public Glyph (string name, unichar unichar_code = 0) {+		this.name = name;+		this.unichar_code = unichar_code;+		+		path_list.append (new Path ());+		+		add_help_lines ();++		left_limit = -28 * SCALE;+		right_limit = 28 * SCALE;+	}++	public Glyph.no_lines (string name, unichar unichar_code = 0) {+		this.name = name;+		this.unichar_code = unichar_code;++		path_list.append (new Path ());+	}+	+	public void clear_active_paths () {+		while (active_paths.length () > 0) {+			active_paths.remove_link (active_paths.first ());+		}+	}+	+	public void add_active_path (Path? p) {+		if (p != null) {+			+			foreach (Path pi in active_paths) {+				if (pi == p) {+					return;+				}+			}+			+			active_paths.append ((!) p);+		}+	}+	+	public Path? get_active_path () {+		return_if_fail (!is_null(active_paths));+		+		if (!is_null(active_paths.last ())) {+			return null;+		}+		+		return active_paths.last ().data;+	}+	+	+	public void add_kerning (string right_glyph, double val) {+		Kerning? kl = null;++		foreach (Kerning k in kerning) {+			if (k.glyph_right == right_glyph) {+				kl = k;+			}+		}+		+		if (kl == null) {+			kerning.append (new Kerning (right_glyph, val));+		} else {+			((!)kl).val = val;+		}+	}++	public double get_kerning (string right_glyph) {+		foreach (Kerning k in kerning) {+			if (k.glyph_right == right_glyph) {+				return k.val;+			}+		}+		+		return 0;+	}+		+	public void set_unassigned (bool u) {+		unassigned = u;+	}+	+	public bool is_unassigned () {+		return unassigned;+	}++	public void boundries (out double x1, out double y1, out double x2, out double y2) {+		if (path_list.length () == 0) {+			x1 = 0;+			y1 = 0;+			x2 = 0;+			y2 = 0;+			return;+		}++		x1 = path_list.first ().data.xmin;+		y1 = path_list.first ().data.ymin;+		x2 = path_list.first ().data.xmax;+		y2 = path_list.first ().data.ymax;+				+		foreach (Path p in path_list) {+			p.update_region_boundries ();+						+			if (p.xmin < x1) x1 = p.xmin;+			if (p.xmax > x2) x2 = p.xmax;+			if (p.ymin < y1) y1 = p.ymin;+			if (p.ymax > y2) y2 = p.ymax;+		}+	}+	+	/** @return centrum pixel for x coordinates. */+	public static double xc () {+		double c = MainWindow.get_current_glyph ().allocation.width / 2.0;+		return c;	+	}++	/** @return centrum pixel for y coordinates. */+	public static double yc () {+		double c = MainWindow.get_current_glyph ().allocation.height / 2.0;+		return c;+	}++	/** @return 1/view_zoom */+	public static double ivz () {+		return 1 / MainWindow.get_current_glyph ().view_zoom;+	}++	public void resized () {+		double a, b, c, d;+		+		if (view_zoom > 1) {	+			a = vertical_help_lines.first ().data.get_coordinate ();+			c = horizontal_help_lines.first ().data.get_coordinate ();+			+			b = vertical_help_lines.first ().data.get_coordinate ();+			d = horizontal_help_lines.first ().data.get_coordinate ();+		+			view_offset_x -= a - b;+			view_offset_y -= c - d;+		}+	}++	public void set_background_image (GlyphBackgroundImage? b) {+		GlyphBackgroundImage bg;+		+		if (b == null) {+			background_image = null;+		}+		+		bg = (!) b;+		+		background_image = bg;+		+		Supplement.get_current_font ().touch ();+	}+	+	public GlyphBackgroundImage? get_background_image () {+		return (!) background_image;+	}+		+	public override void scroll_wheel_up (double x, double y) {+		if (KeyBindings.has_ctrl ()) {+			zoom_in_at_point (x, y);+		} else if (KeyBindings.has_alt ()) { +			view_offset_x -= 10 / view_zoom;+		} else {+			view_offset_y -= 10 / view_zoom;+		}+		+		redraw_area (0, 0, allocation.width, allocation.height);+	}+	+	public override void scroll_wheel_down (double x, double y) {+		if (KeyBindings.has_ctrl ()) {+			zoom_out_at_point (x, y);+		} else	if (KeyBindings.has_alt ()) { +			view_offset_x += 10 / view_zoom;+		} else {+			view_offset_y += 10 / view_zoom;+		}+		+		redraw_area (0, 0, allocation.width, allocation.height);+	}+	+	public void add_path (Path p) {+		path_list.append (p);+	}+	+	public override void selected_canvas () {+		add_help_lines ();+		KeyBindings.singleton.set_require_modifier (false);+		glyph_sequence = Preferences.get ("glyph_sequence");+	}+	+	private void remove_lines () {+		while (vertical_help_lines.length () != 0) {+			vertical_help_lines.remove_link (vertical_help_lines.first ());+		}++		while (horizontal_help_lines.length () != 0) {+			horizontal_help_lines.remove_link (horizontal_help_lines.first ());+		}		+	}+	+	public void add_help_lines () {+		remove_lines ();++		assert (!is_null (Supplement.get_current_font ()));++		double bgt = Supplement.get_current_font ().top_limit;+		Line top_margin_line = new Line ("top margin", bgt, false);+		top_margin_line.set_color (0.7, 0, 0, 0.5);+		top_margin_line.position_updated.connect ((pos) => {+			Supplement.get_current_font ().top_limit = pos;+		});+						+		double thp = Supplement.get_current_font ().top_position;+		Line top_line = new Line ("top", thp, false);+		top_line.position_updated.connect ((pos) => {+				Font f = Supplement.get_current_font ();+				f.top_position = pos;+			});+		+		double xhp = Supplement.get_current_font ().xheight_position;+		Line xheight_line = new Line ("x-height", xhp, false);+		xheight_line.set_color (33 / 255.0, 68 / 255.0, 120 / 255.0, 166 / 255.0);+		xheight_line.position_updated.connect ((pos) => {				+				Font f = Supplement.get_current_font ();+				f.xheight_position = pos;+			});++		double xbl = Supplement.get_current_font ().base_line;+		Line base_line = new Line ("baseline", xbl, false);+		base_line.position_updated.connect ((pos) => {+				Font f = Supplement.get_current_font ();+				f.base_line = pos;+			});++		+		double bp = Supplement.get_current_font ().bottom_position;+		Line bottom_line = new Line ("bottom", bp, false);+		bottom_line.set_color (33 / 255.0, 68 / 255.0, 120 / 255.0, 166 / 255.0);+		bottom_line.position_updated.connect ((pos) => {+				Supplement.get_current_font ().bottom_position = pos;+			});++		double bgb = Supplement.get_current_font ().bottom_limit;+		Line bottom_margin_line = new Line ("bottom margin", bgb, false);+		bottom_margin_line.set_color (0.7, 0, 0, 0.5);+		bottom_margin_line.position_updated.connect ((pos) => {+			Supplement.get_current_font ().bottom_limit = pos;+		});+							+		Line left_line = new Line ("left", left_limit, true);+		left_line.position_updated.connect ((pos) => {+				left_limit = pos;+			});+		+		Line right_line = new Line ("right", right_limit, true);+		right_line.position_updated.connect ((pos) => {+				right_limit = pos;+			});+		+		// lists of help lines are sorted and lines are added only if +		// they are important for a particular glyph.+		+		// left to right+		add_line (left_line);+		add_line (right_line);++		bool glyph_has_top = has_top_line ();++		// top to bottom+		add_line (top_margin_line);+		top_margin_line.set_visible (margin_boundries_visible);+		+		add_line (top_line);+		top_line.set_visible (glyph_has_top || xheight_lines_visible);+		+		add_line (xheight_line);+		xheight_line.set_visible (!glyph_has_top || xheight_lines_visible);+		+		add_line (base_line);+		+		add_line (bottom_line);+		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);+	}+	+	bool has_top_line () {+		return !unichar_code.islower () || CharDatabase.has_ascender (unichar_code);+	}+	+	/** Show both x-height and top lines. */+	public bool get_xheight_lines_visible () {+		return xheight_lines_visible;+	}++	/** Show both x-height and top lines. */+	public void set_xheight_lines_visible (bool x) {+		xheight_lines_visible = x;+		add_help_lines ();+	}+		+	public void set_margin_lines_visible (bool m) {+		margin_boundries_visible = m;+		add_help_lines ();+	}+	+	public bool get_margin_lines_visible () {+		return margin_boundries_visible;+	}+	+	public void remove_empty_paths () {+		foreach (var p in path_list) {+			if (p.points.length () < 2) {+				delete_path (p);+			}+		}+	}+	+	public void delete_path (Path p) {+		path_list.remove_all (p);+	}+	+	public string get_svg_data () {+		return Svg.to_svg_glyph (this, 1 / SCALE);+	}+	+	public int get_height () +		requires (vertical_help_lines.length () >= 2)+	{+		unowned List<Line> a = horizontal_help_lines.first ();+		unowned List<Line> b = horizontal_help_lines.last ();+		+		return (int) Math.fabs (a.data.pos / SCALE - b.data.pos / SCALE); +	}+	+	public double get_width () {+		return Math.fabs (right_limit / SCALE - left_limit / SCALE);+	}++	public unichar get_unichar () {+		return unichar_code;+	}++	public bool get_show_help_lines () {+		return show_help_lines;+	}+	+	public void redraw_help_lines () {+		redraw_area (0, 0, allocation.width, allocation.height);+	}+	+	public void set_show_help_lines (bool hl) {+		show_help_lines = hl;+	}+	+	private void add_line (Line line) {+		if (line.is_vertical ()) {+			vertical_help_lines.append (line);+			line.list_item = vertical_help_lines.last ();+		} else {+			horizontal_help_lines.append (line);+			line.list_item = horizontal_help_lines.last ();+		}++		sort_help_lines ();+		+		line.queue_draw_area.connect ((x, y, w, h) => {+			this.redraw_area (x, y, w, h);+		});+	}+	+	public void sort_help_lines () {+		vertical_help_lines.sort ((a, b) => {+			return (int) (a.get_pos () - b.get_pos ());+		});+		+		horizontal_help_lines.sort ((a, b) => {+			return (int) (a.get_pos () - b.get_pos ());+		});+	}+	+	public override string get_name () {+		return name;+	}+			+	private void help_line_event (double x, double y) {+		double mx = x;+		double my = y;+		+		if (GridTool.is_visible ()) {+			GridTool.tie (ref mx, ref my);+		}+		+		foreach (Line line in get_all_help_lines ()) {+			line.event_move_to (mx, my, x, y, allocation);+		}+	}++	public override void key_release (uint keyval) {	+		Tool t;+		t = MainWindow.get_toolbox ().get_current_tool ();+		t.key_release_action (t, keyval);		+	}++	public override void key_press (uint keyval) {	+		Tool t = MainWindow.get_toolbox ().get_current_tool ();+		t.key_press_action (t, keyval);+	}+	+	/** Delete edit point from path. */+	public void delete_edit_point (EditPoint ep) {+		foreach (Path p in path_list) {+			if (p.points.length () > 0) {+				p.delete_edit_point (ep);+			} else {+				delete_path (p);+			}+		}+	}+	+	public override void motion_notify (double x, double y) {+		Tool t;+		t = MainWindow.get_toolbox ().get_current_tool ();+		+		if (move_view && KeyBindings.has_ctrl ()) {+			move_view_offset  (x, y);+			return;+		}++		t.move_action (t, (int) x, (int) y);++		if (Supplement.show_coordinates) {+			motion_x = x * ivz () - xc () + view_offset_x;+			motion_y = yc () - y * ivz () - view_offset_y;+		}+	+		help_line_event (x, y);	+	}+	+	public override void button_release (int button, double ex, double ey) {+		bool line_moving = false;+		+		if (KeyBindings.has_ctrl ()) {+			move_view = false;+			return;+		}+		+		foreach (Line line in get_all_help_lines ()) {+			if (!line.set_move (false)) {+				line_moving = true;+			}+		}++		if (!line_moving) {+			Tool t = MainWindow.get_toolbox ().get_current_tool ();+			t.release_action (t, (int) button, (int) ex, (int) ey);+		}+		+		update_view ();+	}++	private unowned List<Line> get_all_help_lines () {+		while (all_lines.length () > 0) all_lines.delete_link (all_lines.first ());+		+		all_lines.concat (vertical_help_lines.copy ());+		all_lines.concat (horizontal_help_lines.copy ());+		+		if (GridTool.is_visible ()) {+			all_lines.concat (GridTool.get_vertical_lines ().copy ());+			all_lines.concat (GridTool.get_horizontal_lines ().copy ());+		}+		+		return all_lines;+	}+		+	public void update_view () {+		MainWindow.get_glyph_canvas ().redraw ();+	}+	+	public override void double_click (uint button, double ex, double ey) {	+		Tool t = MainWindow.get_toolbox ().get_current_tool ();+		t.double_click_action (t, (int) button, (int) ex, (int) ey);+	}+	+	public override void button_press (uint button, double ex, double ey) {				+		pointer_begin_x = ex;+		pointer_begin_y = ey;+		+		foreach (Line line in get_all_help_lines ()) {+			if (line.button_press ()) {+				return;+			}+		}+				+		if (KeyBindings.has_ctrl ()) {+			move_view = true;+			move_offset_x = view_offset_x;+			move_offset_y = view_offset_y;+		} else {+			Tool t = MainWindow.get_toolbox ().get_current_tool ();+			t.press_action (t, (int) button, (int) ex, (int) ey);+		}+	}++	/** Insert new edit point for current path on the appropriate zoom+	 * level.+	 */+	public EditPoint add_new_edit_point (int x, int y) {+		insert_edit_point (x, y);+		+		return_if_fail (active_paths.length () > 0);+		+		last_added_edit_point = active_paths.last ().data.get_last_point ();+		return (!) last_added_edit_point;+	}++	public EditPoint get_last_edit_point () {+		return_val_if_fail (last_added_edit_point != null, new EditPoint ());+		return (!) last_added_edit_point;+	}++	/** Move view port centrum to this coordinate. */+	public void set_center (double x, double y) {+		x -= allocation.width / 2.0;+		y -= allocation.height / 2.0;+		+		view_offset_x += x / view_zoom;+		view_offset_y += y / view_zoom;		+	}++	public void set_zoom_from_area () {+		double x = Math.fmin (zoom_x1, zoom_x2);+		double y = Math.fmin (zoom_y1, zoom_y2);++		double w = Math.fabs (zoom_x1 - zoom_x2);+		double h = Math.fabs (zoom_y1 - zoom_y2);+		+		double view_zoom_x, view_zoom_y;+		double ivz, off;++		view_offset_x += x / view_zoom;+		view_offset_y += y / view_zoom;+			+		view_zoom_x = allocation.width * view_zoom / w;+		view_zoom_y = allocation.height * view_zoom / h;+				+		// TODO: there is a max zoom level: probably ivz > 0.1+		+		if (view_zoom_x * allocation.width < view_zoom_y * allocation.height) {+			view_zoom = view_zoom_x;+			ivz = 1 / view_zoom;++			off = (view_zoom / view_zoom_y) * allocation.height / view_zoom;	+			off = allocation.height/view_zoom - off;+			off /= 2;+			+			view_offset_y -= off;++		} else {+			view_zoom = view_zoom_y;+			ivz = 1 / view_zoom;++			off = (view_zoom / view_zoom_x) * allocation.width / view_zoom;	+			off = allocation.width / view_zoom - off;+			off /= 2;			++			view_offset_x -= off;+		}+		+		zoom_area_is_visible = false;+		store_current_view ();+	}+	+	public void show_zoom_area (int sx, int sy, int nx, int ny) {+		double x, y, w, h;+				+		set_zoom_area (sx, sy, nx, ny);+		+		zoom_area_is_visible = true;+		+		x = Math.fmin (zoom_x1, zoom_x2) - 50;+		y = Math.fmin (zoom_y1, zoom_y2) - 50;++		w = Math.fabs (zoom_x1 - zoom_x2) + 100;+		h = Math.fabs (zoom_y1 - zoom_y2) + 100;+				+		redraw_area ((int)x, (int)y, (int)w, (int)h);+	}++	public void set_zoom_area (int sx, int sy, int nx, int ny) {+		zoom_x1 = sx;+		zoom_y1 = sy;+		zoom_x2 = nx;+		zoom_y2 = ny;+	}++	public static double path_coordinate_x (double x) {+		Glyph g = MainWindow.get_current_glyph ();+		return_if_fail (g.view_zoom != 0);+		return x * g.ivz () - g.xc () + g.view_offset_x;+	}++	public static int reverse_path_coordinate_x (double x) {+		Glyph g = MainWindow.get_current_glyph ();+		return_if_fail (g.view_zoom != 0);+		return (int) ((x - g.view_offset_x + g.xc ()) * g.view_zoom);+	}++	public static double path_coordinate_y (double y) {+		Glyph g = MainWindow.get_current_glyph ();+		return g.yc () - y * g.ivz () - g.view_offset_y;+	}++	public static int reverse_path_coordinate_y (double y) {+		Glyph g = MainWindow.get_current_glyph ();+		y = ((y + g.view_offset_y - g.yc ()) * g.view_zoom);+		return (int) (-y);+	}++	public bool select_path (double x, double y) {+		Path? p = null;+		bool found = false;+		+		foreach (Path pt in path_list) {+			if (pt.is_over (x, y)) {+				p = pt;+				found = true;+			}+		}+		+		if (!KeyBindings.has_shift ()) {+			clear_active_paths ();+		}+		+		add_active_path (p);+		+		return found;+	}+	+	public bool is_over_selected_path (double x, double y) {+		foreach (Path pt in active_paths) {+			if (pt.is_over (x, y)) {+				return true;+			}+		}+		return false;		+	}+		+	public void queue_redraw_path (Path path) {+		redraw_path (path.xmin, path.ymin, path.xmax, path.ymax);+	}+	+	private void redraw_path (double xmin, double ymin, double xmax, double ymax) {+		int yc = (int)(allocation.height / 2.0);++		double yta = yc - ymin - view_offset_y;+		double ytb = yc - ymax - view_offset_y;++		double xta = -view_offset_x - xmin;+		double xtb = -view_offset_x - xmax;++		redraw_area ((int)xtb - 10, (int)yta - 10, (int)(xtb - xta) + 10, (int) (yta - ytb) + 10);+	}++	public Path get_closeset_path (double x, double y) {+		double d;+		EditPoint ep = new EditPoint ();+		+		Path min_point = new Path ();+		double min_distance = double.MAX;++		double xt = path_coordinate_x (x);+		double yt = path_coordinate_y (y);++		foreach (Path p in path_list) {+			if (p.is_over (xt, yt)) {+				return p;+			}+		}++		foreach (Path p in path_list) {+			if (p.points.length () == 0) continue;+			+			p.get_closest_point_on_path (ep, xt, yt);+			d = Math.pow (ep.x - xt, 2) + Math.pow (ep.y - yt, 2);+			+			if (d < min_distance) {+				min_distance = d;+				min_point = p;+			}++		}+		+		// a path without any editpoints+		if (path_list.length () > 0) {+			return path_list.first ().data;+		}++		if (unlikely (min_distance == double.MAX)) {+			warning (@"No path found in path_list. Length: $(path_list.length ())");+			+			if (path_list.length () > 0) {+				stderr.printf (@"p.points.length () $(path_list.first ().data.points.length ()) \n");+			}+		}+		+		return min_point;+	}+	+	private EditPoint insert_edit_point (double x, double y) {+		unowned List<Path> paths;+		double xt, yt;+		bool added;+		Path np;+		EditPoint inserted;+		+		if (active_paths.length () == 0) {+			np = new Path ();+			clear_active_paths ();+			active_paths.append (np);+			path_list.append (np);+		}+		+		paths = path_list;+		+		xt = path_coordinate_x (x);+		yt = path_coordinate_y (y);+	+		added = false;++		if (new_point_on_path != null) {+			return_if_fail (active_paths.length () > 0);++			Path p = new Path ();+			EditPoint e = (!) new_point_on_path;++			return_if_fail (e.prev != null);+			+			p.add_point (e);+			e.type = PointType.CURVE;+			+			e.right_handle.type = PointType.CURVE;+			e.left_handle.type = PointType.CURVE;+			+			paths.append (p);+			add_active_path (p);+			flipping_point_on_path = new_point_on_path;+			new_point_on_path = null;+			added = true;+			return e;+		}+		+		if (!added) {+			foreach (Path p in paths) {+				if (p.is_open ()) {+					p.add (xt, yt);+					clear_active_paths ();+					add_active_path (p);+					added = true;+					break;+				}+			}+		}++		if (!added) {+			foreach (Path p in paths) {+				if (p.is_over (xt, yt)) {+					p.add (xt, yt);+					paths.append (p);+					added = true;+					redraw_area (0, 0, allocation.width, allocation.height);+					break;+				}+			}+		}+		+		if (!added) {+			if (paths.length () > 0 && paths.last ().data.is_open ()) {+				paths.last().data.add (xt, yt);+			}+			+			foreach (var p in path_list) {+				p.close ();+			}+			+			np = new Path ();+			paths.append (np);+			np.add (xt, yt);+			+			clear_active_paths ();+			add_active_path (np);+		}+	+		assert (active_paths.length () > 0);+		assert (active_paths.last ().data.points.length () > 0);+		+		inserted = active_paths.last ().data.points.data;+		+		return inserted;+	}+	+	public void move_selected_edit_point_delta (EditPoint selected_point, double dx, double dy) {+		double px = selected_point.x + xc () - view_offset_x;+		double py = -1 * (selected_point.y - yc () + view_offset_y);+		+		px *= view_zoom;+		py *= view_zoom;+		+		move_selected_edit_point (selected_point, dx + px, dy + py);+	}+	+	public void move_selected_edit_point (EditPoint selected_point, double x, double y) {		+		double xc, yc, xt, yt;+		double ivz = 1 / view_zoom;+		EditPoint p;+		+		Supplement.get_current_font ().touch ();++		xc = (allocation.width / 2.0);+		yc = (allocation.height / 2.0);+				+		x *= ivz;+		y *= ivz;+	+		xt = x - xc + view_offset_x;+		yt = yc - y - view_offset_y;+		+		// redraw control point+		redraw_area ((int)(x - 4*view_zoom), (int)(y - 4*view_zoom), (int)(x + 3*view_zoom), (int)(y + 3*view_zoom));+		+		// update position of selected point+		selected_point.set_position (xt, yt);+		+		if (view_zoom >= 2) {+			redraw_area (0, 0, allocation.width, allocation.height);+		} else {+			redraw_last_stroke (x, y);+		}+		+		if (flipping_point_on_path != null) {+			p = (!) flipping_point_on_path;+			p.recalculate_handles (x, y);+			redraw_area (0, 0, allocation.width, allocation.height);+		}		+	}+	+	private void redraw_last_stroke (double x, double y) {+		// redraw line, if we have more than one new point on path+		double px = 0;+		double py = 0;+		int tw = 0;+		int th = 0;++		double xc = (allocation.width / 2.0);++		if (active_paths.length () == 0) {+			return;+		}+		+		foreach (Path path in active_paths) {+			EditPoint? pt = path.get_second_last_point ();+			if (pt != null) {+				EditPoint p = (!) pt;+				+				px = p.x + xc;+				py = p.y - xc;+								+				tw = (px > x) ? (int) (px - x) : (int) (x - px);+				th = (py > y) ? (int) (py - y) : (int) (y - py);+				+				if (px > x) px -= tw + 60;+				if (py > y) py -= th + 60;+				+			} else {+				px = x - 60;+				py = y - 60;+				tw = 0;+				th = 0;+			}+		}+		+		redraw_area ((int)px - 20, (int)py - 20, tw + 120, th + 120); 		+	}+	+	public Path? get_last_path () +		ensures (result != null)+	{+		return_if_fail (path_list.length () > 0);+		return path_list.last ().data;+	}+	+	public bool has_active_path () {+		return active_paths.length () > 0;+	}+	+	public bool is_open () {+		return open;+	}+	+	/** Close all editable paths and return false if no path have boon closed. +	 * Paths without area (points and lines) will be deleted.+	 */+	public bool close_path () {+		bool r = false;+		+		foreach (var p in path_list) {+			if (p.is_editable ()) {+				r = true;+				p.close ();+			}+		}+		+		clear_active_paths ();+		new_point_on_path = null;+		flipping_point_on_path = null;+		+		delete_invisible_paths ();+		+		redraw_area (0, 0, allocation.width, allocation.height);+		+		open = false;+		+		return r;+	}++	public void open_path () {	+		foreach (var p in path_list) {+			p.set_editable (true);+		}+		+		open = true;+		redraw_area (0, 0, allocation.width, allocation.height);+	}+	+	public void redraw_path_region (Path p) {+		int x, y, w, h;+			+		p.update_region_boundries ();+		+		x = reverse_path_coordinate_x (p.xmin);+		y = reverse_path_coordinate_x (p.xmin);+		w = reverse_path_coordinate_x (p.xmax) - x;+		h = reverse_path_coordinate_x (p.ymax) - y; // FIXME: redraw path+					+		redraw_area (x, y, w, h);		+	}++	/** Delete all paths without area. */+	private void delete_invisible_paths () {+		foreach (var p in path_list) {+			+			if (p.points.length () < 2) {+				path_list.remove (p);+				continue;+			}+			+			if (p.points.length () == 2) {+				if (p.points.first ().data.type == PointType.LINE && p.points.last ().data.type == PointType.LINE) {+					path_list.remove (p);+					continue;+				}+			}+			+		}+	}++	public Line get_line (string name) {		+		foreach (Line line in vertical_help_lines) {+			if (likely (line.get_label () == name)) {+				return line;+			}+		}+		+		foreach (Line line in horizontal_help_lines) {+			if (likely (line.get_label () == name)) {+				return line;+			}+		}+		+		warning (@"No line with label $name found");+		return new Line ("Err");+	}+	+	public override void zoom_in () {+		set_zoom_area (10, 10, allocation.width - 10, allocation.height - 10);+		set_zoom_from_area ();+		update_view ();+	}+	+	public override void zoom_out () {+		double w = allocation.width;+		int n = (int) (10 * ((w - 10) / allocation.width));+		set_zoom_area (-n, -n, allocation.width + n, allocation.height + n);+		set_zoom_from_area ();+		update_view ();+	}+	+	public override void zoom_max () {+		default_zoom ();+	}+	+	public override void zoom_min () {+		double ax =  1000;+		double ay =  1000;+		double bx = -1000;+		double by = -1000;+		+		int iax, iay, ibx, iby;+		+		reset_zoom ();++		foreach (var p in path_list) {+			p.update_region_boundries ();+			+			if (p.points.length () > 2) {+				if (p.xmin < ax) ax = p.xmin;+				if (p.ymin < ay) ay = p.ymin;			+				if (p.xmax > bx) bx = p.xmax;+				if (p.ymax > by) by = p.ymax;+			}+		}+	+		if (ax == 1000) return; // empty page+		+		iax = (int) ((ax + view_offset_x + allocation.width / 2.0) * view_zoom);+		iay = (int) ((-ay + view_offset_y + allocation.height / 2.0) * view_zoom);+		ibx = (int) ((bx + view_offset_x + allocation.width / 2.0) * view_zoom);+		iby = (int) ((-by + view_offset_y + allocation.height / 2.0) * view_zoom);++		show_zoom_area (iax, iay, ibx, iby); // set this later on button release+		set_zoom_from_area ();+		zoom_out (); // add some margin+		+		redraw_area (0, 0, allocation.width, allocation.height);+		+	}++	public override void store_current_view () {+		if (zoom_list_index + 1 < zoom_list.length ()) {+			unowned List<ZoomView> n = zoom_list.nth (zoom_list_index);+			while (n != zoom_list.last ()) zoom_list.delete_link (zoom_list.last ());+		}+		+		zoom_list.append (new ZoomView (view_offset_x, view_offset_y, view_zoom, allocation));+		zoom_list_index = (int) zoom_list.length () - 1;+		if (zoom_list.length () > 50) zoom_list.delete_link (zoom_list.first ());+	}+	+	public override void restore_last_view () +		requires (zoom_list.length () > 0)+	{		+		if (zoom_list_index - 1 < 0 || zoom_list.length () == 0)+			return;+		+		zoom_list_index--;+			+		ZoomView z = zoom_list.nth (zoom_list_index).data;+			+		view_offset_x = z.x;+		view_offset_y = z.y;+		view_zoom = z.zoom;+		allocation = z.allocation;+	}++	public override void next_view () +		requires (zoom_list.length () > 0)+	{+		if (zoom_list_index + 1 >= zoom_list.length ())+			return;+		+		zoom_list_index++;+		+		ZoomView z = zoom_list.nth (zoom_list_index).data;+			+		view_offset_x = z.x;+		view_offset_y = z.y;+		view_zoom = z.zoom;+		allocation = z.allocation;		+	}+	+	public override void reset_zoom () {+		view_offset_x = 0;+		view_offset_y = 0;+		+		set_zoom (1);+		+		store_current_view ();+	}+	+	/** Get x-height or top line. */+	public Line get_upper_line () +		requires (horizontal_help_lines.length () > 2)+	{+		+		if (has_top_line () || xheight_lines_visible) {+			return get_line ("top");+		}+		+		return get_line ("x-height");+	}++	/** Get base line. */+	public Line get_lower_line () +		requires (horizontal_help_lines.length () > 2)+	{+		return horizontal_help_lines.last ().prev.data;+	}+		+	/** Set default zoom. See default_zoom. */+	public void set_default_zoom () {+		int l, t, b, r;+		+		double bottom = 0;+		double top = 0;+		double left = 0;+		double right = 0;+	+		unowned List<Line>? n = horizontal_help_lines;+		unowned List<Line>? v = vertical_help_lines;+		+		if (unlikely (n == null)) {+				warning ("n == null");+				warning (@"Can not set default zoom for $name, help lines are not available.");+		}+		+		return_if_fail (v != null);+		return_if_fail (vertical_help_lines.length () != 0);+		return_if_fail (horizontal_help_lines.length () != 0);++		reset_zoom ();+		+		bottom = -get_lower_line ().get_pos ();+		top = -get_upper_line ().get_pos ();++		left = vertical_help_lines.last ().data.get_pos ();+		right = vertical_help_lines.first ().data.get_pos ();+		+		l = reverse_path_coordinate_x (left);+		t = reverse_path_coordinate_y (top);+		r = reverse_path_coordinate_x (right); +		b = reverse_path_coordinate_y (bottom);+		+		set_zoom_area (l + 10, t - 10, r - 10, b + 10);+		set_zoom_from_area ();+	}+	+	/** Set default zoom and redraw canvas. */+	public void default_zoom () {+		set_default_zoom ();+		update_view ();+	}+	+	public bool is_empty () {+		foreach (Path p in path_list) {+			if (p.points.length () > 0) {+				return false;+			}+		}+		+		return true;+	}+	+	private void set_zoom (double z)+		requires (z > 0)+	{		+		view_zoom = z;+	}++	public void set_background_visible (bool visibility) {+		background_image_visible = visibility;+	}+	+	public bool get_background_visible () {+		return background_image_visible;+	}+	+	private void plot_outline (Context cr) {+		foreach (unowned Path p in path_list) {+			p.plot (cr, allocation, view_zoom);+		}		+	}+	+	private void draw_coordinate (Context cr) {+		cr.set_source_rgba (0.5, 0.5, 0.5, 1);+		cr.set_font_size (12);+		cr.move_to (0, 10);+		cr.show_text (@"($motion_x, $motion_y)");+		cr.stroke ();		+	}+	+	public void draw_path (Context cr) {+		double left, baseline;++		// plot_outline (cr);+			+		baseline = get_line ("baseline").pos;+		left = get_line ("left").pos;+		+		if (!is_open ()) {+			Svg.draw_svg_path (cr, get_svg_data (), Glyph.xc () + left, Glyph.yc () + baseline, SCALE);+		}+		+		if (is_open ()) {+			foreach (unowned Path p in path_list) {+				p.draw_outline (cr, allocation, view_zoom);+				p.draw_edit_points (cr, allocation, view_zoom);+			}+		}++		if (!is_open ()) {+			foreach (unowned Path p in active_paths) {+				if (!p.is_editable ()) {+					p.fill_path (cr, allocation, view_zoom);+				}+			}+		}++		if (new_point_on_path != null) {+			Path.draw_edit_point_center ((!) new_point_on_path, cr);+		}+	}+	+	private void draw_zoom_area(Context cr) {+		cr.save ();+		cr.set_line_width (2.0);+		cr.set_source_rgba (0, 0, 1, 0.3);+		cr.rectangle (Math.fmin (zoom_x1, zoom_x2), Math.fmin (zoom_y1, zoom_y2), Math.fabs (zoom_x1 - zoom_x2), Math.fabs (zoom_y1 - zoom_y2));+		cr.stroke ();+		cr.restore ();+	}++	private void draw_background_color (Context cr, double opacity) {+		cr.save ();+		cr.rectangle (0, 0, allocation.width, allocation.height);+		cr.set_line_width (0);+		cr.set_source_rgba (1, 1, 1, opacity);+		cr.fill ();+		cr.stroke ();+		cr.restore ();+	}+	+	private void draw_help_lines (Context cr) {+		foreach (Line line in get_all_help_lines ()) {+			cr.save ();+			line.draw (cr, allocation);+			cr.restore ();+		}+	}+	+	public override void draw (Allocation allocation, Context cr) {+		Tool tool;+		+		this.allocation = allocation;++		ImageSurface ps = new ImageSurface (Format.ARGB32, allocation.width, allocation.height);+		Context cmp = new Context (ps);++		cr.save ();+		draw_background_color (cr, 1);+		cr.restore ();++		if (background_image != null && background_image_visible) {+			((!)background_image).draw (cr, allocation, view_offset_x, view_offset_y, view_zoom);+		}+		+		juxtapose (allocation, cr);++		if (Supplement.show_coordinates) {+			draw_coordinate (cmp);+		}++		if (show_help_lines) {+			cmp.save ();+			cmp.scale (view_zoom, view_zoom);+			cmp.translate (-view_offset_x, -view_offset_y);+			draw_help_lines (cmp);+			cmp.restore ();+		}++		if (zoom_area_is_visible) {+			cmp.save ();+			draw_zoom_area (cmp);+			cmp.restore ();+		}++		if (!is_empty ()) {+			cmp.save ();+			cmp.scale (view_zoom, view_zoom);+			cmp.translate (-view_offset_x, -view_offset_y);+			draw_path (cmp);+			cmp.restore ();+		}++		cmp.save (); +		tool = MainWindow.get_toolbox ().get_current_tool ();+		tool.draw_action (tool, cmp, this);+		cmp.restore ();+				+		cr.save ();+		cr.set_source_surface (ps, 0, 0);+		cr.paint ();+		cr.restore ();+	}	++	private void zoom_in_at_point (double x, double y) {+		int n = -10;+		zoom_at_point (x, y, n);+	}+		+	private void zoom_out_at_point (double x, double y) {+		int n = (int) (10.0 * ((allocation.width - 10.0) / allocation.width));+		zoom_at_point (x, y, n);+	}+	+	/** Zoom in @param n pixels. */+	private void zoom_at_point (double x, double y, int n) {+		double w = allocation.width;+		double h = allocation.height;+		+		double rx = Math.fabs (w / 2 - x) / (w / 2);+		double ry = Math.fabs (h / 2 - y) / (h / 2);+		+		int xd = (x < w / 2) ? (int) (n * rx) : (int) (-n * rx);+		int yd = (y < h / 2) ? (int) (n * ry) : (int) (-n * ry);+			+		show_zoom_area (-n + xd, -n + yd, allocation.width + n + xd, allocation.height + n + yd);+		set_zoom_from_area ();+	}+	+	private void move_view_offset (double x, double y) {+		view_offset_x = move_offset_x + (pointer_begin_x - x) * (1/view_zoom);+		view_offset_y = move_offset_y + (pointer_begin_y - y) * (1/view_zoom);+		redraw_area (0, 0, allocation.width, allocation.height);+	}++	public void store_undo_state () {+		Glyph g = copy ();+		undo_list.append (g);		+	}++	public Glyph copy () {+		Glyph g = new Glyph.no_lines (name, unichar_code);+		+		g.left_limit = left_limit;+		g.right_limit = right_limit;+		+		g.remove_lines ();+		+		foreach (Line line in get_all_help_lines ()) {+			g.add_line (line.copy ());+		}++		foreach (Path p in path_list) {+			g.add_path (p.copy ());+		}+		+		foreach (Path p in active_paths) {+			g.active_paths.append (p);+		}++		if (background_image != null) {+			g.background_image = ((!) background_image).copy ();+		}+		+		return g;+	}++	public void reload () {+		Glyph g;+		Font f = Supplement.get_current_font ();+		+		if (f.has_glyph (name)) {+			set_glyph_data ((!) f.get_glyph (name));+		}+	}++	public override void undo () {+		Glyph g;+		+		if (undo_list.length () == 0) {+			return;+		}+		+		g = undo_list.last ().data;	+		set_glyph_data (g);+		+		undo_list.remove_link (undo_list.last ());+	}+	+	void set_glyph_data (Glyph g) {+		while (path_list.length () > 0) {+			path_list.remove_link (path_list.last ());+		}+		+		foreach (Path p in g.path_list) {+			add_path (p);+		}++		remove_lines ();+		foreach (Line line in g.get_all_help_lines ()) {+			add_line (line.copy ());+		}++		add_help_lines ();+		+		if (g.background_image != null) {+			background_image = ((!) g.background_image).copy ();+		}+		+		clear_active_paths ();+		foreach (Path p in g.active_paths) {+			add_active_path (p);+		}+		+		open_path ();+		close_path ();+		+		redraw_area (0, 0, allocation.width, allocation.height);		+	}+	+	public ImageSurface get_thumbnail () {+		ImageSurface img;+		Context cr;+		double gx, gy;+		double x1, x2, y1, y2;+		Font font = Supplement.get_current_font ();++		remove_empty_paths ();+		boundries (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);+		} else {+			img = new ImageSurface (Format.ARGB32, (int) (x2 - x1), (int) (y2 - y1));+		}+		+		gx = left_limit - x1;+		gy = (y2 - y1) + font.base_line + y1;+		+		cr = new Context (img);+		+		Svg.draw_svg_path (cr, get_svg_data (), gx, gy, 1.0);	+		+		return img;+	}++	/** Split curve in two parts and add a new point in between. */+	public void insert_new_point_on_path (double x, double y) {+		double min, distance;+		Path? p = null;+		EditPoint? np = null;+		EditPoint lep;+		+		double xt;+		double yt;++		xt = x * ivz () - xc () + view_offset_x;+		yt = yc () - y * ivz () - view_offset_y;+		+		min = double.MAX;+		+		delete_invisible_paths ();+		+		foreach (Path pp in path_list) {+			lep = new EditPoint ();+			pp.get_closest_point_on_path (lep, xt, yt);+			distance = Math.sqrt (Math.pow (Math.fabs (xt - lep.x), 2) + Math.pow (Math.fabs (yt - lep.y), 2));+			+			if (distance < min) {+				min = distance;+				p = pp;+				np = lep;+			}+		}++		if (p == null) {+			return;+		}++		lep = new EditPoint ();+		((!)p).get_closest_point_on_path (lep, xt, yt);+		((!)p).insert_new_point_on_path (lep);+	}+	+	/** Merge selected paths. */+	public void merge_all () {+		store_undo_state ();+		delete_invisible_paths ();+		+		if (active_paths.length () < 2) {+			return;+		}+		+		foreach (Path p0 in active_paths) {+			merge_path (p0);+		}+		+		//open_path ();+		close_path ();++		foreach (Path p in path_list) {+			p.update_region_boundries ();+		}		+		+		redraw_area (0, 0, allocation.width, allocation.height);+	}+	+	public bool merge_path (Path p0) {+		Path mp;+		PathList path_list;+		+		foreach (Path p1 in active_paths) {+			if (p0 == p1) {+				p0 = p1;+				continue;+			}+			+			path_list = Path.merge (p1, p0);++			if (path_list.paths.length () == 0) {+				p0 = p1;+				continue;+			}+			+			mp = path_list.paths.first ().data;++			delete_path (p0);+			delete_path (p1);+			+			// add new path + it's counter paths+			foreach (Path p in path_list.paths) {+				p.close ();			+				add_path (p.copy ());+			}+			+			add_active_path (mp);+			+			active_paths.remove_all (p0);+			active_paths.remove_all (p1);++			p0 = mp;+			+			mp.create_list ();+			+			return true;+		}+		+		return false;+	}++	public void juxtapose (Allocation allocation, Context cr) {+		string glyph_sequence = Preferences.get ("glyph_sequence");+		unichar c;+		Font font = Supplement.get_current_font ();+		Glyph glyph = MainWindow.get_current_glyph ();+		Glyph juxtaposed;+		StringBuilder current = new StringBuilder ();+		int pos;+		string name;+		double x, kern;+		double left, baseline;+		string last_name;+				+		x = 0;+		+		current.append_unichar (glyph.unichar_code);+		pos = glyph_sequence.index_of (current.str);+		+		baseline = glyph.get_line ("baseline").pos;+		left = glyph.get_line ("left").pos;++		x = glyph.get_width ();+		last_name = glyph.name;+		for (int i = pos + 1; i < glyph_sequence.char_count (); i++) {+			c = glyph_sequence.get_char (i);+			name = font.get_name_for_character (c);			+			juxtaposed = (font.has_glyph (name)) ? (!) font.get_glyph (name) : font.get_space ();+			+			if (font.has_glyph (last_name) && font.has_glyph (name)) {+				kern = font.get_kerning (last_name, name);+			} else {+				kern = 0;+			}++			if (!juxtaposed.is_empty ()) {+				cr.save ();+				cr.scale (glyph.view_zoom, glyph.view_zoom);+				cr.translate (-glyph.view_offset_x, -glyph.view_offset_y);+				Svg.draw_svg_path (cr, juxtaposed.get_svg_data (), Glyph.xc () + left + x + kern, Glyph.yc () + baseline, Glyph.SCALE);+				cr.restore ();+			}+			+			x += juxtaposed.get_width () + font.get_kerning (glyph.name, name) + kern;+			last_name = name;+		}+		+		x = 0;+		last_name = glyph.name;+		for (int i = pos - 1; i >= 0; i--) {+			c = glyph_sequence.get_char (i);+			name = font.get_name_for_character (c);			+			juxtaposed = (font.has_glyph (name)) ? (!) font.get_glyph (name) : font.get_space ();+			+			if (font.has_glyph (last_name) && font.has_glyph (name)) {+				kern = font.get_kerning (name, last_name);+			} else {+				kern = 0;+			}+			+			x -= juxtaposed.get_width ();+			x -= kern;+			+			if (!juxtaposed.is_empty ()) {+				cr.save ();+				cr.scale (glyph.view_zoom, glyph.view_zoom);+				cr.translate (-glyph.view_offset_x, -glyph.view_offset_y);++				Svg.draw_svg_path (cr, juxtaposed.get_svg_data (), Glyph.xc () + left + x, Glyph.yc () + baseline, Glyph.SCALE);+				cr.restore ();+			}+			+			last_name = name;+		}	+	}+}++}diff --git a/libbirdfont/GlyphBackgroundImage.vala b/libbirdfont/GlyphBackgroundImage.valanew file mode 100644index 0000000..ca141e9--- /dev/null+++ b/libbirdfont/GlyphBackgroundImage.vala@@ -0,0 +1,793 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Math;+using Gdk;++namespace Supplement {+	+public class GlyphBackgroundImage {+		+	public double img_x = 0;+	public double img_y = 0;+	+	public double img_scale_x = 1;+	public double img_scale_y = 1;+	public double img_rotation = 0;+	private int size = -1;++	public int active_handle = -1;+	public int selected_handle = -1;+	+	private ImageSurface? background_image = null;+	private ImageSurface? original_image = null;+	+	private string path;++	private double contrast = 1.0;+	private bool desaturate = false;+	private double threshold = 1.0;+	+	private bool background_image_is_processing = false;++	public signal void updated ();++	public double img_offset_x {+		get { return img_x + Glyph.xc (); }+		set { img_x = value - Glyph.xc (); }+	}++	public double img_offset_y {+		get { return Glyph.yc () - img_y; }+		set { img_y = Glyph.yc () - value; }+	}+	+	public int size_margin {+		get {+			if (unlikely (size == -1)) {+				size = (int) (Math.sqrt (Math.pow (get_img ().get_height (), 2) + Math.pow (get_img ().get_width (), 2)) + 0.5);+			}+			+			return size;+		}+	}+		+	public GlyphBackgroundImage (string fn) {+		path = fn;+	}++	public GlyphBackgroundImage copy () {+		GlyphBackgroundImage bg = new GlyphBackgroundImage (path);+			+		bg.img_x = img_x;+		bg.img_y = img_y;++		bg.img_scale_x = img_scale_x;+		bg.img_scale_y = img_scale_y;+		bg.img_rotation = img_rotation;++		bg.contrast = contrast;+		bg.desaturate = desaturate;+		bg.threshold = threshold;++		return bg;+		+	}++	public double get_margin_width () {+		return ((size_margin - get_img ().get_width ()) / 2.0);+	}++	public double get_margin_height () {+		return ((size_margin - get_img ().get_height ()) / 2.0);+	}++	public void set_img_offset (double x, double y) {+		img_offset_x = x;+		img_offset_y = y;+	}++	public void set_position (double coordinate_x, double coordinate_y) {+		img_x = coordinate_x;+		img_y = coordinate_y;+	}+			+	public ImageSurface get_img () {+		if (path.index_of (".png") == -1) {+			create_png ();+		}+		+		if (background_image == null) {+			background_image = new ImageSurface.from_png (path);+			original_image = new ImageSurface.from_png (path);+		}+		+		return (!) background_image;+	}++	public bool is_valid () {+		FileInfo file_info;+		File file = File.new_for_path (path);+	+		if (!file.query_exists ()) {+			return false;+		}+		+		try {+			file_info = file.query_info ("*", FileQueryInfoFlags.NONE);+			+			if (file_info.get_size () == 0) {+				return false;+			}+		} catch (GLib.Error e) {+			warning (e.message);+			return false;+		}+				+		return true;+	}++	public string get_png_base64 () {+		try {+			File file = File.new_for_path (path);+			FileInfo file_info = file.query_info ("*", FileQueryInfoFlags.NONE);+			uint8[] buffer = new uint8[file_info.get_size ()];+			FileInputStream file_stream;+			DataInputStream png_stream;+			+			if (!file.query_exists ()) {+				warning (@"Failed to save image $path, file does not exist.");+				return "";+			}+			+			if (is_null (buffer)) {+				warning (@"Colud not allocate a buffer of $(file_info.get_size ()) bytes to store $path.");+				return "";+			}+			+			file_stream = file.read ();+			+			png_stream = new DataInputStream (file_stream);+			png_stream.read (buffer);+			+			return Base64.encode (buffer);+		} catch (GLib.Error e) {+			warning (e.message);+		}+		+		return "";+	}++	public string get_sha1 () {+		try {+			File file = File.new_for_path (path);+			FileInfo file_info;+			uint8[] buffer;+			FileInputStream file_stream;+			DataInputStream png_stream;+			+			if (!file.query_exists ()) {+				warning (@"Can't save $path file does not exist.");+				return "";+			}+			+			file_info = file.query_info ("*", FileQueryInfoFlags.NONE);+			+			if (file_info.get_size () == 0) {+				warning (@"length of image $path is zero");+				return "";+			}+			+			buffer = new uint8[file_info.get_size ()];+			file_stream = file.read ();+			png_stream = new DataInputStream (file_stream);++			png_stream.read (buffer);+			+			return Checksum.compute_for_data (ChecksumType.SHA1, buffer);+		} catch (GLib.Error e) {+			warning (e.message);+		}+		+		return "";+	}+	+	private void create_png () {+		string fn = @"$path.png";+		+		Font font = Supplement.get_current_font ();+		File folder = font.get_backgrounds_folder ();+		File original = File.new_for_path (fn);+		File png_image = folder.get_child (@"full_$((!)original.get_basename ())");++		Pixbuf pixbuf;+		+		if (png_image.query_exists ()) {+			path = (!) png_image.get_path ();+			return;+		}+		+		try {+			pixbuf = new Pixbuf.from_file (path);+			pixbuf.save ((!) png_image.get_path (), "png");+			path = (!) png_image.get_path ();+		} catch (GLib.Error e) {+			warning (e.message);+		}+	}+	+	public void set_img_rotation_from_coordinate (double x, double y) {+		double bcx, bcy;+		double a, b, c, length;++		img_center (out bcx, out bcy);++		a = bcx - x;+		b = bcy - y;+		c = a * a + b * b;+		+		if (c == 0) {+			return;+		}+		+		length = sqrt (fabs (c));	+		+		if (c < 0) {+			length = -length;+		}+	+		img_rotation =  (y < bcy) ? acos (a / length) + PI : -acos (a / length) + PI;+	}+	+	public void set_img_scale (double xs, double ys) {+		img_scale_x = xs;+		img_scale_y = ys;+	}++	public void reset_scale (Glyph g) {+		double w, h;++		w = g.get_width ();+		h = g.get_height ();++		img_scale_x = 1;			+		img_scale_y = 1;+		+		img_offset_x = g.get_line ("left").pos;+		img_offset_y = g.get_line ("top").pos;		+	}++	public void draw (Context cr, Allocation allocation, double view_offset_x, double view_offset_y, double view_zoom) {+		double x, y;+		double iw, ih;+		int h, w;+		double scale_x, scale_y;+		double oy, ox;+				+		Surface s;+		Context c;++		Surface sg;+		Context cg;++		Surface st;+		Context ct;++		double wc, hc;++		if (unlikely (get_img ().status () != Cairo.Status.SUCCESS)) {+			stderr.printf (@"Background image is invalid. (\"$path\")\n");+			MainWindow.get_current_glyph ().set_background_visible (false);+			return;+		}+		+		// add margin+		sg = new Surface.similar (get_img (), get_img ().get_content (), size_margin, size_margin);+		cg = new Context (sg);+		+		wc = get_margin_width ();+		hc = get_margin_height ();+		+		cg.set_source_rgba (1, 1, 1, 1);+		cg.rectangle (0, 0, size_margin, size_margin);+		cg.fill ();+		+		cg.set_source_surface (get_img (), wc, hc);+		cg.paint ();++		x = Glyph.reverse_path_coordinate_x (img_offset_x);+		y = Glyph.reverse_path_coordinate_y (img_offset_y);+		+		ih = (int) get_img ().get_height ();+		iw = (int) get_img ().get_width ();+		+		w = (int) iw;+		h = (int) ih;++		oy = size_margin;+		ox = size_margin;++		// rotate image+		s = new Surface.similar (sg, sg.get_content (), (int) (ox), (int) (oy));+		c = new Context (s);+	+		c.save ();++		c.set_source_rgba (1, 1, 1, 1);+		c.rectangle (0, 0, size_margin, size_margin);+		c.fill ();+			+   		c.translate (size_margin * 0.5, size_margin * 0.5);+		c.rotate (img_rotation);+		c.translate (-size_margin * 0.5, -size_margin * 0.5);++		c.set_source_surface (cg.get_target (), 0, 0);+		c.paint ();	+		c.restore ();++		// mask+		st = new Surface.similar (s, s.get_content (), allocation.width, allocation.height);+		ct = new Context (st);+		ct.save ();++		ct.set_source_rgba (1, 1, 1, 1);+		ct.rectangle (0, 0, allocation.width, allocation.height);+		ct.fill ();++		// scale both canvas and image at the same time; rather tricky.+		scale_x = view_zoom * img_scale_x;+		scale_y = view_zoom * img_scale_y;+		+		ct.scale (scale_x, scale_y);+		ct.translate (-view_offset_x / img_scale_x, -view_offset_y / img_scale_y);+		+		ct.set_source_surface (s, img_offset_x / img_scale_x, img_offset_y / img_scale_y);++		ct.paint ();+		ct.restore ();+		+		// append it+		cr.save ();+		cr.set_source_surface (st, 0, 0);+		cr.paint ();+		cr.restore ();+	}+	+	public void handler_release (double nx, double ny) {		+		selected_handle = 0;+		handler_move (nx, ny);+	}+			+	public void handler_press (double nx, double ny) {		+		if (is_over_rotate (nx, ny)) {+			selected_handle = 2;+		} else if (is_over_resize (nx, ny)) {+			selected_handle = 1;+		} else {+			selected_handle = 0;+		}	+	}++	void img_center (out double x, out double y) {+		Glyph g = MainWindow.get_current_glyph ();+		+		x = img_offset_x - g.view_offset_x + (size_margin / 2) * img_scale_x;+		y = img_offset_y - g.view_offset_y + (size_margin / 2) * img_scale_y;++		y *= g.view_zoom;+		x *= g.view_zoom;+	}++	bool is_over_rotate (double nx, double ny) {+		double x, y, d;++		img_center (out x, out y);++		x += cos (img_rotation) * 75;+		y += sin (img_rotation) * 75;++		d = sqrt (pow (x - nx, 2) + pow (y - ny, 2));+		+		return d < 10;+	}++	bool is_over_resize (double nx, double ny) {+		Glyph g = MainWindow.get_current_glyph ();+		double x, y, size;+		bool inx, iny;++		size = 12 * g.view_zoom;++		x = img_offset_x - g.view_offset_x;+		y = img_offset_y - g.view_offset_y + (size_margin) * img_scale_y;+				+		y *= g.view_zoom;+		x *= g.view_zoom;+				+		inx = x - size <= nx <= x + size;+		iny = y - size <= ny <= y + size;+		+		return inx && iny;+	}++	public void handler_move (double nx, double ny) {+		int prev_handle = active_handle;++		if (is_over_rotate (nx, ny)) {+			active_handle = 2;+		} else if (is_over_resize (nx, ny)) {+			active_handle = 1;+		} else {+			active_handle = 0;+		}+		+		if (prev_handle != active_handle) {+			MainWindow.get_glyph_canvas ().redraw ();+		}+	}+	+	public void draw_handle (Context cr, Glyph g) {+		draw_resize_handle (cr, g);+		draw_rotate_handle (cr, g);+	}++	public void draw_resize_handle (Context cr, Glyph g) {+		double x, y;+		cr.save ();+		+		cr.set_source_rgba (1, 0, 0.3, 1);++		x = img_offset_x - g.view_offset_x;+		y = img_offset_y - g.view_offset_y + (size_margin) * img_scale_y;+				+		y *= g.view_zoom;+		x *= g.view_zoom;+				+		draw_handle_triangle (x, y, cr, g, 6);+		+		cr.restore ();+	}+		+	public void draw_rotate_handle (Context cr, Glyph g) {+		double x, y, hx, hy, x2, y2;+		+		double ivz = 1.0 / (g.view_zoom);+		+		cr.save ();+		+		cr.scale (g.view_zoom, g.view_zoom);+		+		if (selected_handle == 2) cr.set_source_rgba (1, 0, 0.3, 1);+		else if (active_handle == 2) cr.set_source_rgba (0, 0, 0.3, 1);+		else cr.set_source_rgba (0.7, 0.7, 0.8, 1);++		x = img_offset_x - g.view_offset_x + (size_margin / 2) * img_scale_x;+		y = img_offset_y - g.view_offset_y + (size_margin / 2) * img_scale_y;+				+		cr.rectangle (x, y, 5 * ivz, 5 * ivz);+		cr.fill ();++		hx = cos (img_rotation) * 75 * ivz;+		hy = sin (img_rotation) * 75 * ivz;+		+		x2 = x + hx;+		y2 = y + hy;++		cr.rectangle (x2, y2, 5 * ivz, 5 * ivz);+		cr.fill ();++		cr.set_line_width (ivz);+		cr.move_to (x + 2.5* ivz, y + 2.5* ivz);+		cr.line_to (x2 + 2.5* ivz, y2 + 2.5* ivz);+		cr.stroke ();+										+		cr.restore ();+	}++	void draw_handle_triangle (double x, double y, Context cr, Glyph g, int direction, double s = 1) +		requires (0 < direction < 8)+	{+		double ivz = 1.0 / (g.view_zoom);+		double size;+		+		cr.save ();+		cr.set_line_width (ivz);+		+		if (selected_handle == 1) cr.set_source_rgba (1, 0, 0.3, 1);+		else if (active_handle == 1) cr.set_source_rgba (0, 0, 0.3, 1);+		else cr.set_source_rgba (0.7, 0.7, 0.8, 1);	++		size = (8) * s;+		+		cr.scale (1, 1);+		cr.new_path ();+		+		// up + left+		if (direction == 1) {+			cr.move_to (x - size, y - size);+			cr.line_to (x + size, y - size);+			cr.line_to (x - size, y + size);	+		}+		+		if (direction == 6) {+			cr.move_to (x + size, y + size);	+			cr.line_to (x - size, y + size);+			cr.line_to (x - size, y - size);	+		}+		+		cr.close_path();+		cr.fill ();++		cr.restore ();+		+	}++	private static uchar ftu (double c) {+		if (c < 0) return 0;+		if (c > 255) return 255;+		return (uchar) c;+	}++	private static ImageSurface? process_background_colors (ImageSurface img, double contrast, bool desaturate, double threshold) {+		int i;+		unowned uchar[]? pix_buff_t;+		unowned uchar[] pix_buff;+		int len;+		uchar[] contrast_img;+		ImageSurface contrast_surface;+		double c, thres;+		uchar nc;++		int w = img.get_width();+		int h = img.get_height();++		if (unlikely (img.status () != Cairo.Status.SUCCESS)) {+			warning ("Err");+			return null;+		}+		+		len = 4 * w * h;+		+		pix_buff_t = img.get_data ();+		return_if_fail (pix_buff_t != null);+		pix_buff = pix_buff_t;+		+		contrast_img = new uchar[len];++		pix_buff_t = contrast_img;+		return_if_fail (pix_buff_t != null);+		+		contrast += 70;+		contrast /= 71;+		+		if (desaturate) {+			i = 0;+			+			while (i < len) {+				c = (pix_buff[i] + pix_buff[i+1] + pix_buff[i+2]) / 3.0;+				nc = ftu (Math.pow (c, contrast));++				contrast_img[i]   = nc;+				contrast_img[i+1] = nc; +				contrast_img[i+2] = nc;+				contrast_img[i+3] = 255;+				+				i += 4;+			}+		} else {+			i = 0;+			while (i < len) {+				contrast_img[i] = ftu (Math.pow (pix_buff[i], contrast) - Math.pow (100, contrast) + 100);+				contrast_img[i+1] = ftu (Math.pow (pix_buff[i+1], contrast) - Math.pow (100, contrast) + 100);+				contrast_img[i+2] = ftu (Math.pow (pix_buff[i+2], contrast) - Math.pow (100, contrast) + 100);+				contrast_img[i+3] = 255;+				+				i += 4;+			}+		}++		if (true) {+			thres = (threshold - 0.5) * 255;+			+			i = 0;+			while (i < len) {+				c = (pix_buff[i] + pix_buff[i+1] + pix_buff[i+2]) / 3.0;+				nc = (c > thres) ? 255 : 0;++				contrast_img[i]   = nc;+				contrast_img[i+1] = nc; +				contrast_img[i+2] = nc;+				contrast_img[i+3] = 255;+				+				i += 4;+			}+		}++		contrast_surface = new ImageSurface.for_data (contrast_img, img.get_format(), w, h, img.get_stride());++		if (unlikely (contrast_surface.status () != Cairo.Status.SUCCESS)) {+			warning ("Err");+			return null;+		}+		+		contrast_surface.mark_dirty	();+			+		return contrast_surface;	+	}++	async ImageSurface? do_process_background_colors () {+		SourceFunc callback = do_process_background_colors.callback;+		ImageSurface? output = null;+		double contrast = get_contrast ();+		bool desaturate = get_desaturate_background ();+		double threshold = this.threshold;++		if (original_image == null) {+			return null;+		}+		+		// Fixa: Add multi threading+		//ThreadFunc<void*> run = () => {+			output = process_background_colors ((!)original_image, contrast, desaturate, threshold);+			Idle.add ((owned) callback);+			//return null;+		//};+		+		try {+			//Thread.create<void*> (run, false);+			yield;+		} catch (ThreadError e) {+			stderr.printf ("Thread error: %s\n", e.message);+		}++		return output;+	}++	public void update_background (double contrast, bool desaturate) {+		background_image_is_processing = true;+		+		do_process_background_colors.begin ((obj, res) => {+			ImageSurface? img;++			img = do_process_background_colors.end (res);+			+			if (img != null) {+				background_image = (!) img;+				updated ();+			} else {+				stderr.printf ("Error: No background image");	+			}+			+			// update image if new value was set while background task was running +			if (get_contrast () != contrast || get_desaturate_background () != desaturate) {++				do_process_background_colors.begin ((obj, res) => {+					img = do_process_background_colors.end (res);+					if (likely (img != null)) {+						background_image = (!) img;+						updated ();+					} else {+						stderr.printf ("Error: No background image");	+					}+				});+			}++			background_image_is_processing = false;+			updated ();+			+		});+	}++	public double get_contrast () {+		double bc;+		lock (contrast) {+			bc = contrast;+		}+		return bc;+	}+		+	public bool get_desaturate_background () {+		bool bw;+		lock (desaturate) {+			bw = desaturate;+		}+		return bw;+	}+	+	public void set_desaturate_background (bool bw) {+		lock (desaturate) {+			desaturate = bw;+		}+	+		if (background_image_is_processing) {+			return;+		}+		+		background_image_is_processing = true;+		+		update_background (get_contrast (), bw);+	}+	+	public void set_threshold (double t) {+		threshold = t;+	}+	+	public Path auto_trace () {+		ImageSurface img;+		int len, w, h, i;+		unowned uchar[] pix_buff;+		Path p = new Path ();+		double x, y;++		if (background_image == null) {+			return p;+		}++		img = (!) background_image;++		w = img.get_width();+		h = img.get_height();++		if (unlikely (img.status () != Cairo.Status.SUCCESS)) {+			warning ("Err");+			return p;+		}+		+		len = 4 * w * h;++		pix_buff = img.get_data ();++		i = 0;+		while (i < len - 4) {+			if (is_edge (pix_buff[i], pix_buff[i + 4])) {+				y = i / w;+				x = i - y;+				+				p.add (x, y);+				+				if (p.points.length () > 10) {+					return p;+				}+			}+			+			i += 4;+		}+		+		return p;+	}+	+	private bool is_edge (uchar a, uchar b) {+		return (a == 255 && b == 0);+	}+	+	public void set_contrast (double contrast) {+		lock (this.contrast) {+			this.contrast = contrast;+		}+		+		if (background_image_is_processing) {+			return;+		}+		+		background_image_is_processing = true;+		+		update_background (contrast, get_desaturate_background ());+	}+}+	+}diff --git a/libbirdfont/GlyphCanvas.vala b/libbirdfont/GlyphCanvas.valanew file mode 100644index 0000000..d95a2cf--- /dev/null+++ b/libbirdfont/GlyphCanvas.vala@@ -0,0 +1,82 @@+/*
+    Copyright (C) 2012 Johan Mattsson
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using Cairo;
+using Math;
+
+namespace Supplement {
+
+public class GlyphCanvas : GLib.Object {
+
+	public FontDisplay current_display;
+	public signal void signal_redraw_area (int x, int y, int w, int h);
+	public Allocation allocation;
+	
+	public GlyphCanvas () {
+	}
+	
+	public void key_release (uint e) {
+		current_display.key_release (e);
+	}
+	
+	public void key_press (uint e) {
+		current_display.key_press (e);
+	}
+	
+	public void set_current_glyph (FontDisplay fd) {
+		if (fd is Glyph) {
+			Glyph g = (Glyph) fd;
+			
+			Supplement.current_glyph = g;
+			Supplement.current_glyph.resized ();
+
+			g.allocation = allocation;
+			
+			warn_if_fail (g.allocation.width != 0 && g.allocation.height != 0);
+		}
+
+		current_display = fd;
+		
+		fd.selected_canvas ();
+		
+		fd.redraw_area.connect ((x, y, w, h) => {
+			signal_redraw_area ((int)x, (int)y, (int)w, (int)h);
+		});
+
+		redraw ();
+
+		MainWindow.native_window.update_window_size ();
+	}
+	
+	public static Glyph get_current_glyph ()  {
+		return Supplement.current_glyph;
+	}
+	
+	public FontDisplay get_current_display () {
+		return current_display;
+	}
+
+	public void redraw_area (int x, int y, int w, int h) {
+		signal_redraw_area (x, y, w, h);
+	}
+	
+	public void redraw () {
+		signal_redraw_area (0, 0, allocation.width, allocation.height);
+	}
+}
+
+}
diff --git a/libbirdfont/GlyphCollection.vala b/libbirdfont/GlyphCollection.valanew file mode 100644index 0000000..c5d2d0b--- /dev/null+++ b/libbirdfont/GlyphCollection.vala@@ -0,0 +1,52 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Math;++namespace Supplement {++public class GlyphCollection : GLib.Object {+	VersionList versions;+	+	public GlyphCollection (Glyph? current = null) {+		versions = new VersionList (current);+	}+	+	public VersionList get_version_list () {+		return versions;+	}+	+	public Glyph get_current () {+		return versions.get_current ();+	}+	+	public void insert_glyph (Glyph g, bool selected) {+		versions.add_glyph (g, selected);		+		assert (versions.glyphs.length () > 0);+	}+	+	public uint length () {+		return versions.glyphs.length ();+	}+	+	public string get_name () {+		return get_current ().get_name ();+	}+}+	+}diff --git a/libbirdfont/GlyphRange.vala b/libbirdfont/GlyphRange.valanew file mode 100644index 0000000..5528060--- /dev/null+++ b/libbirdfont/GlyphRange.vala@@ -0,0 +1,262 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++public class GlyphRange {+	+	List<UniRange> ranges;+	+	public unowned List<string> unassigned = new List<string> ();+	+	uint32 len = 0;+	+	public GlyphRange () {+	}+		+	public unowned List<UniRange> get_ranges () {+		return ranges;+	}+	+	public void use_default_range () {+		/// All lower case letters in alphabetic order separated by space+		string lower_case = _("a b c d e f g h i j k l m n o p q r s t u v w x y z");+		+		/// All upper case letters in alphabetic order separated by space+		string upper_case = _("A B C D E F G H I J K L M N O P Q R S T U V W X Y Z");++		foreach (string c in lower_case.split (" ")) {+			add_single (c.get_char ());+		}++		foreach (string c in upper_case.split (" ")) {+			add_single (c.get_char ());+		}+				+		add_range ('0', '9');+		+		add_single (' '); // TODO: add all spaces here.+		+		add_single ('.');+		add_single ('?');+		+		add_single (',');+		+		add_single ('’');++		add_range ('“', '”');++		add_single ('&');+		+		add_range (':', ';');+		+		add_single ('/'); +		+		add_range ('!', '/');+		+		add_single ('-');+		add_range ('‐', '—');+		add_range ('<', '@');+		add_range ('(', ')');+	}+	+	public void use_full_unicode_range () {+		add_range ('\0', (unichar) 0xFFF8);+	}+	+	// Todo: complete localized alphabetical sort åäö is the right order for example.+	public void sort () {+		ranges.sort ((a, b) => {+			bool r = a.start > b.start;+			+			return_if_fail (a.start != b.start);+			+			return (r) ? 1 : -1;+		});+	}+	+	public void add_single (unichar c) {+		add_range (c, c);+	}+	+	public uint32 get_length () {+		unichar l = len;+		l += unassigned.length ();+		return l;+	}+	+	public void add_range (unichar start, unichar stop) {+		unichar b, s;+		if (unique (start, stop)) {+			append_range (start, stop);+		} else {+			+			// make sure this range does not overlap existing ranges+			b = start;+			s = b;+			if (!unique (b, b)) {			+				while (b < stop) {+					if (!unique (b, b)) {+						b++;+					} else {+						if (s != b) {+							add_range (b, stop);+						}+						+						b++;+						s = b;+					}+				}+			} else {+				while (b < stop) {+					if (unique (b, b)) {+						b++;+					} else {+						if (s != b) {+							add_range (start, b - 1);+						}+						+						b++;+						s = b;+					}+				}				+			}+		}+	}+	+	private void append_range (unichar start, unichar stop) {+		UniRange r;+		+		StringBuilder s = new StringBuilder ();+		StringBuilder e = new StringBuilder ();+		s.append_unichar (start);+		e.append_unichar (stop);+		+		r = insert_range (start, stop); // insert a unique range+		merge_range (r); // join connecting ranges+	}+	+	private void merge_range (UniRange r) {+		foreach (UniRange u in ranges) {+			if (u == r) {+				continue;+			}			+			+			if (u.start == r.stop + 1) {+				u.start = r.start;+				ranges.remove_all (r);+				merge_range (u);+			}+			+			if (u.stop == r.start - 1) {+				u.stop = r.stop;+				ranges.remove_all (r);+				merge_range (u);+			}+		}+	}++	public string get_char (uint32 index) {+		int64 ti;+		string chr;+		UniRange r;+		StringBuilder sb;+		unichar c;+		+		if (index > len + unassigned.length ()) { +			return "\0".dup();+		}+		+		if (index >= len) {+			+			if (index - len >= unassigned.length ()) {+				return "\0".dup();+			} +			+			chr = ((!) unassigned.nth (index - len)).data;+			return chr;+		}++		r = ranges.first ().data;+		ti = index;++		foreach (UniRange u in ranges) {+			ti -= u.length ();+			+			if (ti < 0) {+				r = u;+				break;+			}+		}+				+		sb = new StringBuilder ();+		c = r.get_char ((unichar) (ti + r.length ()));+		sb.append_unichar (c);++		return sb.str;+	}+	+	public uint32 length () {+		return len;+	}++	private bool unique (unichar start, unichar stop) {+		foreach (UniRange u in ranges) {+			+			if (inside (start, u.start, u.stop)) return false;+			if (inside (stop, u.start, u.stop)) return false;+			if (inside (u.start, start, stop)) return false;+			if (inside (u.stop, start, stop)) return false;+			+		}++		return true;+	}++	private bool inside (unichar start, unichar u_start, unichar u_stop) {+		return (u_start <= start <= u_stop);+	}+	+	private UniRange insert_range (unichar start, unichar stop) {+		if (unlikely (start > stop)) {+			warning ("start > stop");+			stop = start;+		}+		+		UniRange ur = new UniRange (start, stop);+		len += ur.length ();+		ranges.append (ur);+		+		return ur;+	}++	public void print_all () {+		StringBuilder s;+		+		stdout.printf ("Ranges:\n");+		foreach (UniRange u in ranges) {+			s = new StringBuilder ();+			s.append_unichar (u.start);+			s.append (" - ");+			s.append_unichar (u.stop);+			s.append ("\n");+			stdout.printf (s.str);+		}+	}+}++}diff --git a/libbirdfont/GlyphTable.vala b/libbirdfont/GlyphTable.valanew file mode 100644index 0000000..ffe69a7--- /dev/null+++ b/libbirdfont/GlyphTable.vala@@ -0,0 +1,412 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++/** A sorted table of glyphs with search index. */+class GlyphTable : GLib.Object {+	List<Item> data = new List<Item> ();+	+	List<RowItem> row1 = new List<RowItem> ();+	List<RowItem> row2 = new List<RowItem> ();+	List<RowItem> row3 = new List<RowItem> ();+	+	public GlyphTable () {+	}++	public void remove_all () {+		while (data.length () > 0) {+			data.remove_link (data.first ());+		}+	}++	public void @for_each (Func<GlyphCollection> func) {+		data.foreach ((v) => {+			func (v.glyph_collection);+		});+	}++	unowned List<Item>? find (string n) {+		return find_index (n);+	}++	unowned List<Item>? find_next (string n) {+		return find_next_index (n);+	}+		+	public void remove (string name) {+		unowned List<Item>? d = find (name);+		+		if (d == null) {+			warning (@"did not find $name");+			return;+		}+		+		data.remove_link ((!) d);+		build_index ();+	}++	public GlyphCollection? nth (uint i) {+		unowned List<Item>? d = data.nth (i);+		+		if (d == null) {+			warning (@"no glyph for index $i");+			return null;+		}+		+		return ((!) d).data.glyph_collection;+	}++	public uint length () {+		return data.length ();+	}++	public new GlyphCollection? @get (string name) {+		unowned List<Item>? d = find_index (name);+		+		if (d == null) {+			return null;+		}+		+		return ((!) d).data.glyph_collection;+	}++	public bool insert (GlyphCollection g) {+		Item item;+		unowned List<Item>? next;+		+		if (g.get_name () == "") {+			warning (@"No proper name for glyph collection. g.get_name (): \"$(g.get_name ())\"");+			return false;+		}++		if (find (g.get_name ()) != null) {+			warning (@"Table does already contain character \"$(g.get_name ())\".");+			return false;+		}+				+		next = find_next (g.get_name ());+		+		item = new Item (g.get_name (), g);+		+		if (next == null) {+			data.append (item);+		} else {+			data.insert_before ((!) next, item); // _sorted+		}+		+		build_index ();+		+		if (find (g.get_name ()) == null) {+			warning (@"Can not find glyph \"$(g.get_name ())\".");+			print_all ();+			return false;+		}+		+		return true;+	}++	public void print_all () {+		int indice = 0;+		foreach (Item i in data) {+			print (@"$(i.name) indice: $(indice)\n");+			indice++;+		}+		+		print ("Index:\n");+		print ("\n");+		print ("row3\n");+		foreach (RowItem i in row3) {+			print (@"$(i.item.data.name)\n");+		}+		+		print ("\n");+		print ("row2\n");+		foreach (RowItem i in row2) {+			print (@"$(i.item.data.name)\n");+		}+		+		print ("\n");+		print ("row1\n");+		foreach (RowItem i in row1) {+			print (@"$(i.item.data.name)\n");+		}		+	}++	static int compare (string an, Item b) {+		string? bt = b.glyph_collection.get_name ();+		string bn = (!) bt;+		+		uint32 ac = 0;+		uint32 bc = 0;+				+		int i1 = 0;+		int i2 = 0;+		+		return_if_fail (bt != null);+		+		int ca = an.char_count ();+		int cb = bn.char_count ();+		+		if (ca != cb) {+			return ca < cb ? 1 : -1;+		}+		+		while (true) {+			an.get_next_char (ref i1, out ac);+			bn.get_next_char (ref i2, out bc);+			+			if (ac == bc && ac != 0) {+				continue;+			}+			+			if (ac == bc) return 0;+			+			return (ac < bc) ? 1 : -1;+		}+	}+	+	public void build_index () {+		uint len;+		RowItem r;+		+		while (row1.length () > 0) {+			row1.remove_link (row1.first ());+		}++		while (row2.length () > 0) {+			row2.remove_link (row2.first ());+		}++		while (row3.length () > 0) {+			row3.remove_link (row3.first ());+		}++		if (data.length () == 0) {+			return;+		}+				+		// row 1+		len = data.length () / 5;+		if (len == 0) len++;+		for (int i = 0; i < len; i++) {+			r = new RowItem ();+			r.item = data.nth (i * 5);+			r.level = 1;+			row1.append (r);+		}+		r = new RowItem ();+		r.item = data.last ();+		r.level = 1;+		r.last = true;+		row1.append (r);	+		+		// row 2+		len = row1.length () / 5;+		if (len == 0) len++;+		for (int i = 0; i < len; i++) {+			r = new RowItem ();+			r.next_row = row1.nth (i * 5);+			r.item = r.next_row.data.item; +			r.level = 2;+			row2.append (r);+		}+		r = new RowItem ();+		r.item = data.last ();+		r.level = 2;+		r.next_row = row1.last ();		+		r.last = true;+		row2.append (r);+		+		// row 3+		len = row2.length () / 5;+		if (len == 0) len++;+		for (int i = 0; i < len; i++) {+			r = new RowItem ();+			r.next_row = row2.nth (i * 5);+			r.item = r.next_row.data.item; +			r.level = 3;+			row3.append (r);+		}+		r = new RowItem ();+		r.item = data.last ();+		r.level = 3;+		r.next_row = row2.last ();+		r.last = true;	+		row3.append (r);+	}++	unowned List<Item>? find_next_index (string n) {+		unowned List<Item>? next = find_next_via_index (n);+		return next;+	}++	unowned List<Item>? find_index (string n) {+		unowned List<Item>? next = find_next_via_index (n);+		+		if (next == null) {+			return null;+		}+		+		if (compare (n, ((!)next).data) == 0) {+			return next;+		}+	+		if (((!)next) != data.first () && compare (n, ((!)next).prev.data) == 0) {+			return ((!)next).prev; // fetch previous since we are searching for the next one+		}+				+		return null; +	}+	+	unowned List<Item>? find_next_via_index (string n) {+		unowned List<Item> i = data.first ();+		unowned List<RowItem> r;+		int cmp;+		+		if (data.length () == 0) {+			return null;+		}++		r = row3.first ();+		int ti = 0;+		while (true) {+			cmp = compare (n, r.data.item.data);+			+			ti++;+			+			if (cmp == 1) {+				break;+			}+			+			if (cmp == 0) {+				return r.data.item;+			}+			+			if (r != row3.last ()) {+				r = r.next;+			} else {+				return null;+			}+		} +		r = r.data.next_row;++		while (true) {+			cmp = compare (n, r.data.item.data);+			if (cmp == -1) {+				break;+			}++			if (cmp == 0) {+				return r.data.item;+			}+						+			if (r == row2.first ()) {;+				return r.data.item;+			} else r = r.prev;+		}+		r = r.data.next_row;+		+		while (true) {+			cmp = compare (n, r.data.item.data);+			if (cmp == 1) {+				break;+			}+			+			if (cmp == 0) {+				return r.data.item;+			}+								+			if (r == row1.last ()) return null;+			else r = r.next;+		}+		i = r.data.item;+		+		+		while (true) {+			cmp = compare (n, i.data);++			if (cmp == -1) {+				// null?+				return i.next;+			}++			if (cmp == 0) {+				return i;+			}+						+			if (i == data.first ()) {+				return i;+			} else { +				i = i.prev;+			}+		}			+	}++	public bool validate_index () {+		int cmp;+		Item prev;+		int index;+		+		if (data.length () == 0) {+			return true;+		}+		+		prev = data.first ().data;+		index = 0;+		foreach (Item i in data) {+			cmp = compare (prev.name, i);+		+			if (cmp == 1) {+				continue;+			}+						+			if (cmp == 0 && index != 0) {+				warning (@"Value is not unique: $(i.name).");+				return false;+			}++			if (cmp == -1) {+				warning (@"Value is not sorted: $cmp == cmp $(prev.name) $(i.name).");+				return false;+			}+			+			index++;+		}+	+		return true;+	}++	class Item : GLib.Object {+		public string name;+		public GlyphCollection glyph_collection;+		+		public Item (string n, GlyphCollection gc) {+			glyph_collection = gc;+			name = n;+		}+	}++	class RowItem : GLib.Object {+		public unowned List<RowItem> next_row;+		public unowned List<Item> item;+		public int level = 0;+		public bool last = false;+	}+}++}diff --git a/libbirdfont/GridTool.vala b/libbirdfont/GridTool.valanew file mode 100644index 0000000..e2dc97c--- /dev/null+++ b/libbirdfont/GridTool.vala@@ -0,0 +1,244 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Math;++namespace Supplement {++class GridTool : Tool {+	+	static List <Line> horizontal;+	static List <Line> vertical;+	+	static bool visible = false;+	+	public static double size_x;+	public static double size_y;+	+	public static List <SpinButton> sizes;+	+	public GridTool (string n) {+		base (n, _("Show grid"), 'g', NONE);+		+		horizontal = new List <Line> ();+		vertical = new List <Line> ();+		+		sizes = new List <SpinButton> ();+		+		size_x = 2;+		size_y = 2;+	+		updata_lines ();+			+		select_action.connect((self) => {+			visible = !visible;+			updata_lines ();+			MainWindow.get_glyph_canvas ().redraw ();+			+			self.set_selected (visible);+			+			Toolbox tb = MainWindow.get_toolbox ();+			Tool t = tb.get_tool ("help_lines");+			+			if (visible && !t.is_selected ()) {+				MainWindow.get_toolbox ().select_tool (t);+			}+			+		});+		+		press_action.connect((self, b, x, y) => {+		});++		release_action.connect((self, b, x, y) => {+		});+		+		move_action.connect ((self, x, y)	 => {+		});+		+		draw_action.connect ((self, cairo_context, glyph) => {+		});+			+	}+	+	public static void set_grid_width (double w) {+		size_x = w;+		size_y = w;+		+		updata_lines ();+	}+	+	private static void updata_lines () {+		Glyph g = MainWindow.get_current_glyph ();+		double step = size_y * Glyph.SCALE;+		double i;+		int max_lines = 400;+		int n;+		Line t, l, u;++		Line baseline = g.get_line ("baseline");+		Line bottom_margin = g.get_line ("bottom margin");+		Line top_margin = g.get_line ("top margin");+		+		Line left = g.get_line ("left");+		Line right = g.get_line ("right");++		while (vertical.length () > 0) vertical.delete_link (vertical.first ());+		while (horizontal.length () > 0) horizontal.delete_link (horizontal.first ());+	+		n = 0;+		for (i = left.get_pos () - 7 * step; i <= right.get_pos () + 7 * step; i += step) {	+			l = new Line ("grid", i, Line.VERTICAL);+			l.set_moveable (false);+			l.set_color (0.2, 0.6, 0.2, 0.2);+			horizontal.append (l);+			+			if (++n >= max_lines) {+				break;+			}+		}+		+		n = 0;+		for (i = baseline.get_pos () - step; i >= top_margin.get_pos () - 7 * step; i -= step) {+			t = new Line ("grid", i, Line.HOROIZONTAL);+			t.set_moveable (false);+			t.set_color (0.2, 0.6, 0.2, 0.2);+			vertical.append (t);	++			if (++n >= max_lines) {+				break;+			}++		}+		+		for (i = baseline.get_pos (); i <= bottom_margin.get_pos () + 7 * step; i += step) {+			u = new Line ("grid", i, Line.HOROIZONTAL);+			u.set_moveable (false);+			u.set_color (0.2, 0.6, 0.2, 0.2);+			vertical.append (u);	++			if (++n >= max_lines) {+				break;+			}++		}	+	}+		+	/** Sets x and y the point closest on grid. */+	public static void tie (ref double x, ref double y) {+		tie_point (ref x, ref y, false);+	}++	public static void tie_coordinate (ref double x, ref double y) {+		tie_point (ref x, ref y, true);+	}+		+	private static void tie_point (ref double x, ref double y, bool coordinate)+		requires (horizontal.length () > 0 && vertical.length () > 0)+	{+		double d, m;+		+		Glyph g = MainWindow.get_current_glyph ();+		+		Line xmin = horizontal.first ().data;+		Line ymin = vertical.first ().data;++		Line xpos;+		Line ypos;+		+		if (!coordinate) {+			xpos = new Line ("", 0, Line.VERTICAL);+			ypos = new Line ("", 0, Line.HOROIZONTAL);++			xpos.set_move (true);+			ypos.set_move (true);+			+			xpos.move_line_to (x, y, g.allocation);+			ypos.move_line_to (x, y, g.allocation);+		} else {+			xpos = new Line ("", x, Line.VERTICAL);+			ypos = new Line ("", y, Line.HOROIZONTAL);+		}++		m = double.MAX;++		foreach (Line line in vertical) {+			d = Math.fabs (line.get_pos () - ypos.get_pos ());+			+			if (d <= m) {+				m = d;+				ymin = line;+			}+			+		}++		m = double.MAX;+		foreach (Line line in horizontal) {+			d = Math.fabs (line.get_pos () - xpos.get_pos ());+			+			if (d <= m) {+				m = d;+				xmin = line;+			}+			+		}+		+		if (!coordinate) {+			x = Glyph.reverse_path_coordinate_x (xmin.get_pos ());+			y = Glyph.reverse_path_coordinate_y (-ymin.get_pos ());+		} else {+			x = xmin.get_pos ();+			y = ymin.get_pos ();+		}+	}++	public static void tie_to_prev (EditPoint ep, double tx, double ty) {+		EditPoint pp;+		double a, b;+		+		return_if_fail (ep.prev != null);+		+		pp = ep.get_prev ().data;+		+		ep.to_coordinate (ref tx, ref ty);+		+		a = fabs (tx - pp.x);+		b = fabs (ty - pp.y);+		+		if (a > b) {+			ep.y = pp.y;+		} else {+			ep.x = pp.x;+		}+	}	++	public static bool is_visible () {+		return visible;+	}+	+	public static unowned List<Line> get_horizontal_lines () {+		return horizontal;+	}+	+	public static unowned List<Line> get_vertical_lines () {+		return vertical;+	}++}++}+diff --git a/libbirdfont/IOThread.vala b/libbirdfont/IOThread.valanew file mode 100644index 0000000..9869591--- /dev/null+++ b/libbirdfont/IOThread.vala@@ -0,0 +1,32 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++class IOThread {++	public static void add (ThreadFunc<weak void*> func) {+		try {+			unowned Thread<void*> thread = Thread.create<void*> (func, false);+		} catch (Error e) {+			warning (@"e.message");+		}+	}+	+}++}diff --git a/libbirdfont/Icons.vala b/libbirdfont/Icons.valanew file mode 100644index 0000000..3dc19f3--- /dev/null+++ b/libbirdfont/Icons.vala@@ -0,0 +1,50 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++using Cairo;	++public class Icons {+	+	public static ImageSurface? get_icon (string? name) {+		ImageSurface? img = null;+		File f;+		+		if (name == null) {+			warning ("Can't find a file for name \"null\".");+			return null;+		}++		f = find_icon ((!) name);+		+		if (!f.query_exists ()) {+			warning (@"Can't load icon: $((!)f.get_path ())");+			return null;+		}+		+		img = new ImageSurface.from_png ((!)f.get_path ());+		+		return img;+	}++	public static File find_icon (string name) {+		return FontDisplay.find_file ("icons", name);+	}+}+	+}diff --git a/libbirdfont/Intersection.vala b/libbirdfont/Intersection.valanew file mode 100644index 0000000..c3e3533--- /dev/null+++ b/libbirdfont/Intersection.vala@@ -0,0 +1,36 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Math;++namespace Supplement {++class Intersection {+	public double x;+	public double y;+	public double distance;+	+	public EditPoint editpoint_a;+	public EditPoint editpoint_b;+	+	public Intersection (double x, double y, double distance) {+		this.x = x;+		this.y = y;+	}+}++}diff --git a/libbirdfont/IntersectionList.vala b/libbirdfont/IntersectionList.valanew file mode 100644index 0000000..aad5713--- /dev/null+++ b/libbirdfont/IntersectionList.vala@@ -0,0 +1,236 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Math;++namespace Supplement {++class IntersectionList {+	public List<Intersection> points = new List<Intersection> ();+	+	public void add (Intersection i) {+		double d;+		Intersection l;+		bool close = false;+			+		if (points.length () == 0) {+			points.append (i);+			return;+		}+		+		if (has_point (i)) {+			return;+		}+	+		for (unowned List<Intersection> pl = points.first (); ; pl = pl.next) {+			l = pl.data;+			d = Math.fabs (Math.sqrt (Math.pow (i.x - l.x, 2) + Math.pow (i.y - l.y, 2)));++			if (l.distance >= i.distance) {+				close = true;+				+				if (d < 0.3) {+					points.remove_link (points.last ());+					points.append (i);+					return;+				}+			}+			+			if (pl == points.last ()) {+				break;+			}+		}+		+		if (!close) {+			points.append (i);+		}+	}+	+	public void clear () {+		while (points.length () > 0) {+			points.remove_link (points.first ());+		}+	}+	+	public void remove_point (EditPoint e) {+		foreach (Intersection n in points) {+			if (n.editpoint_a == e || n.editpoint_b == e) {+				points.remove_all (n);+			}+		}	+	}+	+	public bool has_edit_point (EditPoint e) {+		foreach (Intersection n in points) {+			if (n.editpoint_a == e || n.editpoint_b == e) {+				return true;+			}+		}+		+		return false;+	}+	+	bool has_point (Intersection i) {+		return has_point_at (i.x, i.y);+	}++	bool has_point_at (double x, double y) {+		foreach (Intersection n in points) {+			if (x == n.x && y == n.y) {+				return true;+			}+		}+		+		return false;+	}+		+	public int get_point_index (EditPoint e) {+		int i = 0;+		foreach (Intersection n in points) {+			if (e.x == n.x && e.y == n.y) {+				return i;+			}+			i++;+		}+		return -1;+	}+	+	public Intersection? get_next_intersection (EditPoint e) {+		bool found = false;+		foreach (Intersection n in points) {+			if (found) {+				return n;+			}+			+			if (n.editpoint_a == e || n.editpoint_b == e) {+				found = true;+			}+		}+		return null;+	}+	+	public Intersection? get_intersection (EditPoint e) {+		Intersection? p = null;+		foreach (Intersection n in points) {+			if (n.editpoint_a == e || n.editpoint_b == e) {+				assert (p == null);+				p = n;+			}+		}+		return p;+	}+	+	public void append (IntersectionList i) {+		foreach (Intersection inter in i.points) {+			points.append (inter);+		}+	}++	public static IntersectionList create_intersection_list (Path p1, Path p2) {+		IntersectionList il = new IntersectionList ();+		+		unowned List<EditPoint> a_start, a_stop;+		unowned List<EditPoint> b_start, b_stop;+		+		if (p1 == p2) {+			return il;+		}+		+		// find crossing paths+		a_start = p1.points.first ();+		for (int i = 0; i < p1.points.length (); i++) {+			+			if (a_start.data.next == null) {+				a_stop = p1.points.first ();+			} else {+				a_stop = a_start.data.get_next ();+			}+			+			b_start = p2.points.first ();+			for (int j = 0; j < p2.points.length (); j++) {+				+				if (b_start.data.next == null) {+					b_stop = p2.points.first ();+				} else {+					b_stop = b_start.data.get_next ();+				}++				il.append (find_intersections (a_start.data, a_stop.data, b_start.data, b_stop.data));++				b_start = b_stop;+			}+			+			a_start = a_stop;+		}+		+		return il;+	}++	static IntersectionList find_intersections (EditPoint a0, EditPoint a1, EditPoint b0, EditPoint b1) {+		IntersectionList il = new IntersectionList ();+		+		double mind = double.MAX; ;+		+		Path.all_of (a0, a1, (ax, ay, at) => {+			if (at == 0 || at == 1) {+				return true;+			}+			+			Path.all_of (b0, b1, (bx, by, bt) => {+				double d = Math.fabs (Math.sqrt (Math.pow (ax - bx, 2) + Math.pow (ay - by, 2)));+				+				if (d < 0.2) {+					il.add (new Intersection (ax, ay, d));+				}+				+				if (d < mind) mind = d;+				return true;+			});+			+			return true;+		});++		return il;+	}+	+	/** return true if ranges does overlap */+	bool has_intersection (double a0, double a1, double b0, double b1) {+		if (a0 <= b0 <= a1 || a1 <= b0 <= a0) {+			if (a0 <= b1 <= a1 || a1 <= b1 <= a0) {+				return true;+			}+			+			if (b0 <= a1 <= b1 || b1 <= a1 <= b0) {+				return true;+			}+		}+		+		if (b0 <= a0 <= b1 || b1 <= a0 <= b0) {+			if (a0 <= a0 <= a1 || a1 <= a0 <= a0) {+				return true;+			}+			+			if (b0 <= a0 <= b1 || b1 <= a0 <= b0) {+				return true;+			}+		}+		+		return false;+	}+}++}diff --git a/libbirdfont/Kerning.vala b/libbirdfont/Kerning.valanew file mode 100644index 0000000..537e57f--- /dev/null+++ b/libbirdfont/Kerning.vala@@ -0,0 +1,33 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Xml;++namespace Supplement {++public class Kerning : GLib.Object {+	public string glyph_right;+	public double val;+	+	public Kerning (string right, double v) {+		glyph_right = right;+		val = v;+	}+}++}diff --git a/libbirdfont/KeyBindings.vala b/libbirdfont/KeyBindings.valanew file mode 100644index 0000000..14ce53b--- /dev/null+++ b/libbirdfont/KeyBindings.vala@@ -0,0 +1,366 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Gdk;++namespace Supplement {++// FIXA a lot of these things have been replaced and can safely be removed++public enum Key {+	NONE = 0,+	UP = 65362,+	RIGHT = 65363,+	DOWN = 65364,+	LEFT = 65361,+	PG_UP = 65365,+	PG_DOWN = 65366,+	ENTER = 65293,+	BACK_SPACE = 65288,+	SHIFT_LEFT = 65505,+	SHIFT_RIGHT = 65506,+	CTRL_LEFT = 65507,+	CTRL_RIGHT = 65508,+	CAPS_LOCK = 65509,+	ALT_LEFT = 65513,+	ALT_RIGHT = 65514,+	ALT_GR = 65027,+	LOGO_LEFT = 65515,+	LOGO_RIGHT = 65516,+	CONTEXT_MENU = 65383,+	TAB = 65289,+	DEL = 65535+}++bool is_modifier_key (uint i) {+	return Key.UP == i ||+		Key.RIGHT == i ||+		Key.DOWN == i ||+		Key.LEFT == i ||+		Key.PG_UP == i ||+		Key.PG_DOWN == i ||+		Key.ENTER == i ||+		Key.BACK_SPACE == i ||+		Key.SHIFT_LEFT == i ||+		Key.SHIFT_RIGHT == i ||+		Key.CTRL_LEFT == i ||+		Key.CTRL_RIGHT == i ||+		Key.ALT_LEFT == i ||+		Key.ALT_RIGHT == i ||+		Key.ALT_GR == i || +		Key.LOGO_LEFT == i || +		Key.LOGO_RIGHT == i || +		Key.TAB == i || +		Key.CAPS_LOCK == i || +		Key.LOGO_RIGHT == i;+}++/** Modifier flags */+public static const uint NONE  = 0;+public static const uint CTRL  = 1 << 0;+public static const uint ALT   = 1 << 2;+public static const uint SHIFT = 1 << 3;++/** A list of all valid key bindings. */+public class BindingList {+	static ShortCut[]? nc = null;+	+	public static ShortCut[] get_default_bindings () {	+		+		if (nc == null) {+		+			// add all bindings to this list+			ShortCut[] default_bindings = { };++			nc = default_bindings;+		}+		+		return nc;+	}+}+++/** Function to be executed for each global key binding. */+public abstract class ShortCut : GLib.Object {+	uint modifier = 0;+	uint key = 0;+	+	public abstract void run ();+	+	public void set_modifier (uint m) {+		modifier = m;+	}++	public void set_key (uint k) {+		key = k;+	}+	+	public uint get_modifier () {+		return modifier;+	}+	+	public uint get_key () {+		return key;+	}+	+	public abstract uint get_default_modifier ();+	public abstract uint get_default_key ();+	+	public abstract unowned string get_description ();+	+	public static EqualFunc<ShortCut> equal = (a, b) => {+		if (a.get_key () != b.get_key ()) return false;+		if (a.get_modifier () != b.get_modifier ()) return false;+		if (a.get_description () != b.get_description ()) return false;+		return true;+	};+	+}++public class KeyBindings {+	+	bool modifier_ctrl = false;+	bool modifier_alt = false;+	bool modifier_shift = false;+		+	public static uint modifier = 0;+	+	bool require_modifier = false;+	+	/** First uint is modifer flag, second uint is keyval */+	HashTable<uint, HashTable<uint, ShortCut>> short_cuts = new HashTable<uint, HashTable<uint, ShortCut>> (null, null);+	+	public static KeyBindings singleton;+	+	public KeyBindings () {+		+		HashFunc<uint> hash = (v) => {+			return v;+		};+	+		HashTable<uint, ShortCut> alt_ctrl_shift = new HashTable<uint, ShortCut> (hash, ShortCut.equal);+		HashTable<uint, ShortCut> alt_shift      = new HashTable<uint, ShortCut> (hash, ShortCut.equal);+		HashTable<uint, ShortCut> ctrl_shift     = new HashTable<uint, ShortCut> (hash, ShortCut.equal);+		HashTable<uint, ShortCut> alt            = new HashTable<uint, ShortCut> (hash, ShortCut.equal);+		HashTable<uint, ShortCut> ctrl           = new HashTable<uint, ShortCut> (hash, ShortCut.equal);+		+		// Possible modifiers+		short_cuts.insert (ALT|CTRL|SHIFT, alt_ctrl_shift);+		short_cuts.insert (ALT|SHIFT,      alt_shift);+		short_cuts.insert (CTRL|SHIFT,     ctrl_shift);+		short_cuts.insert (ALT,            alt);+		short_cuts.insert (CTRL,           ctrl);+		+		// Add default bindings+		foreach (var b in BindingList.get_default_bindings ()) {+			add_binding (b);+		}+		+		save ();+		load ();+		+		singleton = this;+	}+	+	public void reset () {+		modifier = NONE;+		modifier_ctrl = false;+		modifier_alt = false;+		modifier_shift = false;+	}+	+	public void set_require_modifier (bool m) {+		require_modifier = m;+	}+	+	void load () {+		File home = File.new_for_path (Environment.get_home_dir ());+		File settings = home.get_child (".birdfont");+		File bindings = settings.get_child ("keybindings");++		if (!bindings.query_exists ()) {+			return;+		}++		FileStream? bindings_file = FileStream.open ((!) bindings.get_path (), "r");+		+		if (bindings_file == null) {+			stderr.printf ("Failed to load keybindings from file %s.\n", (!) bindings.get_path ());+			return;+		}+		+		return_if_fail (bindings_file != null);+		+		unowned FileStream b = (!) bindings_file;+		+		string? l;+		l = b.read_line ();+		while ((l = b.read_line ())!= null) {+			string line;+			+			line = (!) l;+			+			if (line.get_char (0) == '#') {+				continue;+			}+			+			int i = 0;+			int s = 0;+			+			i = line.index_of_char(' ', s);+			string mod = line.substring (s, i - s);+			+			s = i + 1;+			i = line.index_of_char(' ', s);+			string key = line.substring (s, i - s);+			+			s = i + 1;+			i = line.index_of_char('\n', s);+			string description = line.substring (s, i - s);++			update_short_cut (mod, key, description);+		}+		+	}+	+	void save () {+		try {+			File settings = Supplement.get_settings_directory ();+			File bindings = settings.get_child ("keybindings");+			+			if (bindings.query_exists ()) {+				bindings.delete ();+			}++			DataOutputStream os = new DataOutputStream(bindings.create(FileCreateFlags.REPLACE_DESTINATION));+			uint8[] data;+			long written = 0;+			+			StringBuilder sb = new StringBuilder ();+			+			sb.append_printf ("# Supplement keybindings\n");+			sb.append_printf ("# Version: 1.0\n");+			+			short_cuts.foreach ( (k, v) => {+				v.foreach ( (k, s) => {+					sb.append_printf ("%u %u %s\n", s.get_modifier (), s.get_key (), s.get_description ());+				});+			});+			+			data = sb.str.data;+			+			while (written < data.length) { +				written += os.write (data[written:data.length]);+			}+			+		} catch (Error e) {+			stderr.printf ("Can not save key bindings. (%s)", e.message);	+		}+		+	}+	+	void update_short_cut (string mod, string key, string description) {+		ShortCut? sc = null;+		ShortCut binding;+		short_cuts.foreach ( (k, ht) => {+			ht.foreach ((ki, s) => {+				if (s.get_description () == description) {+					sc = s;+				}+				+				// ht.remove (ki); // FIXME! why does it remove all items?+				+			});+		});+		+		if (unlikely (sc == null)) {+			stderr.printf ("Can not set bindings for \"%s-%s-%s-\"\n", mod, key, description);+			return;+		}+		+		binding = (!) sc;++		binding.set_modifier ((uint) uint64.parse (mod));+		binding.set_key ((uint) uint64.parse (key));+		+		add_binding (binding);+	}+	+	void add_binding (ShortCut binding) {+		uint m = binding.get_modifier ();+		+		if (m == 0) {+			m = binding.get_default_modifier ();+			binding.set_modifier (m);+		}++		if (binding.get_key () == 0) {+			binding.set_key (binding.get_default_key ());+		}+		+		HashTable<uint, ShortCut>? e = short_cuts.lookup (m);+		+		if (e == null) {+			stderr.printf ("Invalid modifier in key binding: expecting ctrl, alt or shift. Flag: (%u) \n", modifier);+			return;+		}+		+		var events = (!) e;+		events.insert (binding.get_key (), binding);+	}+	+	bool get_short_cut (uint modifier, uint key, out ShortCut? sc) {+		sc = null;+		+		foreach (var v in BindingList.get_default_bindings ()) {+			if (v.get_default_modifier () == modifier && v.get_default_key () == key) {+				sc = v;+				return true;+			}+		}+		+		return false;+	}++	public static void set_modifier (uint mod) {+		modifier = mod;++		singleton.modifier_ctrl = ((modifier & CTRL) > 0);+		singleton.modifier_alt = ((modifier & ALT) > 0);+		singleton.modifier_shift = ((modifier & SHIFT) > 0);+	}++	public static bool has_alt () {+		return singleton.modifier_alt;+	}+	+	public static bool has_shift () {+		return singleton.modifier_shift;+	}+		+	public static bool has_ctrl () {+		return singleton.modifier_ctrl;+	}+	+	public void key_release (uint keyval) {+	}+	+	public void key_press (uint keyval) {+	}+}++}diff --git a/libbirdfont/Line.vala b/libbirdfont/Line.valanew file mode 100644index 0000000..d5adc9f--- /dev/null+++ b/libbirdfont/Line.vala@@ -0,0 +1,342 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++/** Help line */+public class Line : GLib.Object {+	public static const bool VERTICAL = true;+	public static const bool HOROIZONTAL = false;+	+	string label;+	bool vertical;+	+	public double pos;+	+	bool active = false;+	bool move = false;+	+	public signal void queue_draw_area (int x, int y, int w, int h);+	public signal void position_updated (double pos);+	+	public unowned List<Line>? list_item = null;+	+	double r;+	double g;+	double b;+	double a;+	+	bool visible = true;+	bool moveable = true;+	+	public Line (string label = "No label set", double position = 10, bool vertical = false) {+		this.label = label;+		this.vertical = vertical;+		this.pos = position;+		+		r = 0.7;+		g = 0.7;+		b = 0.8;+		a = 1;+	}+	+	public Line copy () {+		Line l = new Line (label, pos, vertical);+		+		l.r = r;+		l.g = g;+		l.b = b;+		l.a = a;+		+		l.visible = visible;+		+		return l;+	}+	+	public void set_visible (bool v) {+		visible = v;+	}+	++	public void set_moveable (bool m) {+		moveable = m;+	}+	+	public void set_color (double r, double g, double b, double a) {+		this.r = r;+		this.g = g;+		this.b = b;+		this.a = a;+	}+	+	// Valabug, it should be: public “void set_list_item (unowned List<Line> ll) {”+	// Keeping list_item public for now+	public void set_list_item (void* ll) {+		// list_item = ll;+	}+	+	public bool is_moving () {+		return move;+	}+	+	public bool set_move (bool moving) {+		bool r = move;+		move = moving;+		return (r == moving);+	}+	+	public bool button_press () {+		Glyph g;+		if (get_active ()) {+			move = true;+			+			g = MainWindow.get_current_glyph ();+			g.store_undo_state ();+			+			return true;+		}+		+		return false;+	}++	public void redraw_line () {+		double p;+		Glyph g = MainWindow.get_current_glyph ();+		+		if (vertical) {+			p = g.reverse_path_coordinate_x (pos);+			queue_draw_area ((int)p - 10, 0, 200, g.allocation.height);+		} else {+			p = g.reverse_path_coordinate_y (pos);+			queue_draw_area (0, (int)p - 10, g.allocation.width, 300);+		}+	}	+	+	public void move_line_to (double x, double y, Allocation allocation) {+		set_move (true);+		event_move_to (x, y, x, y, allocation);+	}+	+	public void event_move_to (double x, double y, double ax, double ay, Allocation allocation) {+		double p, c;+		bool a = false;+		Glyph g = MainWindow.get_current_glyph ();+		double ivz = 1/g.view_zoom;+		double margin = 10;++		if (!moveable) return;++		if (move) {+			double np = pos;+		+			redraw_line (); // clear old position+			+			if (is_vertical ()) {+				pos = Glyph.path_coordinate_x (x);+				redraw_line (); // draw at new position+			} else {+				pos = -Glyph.path_coordinate_y (y);+				redraw_line ();+			}++			if (Math.fabs (np - pos) > 10) {+				queue_draw_area (0, 0, g.allocation.width, g.allocation.height);+			}++			position_updated (pos); // sinal update++			Supplement.get_current_font ().touch ();+			+			swap_lines ();+		}++		if (is_vertical ()) {+			if (ay > g.allocation.height - 10) {+				+				p = get_coordinate ();++				c = ax * ivz + g.view_offset_x;+				a = (p - margin * ivz <= c <= p + margin * ivz);+			}+					+			if (a != get_active ()) {+				redraw_line ();+			}++			set_active (a);+			+		} else {+			if (ax > g.allocation.width - 10) {+				p = get_coordinate ();+				c = ay * ivz + g.view_offset_y;+				a = (p - margin * ivz <= c <= p + margin * ivz);+			}+			+			if (a != get_active ()) {+				redraw_line ();+			}+			+			set_active (a);+		}+	}+	+	private void swap_lines () {+		double ivz = 1 / MainWindow.get_current_glyph ().view_zoom;+		+		if (!moveable) return;+		+		// switch to correct line if this line passes the next one in the sorted list+		if (list_item != null) {+			unowned List<Line> ll = (!) list_item;+			+			if (ll != ll.first ()) {+				if (ll.prev.data.pos > pos) {+					ll.prev.data.set_move (true);+					ll.prev.data.set_visible (true);+					set_move (false);+					pos = ll.prev.data.pos;+					ll.prev.data.pos += 1 * ivz;+				}+			}+			+			if (ll != ll.last ()) {+				if (ll.next.data.pos < pos) {+					ll.next.data.set_move (true);+					ll.next.data.set_visible (true);+					pos = ll.next.data.pos;+					ll.next.data.pos -= 1 * ivz;+					set_move (false);+				}+			}+			+		}+		+	}+	+	public bool get_active () {+		return active;+	}+	+	protected void set_active (bool active) {+		this.active = active;+	}+	+	public string get_label () {+		return label;+	}+	+	public bool is_vertical () {+		return vertical;+	}++	public double get_coordinate () {+		Glyph g = MainWindow.get_current_glyph ();+		double t = (is_vertical ()) ? (g.allocation.width / 2.0) : (g.allocation.height / 2.0);+		return pos + t;+	}+	+	public double get_pos () {+		return pos;+	}+	+	private double get_handle_size () {+		Glyph g = MainWindow.get_current_glyph ();+		double ivz = 1/g.view_zoom;+		return (get_active ()) ? (10 * ivz) : (5 * ivz);+	}+	+	public void draw (Context cr, Allocation allocation) {+		Glyph g = MainWindow.get_current_glyph ();+		+		double p, h, w;+		double ivz = 1/g.view_zoom;++		double size = get_handle_size ();+		+		if (!visible) return;+		+		cr.save ();+		cr.set_line_width (ivz);+		+		if (active) cr.set_source_rgba (0, 0, 0.3, 1);+		else cr.set_source_rgba (r, this.g, b, a);+		+		// Line+		if (is_vertical ()) {+			p = get_coordinate ();+			+			h = g.allocation.height * ivz + g.view_offset_y;+			+			cr.move_to (p, g.view_offset_y);+			cr.line_to (p, h);+			cr.stroke ();++			cr.scale (1, 1);++			if (moveable) {+				cr.new_path ();+				cr.move_to (p - size, h);	+				cr.line_to (p, h - size);	+				cr.line_to (p + size, h);+				cr.close_path();+				cr.fill ();+			}+			+		} else {+			p = get_coordinate ();+			+			w = g.allocation.width * ivz  + g.view_offset_x;+			+			cr.move_to (g.view_offset_x, p);+			cr.line_to (w, p);	+			cr.stroke ();+			+			if (moveable) {+				cr.new_path ();+				cr.move_to (w, p - size);	+				cr.line_to (w - size, p);	+				cr.line_to (w, p + size);+				cr.close_path();+				cr.fill ();+			}+		}+		+		// Label+		if (get_active ()) {				 +			if (is_vertical ()) {+				h = g.allocation.height * ivz + g.view_offset_y;+				cr.move_to (p + 8*ivz, h - 30*ivz);+			} else {+				w = g.allocation.width * ivz + g.view_offset_x;+				cr.move_to (w - 70*ivz, p + 15*ivz);+			}++			cr.set_font_size (12 * ivz);+			cr.select_font_face ("Cantarell", FontSlant.NORMAL, FontWeight.BOLD);+	+			cr.show_text (get_label ());+			cr.stroke ();+		}+		+		cr.restore ();+	}++}++}diff --git a/libbirdfont/MainWindow.vala b/libbirdfont/MainWindow.valanew file mode 100644index 0000000..c00409c--- /dev/null+++ b/libbirdfont/MainWindow.vala@@ -0,0 +1,158 @@+/*
+    Copyright (C) 2012 Johan Mattsson
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace Supplement {
+
+public class MainWindow {
+	
+	public static Toolbox tools;
+	public static GlyphCanvas glyph_canvas;
+	public static MainWindow singleton;
+	public static KeyBindings key_bindings;
+	public static MenuTab menu_tab;
+	public static TooltipArea tool_tip;
+	public static OverView over_view;	
+	public static TabBar tabs;
+	public static NativeWindow native_window;
+
+	public MainWindow () {
+		singleton = this;
+
+		key_bindings = new KeyBindings ();
+		glyph_canvas = new GlyphCanvas ();
+		tools = new Toolbox (glyph_canvas);
+		tabs = new TabBar ();
+		menu_tab = new MenuTab ();
+		tool_tip = new TooltipArea ();
+		over_view = new OverView();
+	}
+
+	public void set_native (NativeWindow nw) {
+		native_window = nw;
+	}
+
+	internal static NativeWindow get_native () {
+		return native_window;
+	}
+	
+	internal static MainWindow get_current_window () {
+		return singleton;
+	}
+	 
+	public static FontDisplay get_current_display () {
+		return get_glyph_canvas ().get_current_display ();
+	}
+	
+	public static GlyphCanvas get_glyph_canvas () {
+		return glyph_canvas;
+	}
+	
+	internal static Glyph get_current_glyph () {
+		if (unlikely (is_null (Supplement.current_glyph))) {
+			warning ("No default glyph have been set yet.\n");
+			return new Glyph ("no_glyph_created");
+		}
+		
+		return Supplement.current_glyph;
+	}
+	
+	public static Toolbox get_toolbox () {
+		return get_singleton ().tools;
+	}
+	
+	internal static Tool get_tool (string n) {
+		return get_singleton ().tools.get_tool (n);
+	}
+	
+	public static TabBar get_tab_bar () {
+		return get_singleton ().tabs;
+	}
+
+	internal static Tab get_current_tab () {
+		return get_singleton ().tabs.get_selected_tab ();
+	}
+
+	internal static TooltipArea get_tool_tip () {
+		return tool_tip;
+	}
+
+	internal static bool select_tab (Tab t) {
+		return get_singleton ().tabs.selected_open_tab (t);
+	}
+
+	internal static OverView get_overview () {
+		OverView over_view;
+		
+		foreach (var t in tabs.tabs) {
+			if (t.get_display () is OverView) {
+				return (OverView) t.get_display ();
+			}
+		}
+		
+		over_view = new OverView();
+		tabs.add_unique_tab (over_view, 100, false);
+				
+		return over_view;
+	}
+	
+	internal static MenuTab get_content_display () {
+		if (unlikely ((MenuTab?) get_singleton ().menu_tab == null)) {
+			warning ("MenuTab not instantiated.");
+		}
+		
+		return get_singleton ().menu_tab;
+	}
+	
+	public static MainWindow get_singleton () {
+		return singleton;
+	}
+	
+	public static string? file_chooser (string title) {
+		return get_singleton ().native_window.file_chooser (title);
+	}
+	
+	public void set_title (string title) {
+		// FIXA:
+		// native_window.set_title (title);
+	}
+	
+	/** Reaload all paths and help lines from disk. */
+	internal static void clear_glyph_cache () {
+		Glyph g;
+		foreach (Tab t in get_tab_bar ().tabs) {
+			if (t.get_display () is Glyph) {
+				g = (Glyph) t.get_display ();
+				g.add_help_lines ();
+			}
+		}
+		
+		get_glyph_canvas ().redraw ();
+	}
+		
+	internal static void close_all_tabs () {
+		uint i = 0;
+		uint len = get_tab_bar ().get_length ();
+		
+		while (i < len) {
+			if (!get_tab_bar ().close_tab ((int) i)) {
+				i++;
+			}
+		}
+	}
+}
+
+}
diff --git a/libbirdfont/MenuAction.vala b/libbirdfont/MenuAction.valanew file mode 100644index 0000000..d44e7c4--- /dev/null+++ b/libbirdfont/MenuAction.vala@@ -0,0 +1,62 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++public class MenuAction : GLib.Object {+	public string label;+	public DropMenu.Selected action;+	public DropMenu? parent = null;+	public int index = -1;+	bool selected = false;+	+	public MenuAction (string label) {+		this.label = label;+	}+	+	public void set_selected (bool s) {+		selected = s;+	}+	+	public virtual void draw (double x, double y, Context cr) {+		if (selected) {+			cr.save ();+			cr.set_line_join (LineJoin.ROUND);+			cr.set_line_width (12);+			cr.set_source_rgba (102/255.0, 120/255.0, 149/255.0, 1);+			cr.rectangle (x - 2, y - 9, 88, 8);+			cr.fill_preserve ();+			cr.stroke ();+			cr.restore ();			+		}+		+		cr.save ();+		cr.set_source_rgba (1, 1, 1, 1);+		+		cr.set_font_size (12);+		cr.select_font_face ("Cantarell", FontSlant.NORMAL, FontWeight.NORMAL);+		+		cr.move_to (x, y);++		cr.show_text (label);+		cr.restore ();+	}+}++}diff --git a/libbirdfont/MenuTab.vala b/libbirdfont/MenuTab.valanew file mode 100644index 0000000..9371772--- /dev/null+++ b/libbirdfont/MenuTab.vala@@ -0,0 +1,339 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using WebKit;++namespace Supplement {++public class MenuTab : FontDisplay {	+	+	List<Font> recent_fonts = new List<Font> ();++	public MenuTab () {+		// html callbacks:+		add_html_callback ("export_name", (val) => {+			Font f = Supplement.get_current_font ();+			f.set_name (val);+			f.touch ();+		});++		add_html_callback ("export", (val) => {+			ExportTool.export_all ();+		});++		add_html_callback ("preview", (val) => {+			preview ();+		});		++		add_html_callback ("load", (val) => {+			load_font (val);+		});	++		add_html_callback ("glyph_sequence", (val) => {+			Preferences.set ("glyph_sequence", val);+		});++		add_html_callback ("help", (val) => {+			MainWindow.get_tool_tip ().show_text (val);+		});+	}+		+	public override string get_name () {+		return "Menu";+	}+	+	public override bool is_html_canvas () {+		return true;+	}++	public override string get_html () {+		Font f = Supplement.get_current_font ();+		StringBuilder c = new StringBuilder ();+		string fn;+		+		propagate_recent_files ();+		+		c.append (+"""+<html>+<head>+	<script type="text/javascript" src="supplement.js"></script>+	<style type="text/css">@import url("style.css");</style>+	<script type="text/javascript">+		document.onkeyup = update_text_fields; +	</script>+</head>+<body>+	<div class="inner_format_box">+		<div class="content">+			<div class="heading"><h2>""" + _("Preferences") + """</h2></div>+			<form>+""");+				+c.append ("""+				<h3>""" + _("Glyph sequence") + """</h3>+				<input class="text" type="text" id="glyph_sequence" value=""" + "\"" + Preferences.get ("glyph_sequence") + "\"" + """ onchange="update_text_fields ();"/><br />++				<h3>""" + _("Name") + """</h3>+				<input class="text" type="text" id="fontname" value=""" + "\"" + f.get_name () + "\"" + """ onchange="update_text_fields ();"/><br />+				+				<input class="button" type="button" value=""" + "\"" + _("Export") + "\"" + """ id="export_button" onclick="call ('export:fonts');" onmouseover="call ('help:(Ctrl+e) """ + _("Export SVG, TTF & EOT fonts") + """');"/>+				<input class="button" type="button" value=""" + "\"" + _("Preview") + "\"" + """ id="preview_button" onclick="call ('preview:fonts');" onmouseover="call ('help:(Ctrl+p) """ + _("Export SVG font and view the result") + """');"/><br />+""");+	+c.append ("""++			</form> ++		</div>+	</div>+	+	<img src="birdfont_logo.png" alt="" style="float:right;margin: 50px 0 0 0;">+	+	<br class="clearBoth" />+	+	<div class="recent_list">+""");++if (recent_fonts.length () > 0) {+	c.append ("""<div class="heading"><h2>""" + _("Recent files") + """</h2></div>""");+	c.append ("\n");+}++foreach (Font font in recent_fonts) {+	fn = (!) font.font_file;+	fn = fn.substring (fn.replace ("\\", "/").last_index_of ("/") + 1);	+	+	c.append ("""<div class="recent_font" """ + "onclick=\"call ('load:" + ((!) font.font_file).replace ("\\", "\\\\") + "');\">");++	c.append ("<div class=\"one_line\">");+	c.append (fn);+	c.append ("</div>");++	c.append ("<img src=\"");+	c.append (path_to_uri ((!) Supplement.get_thumbnail_directory ().get_path ()));+	c.append ("/");+	c.append (fn);+	c.append (@".png?$(Random.next_int ())\" alt=\"\">");+	+	c.append ("<br /><br />");+	c.append ("</div>\n");+}++c.append ("</div>\n");++c.append ("""+</body>+</html>+""");++		return c.str;+	}+	+	void translations () {+		// xgettext needs these lines in order to extract strings properly+		// TODO: update xgettext to handle vala strings well.+		_("Preferences");+		_("Export SVG, TTF & EOT fonts");+		_("Name");+		_("Glyph sequence");+		_("Export SVG font and view the result");+		_("Export SVG font and view the result");+	}++	public void propagate_recent_files () {+		Font font;++		while (recent_fonts.length () != 0) {+			recent_fonts.delete_link (recent_fonts.first ());+		}+		+		foreach (var f in Preferences.get_recent_files ()) {+			if (f == "") continue;+			+			File file = File.new_for_path (f);++			font = new Font ();+			+			font.set_font_file (f);+			+			if (file.query_exists ()) { +				recent_fonts.append (font);+			}+		}+		+		recent_fonts.reverse ();+	}+	+	public void load_font (string fn) {+		Font font = Supplement.get_current_font ();+			+		SaveDialog save = new SaveDialog ();+		save.finished.connect (() => {+			Font f;+			bool loaded;+			+			f = Supplement.get_current_font ();+			f.delete_backup ();+			+			MainWindow.clear_glyph_cache ();+			MainWindow.close_all_tabs ();+			+			loaded = f.load (fn);+			+			if (!unlikely (loaded)) {+				warning (@"Failed to load fond $fn");+				return;+			}+				+			MainWindow.get_singleton ().set_title (f.get_name ());+			+			MainWindow.get_toolbox ().remove_all_grid_buttons ();+			foreach (string v in f.grid_width) {+				MainWindow.get_toolbox ().parse_grid (v);+			}+			+			MainWindow.get_toolbox ().background_scale.set_value (f.background_scale);+			+			select_overview ();+		});+		+		MainWindow.get_tab_bar ().add_unique_tab (save, 80);+		+		if (!font.is_modified ()) {+			save.finished ();+		}+	}+	+	private static void select_overview () {+		Toolbox tb = MainWindow.get_toolbox ();+		+		if (Supplement.get_current_font ().is_empty ()) {+			tb.select_tool_by_name ("custom_character_set");+		} else {+			tb.select_tool_by_name ("available_characters");	+		}+	}++	public static bool save_as ()  {+		string? fn = null;+		string f;+		bool saved = false;+		Font font = Supplement.get_current_font ();+		+		fn = MainWindow.file_chooser (_("Save"));+		+		if (fn != null) {+			f = (!) fn;+			+			if (!f.has_suffix (".ffi")) {+				f += ".ffi";+			}+			+			font.font_file = f;+			save ();+			saved = true;+		}++		return saved;+	}++	public static bool save () {+		Font f = Supplement.get_current_font ();+		string fn;+		bool saved = false;+		+		fn = (!) f.font_file;+		+		if (f.font_file != null && fn.has_suffix (".ffi")) {+			f.background_scale = MainWindow.get_toolbox ().background_scale.get_display_value ();+			+			while (f.grid_width.length () > 0) {+				f.grid_width.remove_link (f.grid_width.first ());+			}+			+			foreach (SpinButton s in GridTool.sizes) {+				f.grid_width.append (s.get_display_value ());+			}+			+			f.save (fn);+			saved = true;+		} else {+			saved = save_as ();+		}+		+		return saved;+	}+	+	public static void new_file () {+		Font font = Supplement.get_current_font ();+		SaveDialog save = new SaveDialog ();+		save.finished.connect (() => {+			Font f = Supplement.get_current_font ();+			f.delete_backup ();+			+			Supplement.new_font ();+			MainWindow.close_all_tabs ();+			+			MainWindow.get_toolbox ().remove_all_grid_buttons ();+			MainWindow.get_toolbox ().add_new_grid ();+			MainWindow.get_toolbox ().add_new_grid ();+			+			select_overview ();+		});+		+		MainWindow.get_tab_bar ().add_unique_tab (save, 80);+		+		if (!font.is_modified ()) {+			save.finished ();+		}+	}+	+	public static void load () {+		SaveDialog save = new SaveDialog ();+		save.finished.connect (() => {+			load_new_font ();+		});+		MainWindow.get_tab_bar ().add_unique_tab (save, 80);+	}++	private static void load_new_font () {+		string? fn = MainWindow.file_chooser ("load");+		Font f = Supplement.get_current_font ();+		+		if (fn != null) {+			f.delete_backup ();+			+			MainWindow.clear_glyph_cache ();+			MainWindow.close_all_tabs ();+			f.load ((!)fn);+			+			MainWindow.get_singleton ().set_title (f.get_name ());+			select_overview ();		+		}+	}+	+	public static void show_kerning_context () {+		MainWindow.get_tab_bar ().add_unique_tab (new ContextDisplay (), 85, false);+	}+	+	public static void preview () {+		TabBar t = MainWindow.get_tab_bar ();+		t.add_unique_tab (new Preview (), 80);		+	}+}+}diff --git a/libbirdfont/MergeTool.vala b/libbirdfont/MergeTool.valanew file mode 100644index 0000000..030d9bb--- /dev/null+++ b/libbirdfont/MergeTool.vala@@ -0,0 +1,271 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++class MergeTool : Tool {++	public MergeTool (string n) {+		base (n, _("Merge paths"));+	+		select_action.connect((self) => {+			Glyph g = MainWindow.get_current_glyph ();+			g.merge_all ();+		});+	}++	public override bool test () {+		test_merge_second_triangle ();+		test_merge_simple_path_box_and_triangle ();+		test_merge_simple_path_box ();+		test_merge_odd_paths ();+		return true;+	}+	+	private void test_merge_simple_path_box () {+		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");++		this.yield ();+		MainWindow.get_tab_bar ().select_overview ();++		this.yield ();+		MainWindow.get_overview ().open_current_glyph ();+		+		pen_tool.test_select_action ();++		int x_offset = 10;+		int y_offset = 350;+		+		// draw it+		pen_tool.test_click_action (1, 10 + x_offset, 10 + y_offset);+		pen_tool.test_click_action (1, 20 + x_offset, 10 + y_offset);+		pen_tool.test_click_action (1, 20 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset); +		pen_tool.test_click_action (3, 0, 0);++		pen_tool.test_press_action (1, 1, 1);+		pen_tool.test_move_action (15 + x_offset, 15 + y_offset);+		pen_tool.test_release_action (1, 15 + x_offset, 15 + y_offset);+				+		pen_tool.test_click_action (1, 25 + x_offset, 15 + y_offset);+		pen_tool.test_click_action (1, 25 + x_offset, 25 + y_offset);+		pen_tool.test_click_action (1, 15 + x_offset, 25 + y_offset); +		pen_tool.test_click_action (3, 0, 0);+		+		// merge it+		this.test_select_action ();+		+		// test result+		Path merged_outline = new Path ();+		add_point_on_path (merged_outline, 10 + x_offset, 10 + y_offset);+		add_point_on_path (merged_outline, 20 + x_offset, 10 + y_offset);+		add_point_on_path (merged_outline, 20 + x_offset, 15 + y_offset);+		add_point_on_path (merged_outline, 25 + x_offset, 15 + y_offset);+		add_point_on_path (merged_outline, 25 + x_offset, 25 + y_offset);+		add_point_on_path (merged_outline, 15 + x_offset, 25 + y_offset);+		add_point_on_path (merged_outline, 15 + x_offset, 20 + y_offset);+		add_point_on_path (merged_outline, 10 + x_offset, 20 + y_offset);+		merged_outline.close ();+		+		// select path+		pen_tool.test_click_action (3, 12 + x_offset, 12 + y_offset);+		+		Glyph g = MainWindow.get_current_glyph ();+		Path? l = g.get_active_path ();+		+		if (l == null) {+			critical ("No path found in merge test, it did not merge correctly.");+			return;+		}+		+		Path last = (!) l;+		bool merged_path_looks_good = false;+		 merged_path_looks_good = last.test_is_outline (merged_outline);+		+		if (!merged_path_looks_good) critical ("Failed to merge path correctly.");+	}++	private void test_merge_simple_path_box_and_triangle () {+		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");++		this.yield ();+		MainWindow.get_tab_bar ().select_overview ();++		this.yield ();+		MainWindow.get_overview ().open_current_glyph ();+		+		pen_tool.test_select_action ();++		int x_offset = 40;+		int y_offset = 350;+		+		// draw it+		pen_tool.test_click_action (1, 0 + x_offset, -50 + y_offset);+		pen_tool.test_click_action (1, 25 + x_offset, -50 + y_offset);+		pen_tool.test_click_action (1, 25 + x_offset, 0 + y_offset);+		pen_tool.test_click_action (1, 0 + x_offset, 0 + y_offset); +		pen_tool.test_click_action (3, 0, 0);+		+		pen_tool.test_click_action (1, 0 + x_offset, 0 + y_offset);+		pen_tool.test_click_action (1, 50 + x_offset, -50 + y_offset);+		pen_tool.test_click_action (1, 50 + x_offset, 0 + y_offset); +		pen_tool.test_click_action (3, 0, 0);++		// merge it+		this.test_select_action ();+		+		// test result+		Path merged_outline = new Path ();+		add_point_on_path (merged_outline, 0 + x_offset, -50 + y_offset);+		add_point_on_path (merged_outline, 25 + x_offset, -50 + y_offset);+		add_point_on_path (merged_outline, -50 + x_offset, -50 + y_offset);+		add_point_on_path (merged_outline, -50 + x_offset, 0 + y_offset);+		add_point_on_path (merged_outline, 0 + x_offset, 0 + y_offset);+		merged_outline.close ();++		// select path+		pen_tool.test_click_action (3, 21 + x_offset, -11 + y_offset);+		+		Glyph g = MainWindow.get_current_glyph ();+		Path? l = g.get_active_path ();+		+		if (l == null) {+			critical ("No path found in test_merge_simple_path_box_and_triangle, it did not merge correctly.");+			return;+		}+		+		Path last = (!) l;+		bool merged_path_looks_good = false;+		 merged_path_looks_good = last.test_is_outline (merged_outline);+		+		if (!merged_path_looks_good) critical ("Failed to merge path correctly.");+	}++	private void test_merge_second_triangle () {+		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");++		this.yield ();+		MainWindow.get_tab_bar ().select_overview ();++		this.yield ();+		MainWindow.get_overview ().open_current_glyph ();+		+		pen_tool.test_select_action ();++		int x_offset = 100;+		int y_offset = 350;+		+		// draw it+		pen_tool.test_click_action (1, 25 + x_offset, 0 + y_offset);+		pen_tool.test_click_action (1, 25 + x_offset, -50 + y_offset);+		pen_tool.test_click_action (1, 0 + x_offset, -50 + y_offset);+		pen_tool.test_click_action (1, 0 + x_offset, 0 + y_offset); ++		pen_tool.test_click_action (3, 0, 0);+		+		pen_tool.test_click_action (1, 40 + x_offset, -50 + y_offset);+		pen_tool.test_click_action (1, 10 + x_offset, -20 + y_offset);+		pen_tool.test_click_action (1, 25 + x_offset, -20 + y_offset); +		pen_tool.test_click_action (3, 0, 0);+		+		// merge it+		this.test_select_action ();+		+		// test result+		Path merged_outline = new Path ();+		add_point_on_path (merged_outline, 0 + x_offset, -50 + y_offset);+		add_point_on_path (merged_outline, 25 + x_offset, -50 + y_offset);+		add_point_on_path (merged_outline, -50 + x_offset, -50 + y_offset);+		add_point_on_path (merged_outline, -50 + x_offset, 0 + y_offset);+		add_point_on_path (merged_outline, 0 + x_offset, 0 + y_offset);+		merged_outline.close ();++		// select path+		pen_tool.test_click_action (3, 21 + x_offset, -11 + y_offset);+		+		Glyph g = MainWindow.get_current_glyph ();+		Path? l = g.get_active_path ();+		+		if (l == null) {+			critical ("No path found in triangle_right, it did not merge correctly.");+			return;+		}+		+		Path last = (!) l;+		bool merged_path_looks_good = false;+		 merged_path_looks_good = last.test_is_outline (merged_outline);+		+		if (!merged_path_looks_good) critical ("Failed to merge path correctly.");+	}+	+	private void add_point_on_path (Path p, int x, int y) {+		Glyph g = MainWindow.get_current_glyph ();+		p.add (g.path_coordinate_x (x), g.path_coordinate_y (y));+	}+	+	+	private void test_merge_odd_paths () {+		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");++		this.yield ();+		MainWindow.get_tab_bar ().select_overview ();++		this.yield ();+		MainWindow.get_overview ().open_current_glyph ();+		+		pen_tool.test_select_action ();++		// paint+		int x_offset = 100;+		int y_offset = 100;+						+		// rectangle+		pen_tool.test_click_action (1, 100 + x_offset, 100 + y_offset);+		pen_tool.test_click_action (1, 170 + x_offset, 100 + y_offset);+		pen_tool.test_click_action (1, 170 + x_offset, 120 + y_offset);+		pen_tool.test_click_action (1, 100 + x_offset, 120 + y_offset); +		pen_tool.test_click_action (3, 0, 0); // close+		+		// triangle+		pen_tool.test_click_action (1, 100 + x_offset, 110 + y_offset);+		pen_tool.test_click_action (1, 180 + x_offset, 130 + y_offset);+		pen_tool.test_click_action (1, -10 + x_offset, 140 + y_offset);+		pen_tool.test_click_action (3, 0, 0);++		// several triangles+		pen_tool.test_click_action (1, 198, 379); +		pen_tool.test_click_action (1, 274, 328); +		pen_tool.test_click_action (1, 203, 286); +		pen_tool.test_click_action (3, 230, 333);++		pen_tool.test_click_action (1, 233, 429); +		pen_tool.test_click_action (1, 293, 382); +		pen_tool.test_click_action (1, 222, 322); +		pen_tool.test_click_action (3, 225, 406);++		pen_tool.test_click_action (1, 164, 316); +		pen_tool.test_click_action (1, 262, 289); +		pen_tool.test_click_action (1, 203, 260); +		pen_tool.test_click_action (3, 203, 260); ++		this.yield ();+		+		test_select_action ();+	}	+}++}diff --git a/libbirdfont/MoveTool.vala b/libbirdfont/MoveTool.valanew file mode 100644index 0000000..6b23ba6--- /dev/null+++ b/libbirdfont/MoveTool.vala@@ -0,0 +1,187 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Math;+using Cairo;++namespace Supplement {++class MoveTool : Tool {++	bool move_path = false;+	double last_x = 0;+	double last_y = 0;++	bool resize_path = false;+	Path? resized_path = null;+	double last_resize_y;+	+	ImageSurface? resize_handle;+	+	public MoveTool (string n) {+		base (n, _("Move paths"), 'm', CTRL);+		+		resize_handle = Icons.get_icon ("resize_handle.png");+	+		select_action.connect((self) => {+		});++		deselect_action.connect((self) => {+			Glyph glyph = MainWindow.get_current_glyph ();+			glyph.clear_active_paths ();+		});+				+		press_action.connect((self, b, x, y) => {+			Glyph glyph = MainWindow.get_current_glyph ();+						+			glyph.store_undo_state ();+			+			foreach (Path p in glyph.active_paths) {+				if (is_over_resize_handle (p, x, y)) {+					resize_path = true;+					resized_path = p;+					last_resize_y = y;+					return;+				}+			}+			+			if (resized_path != null) {+				if (is_over_resize_handle ((!) resized_path, x, y)) {+					resize_path = true;+					last_resize_y = y;+					return;					+				}+			}+			+			if (!glyph.is_over_selected_path (x, y)) {+				if (!glyph.select_path (x, y)) {+					glyph.clear_active_paths ();+				}+			}+			+			move_path = true;+				+			last_x = x;+			last_y = y;+		});++		release_action.connect((self, b, x, y) => {+			Glyph glyph = MainWindow.get_current_glyph ();+			+			move_path = false;+			resize_path = false;+				+			if (GridTool.is_visible ()) {+				foreach (Path p in glyph.active_paths) {+					tie_path_to_grid (p, x, y);+				}+			}+		});+		+		move_action.connect ((self, x, y)	 => {+			Glyph glyph = MainWindow.get_current_glyph ();+			double dx = last_x - x;+			double dy = last_y - y; +			double p = PenTool.precision;+			Path rp;+			double h, ratio;+			+			if (move_path) {+				foreach (Path path in glyph.active_paths) {+					path.move (glyph.ivz () * -dx * p, glyph.ivz () * dy * p);+				}+			}+			+			if (resize_path) {+				return_if_fail (!is_null (resized_path));+				rp = (!) resized_path;+				h = rp.xmax - rp.xmin;+				+				ratio = 1;+				ratio -= 0.7 * p * (Glyph.path_coordinate_y (last_resize_y) - Glyph.path_coordinate_y (y)) / h;+				+				rp.resize (ratio);+				last_resize_y = y;+			}++			last_x = x;+			last_y = y;++			MainWindow.get_glyph_canvas ().redraw ();+		});+		+		key_press_action.connect ((self, keyval) => {+			Glyph g = MainWindow.get_current_glyph ();+			+			if (keyval == Key.DEL) {+				foreach (Path p in g.active_paths) {+					g.path_list.remove (p);+					g.update_view ();+				}+			}+		});+		+		draw_action.connect ((self, cr, glyph) => {+			Glyph g = MainWindow.get_current_glyph ();+			ImageSurface img = (!) resize_handle;+		+			foreach (Path p in g.active_paths) {+				cr.set_source_surface (img, Glyph.reverse_path_coordinate_x (p.xmax) - 10, Glyph.reverse_path_coordinate_y (p.ymax) - 10);+				cr.paint ();	+			}+			+			if (resized_path != null) {+				cr.set_source_surface (img, Glyph.reverse_path_coordinate_x (((!) resized_path).xmax) - 10, Glyph.reverse_path_coordinate_y (((!)resized_path).ymax) - 10);+				cr.paint ();+			}+		});+	}++	bool is_over_resize_handle (Path p, double x, double y) {+		double handle_x = Math.fabs (Glyph.reverse_path_coordinate_x (p.xmax)); +		double handle_y = Math.fabs (Glyph.reverse_path_coordinate_y (p.ymax));+		return fabs (handle_x - x + 10) < 20 && fabs (handle_y - y + 10) < 20;+	}++	void tie_path_to_grid (Path p, double x, double y) {+		double sx, sy, qx, qy;	+		+		// tie to grid+		sx = p.xmax;+		sy = p.ymax;+		qx = p.xmin;+		qy = p.ymin;+		+		GridTool.tie_coordinate (ref sx, ref sy);+		GridTool.tie_coordinate (ref qx, ref qy);+		+		if (Math.fabs (qy - p.ymin) < Math.fabs (sy - p.ymax)) {+			p.move (0, qy - p.ymin);+		} else {+			p.move (0, sy - p.ymax);+		}++		if (Math.fabs (qx - p.xmin) < Math.fabs (sx - p.xmax)) {+			p.move (qx - p.xmin, 0);+		} else {+			p.move (sx - p.xmax, 0);+		}		+	}++}++}diff --git a/libbirdfont/NativeWindow.vala b/libbirdfont/NativeWindow.valanew file mode 100644index 0000000..3adf50b--- /dev/null+++ b/libbirdfont/NativeWindow.vala@@ -0,0 +1,35 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++public interface NativeWindow : GLib.Object {+	public abstract string? file_chooser (string title);+	public abstract string? set_title (string title);++	public abstract void toggle_expanded_margin_bottom ();+	public abstract void toggle_expanded_margin_right ();+	+	public abstract void update_window_size ();++	protected void webkit_callback (string s) {+		FontDisplay fd = MainWindow.get_current_display ();+		fd.process_property (s);+	}+}++}diff --git a/libbirdfont/OpenFontFormatReader.vala b/libbirdfont/OpenFontFormatReader.valanew file mode 100644index 0000000..d7815ed--- /dev/null+++ b/libbirdfont/OpenFontFormatReader.vala@@ -0,0 +1,136 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++[SimpleType]+[CCode (has_type_id = false)] // Vala bug+internal struct Fixed : uint32 {++	public void @set (uint16 upper, uint16 lower) {+		uint32* p = &this;+		*p = (upper << 16) + lower;+	}++	public bool @equals (uint16 upper, uint16 lower) {+		uint32 t = (upper << 16) + lower;+		return (t == this);		+	}++	public string get_string () {+		uint a = this >> 16;+		return @"$(a).$(this - (a << 16))";+	}+}++[SimpleType]+[CCode (has_type_id = false)] // same as above+internal struct F2Dot14 : uint32 {+}++errordomain BadFormat {+	PARSE+}++class OpenFontFormatReader : Object {+	+	public DirectoryTable directory_table;+	+	OtfInputStream dis;+	File file;+	+	public OpenFontFormatReader () {+		directory_table = new DirectoryTable ();+	}+	+	public void close () {+		dis.close ();+	}+	+	public Glyph? read_glyph (string name) {+		try {+			return directory_table.glyf_table.read_glyph (name);+		} catch (GLib.Error e) {+			warning (e.message);+		}+		+		return null;+	}+	+	public void parse_index (string file_name) throws Error {+		file = File.new_for_path (file_name);+		if (!file.query_exists ()) {+			throw new FileError.EXIST(@"OpenFontFormatReader: file does not exist. $((!) file.get_path ())");+		}+			+		dis = new OtfInputStream (file.read ());+		+		parse_index_tables ();+	}+	+	void parse_index_tables () throws Error {+		OffsetTable offset_table;+		FontData fd = new FontData ();+		+		FileInfo file_info = file.query_info ("*", FileQueryInfoFlags.NONE);+		uint32 file_size = (uint32) file_info.get_size ();++        try {+			fd.write_table (dis, 0, file_size);+		} catch (GLib.Error e) {+			warning (@"Failed to read font data. $(e.message)");+		}++		offset_table = new OffsetTable (directory_table);+		offset_table.parse (fd);+		+		directory_table = new DirectoryTable ();+		directory_table.set_offset_table (offset_table);+		directory_table.parse (fd, file, this);+	}++	public void set_limits () {+		Font f = OpenFontFormatWriter.font;+		+		if (is_null (f)) {+			f = Supplement.get_current_font ();+		}+		+		f.top_position = -get_ascender ();+		f.top_limit = f.top_position - 5;+		f.xheight_position = f.top_position - 5;+		f.bottom_position = -get_descender ();+		f.bottom_limit = f.bottom_position + 5;	+	}++	public double get_ascender () {+		return directory_table.hhea_table.get_ascender ();+	}+	+	public double get_descender () {+		return directory_table.hhea_table.get_descender ();+	}++	public unowned List<string> get_all_names () {+		return directory_table.post_table.get_all_names ();+	}+	+	public uint32 get_head_checksum () {+		return directory_table.head_table.get_adjusted_checksum ();+	}+}+}diff --git a/libbirdfont/OpenFontFormatWriter.vala b/libbirdfont/OpenFontFormatWriter.valanew file mode 100644index 0000000..71e2350--- /dev/null+++ b/libbirdfont/OpenFontFormatWriter.vala@@ -0,0 +1,4875 @@+/*
+    Copyright (C) 2012 Johan Mattsson
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using Math;
+
+namespace Supplement {
+
+const double UNITS = 10;
+
+class OpenFontFormatWriter : Object  {
+
+	DataOutputStream os;
+	DirectoryTable directory_table;
+	
+	public static Font font;
+	
+	public OpenFontFormatWriter () {
+		directory_table = new DirectoryTable ();
+	}
+	
+	public static Font get_current_font () {
+		return font;
+	}
+	
+	public void open (File file) throws Error {
+		assert (!is_null (file));
+		
+		if (file.query_exists ()) {
+			warning ("File exists in export.");
+			throw new FileError.EXIST("OpenFontFormatWriter: file exists.");
+		}
+		
+		os = new DataOutputStream(file.create (FileCreateFlags.REPLACE_DESTINATION));
+	}
+	
+	public void write_ttf_font (Font font) throws Error {
+		long dl;
+		uint8* data;
+		unowned List<Table> tables;
+		FontData fd;
+		uint l;
+		
+		this.font = font;
+				
+		directory_table.process ();	
+		tables = directory_table.get_tables ();
+
+		dl = directory_table.get_font_file_size ();
+		
+		if (dl == 0) {
+			warning ("font is of zero size.");
+			return;
+		}
+		
+		foreach (Table t in tables) {
+			fd = t.get_font_data ();
+			data = fd.table_data;
+			l = fd.length_with_padding ();
+			
+			for (int j = 0; j < l; j++) {
+				os.put_byte (data[j]);
+			}
+		}
+	}
+	
+	public void close () throws Error {
+		os.close ();
+	}
+}
+
+/** Reader for otf data types. */
+class OtfInputStream : Object  {
+	
+	public FileInputStream fin;
+	public DataInputStream din;
+	
+	public OtfInputStream (FileInputStream i) throws Error {
+		din = new DataInputStream (i);
+		fin = i;
+	}
+	
+	public void seek (int64 pos) throws Error
+		requires (fin.can_seek ()) {
+		int64 p = fin.tell ();		
+		fin.seek (pos - p, SeekType.CUR);
+	}
+
+	public uint8 read_byte () throws Error {
+		return din.read_byte ();
+	}
+	
+	public void close () {
+		try {
+			fin.close ();
+			din.close ();
+		} catch (GLib.IOError e) {
+			warning (e.message);
+		}
+	}
+}
+
+class FontData : Object {
+
+	// Read pointer
+	uint rp = 0;
+
+	// Write pointer
+	uint wp = 0;
+	
+	// length without padding
+	uint32 len = 0;
+	uint32 padding = 0;
+	
+	uint32 capacity;
+	
+	public uint8* table_data = null;
+	
+	public FontData (uint32 size = 1024) {
+		capacity = size;
+		table_data = malloc (size);
+	}
+	
+	~FontData () {
+		if (table_data != null) {
+			delete table_data;
+			table_data = null;
+		}
+	}
+	
+	public void write_at (uint pos, uint8 new_data) throws GLib.Error
+		requires (pos <= capacity) 
+	{		
+		if (unlikely (pos >= len)) {
+			warning ("end of table reached");
+			assert (false);
+		}
+		
+		table_data[pos]= new_data;
+	}
+	
+	public void write_table_data (FontData fd, uint32 offset, uint32 length) {
+		fd.seek (offset);
+		for (uint32 i = 0; i < length; i++) {
+			add (table_data [rp++]);
+		}
+		fd.seek (0);
+	}
+	
+	public void write_table (OtfInputStream dis, uint32 offset, uint32 length) throws Error {
+		uint32 l = length + (length % 4);  // padding after end of table
+		uint8 b;
+		
+		if (length >= l) {
+			expand (l);
+		}
+		
+		if (table_data == null) {
+			warning ("Failed to allocate memory for ttf data.");
+			return;
+		}
+		
+		seek (0);
+		dis.seek (offset);
+		
+		wp = 0;
+		while (wp < l) {
+			b = dis.read_byte ();
+			add (b);
+		}
+		
+		rp = 0;
+	}
+	
+	public uint length_with_padding () {
+		return len;
+	}	
+	
+	public uint length () {
+		return len - padding;
+	}
+	
+	public void pad () {
+		while (len % 4 != 0) {
+			add (0);
+			padding++;
+		}
+	}
+	
+	/** Add additional checksum data to this checksum. */
+	public void continous_check_sum (ref uint32 current_check_sum) {
+		uint trp = rp;
+		uint l;
+		
+		if (length_with_padding () % 4 != 0) {
+			warning ("Table is not padded to correct size.");
+		}
+				
+		seek (0);
+
+		l = (length () % 4 > 0) ? length () / 4 + 1 : length () / 4; 
+
+		for (uint32 i = 0; i < l; i++) {
+			current_check_sum += read_uint32 ();
+		}
+		
+		rp = trp;
+		
+		return;
+	}
+
+	public uint32 check_sum () {
+		uint32 sum = 0;
+		continous_check_sum (ref sum);
+		return sum;
+	}
+	
+	public void seek_end () {
+		seek (len);
+	}
+		
+	public void seek (uint i) {
+		rp = i;
+		wp = i;
+	}
+	
+	public uint8 read () {
+		if (unlikely (rp >= len)) {
+			warning ("end of table reached");
+			return 0;
+		}
+		
+		return table_data [rp++];
+	}
+	
+	public Fixed read_fixed () {
+		return (Fixed) read_uint32 ();
+	}
+
+	public uint32 read_ulong () {
+		return read_uint32 ();
+	}
+
+	public uint16 read_ushort () {
+		uint16 f;
+		f = read () << 8;
+		f += read ();
+		return f;
+	}
+	
+	public int16 read_int16 () {
+		int16 f;
+		f = read () << 8;
+		f += read ();
+		return f;		
+	}
+	
+	public int16 read_short () throws Error {
+		return read_int16 ();
+	}
+	
+	public uint32 read_uint32 () {
+		uint32 f;
+		f = read () << 8 * 3;
+		f += read () << 8 * 2;
+		f += read () << 8 * 1;
+		f += read () << 8 * 0;
+		return f;
+	}
+
+	public uint64 read_uint64 () {
+		uint64 f;
+		f = read () << 8 * 7;
+		f += read () << 8 * 6;
+		f += read () << 8 * 5;
+		f += read () << 8 * 4;
+		f += read () << 8 * 3;
+		f += read () << 8 * 2;
+		f += read () << 8 * 1;
+		f += read () << 8 * 0;
+		return f;
+	}
+
+	public F2Dot14 read_f2dot14 () throws Error {
+		F2Dot14 f = (F2Dot14) read_int16 ();
+		return f;
+	}
+
+	public uint64 read_udate () throws Error {
+		return read_uint64 ();
+	}
+
+	public uint8 read_byte () throws Error {
+		return read ();
+	}
+
+	public char read_char () throws Error {
+		return (char) read_byte ();
+	}
+	
+	public void add_udate (int64 d) throws Error {
+		add_64 (d);
+	}
+	
+	public void add_fixed (Fixed f) throws Error {
+		add_u32 (f);
+	}
+
+	public void add_fword (int16 word) {
+		add_16 (word);
+	}
+
+	public void add_f2dot14 (F2Dot14 f) throws Error {
+		add_u32 (f);
+	}
+
+	public void add_short (int16 d) throws Error {
+		add_16 (d);
+	}
+	
+	public void add_ushort (uint16 d) throws Error {
+		add_u16 (d);
+	}
+		
+	public void add_ulong (uint32 d) throws Error {
+		add_u32 (d);
+	}
+		
+	public void add_byte (uint8 b) throws Error {
+		add (b);
+	}
+	
+	private void expand (uint extra_bytes = 1024) {
+		capacity += extra_bytes;
+		table_data = (uint8*) try_realloc (table_data, capacity);
+		
+		if (table_data == null) {
+			warning ("Out of memory.");
+			capacity = 0;
+		}		
+	}
+	
+	public void add (uint8 d) {
+		if (unlikely (len == capacity)) {
+			expand ();
+		}
+		
+		table_data[wp] = d;
+
+		if (wp == len) {
+			len++;
+		}
+				
+		wp++;
+	}
+
+	public void add_littleendian_u16 (uint32 i) {
+		add ((int8) (i & 0x00FF));
+		add ((int8) ((i & 0xFF00) >> 8));
+	}
+		
+	public void add_u16 (uint16 d) {
+		uint16 n = d >> 8;
+		add ((uint8)n);
+		add ((uint8)(d - (n << 8)));
+	}
+
+	public void add_16 (int16 i) {
+		uint8 s = (uint8) (i >> 8);
+		
+		add ((uint8) s);
+		add ((uint8) (i - (s << 8)));
+	}
+
+	public void add_littleendian_u32 (uint32 i) {
+		add ((int8) (i & 0x000000FF));
+		add ((int8) ((i & 0x0000FF00) >> 8));
+		add ((int8) ((i & 0x00FF0000) >> 16));
+		add ((int8) ((i & 0xFF000000) >> 24));
+	}
+		
+	public void add_u32 (uint32 i) {
+		uint32 s = (uint16) (i >> 16);
+		
+		add_u16 ((uint16) s);
+		add_u16 ((uint16) (i - (s << 16)));
+	}
+
+	public void add_32 (int32 i) {
+		int32 s = (int16) (i >> 16);
+		
+		add_16 ((int16) s);
+		add_16 ((int16) (i - (s << 16)));
+	}
+	
+	public void add_u64(uint64 i) {
+		uint64 s = (uint32) (i >> 32);
+		
+		add_u32 ((uint32) s);
+		add_u32 ((uint32)(i - (s << 32)));		
+	}
+
+	public void add_64(int64 i) {
+		int64 s = (int32) (i >> 32);
+		
+		add_u32 ((int32) s);
+		add_u32 ((int32)(i - (s << 32)));		
+	}
+	
+	public void add_str_littleendian_utf16 (string s) {
+		add_str_utf16 (s, true);
+	}
+	
+	public void add_str_utf16 (string s, bool little_endian = false) {
+		int index = 0;
+		unichar c;
+		uint8 c0;
+		uint8 c1;
+		int l = 0;
+		
+		while (s.get_next_char (ref index, out c)) {
+			c0 = (uint8) (c >> 8);
+			c1 = (uint8) (c - (c0 << 8));
+			
+			if (little_endian) {
+				add (c1);
+				add (c0);
+			} else {
+				add (c0);
+				add (c1);
+			}
+			
+			l += 2;
+		}
+				
+		assert (l == 2 * s.char_count ());
+	}
+	
+	public void add_str (string s) {
+		uint8[] data = s.data;
+		for (int n = 0; n < data.length; n++) { 
+			add (data[n]);
+		}		
+	}
+	
+	public void add_tag (string s) 
+		requires (s.length == 4 && s.data.length == 4) {
+		add_str (s);
+	}
+}
+
+class Coordinate {
+	/** TTF coordinate flags. */
+
+	public static const uint8 NONE           = 0;
+	public static const uint8 ON_PATH        = 1 << 0;
+	public static const uint8 X_SHORT_VECTOR = 1 << 1;
+	public static const uint8 Y_SHORT_VECTOR = 1 << 2;
+	public static const uint8 REPEAT         = 1 << 3;
+
+	// same flag or short vector sign flag
+	public static const uint8 X_IS_SAME               = 1 << 4; 
+	public static const uint8 Y_IS_SAME               = 1 << 5;
+	public static const uint8 X_SHORT_VECTOR_POSITIVE = 1 << 4;
+	public static const uint8 Y_SHORT_VECTOR_POSITIVE = 1 << 5;
+
+}
+
+class Table : Object {
+
+	public string id = "NO_ID";
+
+	public uint32 checksum = 0;
+	public uint32 offset = 0;
+	public uint32 length = 0;
+
+	protected FontData? font_data = null;
+
+	public virtual string get_id () {
+		return id;
+	}
+	
+	public FontData get_font_data () {
+		if (font_data == null) {
+			warning (@"No font data for $(id).");
+			font_data = new FontData ();
+		}
+		
+		return (!) font_data;
+	}
+
+	public bool has_data () {
+		return length > 0;
+	}
+
+	public virtual void parse (FontData dis) throws GLib.Error {
+		warning (@"Parse is not implemented for $(id).");
+	}
+
+	/** Validate table checksum. */
+	public bool validate (FontData dis) {
+		bool valid;
+		
+		if (length == 0) {
+			stderr.printf (@"Table $id is of zero length.\n");			
+			valid = false;
+		} else {
+			valid = Table.validate_table (dis, checksum, offset, length, id);
+		}
+		
+		if (!valid) {
+			stderr.printf (@"Table $id is invalid.\n");
+		}
+		
+		return valid;
+	}
+
+	public static bool validate_table (FontData dis, uint32 checksum, uint32 offset, uint32 length, string name) {
+		uint32 ch = calculate_checksum (dis, offset, length, name);
+		bool c;
+		
+		c = (ch == checksum);
+	
+		if (!c) {
+			stderr.printf(@"Checksum does not match data for $(name).\n");
+			stderr.printf(@"name: $name, checksum: $checksum, offset: $offset, length: $length\n");
+			stderr.printf(@"calculated checksum $(ch)\n");
+		}
+		
+		return c;	
+	}
+	
+	public static uint32 calculate_checksum (FontData dis, uint32 offset, uint32 length, string name) {
+		uint32 checksum = 0;
+		uint32 l;
+			 
+		dis.seek (offset);
+
+		l = (length % 4 > 0) ? length / 4 + 1 : length / 4; 
+
+		for (uint32 i = 0; i < l; i++) {
+			checksum += dis.read_ulong ();
+		}
+		
+		return checksum;
+	}
+	
+	public static uint16 max_pow_2_less_than_i (uint16 ind) {
+		uint16 last = 0;
+		uint16 i = 1;
+		
+		while ((i <<= 1) < ind) {
+			last = i;
+		}
+		
+		return last;
+	}
+
+	public static uint16 max_log_2_less_than_i (uint16 ind) {
+		return (uint16) (Math.log (ind) / Math.log (2));
+	}
+}
+
+class LocaTable : Table {
+	
+	uint32* glyph_offsets = null;
+	public uint32 size = 0;
+	
+	public LocaTable () {
+		id = "loca";
+	}	
+	
+	~LocaTable () {
+		if (glyph_offsets != null) delete glyph_offsets;
+	}
+	
+	public uint32 get_offset (uint32 i) {
+		return_if_fail (glyph_offsets != null);
+		
+		if (size == 0) {
+			warning ("No glyphs in loca table");
+		}
+		
+		if (!(0 <= i < size + 1)) {
+			warning (@"No offset for glyph $i. Requires (0 <= $i < $(size + 1)");
+		}
+		
+		return glyph_offsets [i];
+	}
+	
+	/** Returns true if glyph at index i is empty and have no body to parse. */
+	public bool is_empty (uint32 i) {
+		return_if_fail (glyph_offsets != null);
+
+		if (size == 0) {
+			warning ("No glyphs in loca table");
+		}
+				
+		if (!(0 <= i < size + 1)) {
+			warning (@"No offset for glyph $i. Requires (0 <= $i < $(size + 1)");
+		}
+		
+		return glyph_offsets[i] == glyph_offsets[i + 1];
+	}
+	
+	public new void parse (FontData dis, HeadTable head_table, MaxpTable maxp_table) throws GLib.Error {
+		size = maxp_table.num_glyphs;
+		glyph_offsets = new uint32[size + 1];
+		
+		dis.seek (offset);
+		
+		printd (@"size: $size\n");
+		printd (@"length: $length\n");
+		printd (@"length/4-1: $(length / 4 - 1)\n");
+		printd (@"length/2-1: $(length / 2 - 1)\n");
+		printd (@"head_table.loca_offset_size: $(head_table.loca_offset_size)\n");
+		
+		switch (head_table.loca_offset_size) {
+			case 0:
+				for (long i = 0; i < size + 1; i++) {
+					glyph_offsets[i] = 2 * dis.read_ushort ();	
+					
+					if (0 < i < size && glyph_offsets[i - 1] > glyph_offsets[i]) {
+						warning (@"Invalid loca table, it must be sorted. ($(glyph_offsets[i - 1]) > $(glyph_offsets[i]))");
+					}
+				}
+				break;
+				
+			case 1:
+				for (long i = 0; i < size + 1; i++) {
+					glyph_offsets[i] = 	dis.read_ulong ();
+									
+					if (0 < i < size && glyph_offsets[i - 1] > glyph_offsets[i]) {
+						warning (@"Invalid loca table, it must be sorted. ($(glyph_offsets[i - 1]) > $(glyph_offsets[i]))");
+					}		
+				}
+
+				break;
+			
+			default:
+				warning ("unknown size for offset in loca table");
+				break;
+		}
+	}
+
+	public void print_offsets () {
+		for (int i = 0; i < size; i++) {
+			print (@"get_offset ($i): $(get_offset (i))\n");
+		}
+	}
+	
+	public void process (GlyfTable glyf_table, HeadTable head_table) {
+		FontData fd = new FontData ();
+		uint32 last = 0;
+		uint32 prev = 0;
+		int i = 0;
+		
+		foreach (uint32 o in glyf_table.location_offsets) {
+			if (i != 0 && (o - prev) % 4 != 0) {
+				warning (@"glyph length is not a multiple of four in gid $i");
+			}
+			
+			if (o % 4 != 0) {
+				warning ("glyph is not on a four byte boundry");
+				assert_not_reached ();
+			}
+			
+			prev = o;
+			i++;
+		}
+	
+		if (head_table.loca_offset_size == 0) {
+			foreach (uint32 o in glyf_table.location_offsets) {
+				fd.add_u16 ((uint16) (o / 2));
+				
+				if (o < last) {
+					warning (@"Loca table must be sorted. ($o < $last)");
+				}
+				
+				last = o;
+			}
+			
+		} else if (head_table.loca_offset_size == 1) {
+			foreach (uint32 o in glyf_table.location_offsets) {
+				fd.add_u32 (o);
+
+				if (o < last) {
+					warning (@"Loca table must be sorted. ($o < $last)");
+				}
+				
+				last = o;
+			}
+			
+		} else {
+			warn_if_reached ();
+		}
+
+		if (!(glyf_table.location_offsets.length () == glyf_table.glyphs.length () + 1)) {
+			warning (@"(glyf_table.location_offsets.length () == glyf_table.glyphs.length () + 1) ($(glyf_table.location_offsets.length ()) == $(glyf_table.glyphs.length () + 1))");
+		}
+
+		fd.pad ();		
+		font_data = fd;		
+	}
+}
+
+class GlyfTable : Table {
+	
+	// Flags for composite glyph
+	static const uint16 BOTH_ARE_WORDS = 1 << 0;
+	static const uint16 BOTH_ARE_XY_VALUES = 1 << 1;
+	static const uint16 ROUND_TO_GRID = 1 << 2;
+	static const uint16 SCALE = 1 << 3;
+	static const uint16 RESERVED = 1 << 4;
+	static const uint16 MORE_COMPONENTS = 1 << 5;
+	static const uint16 SCALE_X_Y = 1 << 6;
+	static const uint16 SCALE_WITH_ROTATTION = 1 << 7;
+	static const uint16 INSTRUCTIONS = 1 << 8;
+
+	public int16 xmin = int16.MAX;
+	public int16 ymin = int16.MAX;
+	public int16 xmax = int16.MIN;
+	public int16 ymax = int16.MIN;
+
+	public FontData dis;
+	public HeadTable head_table;
+	public HmtxTable hmtx_table;
+	public LocaTable loca_table;
+	public CmapTable cmap_table; // hack, hack, hack. cmap and post is null when inistialized and set in parse method
+	public PostTable post_table;
+	public KernTable kern_table;
+	
+	public List<uint32> location_offsets; 
+
+	// list of glyphs sorted in the order we expect to find them in a
+	// ttf font. notdef is the firs glyph followed by null and nonmarkingreturn.
+	// after that will all assigned glyphs appear in sorted order, all 
+	// remaining unassigned glyphs live in the last part of the file.	
+	public List<Glyph> glyphs;
+	
+	uint16 max_points = 0;
+	uint16 max_contours = 0;
+	
+	double total_width = 0;
+	int non_zero_glyphs = 0;
+	
+	public GlyfTable (LocaTable l) {
+		id = "glyf";
+		loca_table = l;
+		location_offsets = new List<uint32> ();
+		glyphs = new List<Glyph> ();
+	}	
+
+	public int get_gid (string name) {
+		int i = 0;
+		foreach (Glyph g in glyphs) {
+			if (g.name == name) {
+				return i;
+			}
+		
+			i++;
+		}
+		
+		return -1;
+	}
+
+	public int16 get_average_width () {
+		return (int16) (total_width / non_zero_glyphs);
+	}
+
+	public uint16 get_max_contours () {
+		return max_contours;
+	}
+
+	public uint16 get_max_points () {
+		return max_points;
+	}
+
+	public uint16 get_first_char () {
+		return 32; // space
+	}
+	
+	public uint16 get_last_char () {
+		unowned List<Glyph> gl = glyphs.last ();
+		Glyph g = (!) gl.data;
+		
+		while (gl != glyphs.first ()) {
+			g = (!) gl.data;
+			
+			if (!g.is_unassigned ()) {
+				break;
+			}
+			
+			gl = gl.prev;
+		}
+		
+		return (uint16)g.unichar_code; 
+	}
+	
+	public Glyph? read_glyph (string name) throws GLib.Error {
+		Glyph? glyph;
+		Glyph g;
+		int i;
+		KernList kl;
+		string right;
+		double units_per_em;
+		double kern_val;
+		
+		i = post_table.get_gid (name);
+		
+		if (i == -1) {
+			return null;
+		}
+		
+		glyph = parse_index (i, dis, loca_table, hmtx_table, head_table, post_table);
+		
+		if (glyph != null) {
+			printd (@"kern: ");
+			units_per_em = head_table.get_units_per_em ();
+			kl = kern_table.get_all_pairs (i);
+			foreach (Kern k in kl.kernings) {
+				g = (!) glyph;
+				right = post_table.get_name (k.right);
+				kern_val = k.kerning * 1000.0 / units_per_em;
+				g.add_kerning (right, kern_val);
+			}
+		}
+		
+		return glyph;
+	}
+	
+	public new void parse (FontData dis, CmapTable cmap_table, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table, KernTable kern_table) throws GLib.Error {
+		this.cmap_table = cmap_table;
+		this.post_table = post_table;
+		this.loca_table = loca;
+		this.hmtx_table = hmtx_table;
+		this.head_table = head_table;
+		this.kern_table = kern_table;
+		this.dis = dis;
+	}
+	
+	Glyph parse_index (int index, FontData dis, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table) throws GLib.Error {
+		Glyph glyph = new Glyph ("");
+		double xmin, xmax;
+		double units_per_em = head_table.get_units_per_em ();
+		unichar character = 0;
+		string name;	
+		uint32 end;
+		
+		character = cmap_table.get_char (index);
+		name = post_table.get_name (index);
+		
+		if (name == "") {
+			StringBuilder name_c = new StringBuilder ();
+			name_c.append_unichar (character);
+			name = name_c.str;
+		}
+
+		printd (@"name: $(name)\n");
+
+		if (!loca.is_empty (index)) {	
+			glyph = parse_next_glyf (dis, character, index, out xmin, out xmax, units_per_em);
+			
+			glyph.left_limit = xmin - hmtx_table.get_lsb (index);
+			glyph.left_limit = 0;
+			glyph.right_limit = glyph.left_limit + hmtx_table.get_advance (index);
+		} else {
+			// add empty glyph
+			glyph = new Glyph (name, character);
+			glyph.left_limit = -hmtx_table.get_lsb (index);
+			glyph.right_limit = hmtx_table.get_advance (index) - hmtx_table.get_lsb (index);				
+		}
+		
+		glyph.name = name;
+
+		if (character == 0) {
+			glyph.set_unassigned (true);
+		}
+		
+		if (character == 0 && name != "") {
+			stderr.printf (@"Got null character\n");
+			stderr.printf (@"gid: $index\n");
+			stderr.printf (@"char: $((uint) character)\n");
+			stderr.printf (@"name: $(name)\n");
+		}
+		
+		return glyph;
+	}
+	
+	Glyph parse_next_composite_glyf (FontData dis, unichar character, int pgid) throws Error {
+		uint16 component_flags = 0;
+		uint16 glyph_index;
+		int16 arg1 = 0;
+		int16 arg2 = 0;
+		uint16 arg1and2;
+		F2Dot14 scale;
+		
+		F2Dot14 scalex;
+		F2Dot14 scaley;
+		
+		F2Dot14 scale01;
+		F2Dot14 scale10;
+
+		Glyph glyph, linked_glyph;
+		List<int> x = new List<int> ();
+		List<int> y = new List<int> ();
+		List<int> gid = new List<int> ();
+		
+		double xmin, xmax;
+		double units_per_em = head_table.get_units_per_em ();
+		
+		int glid;
+		
+		StringBuilder name = new StringBuilder ();
+		name.append_unichar (character);
+		
+		glyph = new Glyph (name.str, character);
+		
+		do {
+			component_flags = dis.read_ushort ();
+			glyph_index = dis.read_ushort ();
+			
+			if ((component_flags & BOTH_ARE_WORDS) > 0) {
+				arg1 = dis.read_short ();
+				arg2 = dis.read_short ();			
+			} else if ((component_flags & BOTH_ARE_XY_VALUES) > 0) {
+				arg1 = dis.read_byte ();
+				arg2 = dis.read_byte ();
+			}
+			
+			gid.append (glyph_index);
+			x.append (arg1);
+			y.append (arg2);
+
+			// if ((component_flags & RESERVED) > 0)
+			
+			if ((component_flags & SCALE) > 0) {
+				scale = dis.read_f2dot14 ();
+			} else if ((component_flags & SCALE_X_Y) > 0) {
+				scalex = dis.read_f2dot14 ();
+				scaley = dis.read_f2dot14 ();
+			} else if ((component_flags & SCALE_WITH_ROTATTION) > 0) {
+				scalex = dis.read_f2dot14 ();
+				scale01 = dis.read_f2dot14 ();
+				scale10 = dis.read_f2dot14 ();
+				scaley = dis.read_f2dot14 ();
+			}
+			
+		} while ((component_flags & MORE_COMPONENTS) > 0);
+	
+		
+		for (int i = 0; i < gid.length (); i++) {
+			// compensate xmax ymax with coordinate
+			glid = gid.nth (i).data;
+
+			if (glid == pgid) {
+				warning ("Cannot link a glyph to it self.");
+				continue;
+			}
+
+			linked_glyph = parse_next_glyf (dis, character, glid, out xmin, out xmax, units_per_em);
+		}
+
+		
+		/*
+		if ((component_flags & INSTRUCTIONS) > 0) {
+			num_instructions = dis.read_ushort ();
+			
+			for (int i = 0; i < num_instructions; i++) {
+				dis.read_byte ();
+			}
+		}
+		*/
+		
+		return glyph;
+	}
+	
+	Glyph parse_next_glyf (FontData dis, unichar character, int gid, out double xmin, out double xmax, double units_per_em) throws Error {
+
+		uint16* end_points = null;
+		uint8* instructions = null;
+		uint8* flags = null;
+		int16* xcoordinates = null;
+		int16* ycoordinates = null;
+		
+		int npoints = 0;
+		
+		int16 ncontours;
+		int16 ixmin; // set boundries
+		int16 iymin;
+		int16 ixmax;
+		int16 iymax;
+		uint16 ninstructions;
+
+		int16 rxmin = int16.MAX; // real xmin
+		int16 rymin = int16.MAX;;
+		int16 rxmax = int16.MIN;
+		int16 rymax = int16.MIN;
+				
+		int nflags;
+		
+		Error? error = null;
+		
+		uint start, end, len;
+		
+		StringBuilder name = new StringBuilder ();
+		name.append_unichar (character);
+
+		start = loca_table.get_offset (gid);
+		end = loca_table.get_offset (gid + 1);
+		len = start - end;
+
+		dis.seek (offset + start);
+		
+		ncontours = dis.read_short ();
+		
+		return_val_if_fail (start < end, new Glyph (""));
+
+		if (ncontours == 0) {
+			warning (@"Got zero contours in glyph $(name.str).");
+
+			// should skip body
+		}
+				
+		if (ncontours == -1) {
+			return parse_next_composite_glyf (dis, character, gid);
+		}
+
+		return_val_if_fail (ncontours < len, new Glyph (""));
+						
+		if (ncontours < -1) {
+			warning (@"Got $ncontours contours in glyf table.");
+			error = new BadFormat.PARSE ("Invalid glyf");
+			throw error;
+		}
+		
+		ixmin = dis.read_short ();
+		iymin = dis.read_short ();
+		ixmax = dis.read_short ();
+		iymax = dis.read_short ();
+
+		end_points = new uint16[ncontours + 1];
+		for (int i = 0; i < ncontours; i++) {
+			end_points[i] = dis.read_ushort (); // FIXA: mind shot vector is negative
+			
+			if (i > 0 && end_points[i] < end_points[i -1]) {
+				warning (@"Next endpoint has bad value in $(name.str). (end_points[i] > end_points[i -1])  ($(end_points[i]) > $(end_points[i -1])) i: $i ncontours: $ncontours");
+			}
+		}
+		
+		if (ncontours > 0) {
+			npoints = end_points[ncontours - 1] + 1;
+		} else {
+			npoints = 0;
+		}
+		
+		return_val_if_fail (npoints < len, new Glyph.no_lines (""));
+		
+		ninstructions = dis.read_ushort ();
+		
+		return_val_if_fail (ninstructions < len, new Glyph.no_lines (""));
+		
+		instructions = new uint8[ninstructions + 1];
+		uint8 repeat;
+		for (int i = 0; i < ninstructions; i++) {
+			instructions[i] = dis.read_byte ();
+		}
+
+		nflags = 0;
+		flags = new uint8[npoints + 1];
+		for (int i = 0; i < npoints; i++) {
+			flags[i] = dis.read_byte ();
+			
+			if ((flags[i] & Coordinate.REPEAT) > 0) {
+				repeat = dis.read_byte ();
+				
+				if (i + repeat >= npoints) {
+					error = new BadFormat.PARSE ("Too many flags in glyf in glyph $(name.str). (i >= ninstructions).");
+					break;
+				}
+				
+				for (int j = 0; j < repeat; j++) {
+					flags[j + i + 1] = flags[i];
+				}
+				
+				nflags += repeat;
+				i += repeat;
+			}
+			
+			nflags++;
+		}
+		
+		if (nflags != npoints) {
+			warning (@"(nflags != npoints) ($nflags != $npoints) in $(name.str)");
+			error = new BadFormat.PARSE (@"Wrong number of flags in glyph $(name.str). (nflags != npoints) ($nflags != $npoints)");
+		}
+		
+		warn_if_fail (nflags == npoints);
+
+		printd (@"npoints: $npoints\n");
+		printd (@"ncontours: $ncontours\n");
+		printd (@"ninstructions: $ninstructions\n");
+		printd (@"nflags: $nflags\n");
+				
+		int16 last = 0;
+		xcoordinates = new int16[npoints + 1];
+		for (int i = 0; i < npoints; i++) {
+			if ((flags[i] & Coordinate.X_SHORT_VECTOR) > 0) {	
+				if ((flags[i] & Coordinate.X_SHORT_VECTOR_POSITIVE) > 0) {
+					xcoordinates[i] = last + dis.read_byte ();
+				} else {
+					xcoordinates[i] = last - dis.read_byte ();
+				}
+			} else {
+				if ((flags[i] & Coordinate.X_IS_SAME) > 0) {
+					xcoordinates[i] = last;
+				} else {
+					xcoordinates[i] = last + dis.read_short ();
+				}
+			}
+			
+			last = xcoordinates[i];
+			
+			if (last > rxmax) rxmax = last;
+			if (last < rxmin) rxmin = last;
+			
+			if (!(ixmin <= last <= ixmax))	{
+				stderr.printf (@"x is out of bounds in glyph $(name.str). ($ixmin <= $last <= $ixmax) char $((uint)character)\n");
+			}
+			
+			if (!(head_table.xmin <= last <= head_table.xmax))	{
+				stderr.printf (@"x is outside of of font bounding box in glyph $(name.str). ($(head_table.xmin) <= $last <= $(head_table.xmax)) char $((uint)character)\n");
+			}			
+		}
+		
+		last = 0;
+		ycoordinates = new int16[npoints + 1];
+		for (int i = 0; i < npoints; i++) {
+			if ((flags[i] & Coordinate.Y_SHORT_VECTOR) > 0) {	
+				if ((flags[i] & Coordinate.Y_SHORT_VECTOR_POSITIVE) > 0) {
+					ycoordinates[i] = last + dis.read_byte ();
+				} else {
+					ycoordinates[i] = last - dis.read_byte ();
+				}
+			} else {
+				if ((flags[i] & Coordinate.Y_IS_SAME) > 0) {
+					ycoordinates[i] = last;
+				} else {
+					ycoordinates[i] = last + dis.read_short ();
+				}
+			}
+			
+			last = ycoordinates[i];
+
+			if (last > rymax) rymax = last;
+			if (last < rymin) rymin = last;
+			
+			if (!(iymin <= last <= iymax))	{
+				stderr.printf (@"y is out of bounds in glyph $(name.str). ($iymin <= $last <= $iymax) char $((uint)character)\n");
+			}
+			
+			if (!(head_table.ymin <= last <= head_table.ymax))	{
+				stderr.printf (@"y is outside of of font bounding box in glyph $(name.str). ($(head_table.ymin) <= $last <= $(head_table.ymax)) char $((uint)character)\n");
+			}
+		}
+		
+		if (rymin != iymin || rxmin != ixmin || rxmax != ixmax || rymax != iymax) {
+			warning (@"Warning real boundry for glyph does not match boundry set in glyph header for glyph $(name.str).");
+			stderr.printf (@"ymin: $rymin header: $iymin\n");
+			stderr.printf (@"xmin: $rxmin header: $ixmin\n");
+			stderr.printf (@"ymax: $rymax header: $iymax\n");
+			stderr.printf (@"xmax: $rxmax header: $ixmax\n");
+		} 
+		
+		int j = 0;
+		int first_point;
+		int last_point = 0;
+		Glyph glyph;
+		double x, y;
+
+		glyph = new Glyph (name.str, character);
+		
+		xmin = ixmin * 1000.0 / units_per_em;
+		xmax = ixmax * 1000.0 / units_per_em;
+		
+		for (int i = 0; i < ncontours; i++) {
+			x = 0;
+			y = 0;
+			
+			Path path = new Path ();
+			EditPoint edit_point = new EditPoint ();
+			bool prev_is_curve = false;
+			
+			first_point = j;
+			last_point = end_points[i];
+			for (; j <= end_points[i]; j++) {
+
+				if (j >= npoints) {
+					warning (@"j >= npoints in glyph $(name.str). (j: $j, end_points[i]: $(end_points[i]), npoints: $npoints)");
+					break;
+				}
+								
+				x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width
+				y = ycoordinates[j] * 1000.0 / units_per_em;
+				
+				if ((flags[j] & Coordinate.ON_PATH) > 0) {
+					// Point
+					edit_point = new EditPoint ();
+					edit_point.set_position (x, y);
+					path.add_point (edit_point);
+					
+					if (prev_is_curve) {
+						edit_point.get_left_handle ().set_point_type (PointType.NONE);
+						edit_point.get_left_handle ().length = 0;
+					} else {
+						edit_point.recalculate_linear_handles ();
+					}
+									
+					prev_is_curve = false;
+				} else {
+									
+					if (prev_is_curve) {
+						x = x - (x - edit_point.right_handle.x ()) / 2;
+						y = y - (y - edit_point.right_handle.y ()) / 2;
+
+						edit_point = new EditPoint ();
+						edit_point.set_position (x, y);
+						path.add_point (edit_point);
+					}
+
+					x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width
+					y = ycoordinates[j] * 1000.0 / units_per_em;
+
+					edit_point.get_left_handle ().set_point_type (PointType.NONE);
+					edit_point.get_left_handle ().length = 0;
+						
+					edit_point.type = PointType.CURVE;
+					edit_point.get_right_handle ().set_point_type (PointType.CURVE);
+					edit_point.get_right_handle ().move_to_coordinate (x, y);
+					
+					prev_is_curve = true;
+				} 
+			}
+			
+			// last to first point
+			if (prev_is_curve) {
+				x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width
+				y = ycoordinates[first_point] * 1000.0 / units_per_em;
+				
+				x = x - (x - edit_point.right_handle.x ()) / 2;
+				y = y - (y - edit_point.right_handle.y ()) / 2;
+				
+				edit_point = new EditPoint ();
+				edit_point.set_position (x, y);
+				path.add_point (edit_point);
+				
+				x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width
+				y = ycoordinates[first_point] * 1000.0 / units_per_em;
+
+				edit_point.get_left_handle ().set_point_type (PointType.CURVE);
+				edit_point.get_left_handle ().length = 0;
+					
+				edit_point.type = PointType.CURVE;
+				edit_point.get_right_handle ().set_point_type (PointType.CURVE);
+				edit_point.get_right_handle ().move_to_coordinate (x, y);
+			}
+			
+			// curve last to first
+			x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width
+			y = ycoordinates[first_point] * 1000.0 / units_per_em;
+			edit_point.type = PointType.CURVE;
+			edit_point.get_right_handle ().set_point_type (PointType.CURVE);
+			edit_point.get_right_handle ().move_to_coordinate (x, y);
+			
+			path.close ();
+			
+			glyph.add_path (path);
+		}
+		
+		// glyphs with no bounding boxes
+		if (ixmax <= ixmin) {
+			warning (@"Bounding box is bad. (xmax == xmin) ($xmax == $xmin)");
+			
+			if (glyph.path_list.length () > 0) {
+				
+				Path ps = ((!) glyph.path_list.first ()).data;
+				
+				ps.update_region_boundries ();
+				xmin = ps.xmin;
+				xmax = ps.xmax;
+
+				foreach (Path p in glyph.path_list) {
+					p.update_region_boundries ();
+					
+					if (p.xmin < xmin) {
+						xmin = p.xmin;
+					}
+					
+					if (p.xmax > xmax) {
+						xmax = p.xmax;
+					}
+				}
+				
+			}
+		}
+						
+		if (end_points != null) delete end_points;
+		if (instructions != null) delete instructions;
+		if (flags != null) delete flags;
+		if (xcoordinates != null) delete xcoordinates;
+		if (ycoordinates != null) delete ycoordinates;
+		
+		if (error != null) {
+			warning ("failed to parse glyph");
+			throw (!) error;
+		}
+		
+		return glyph;
+	}
+
+	public void process_glyph (Glyph g, FontData fd) throws GLib.Error {
+		int16 txmin, tymin, txmax, tymax;
+
+		int16 end_point;
+		int16 last_end_point;
+		int16 npoints;
+		int16 ncontours;
+		int16 nflags;
+		
+		double x, y;
+
+		Font font = OpenFontFormatWriter.get_current_font ();
+		int glyph_offset;
+		
+		uint len; 
+		uint coordinate_length;
+		
+		fd.seek_end (); // append glyph
+		
+		glyph_offset = (int) fd.length ();
+		
+		printd (@"glyph_offset: $(glyph_offset)\n");
+		
+		g.remove_empty_paths ();
+		if (g.path_list.length () == 0) {
+			// location_offsets == location_offset + 1 to tell parser that this glyf does not have a body
+			return;
+		}
+		
+		non_zero_glyphs++;
+		
+		ncontours = (int16) g.path_list.length ();
+		fd.add_short (ncontours);
+		
+		txmin = int16.MAX;
+		tymin = int16.MAX;
+		txmax = int16.MIN;
+		tymax = int16.MIN;
+		
+		// bounding box will be set again after coordinate arrays have been created
+		fd.add_16 (10);
+		fd.add_16 (20);
+		fd.add_16 (30);
+		fd.add_16 (40);
+		
+		// end points
+		end_point = 0;
+		last_end_point = 0;
+		foreach (Path p in g.path_list) {
+			p = p.get_quadratic_points ();
+			foreach (EditPoint e in p.points) {
+				end_point++;
+				
+				if (e.get_right_handle ().type == PointType.CURVE) {
+					end_point++;
+				}
+			}
+			fd.add_u16 (end_point - 1);
+			
+			if (end_point - 1 < last_end_point) {
+				warning (@"Next endpoint has bad value. (end_point - 1 < last_end_point)  ($(end_point - 1) < $last_end_point");
+			}
+			
+			last_end_point = end_point - 1;
+		}
+		
+		fd.add_u16 (0); // instruction length 
+		
+		uint glyph_header = 12 + ncontours * 2;
+		
+		printd (@"\next glyf: $(g.name)\n");
+		printd (@"glyf header length: $(glyph_header)\n");
+		
+		// instructions should go here 
+		
+		npoints = (ncontours > 0) ? end_point : 0; // +1?
+		
+		if (npoints > max_points) {
+			max_points = npoints;
+		}
+		
+		if (ncontours > max_contours) {
+			max_contours = ncontours;
+		}
+		
+		// flags
+		nflags = 0;
+		List<uint8> flags = new List<uint8> ();
+		foreach (Path p in g.path_list) {
+			p = p.get_quadratic_points ();
+			foreach (EditPoint e in p.points) {
+				fd.add_byte (Coordinate.ON_PATH);
+				flags.append (Coordinate.ON_PATH);
+				nflags++;
+				
+				if (e.get_right_handle ().type == PointType.CURVE) {
+					fd.add_byte (Coordinate.NONE);
+					flags.append (Coordinate.NONE);
+					nflags++;
+				}
+			}
+		}
+		
+		if (nflags != npoints) {
+			warning (@"(nflags != npoints)  ($nflags != $npoints) in glyph $(g.name). ncontours: $ncontours");
+		}
+		assert (nflags == npoints);
+
+		printd (@"flags: $(nflags)\n");
+		
+		// x coordinates
+		List<int16> coordinate_x = new List<int16> ();
+		List<int16> coordinate_y = new List<int16> ();
+		double prev = 0;
+		
+		foreach (Path p in g.path_list) {
+			p = p.get_quadratic_points ();
+
+			foreach (EditPoint e in p.points) {
+				x = rint (e.x * UNITS - prev - g.left_limit * UNITS);
+
+				fd.add_16 ((int16) x);
+				coordinate_x.append ((int16) x);
+				+				prev = rint (e.x * UNITS - g.left_limit * UNITS);
+				+				if (e.get_right_handle ().type == PointType.CURVE) {
+					x = rint (e.get_right_handle ().x () * UNITS - prev - g.left_limit * UNITS);
+
+					fd.add_16 ((int16) x);
+					coordinate_x.append ((int16) x);
+					
+					prev = rint (e.get_right_handle ().x () * UNITS - g.left_limit * UNITS);
+				}
+			}
+		}
+
+		// y coordinates
+		prev = 0;
+		foreach (Path p in g.path_list) {
+			p = p.get_quadratic_points ();
+			foreach (EditPoint e in p.points) {
+				y = rint (e.y * UNITS - prev + font.base_line  * UNITS);
+				fd.add_16 ((int16) y);
+				+				coordinate_y.append ((int16) y);+				
+				prev = rint (e.y * UNITS + font.base_line * UNITS);
+
+				if (e.get_right_handle ().type == PointType.CURVE) {
+					y = rint (e.get_right_handle ().y () * UNITS - prev + font.base_line * UNITS);
+					
+					fd.add_16 ((int16) y);
+					coordinate_y.append ((int16) y);
+					
+					prev = rint (e.get_right_handle ().y () * UNITS + font.base_line  * UNITS);
+				}
+			}
+		}
+		
+		len = fd.length ();
+		printd (@"fd.length (): $(fd.length ())\n");
+		coordinate_length = fd.length () - nflags - glyph_header;
+		printd (@"coordinate_length: $(coordinate_length)\n");
+		assert (fd.length () > nflags + glyph_header);
+				
+		// bounding box	
+		int16 last = 0;
+			
+		int i = 0;
+		foreach (int16 c in coordinate_x) {
+			c += last;
+			
+			// Only on curve points are good for calculating bounding box
+			if ((flags.nth (i).data & Coordinate.ON_PATH) > 0) { 
+				if (c < txmin) txmin = c;
+				if (c > txmax) txmax = c;
+			}
+				
+			last = c;
+			i++;
+		}
+
+		last = 0;
+		i = 0;
+		foreach (int16 c in coordinate_y) {
+			c += last;
+			
+			if ((flags.nth (i).data & Coordinate.ON_PATH) > 0) {
+				if (c < tymin) tymin = c;
+				if (c > tymax) tymax = c;			
+			}
+			
+			last = c;
+			i++;
+		}
+		
+		fd.seek_end ();
+		
+		printd (@"glyph_offset: $(glyph_offset)\n");
+		printd (@"len: $(len)\n");
+		
+		fd.seek (glyph_offset + 2); // go to box boundries for this glyf
+		// assert (fd.read_short () == int16.MAX);
+
+		// add bounding box
+		fd.add_16 (txmin);
+		fd.add_16 (tymin);
+		fd.add_16 (txmax);
+		fd.add_16 (tymax);
+		fd.seek_end ();
+	
+		assert (len == fd.length ());
+		
+		fd.seek (glyph_offset + 2);
+		assert (fd.read_int16 () == txmin);
+		assert (fd.read_int16 () == tymin);
+		assert (fd.read_int16 () == txmax);
+		assert (fd.read_int16 () == tymax);
+		fd.seek_end ();
+
+		printd (@"\n");
+		printd (@"txmin: $txmin\n");
+		printd (@"tymin: $tymin\n");
+		printd (@"txmax: $txmax\n");
+		printd (@"tymax: $tymax\n");
+
+		// save this for head table
+		if (txmin < this.xmin) this.xmin = txmin;
+		if (tymin < this.ymin) this.ymin = tymin;
+		if (txmax > this.xmax) this.xmax = txmax;
+		if (tymax > this.ymax) this.ymax = tymax;
+				
+		// part of average width calculation for OS/2 table
+		total_width += xmax - xmin;
+		
+		printd (@"length before padding: $(fd.length ())\n");
+		
+		// all glyphs needs padding for loca table to be correct
+		while (fd.length () % 4 != 0) {
+			fd.add (0);
+		}
+		printd (@"length after padding: $(fd.length ())\n");
+	}
+
+	// necessary in order to have glyphs sorted according to ttf specification
+	public void create_glyph_table () {
+		Glyph? gl;
+		Glyph g;
+		Font font = OpenFontFormatWriter.get_current_font ();
+		uint32 indice;
+		List<Glyph> unassigned_glyphs;
+		
+		// add notdef. character at index zero + other special chars first
+		glyphs.append (font.get_not_def_character ());
+		glyphs.append (font.get_null_character ());
+		glyphs.append (font.get_nonmarking_return ());
+		glyphs.append (font.get_space ());
+		
+		unassigned_glyphs = new List<Glyph> ();
+		
+		// add glyphs, first all assigned then the unassigned ones
+		for (indice = 0; (gl = font.get_glyph_indice (indice)) != null; indice++) {		
+			g = (!) gl;
+			
+			if (g.unichar_code <= 27) { // skip control characters
+				continue;
+			}
+
+			if (g.unichar_code == 32) { // skip space
+				continue;
+			}
+						
+			if (g.name == ".notdef") {
+				continue;
+			}
+			
+			g.remove_empty_paths ();
+
+			if (!g.is_unassigned ()) {
+				glyphs.append (g);
+			} else {
+				unassigned_glyphs.append (g);
+			}
+		}
+		
+		foreach (Glyph ug in unassigned_glyphs) {
+			// glyphs.append (ug);
+		}
+		
+	}
+
+	public void process () throws GLib.Error {
+		FontData fd = new FontData ();
+		
+		create_glyph_table ();
+		uint last_len = 0;
+		foreach (Glyph g in glyphs) {
+			// set values for loca table
+			assert (fd.length () % 4 == 0);
+			location_offsets.append (fd.length ());
+			process_glyph (g, fd);
+
+			printd (@"glyf length: $(fd.length () - last_len)\n");
+			printd (@"loca fd.length (): $(fd.length ())\n");
+
+			last_len = fd.length ();
+		}
+
+		location_offsets.append (fd.length ()); // last entry in loca table is special
+		
+		// every glyph is padded, no padding to be done here
+		assert (fd.length () % 4 == 0);
+
+		font_data = fd;	
+	}
+}
+
+class CmapSubtable : Table {
+
+	// Override these methods in subtables for each format.
+	
+	/** Obtain length of subtable in bytes. */
+	public virtual uint get_length () {
+		warning ("Invalid CmapSubtable");
+		return 0;
+	}
+	
+	/** Get char code for a glyph id. */
+	public virtual unichar get_char (uint32 i) {
+		warning ("Invalid CmapSubtable");
+		return 0;
+	}
+	
+	public void print_cmap () {
+		StringBuilder s;
+		unichar c;
+		for (uint32 i = 0; i < get_length (); i++) {
+			s = new StringBuilder ();
+			c = get_char (i);
+			s.append_unichar (c);
+			printd (@"Char: $(s.str)  val ($((uint32)c))\tindice: $(i)\n");
+		}
+	}
+}
+
+/** Format 4 cmap subtable */
+class CmapSubtableWindowsUnicode : CmapSubtable {
+	uint16 format = 0;
+	HashTable <uint64?, unichar> table = new HashTable <uint64?, unichar> (int64_hash, int_equal);
+	
+	public CmapSubtableWindowsUnicode () {
+	}
+	
+	~CmapSubtableWindowsUnicode () {
+
+	}
+
+	public override uint get_length () {
+		return table.size ();
+	}
+	
+	public override unichar get_char (uint32 indice) {
+		int64? c = table.lookup (indice);
+		
+		if (c == 0 && indice == 0) {
+			return 0;
+		}
+		
+		if (c == 0) {
+			while (table.lookup (--indice) == 0) {
+				if (indice == 0) {
+					return 0;
+				}
+			} 
+			
+			warning (@"There is no char for glyph number $indice in cmap table. table.size: $(table.size ()))");
+			return 0;
+		}
+		
+		return (unichar) c;
+	}
+	
+	public override void parse (FontData dis) throws GLib.Error {
+		dis.seek (offset);
+		
+		format = dis.read_ushort ();
+		
+		switch (format) {
+			case 4:
+				parse_format4 (dis);
+				break;
+			
+			default:
+				stderr.printf (@"CmapSubtable is in format $format, it is not supportet (yet).\n");
+				break;
+		}
+	}
+		
+	public void parse_format4 (FontData dis) throws GLib.Error {
+		uint16 lang;
+		uint16 seg_count_x2;
+		uint16 seg_count;
+		uint16 search_range;
+		uint16 entry_selector;
+		uint16 range_shift;
+
+		uint16* end_char = null;
+		uint16* start_char = null;
+		int16* id_delta = null;
+		uint16* id_range_offset = null;
+		uint16* glyph_id_array = null;
+	
+		uint32 gid_len;
+		
+		length = dis.read_ushort ();
+		lang = dis.read_ushort ();
+		seg_count_x2 = dis.read_ushort ();
+		search_range = dis.read_ushort ();
+		entry_selector = dis.read_ushort ();
+		range_shift = dis.read_ushort ();
+		
+		return_if_fail (seg_count_x2 % 2 == 0);
+
+		seg_count = seg_count_x2 / 2;
+
+		end_char = new uint16[seg_count];
+		for (int i = 0; i < seg_count; i++) {
+			end_char[i] = dis.read_ushort ();
+		}
+		
+		if (end_char[seg_count - 1] != 0xFFFF) {
+			warning ("end_char is $(end_char[seg_count - 1]), expecting 0xFFFF.");
+		}
+		
+		dis.read_ushort (); // Reserved
+		
+		start_char = new uint16[seg_count];
+		for (int i = 0; i < seg_count; i++) {
+			start_char[i] = dis.read_ushort ();
+		}
+
+		id_delta = new int16[seg_count];
+		for (int i = 0; i < seg_count; i++) {
+			id_delta[i] = dis.read_short ();
+		}
+
+		id_range_offset = new uint16[seg_count];
+		for (int i = 0; i < seg_count; i++) {
+			id_range_offset[i] = dis.read_ushort ();
+		}
+
+		if (length == 0) {
+			warning ("cmap subtable version 4 has length 0.");
+			return;
+		}
+
+		gid_len = (length - 16 - 8 * seg_count) / 2;
+		glyph_id_array = new uint16[gid_len];
+		for (int i = 0; i < gid_len; i++) {
+			glyph_id_array[i] = dis.read_ushort ();
+		}
+		
+		// map all values in a hashtable
+		int indice = 0;
+		unichar character = 0;
+		uint32 id;
+		for (uint16 i = 0; i < seg_count && start_char[i] != 0xFFFF; i++) {
+			
+			// print_range (start_char[i], end_char[i], id_delta[i], id_range_offset[i]);
+			
+			uint16 j = 0;
+			do {
+				character = start_char[i] + j;
+				indice = start_char[i] + id_delta[i] + j;
+				
+				if (id_range_offset[i] == 0) {
+					table.insert (indice, character);
+				} else {
+					// the awkward indexing trick:
+					id = id_range_offset[i] / 2 + j + i - seg_count;
+					
+					if (!(0 <= id < gid_len)) {
+						warning (@"(0 <= id < gid_len) (0 <= $id < $gid_len)");
+						break;
+					}
+					
+					indice = glyph_id_array [id] + id_delta[i];
+										
+					StringBuilder s = new StringBuilder ();
+					s.append_unichar (character);
+										
+					table.insert (indice, character);
+				}
+				
+				j++;
+			} while (character != end_char[i]);
+	
+		}
+		
+		if (end_char != null) delete end_char;
+		if (start_char != null) delete start_char;
+		if (id_delta != null) delete id_delta;
+		if (id_range_offset != null) delete id_range_offset;
+		if (glyph_id_array != null) delete glyph_id_array;
+		
+		// it has a character for every glyph indice
+		// assert (validate_subtable ());
+	}
+	
+	void print_range (unichar start_char, unichar end_char, uint16 delta_offset, uint16 range_offset) {
+		StringBuilder s = new StringBuilder ();
+		StringBuilder e = new StringBuilder ();
+		
+		s.append_unichar (start_char);
+		e.append_unichar (end_char);
+		
+		print (@"New range $(s.str) - $(e.str) delta: $delta_offset, range: $range_offset\n");
+	}
+	
+	public void process (FontData fd, GlyfTable glyf_table) throws GLib.Error {
+		GlyphRange glyph_range = new GlyphRange ();
+		unowned List<UniRange> ranges;
+
+		uint16 seg_count_2;
+		uint16 seg_count;
+		uint16 search_range;
+		uint16 entry_selector;
+		uint16 range_shift;				
+		
+		uint16 gid_length = 0;
+		
+		uint32 indice;
+		uint32 first_assigned = 1;
+		
+		foreach (Glyph g in glyf_table.glyphs) {
+			if (!g.is_unassigned ()) {
+				glyph_range.add_single (g.unichar_code);
+			}
+		}
+		
+		// glyph_range.print_all ();
+		
+		ranges = glyph_range.get_ranges ();
+		seg_count = (uint16) ranges.length () + 1;
+		seg_count_2 =  seg_count * 2;
+		search_range = 2 * largest_pow2 (seg_count);
+		entry_selector = largest_pow2_exponent (seg_count);
+		range_shift = seg_count_2 - search_range;
+		
+		// format
+		fd.add_ushort (4);
+		
+		// length of subtable
+		fd.add_ushort (16 + 8 * seg_count + gid_length);
+		
+		// language
+		fd.add_ushort (0);
+		
+		fd.add_ushort (seg_count_2);
+		fd.add_ushort (search_range);
+		fd.add_ushort (entry_selector);
+		fd.add_ushort (range_shift);
+		
+		// end codes
+		indice = first_assigned;
+		foreach (UniRange u in ranges) {
+			if (u.stop >= 0xFFFF) {
+				warning ("Not implemented yet.");
+			}
+			
+			fd.add_ushort ((uint16) u.stop);
+			indice += u.length ();
+		}
+		fd.add_ushort (0xFFFF);
+		
+		fd.add_ushort (0); // Reserved
+		
+		// start codes
+		indice = first_assigned; // since first glyph are notdef, null and nonmarkingreturn
+		foreach (UniRange u in ranges) {
+			if (u.start >= 0xFFFF) {
+				warning ("Not implemented yet.");
+			}
+			
+			fd.add_ushort ((uint16) u.start);
+			indice += u.length ();
+		}
+		fd.add_ushort (0xFFFF);
+
+		// delta
+		indice = first_assigned;
+		foreach (UniRange u in ranges) {
+			
+			if ((u.start - indice) > 0xFFFF && u.start > indice) {
+				warning ("Need range offset.");
+			}
+			
+			fd.add_ushort ((uint16) (indice - u.start));
+			indice += u.length ();
+		}
+		fd.add_ushort (1);
+		
+		// range offset
+		foreach (UniRange u in ranges) {
+			if (u.stop <= 0xFFFF) {
+				fd.add_ushort (0);
+			} else {
+				warning ("Not implemented yet.");
+			}
+		}
+		fd.add_ushort (0);
+		
+		// Fixa: implement rest of type 4 (mind gid_length in length field)
+	}
+	
+	public bool validate_subtable () {
+		uint32 length = get_length ();
+		unichar c;
+		unichar prev;
+		uint32 i = 0;
+		uint32 err = 0;
+		StringBuilder s;
+		
+		c = get_char (i);
+		if (c != 0) {
+			s = new StringBuilder ();
+			s.append_unichar (c);
+			warning ("nodef. is mapped to $(s.str)");
+		}
+		
+		i++;
+		
+		while (i < length) {
+			if (c == 0) {
+				s = new StringBuilder ();
+				s.append_unichar (c);
+				warning (@"No entry in cmap for index $i. Last avalable char is $(s.str) Got $(s.str), ($((uint32)c))");
+				err++;
+				return false;
+			} else {
+				prev = c;
+			}
+			
+			c = get_char (i);
+			i++;
+		}
+		
+		if (err > 0) {
+			stderr.printf (@"$err glyphs without mapping to a charactercode were found in this font.\n");
+		}
+		
+		return true;
+	}
+}
+
+class CmapTable : Table { 
+	
+	GlyfTable glyf_table;	
+	List<CmapSubtable> subtables;
+
+	public CmapTable(GlyfTable gt) {
+		glyf_table = gt;
+		subtables = new List<CmapSubtable> ();
+		id = "cmap";
+	}
+	
+	public uint32 get_length () {
+		return get_prefered_table ().get_length ();
+	}
+	
+	public unichar get_char (uint32 i) {
+		return get_prefered_table ().get_char (i) ;
+	}
+	
+	CmapSubtable get_prefered_table () {
+		if (subtables.length () == 0) {
+			warning ("No cmap table has been parsed.");
+			return new CmapSubtable ();
+		}
+		
+		return subtables.first ().data;
+	}
+	
+	public override string get_id () {
+		return "cmap";
+	}
+	
+	public override void parse (FontData dis) throws GLib.Error {
+		uint16 version;
+		uint16 nsubtables;
+		
+		uint16 platform;
+		uint16 encoding;
+		uint32 sub_offset;
+		
+		CmapSubtable subtable;
+
+		return_if_fail (offset > 0 && length > 0);
+		
+		dis.seek (offset);
+		
+		version = dis.read_ushort ();
+		nsubtables = dis.read_ushort ();
+
+		printd (@"cmap version: $version\n");
+		printd (@"cmap subtables: $nsubtables\n");
+				
+		if (version != 0) {
+			warning (@"Bad version for cmap table: $version expecting 0. Number of subtables: $nsubtables");
+			return;
+		}
+		
+		for (uint i = 0; i < nsubtables; i++) {
+			platform = dis.read_ushort ();
+			encoding = dis.read_ushort ();
+			sub_offset = dis.read_ulong ();	
+		
+			if (platform == 3 && encoding == 1) {
+				printd (@"Parsing Unicode BMP (UCS-2) Platform: $platform Encoding: $encoding\n");
+				subtable = new CmapSubtableWindowsUnicode ();
+				subtable.offset = offset + sub_offset;
+				subtables.append (subtable);
+			} else {
+				stderr.printf (@"Unknown cmap format. Platform: $platform Encoding: $encoding.\n");
+			}
+			
+			if (encoding == 3) {
+				stderr.printf ("Font contains a cmap table with the obsolete encoding 3.\n");
+			}
+		}
+		
+		if (subtables.length () == 0) {
+			warning ("No suitable cmap subtable found.");
+		}
+		
+		foreach (CmapSubtable t in subtables) {
+			t.parse (dis);
+			t.print_cmap ();
+		}
+
+	}
+	
+	/** Character to glyph mapping */
+	public void process (GlyfTable glyf_table) throws GLib.Error {
+		FontData fd = new FontData ();
+		CmapSubtableWindowsUnicode cmap = new CmapSubtableWindowsUnicode ();
+		uint16 n_encoding_tables;
+		uint32 subtable_offset = 0;
+			
+		n_encoding_tables = 1;
+		
+		fd.add_u16 (0); // table version
+		fd.add_u16 (n_encoding_tables);
+		
+		fd.add_u16 (3); // platform 
+		fd.add_u16 (1); // encoding (Format Unicode UCS-4)
+
+		subtable_offset = fd.length () + 4;
+		printd (@"subtable_offset: $(subtable_offset)\n");
+		
+		fd.add_ulong (subtable_offset);
+		cmap.process (fd, glyf_table);
+
+		// padding
+		fd.pad ();
+
+		this.font_data = fd;
+	}
+}
+
+class HeadTable : Table {
+
+	public int16 xmin = int16.MIN;
+	public int16 ymin = int16.MIN;
+	public int16 xmax = int16.MAX;
+	public int16 ymax = int16.MAX;
+	
+	uint32 adjusted_checksum = 0;
+
+	uint16 mac_style;
+	uint16 lowest_PPEM;
+	int16 font_direction_hint;
+		
+	public int16 loca_offset_size = 0; // 0 for int16 1 for int32
+	int16 glyph_data_format;
+
+	Fixed version;
+	Fixed font_revision;
+	
+	uint32 magic_number;
+	
+	uint16 flags;
+	
+	uint64 created;
+	uint64 modified;
+		
+	uint16 units_per_em = 2048;
+	
+	const uint8 BASELINE_AT_ZERO = 1 << 0;
+	const uint8 LSB_AT_ZERO = 1 << 1;
+	
+	GlyfTable glyf_table;
+	
+	public HeadTable (GlyfTable gt) {
+		glyf_table = gt;
+		id = "head";
+	}
+	
+	public uint32 get_adjusted_checksum () {
+		return adjusted_checksum;
+	}
+	
+	public double get_units_per_em () {
+		return units_per_em * 10; // Fixa: we can refactor this number
+	}
+	
+	public override void parse (FontData dis) throws GLib.Error {
+		return_if_fail (offset > 0 && length > 0);
+
+		dis.seek (offset);
+		
+		font_data = new FontData ();		
+	
+		version = dis.read_fixed ();
+
+		if (!version.equals (1, 0)) {
+			warning (@"Expecting head version 1.0 got $(version.get_string ())\n");
+		}
+		
+		font_revision = dis.read_fixed ();
+		adjusted_checksum = dis.read_ulong ();
+		magic_number = dis.read_ulong ();
+		
+		if (magic_number != 0x5F0F3CF5) {
+			warning (@"Magic number is invalid. Got $(magic_number).");
+			return;
+		}
+		
+		flags = dis.read_ushort ();
+		
+		if ((flags & BASELINE_AT_ZERO) > 0) {
+			printd ("Flag BASELINE_AT_ZERO has been set.\n");
+		}
+
+		if ((flags & LSB_AT_ZERO) > 0) {
+			printd ("Flags LSB_AT_ZERO has been set.\n");
+		}
+		
+		units_per_em = dis.read_ushort ();
+		
+		created = dis.read_udate ();
+		modified = dis.read_udate ();
+		
+		xmin = dis.read_short ();
+		ymin = dis.read_short ();
+		
+		xmax = dis.read_short ();
+		ymax = dis.read_short ();
+
+		printd (@"font boundries:\n");
+		printd (@"xmin: $xmin\n");
+		printd (@"ymin: $ymin\n");
+		printd (@"xmax: $xmax\n");
+		printd (@"ymax: $ymax\n");
+				
+		mac_style = dis.read_ushort ();
+		lowest_PPEM = dis.read_ushort ();
+		font_direction_hint = dis.read_short ();
+		
+		loca_offset_size = dis.read_short ();
+		glyph_data_format = dis.read_short ();
+		
+		if (glyph_data_format != 0) {
+			warning (@"Unknown glyph data format. Expecting 0 got $glyph_data_format.");
+		}
+		
+		// print_values ();
+		// Some deprecated values follow here ...
+	}
+	
+	void print_values () {
+		print (@"Version: $(version.get_string ())\n");
+		print (@"flags: $flags\n");
+		print (@"font_revision: $(font_revision.get_string ())\n");
+		print (@"flags: $flags\n");
+		print (@"Units per em: $units_per_em\n");
+		print (@"lowest_PPEM: $lowest_PPEM\n");
+		print (@"font_direction_hint: $font_direction_hint\n");
+		print (@"loca_offset_size: $loca_offset_size\n");
+		print (@"glyph_data_format: $glyph_data_format\n");
+	}
+	
+	public uint32 get_font_checksum () {
+		return adjusted_checksum;
+	}
+	
+	public void set_check_sum_adjustment (uint32 csa) {
+		this.adjusted_checksum = csa;
+	}
+	
+	public uint32 get_checksum_position () {
+		return 8;
+	}
+	
+	public void process () throws GLib.Error {
+		FontData font_data = new FontData ();
+		Fixed version = 1 << 16;
+		Fixed font_revision = 1 << 16;
+
+		font_data.add_fixed (version);
+		font_data.add_fixed (font_revision);
+		
+		// Zero on the first run and updated by directory tables checksum calculation
+		// for the entire font.
+		font_data.add_u32 (adjusted_checksum);
+		
+		font_data.add_u32 (0x5F0F3CF5); // magic number
+		
+		//font_data.add_u16 (BASELINE_AT_ZERO | LSB_AT_ZERO);
+		font_data.add_u16 (0); // flags
+		
+		font_data.add_u16 (1000); // units per em (should be a power of two for ttf fonts)
+		
+		font_data.add_64 (0); // creation time since 1904-01-01
+		font_data.add_64 (0); // modified time since 1904-01-01
+
+		xmin = glyf_table.xmin;
+		ymin = glyf_table.ymin;
+		xmax = glyf_table.xmax;
+		ymax = glyf_table.ymax;
+
+		printd (@"font boundries:\n");
+		printd (@"xmin: $xmin\n");
+		printd (@"ymin: $ymin\n");
+		printd (@"xmax: $xmax\n");
+		printd (@"ymax: $ymax\n");
+
+		font_data.add_short (xmin);
+		font_data.add_short (ymin);
+		font_data.add_short (xmax);
+		font_data.add_short (ymax);
+	
+		font_data.add_u16 (0); // mac style
+		font_data.add_u16 (2); // smallest recommended size in pixels, ppem
+		font_data.add_16 (2); // deprecated direction hint
+		font_data.add_16 (loca_offset_size);  // long offset
+		font_data.add_16 (0);  // Use current glyph data format
+		
+		font_data.pad ();
+		
+		this.font_data = font_data;
+	}
+}
+
+class HheaTable : Table {
+
+	Fixed version;
+	int16 ascender;
+	int16 descender;
+	int16 linegap;
+	uint16 max_advance;
+	int16 min_lsb;
+	int16 min_rsb;
+	int16 xmax_extent;
+	int16 carret_slope;
+	int16 carret_slope_run;
+	int16 carret_offset;
+	
+	int16 metric_format;
+	public int16 num_horizontal_metrics;
+		
+	GlyfTable glyf_table;
+	HeadTable head_table;
+	HmtxTable hmtx_table;
+	
+	public HheaTable (GlyfTable g, HeadTable h, HmtxTable hm) {
+		glyf_table = g;
+		head_table = h;
+		hmtx_table = hm;
+		id = "hhea";
+	}
+	
+	public double get_ascender () {
+		return ascender * 1000 / head_table.get_units_per_em ();
+	}
+
+	public double get_descender () {
+		return descender * 1000 / head_table.get_units_per_em ();
+	}
+	
+	public override void parse (FontData dis) throws GLib.Error {
+		dis.seek (offset);
+		
+		version = dis.read_fixed ();
+		
+		if (!version.equals (1, 0)) {
+			warning (@"wrong version in hhea table $(version.get_string ())");
+		}
+		
+		ascender = dis.read_short ();
+		descender = dis.read_short ();
+		linegap = dis.read_short ();
+		max_advance = dis.read_ushort ();
+		min_lsb = dis.read_short ();
+		min_rsb = dis.read_short ();
+		xmax_extent = dis.read_short ();
+		carret_slope = dis.read_short ();
+		carret_slope_run = dis.read_short ();
+		carret_offset = dis.read_short ();
+		
+		// reserved x 4
+		dis.read_short ();
+		dis.read_short ();
+		dis.read_short ();
+		dis.read_short ();
+		
+		metric_format = dis.read_short ();
+		num_horizontal_metrics = dis.read_short ();
+	}
+	
+	public void process () throws GLib.Error {
+		Font font = OpenFontFormatWriter.get_current_font ();
+		FontData fd = new FontData ();
+		Fixed version = 1 << 16;
+		
+		fd.add_fixed (version); // table version
+		
+		fd.add_16 ((int16) (-1 * (font.top_position - font.base_line) * UNITS)); // Ascender
+		fd.add_16 ((int16) (-1 * font.bottom_position * UNITS)); // Descender
+		fd.add_16 (0); // LineGap
+				
+		fd.add_u16 (hmtx_table.max_advance); // maximum advance width value in 'hmtx' table.
+		
+		fd.add_16 (hmtx_table.min_lsb); // min left side bearing
+		fd.add_16 (hmtx_table.min_rsb); // min right side bearing
+		fd.add_16 (hmtx_table.max_extent); // x max extent Max(lsb + (xMax - xMin))
+		
+		fd.add_16 (1); // caretSlopeRise
+		fd.add_16 (0); // caretSlopeRun
+		fd.add_16 (0); // caretOffset
+		
+		// reserved
+		fd.add_16 (0);
+		fd.add_16 (0);
+		fd.add_16 (0);
+		fd.add_16 (0);
+		
+		fd.add_16 (0); // metricDataFormat 0 for current format.
+		
+		fd.add_u16 ((uint16) glyf_table.glyphs.length()); // numberOfHMetrics Number of hMetric entries in 'hmtx' table
+
+		// padding
+		fd.pad ();
+		this.font_data = fd;
+	}
+}
+
+class HmtxTable : Table {
+	
+	uint32 nmetrics;
+	uint32 nmonospaced;
+		
+	uint16* advance_width = null;
+	uint16* left_side_bearing = null;
+	uint16* left_side_bearing_monospaced = null;
+	
+	public int16 max_advance = 0;
+	public int16 max_extent = 0;
+	public int16 min_lsb = 0; 
+	public int16 min_rsb = 0;
+			
+	HeadTable head_table;
+	GlyfTable glyf_table;
+	
+	public HmtxTable (HeadTable h, GlyfTable gt) {
+		head_table = h;
+		glyf_table = gt;
+		id = "hmtx";
+	}
+	
+	~HmtxTable () {
+		if (advance_width != null) delete advance_width;
+		if (left_side_bearing != null) delete left_side_bearing; 
+	}
+
+	public double get_advance (uint32 i) {
+		if (i >= nmetrics) {
+			warning (@"i >= nmetrics $i >= $nmetrics");
+			return 0;
+		}
+		
+		return_val_if_fail (advance_width != null, 0.0);
+		
+		return advance_width[i] * 1000 / head_table.get_units_per_em ();
+	}
+		
+	/** Get left side bearing relative to xmin. */
+	public double get_lsb (uint32 i) {
+		return_val_if_fail (i < nmetrics, 0.0);
+		return_val_if_fail (left_side_bearing != null, 0.0);
+		
+		return left_side_bearing[i] * 1000 / head_table.get_units_per_em ();
+	}
+
+	/** Get left side bearing relative to xmin for monospaces fonts. */
+	public double get_lsb_mono (uint32 i) 
+		requires (i < nmonospaced && left_side_bearing_monospaced != null) {
+		return left_side_bearing_monospaced[i] * 1000 / head_table.get_units_per_em ();
+	}
+	
+	public void print_all () {
+		return_if_fail (advance_width != null);
+		return_if_fail (left_side_bearing != null);
+		
+		for (int i = 0; i < nmetrics; i++) {
+			print (@"get_lsb ($i): $(get_lsb(i))\n");
+			print (@"get_advance ($i): $(get_advance(i))\n");
+		}
+
+		if (left_side_bearing_monospaced != null) {
+			for (int i = 0; i < nmonospaced; i++) {
+				print (@"get_lsb_mono ($i): $(get_lsb_mono(i))\n");
+			}
+		}
+
+	}
+			
+	public new void parse (FontData dis, HheaTable hhea_table, LocaTable loca_table) throws GLib.Error {
+		nmetrics = hhea_table.num_horizontal_metrics;
+		nmonospaced = loca_table.size - nmetrics;
+		
+		dis.seek (offset);
+		
+		if (nmetrics > loca_table.size) {
+			warning (@"(nmetrics > loca_table.size) ($nmetrics > $(loca_table.size))");
+			return;
+		}
+		
+		printd (@"nmetrics: $nmetrics\n");
+		printd (@"loca_table.size: $(loca_table.size)\n");
+		
+		advance_width = new uint16[nmetrics];
+		left_side_bearing = new uint16[nmetrics];
+		left_side_bearing_monospaced = new uint16[nmonospaced];
+		
+		for (int i = 0; i < nmetrics; i++) {
+			advance_width[i] = dis.read_ushort ();
+			left_side_bearing[i] = dis.read_short ();
+		}
+		
+		for (int i = 0; i < nmonospaced; i++) {
+			left_side_bearing_monospaced[i] = dis.read_short ();
+		}
+	}
+	
+	public void process () {
+		FontData fd = new FontData ();
+
+		int16 advance;
+		int16 extent;
+		int16 rsb;
+		int16 lsb;
+		
+		double xmin;
+		double ymin;
+		double xmax;
+		double ymax;
+		
+		// advance and lsb
+		foreach (Glyph g in glyf_table.glyphs) {
+			g.boundries (out xmin, out ymin, out xmax, out ymax);
+			lsb = (int16) (-1 * (g.left_limit - xmin) * UNITS);
+			advance = (int16) ((g.right_limit - g.left_limit) * UNITS);
+			extent = (int16) (lsb + (xmax - xmin) * UNITS);
+			rsb = (int16) (advance - extent);
+						
+			fd.add_u16 (advance);
+			fd.add_16 (lsb);
+	
+			if (advance > max_advance) {
+				max_advance = advance;
+			}
+			
+			if (extent > max_extent) {
+				max_extent = extent;
+			}
+			
+			if (rsb < min_rsb) {
+				min_rsb = rsb;
+			}
+
+			if (lsb < min_lsb) {
+				min_lsb = lsb;
+			}
+		}
+		
+		// monospaced lsb ...
+		
+		font_data = fd;
+		
+		warn_if_fail (max_advance != 0);
+	}
+}
+
+
+class MaxpTable : Table {
+	
+	GlyfTable glyf_table;
+	
+	public uint16 num_glyphs = 0;
+	
+	public MaxpTable (GlyfTable g) {
+		glyf_table = g;
+		id = "maxp";
+	}
+	
+	public override void parse (FontData dis) 
+		requires (offset > 0 && length > 0) {
+		Fixed format;
+		
+		dis.seek (offset);
+		
+		format = dis.read_fixed ();
+		printd (@"Maxp version: $(format.get_string ())\n");
+		
+		num_glyphs = dis.read_ushort ();
+		
+		if (format == 0x00005000) {
+			return;
+		}
+		
+		// Format 1.0 continues here
+	}
+	
+	public void process () {
+		FontData fd = new FontData();
+
+		// Version 0.5 for fonts with cff data and 1.0 for ttf
+		fd.add_u32 (0x00010000);
+		
+		if (glyf_table.glyphs.length () == 0) {
+			warning ("Zero glyphs in maxp table.");
+		}
+		
+		fd.add_u16 ((uint16) glyf_table.glyphs.length ()); // numGlyphs in the font
+
+		fd.add_u16 (glyf_table.get_max_points ()); // max points
+		fd.add_u16 (glyf_table.get_max_contours ()); // max contours
+		fd.add_u16 (0); // max composite points
+		fd.add_u16 (0); // max composite contours
+		fd.add_u16 (1); // max zones
+		fd.add_u16 (0); // twilight points
+		fd.add_u16 (0); // max storage
+		fd.add_u16 (0); // max function defs
+		fd.add_u16 (0); // max instruction defs
+		fd.add_u16 (0); // max stack elements
+		fd.add_u16 (0); // max size of instructions
+		fd.add_u16 (0); // max component elements
+		fd.add_u16 (0); // component depth
+		
+		fd.pad ();
+		
+		this.font_data = fd;
+	}
+}
+
+class OffsetTable : Table {
+	DirectoryTable directory_table;
+		
+	public uint16 num_tables = 0;
+	uint16 search_range = 0;
+	uint16 entry_selector = 0;
+	uint16 range_shift = 0;
+	
+	public OffsetTable (DirectoryTable t) {
+		id = "Offset table";
+		directory_table = t;
+	}
+		
+	public override void parse (FontData dis) throws Error {
+		Fixed version;
+		
+		dis.seek (offset);
+		
+		version = dis.read_fixed ();
+		num_tables = dis.read_ushort ();
+		search_range = dis.read_ushort ();
+		entry_selector = dis.read_ushort ();
+		range_shift = dis.read_ushort ();
+		
+		printd (@"Font file version $(version.get_string ())\n");
+		printd (@"Number of tables $num_tables\n");		
+	}
+	
+	public void process () throws GLib.Error {
+		FontData fd = new FontData ();
+		Fixed version = 0x00010000; // sfnt version 1.0 for TTF CFF else use OTTO
+
+		
+		num_tables = (uint16) directory_table.get_tables ().length () - 2; // number of tables, skip DirectoryTable and OffsetTable
+		
+		search_range = max_pow_2_less_than_i (num_tables) * 16;
+		entry_selector = max_log_2_less_than_i (num_tables);
+		range_shift = 16 * num_tables - search_range;
+
+		fd.add_fixed (version); 
+		fd.add_u16 (num_tables);
+		fd.add_u16 (search_range);
+		fd.add_u16 (entry_selector);
+		fd.add_u16 (range_shift);
+		
+		// skip padding for offset table 
+		
+		this.font_data = fd;
+	}
+}
+
+class NameTable : Table {
+
+	public static const uint16 COPYRIGHT_NOTICE = 0;
+	public static const uint16 FONT_NAME = 1;
+	public static const uint16 SUBFAMILY_NAME = 2;
+	public static const uint16 UNIQUE_IDENTIFIER = 3;
+	public static const uint16 FULL_FONT_NAME = 4; // name + subfamily
+	public static const uint16 VERSION = 5;
+	public static const uint16 DESCRIPTION = 10;
+	public static const uint16 PREFERED_FAMILY = 16;
+	public static const uint16 PREFERED_SUB_FAMILY = 17;
+	
+	List<uint16> identifiers;
+	List<string> text;
+			
+	public NameTable () {
+		id = "name";
+		text = new List<string> ();
+		identifiers = new List<uint16> ();
+	}
+	
+	public string get_name (uint16 identifier) {
+		int i = 0;
+		
+		foreach (uint16 n in identifiers) {
+			if (n == identifier) {
+				return text.nth (i).data;
+			}
+			i++;
+		}
+		
+		return "";
+	}
+
+	public void print_all () {
+		stdout.printf (@"$(text.length ()) name table texts:\n");
+		foreach (string s in text) {
+			stdout.printf (s);
+			stdout.printf ("\n");
+		}
+	}
+
+	public override void parse (FontData dis) throws Error {
+		uint16 format;
+
+		dis.seek (offset);
+		
+		format = dis.read_ushort ();
+		
+		switch (format) {
+			case 0:
+				parse_format0 (dis);
+				break;
+				
+			case 1:
+				warning ("name table format 1 is not implemented yet");
+				break;
+			
+			default:
+				warning (@"unknown format $format in name table");
+				break;
+		}
+	}
+	
+	public void parse_format0 (FontData dis) throws Error {
+		uint16 count;
+		uint16 storage_offset;
+		
+		List<uint16> strlen = new List<uint16> ();
+		List<uint16> off = new List<uint16> ();
+		List<uint16> name_id = new List<uint16> ();
+		List<uint16> encoding_id = new List<uint16> ();
+		List<uint16> platform = new List<uint16> ();
+		List<uint16> lang = new List<uint16> ();
+				
+		count = dis.read_ushort ();
+		storage_offset = dis.read_ushort ();
+		
+		for (int i = 0; i < count; i++) {
+			platform.append (dis.read_ushort ());
+			encoding_id.append (dis.read_ushort ());
+			lang.append (dis.read_ushort ());
+			name_id.append (dis.read_ushort ());
+			strlen.append (dis.read_ushort ());
+			off.append (dis.read_ushort ());
+			
+			identifiers.append (name_id.last ().data);	
+		}
+
+		int plat;
+		StringBuilder str;
+		for (int i = 0; i < count; i++) {
+			plat = platform.nth (i).data;
+			dis.seek (offset + storage_offset + off.nth (i).data);
+			str = new StringBuilder ();
+			
+			switch (plat) {
+				case 1:
+					for (int j = 0; j < strlen.nth (i).data; j++) {
+						char c = dis.read_char ();
+						str.append_c (c);
+					}
+					break;
+					
+				case 3:
+					for (int j = 0; j < strlen.nth (i).data; j += 2) {
+						unichar c;
+						char c0 = dis.read_char ();
+						char c1 = dis.read_char ();
+												
+						c = c0 << 8;
+						c += c1;
+						
+						str.append_unichar (c);
+					}
+					break;
+				
+				default:
+					break;
+			} 
+			
+			text.append (str.str);
+			printd (@"Name id: $(name_id.nth (i).data) platform:  $(platform.nth (i).data) enc: $(encoding_id.nth (i).data) lang: $(lang.nth (i).data) len: $(strlen.nth (i).data) str: \"$(str.str)\"\n");		
+		}
+	}
+	
+	public void process () throws GLib.Error {
+		FontData fd = new FontData ();
+		Font font = OpenFontFormatWriter.get_current_font ();
+		uint16 len = 0;
+		string t;
+		uint16 p;
+		uint16 l;
+		uint16 num_records;
+
+		List<uint16> type = new List<uint16> ();
+		List<string> text = new List<string> ();
+		
+		text.append ("Copyright");
+		type.append (COPYRIGHT_NOTICE);
+		
+		text.append (font.get_name ());
+		type.append (FONT_NAME);
+
+		text.append ("Regular");
+		type.append (SUBFAMILY_NAME);
+
+		text.append (font.get_name ()); // TODO: validate
+		type.append (UNIQUE_IDENTIFIER);
+
+		text.append (font.get_name ());
+		type.append (FULL_FONT_NAME);
+		
+		// This does for some reason cause an internal error in ms fontvalidatior utility.
+		// Head table can't parse integer from string.
+		text.append ("Version 1.0");
+		type.append (VERSION);		
+		
+		text.append ("");
+		type.append (DESCRIPTION);
+				
+		text.append (font.get_name ());
+		type.append (PREFERED_FAMILY);
+		
+		text.append ("Regular");
+		type.append (PREFERED_SUB_FAMILY);
+			
+		num_records = (uint16) text.length ();
+		
+		fd.add_ushort (0); // format 1
+		fd.add_ushort (2 * num_records); // nplatforms * nrecords 
+		fd.add_ushort (6 + 12 * 2 * num_records); // string storage offset
+
+		for (int i = 0; i < num_records; i++) {
+			t = (!) text.nth (i).data;
+			p = (!) type.nth (i).data;
+			l = (uint16) t.length;
+			
+			fd.add_ushort (1); // platform
+			fd.add_ushort (0); // encoding id
+			fd.add_ushort (0); // language
+			fd.add_ushort (p); // name id 
+			fd.add_ushort (l); // strlen
+			fd.add_ushort (len); // offset from begining of string storage
+			len += l;				
+		}	
+
+		for (int i = 0; i < num_records; i++) {
+			t = (!) text.nth (i).data;
+			p = (!) type.nth (i).data;
+			l = (uint16) (2 * t.char_count ());
+
+			fd.add_ushort (3); // platform
+			fd.add_ushort (1); 	// encoding id
+			fd.add_ushort (0x0409); // language
+			fd.add_ushort (p); // name id 
+			fd.add_ushort (l); // strlen
+			fd.add_ushort (len); // offset from begining of string storage
+			len += l;
+		}
+
+		// platform 1
+		foreach (string s in text) {
+			fd.add_str (s); 
+		}
+		
+		// platform 3
+		foreach (string s in text) {
+			fd.add_str_utf16 (s); 
+		}
+
+		fd.pad ();
+		
+		this.font_data = fd;
+	}
+}
+
+class Os2Table : Table {
+	
+	public Os2Table () {
+		id = "OS/2";
+	}
+	
+	public override void parse (FontData dis) throws Error {
+		
+	}
+	
+	public void process (GlyfTable glyf_table) {
+		FontData fd = new FontData ();
+		Font font = OpenFontFormatWriter.get_current_font ();
+		
+		int16 ascender;
+		int16 descender;
+		
+		fd.add_u16 (0x0002); // USHORT Version 0x0000, 0x0001, 0x0002, 0x0003, 0x0004
+
+		fd.add_16 (glyf_table.get_average_width ()); // SHORT xAvgCharWidth
+
+		fd.add_u16 (400); // USHORT usWeightClass (400 is normal)
+		fd.add_u16 (5); // USHORT usWidthClass (5 is normal)
+		fd.add_u16 (0); // USHORT fsType
+
+		fd.add_16 (40); // SHORT ySubscriptXSize
+		fd.add_16 (40); // SHORT ySubscriptYSize
+		fd.add_16 (40); // SHORT ySubscriptXOffset
+		fd.add_16 (40); // SHORT ySubscriptYOffset
+		fd.add_16 (40); // SHORT ySuperscriptXSize
+		fd.add_16 (40); // SHORT ySuperscriptYSize
+		fd.add_16 (40); // SHORT ySuperscriptXOffset
+		fd.add_16 (40); // SHORT ySuperscriptYOffset
+		fd.add_16 (40); // SHORT yStrikeoutSize
+		fd.add_16 (200); // SHORT yStrikeoutPosition
+		fd.add_16 (40); // SHORT sFamilyClass
+
+		// PANOSE
+		fd.add (0); 
+		fd.add (0); 
+		fd.add (0); 
+		fd.add (0); 
+		fd.add (0); 
+		fd.add (0); 
+		fd.add (0); 
+		fd.add (0); 
+		fd.add (0); 
+		fd.add (0); 
+
+		// FIXA:
+		fd.add_u32 (0); // ulUnicodeRange1 Bits 0-31
+		fd.add_u32 (0); // ULONG ulUnicodeRange2 Bits 32-63
+		fd.add_u32 (0); // ULONG ulUnicodeRange3 Bits 64-95
+		fd.add_u32 (0); // ULONG ulUnicodeRange4 Bits 96-127
+
+		fd.add_tag ("----"); // VendID
+
+		fd.add_u16 (0); // USHORT fsSelection
+		
+		fd.add_u16 (glyf_table.get_first_char ()); // USHORT usFirstCharIndex
+		fd.add_u16 (glyf_table.get_last_char ()); // USHORT usLastCharIndex
+
+		ascender = (int16) (glyf_table.xmax + font.base_line * UNITS);
+		descender = (int16) (-glyf_table.xmin  + font.base_line * UNITS);
+		
+		fd.add_16 (ascender); // SHORT sTypoAscender
+		fd.add_16 (descender); // SHORT sTypoDescender
+		fd.add_16 (3); // SHORT sTypoLineGap
+
+		fd.add_u16 (ascender); // USHORT usWinAscent
+		fd.add_u16 (descender); // USHORT usWinDescent
+
+		// FIXA:
+		fd.add_u32 (0); // ULONG ulCodePageRange1 Bits 0-31
+		fd.add_u32 (0); // ULONG ulCodePageRange2 Bits 32-63
+
+		fd.add_16 (ascender); // SHORT sxHeight version 0x0002 and later
+		fd.add_16 (ascender); // SHORT sCapHeight version 0x0002 and later
+
+		fd.add_16 (0); // USHORT usDefaultChar version 0x0002 and later
+		fd.add_16 (0x0020); // USHORT usBreakChar version 0x0002 and later, also known as space
+		
+		// FIXA: calculate these values
+		fd.add_16 (1); // USHORT usMaxContext version 0x0002 and later
+
+		// padding
+		fd.pad ();
+	
+		this.font_data = fd;
+	}
+
+}
+
+class PostTable : Table {
+	
+	GlyfTable glyf_table;
+	
+	List<uint16> index = new List<uint16> ();
+	List<string> names = new List<string> ();
+
+	List<string> available_names = new List<string> ();
+	
+	public PostTable (GlyfTable g) {
+		id = "post";
+		glyf_table = g;
+	}
+
+	public unowned List<string> get_all_names () {
+		return available_names;
+	}
+		
+	public int get_gid (string name) { // fixa do fast lookup
+		int i = 0;
+		int j = 0;
+		foreach (string n in names) {
+			if (n == name) {				
+				j = 0;
+				foreach (uint16 k in index) {
+					if (k == i) {
+						return j;
+					}
+					j++;
+				}
+								
+				return i;
+			}
+			i++;
+		}
+		
+		// warn_if_reached ();
+		return -1;
+	}
+
+	public string get_name (int gid) {
+		int k;
+		
+		if (!(0 <= gid < index.length ())) {
+			warning ("gid is out of range.");
+			return "";
+		}
+				
+		k = (!) index.nth (gid).data;
+		
+		if (gid != 0 && k == 0) {
+			warning (@"Glyph $gid is assigned to name .notdef, only gid 0 can be .notdef character.");
+			return "";
+		}
+		
+		if (!(0 <= k < names.length ())) {
+			warning ("k is out of range.");
+			return "";
+		}
+				
+		return (!) names.nth (k).data;
+	}
+	
+	public override void parse (FontData dis) throws Error {
+		dis.seek (offset);
+		
+		Fixed format = dis.read_fixed ();
+		Fixed italic = dis.read_fixed ();
+		
+		int16 underlie_pos = dis.read_short ();
+		int16 underlie_thickness = dis.read_short ();
+		uint32 is_fixed_pitch  = dis.read_ulong ();
+		
+		uint32 mem_min42  = dis.read_ulong ();
+		uint32 mem_max42  = dis.read_ulong ();
+		uint32 mem_min1  = dis.read_ulong ();
+		uint32 mem_max1  = dis.read_ulong ();
+		
+		uint16 nnames  = dis.read_ushort ();
+		
+		if (format != 0x00020000) {
+			warning ("Only post tables of version 2 will parset got $(format.get_string ())");
+			return;
+		}
+		
+		printd (@"format: $(format.get_string ())\n");
+		printd (@"italic: $(italic.get_string ())\n");
+		printd (@"underlie_pos: $(underlie_pos)\n");
+		printd (@"underlie_thickness: $(underlie_thickness)\n");
+		printd (@"is_fixed_pitch: $(is_fixed_pitch)\n");
+		printd (@"mem_min42: $(mem_min42)\n");
+		printd (@"mem_max42: $(mem_max42)\n");
+		printd (@"mem_min1: $(mem_min1)\n");
+		printd (@"mem_max1: $(mem_max1)\n");
+		printd (@"\n");
+		
+		printd (@"Num names: $(nnames)\n");
+		
+		uint16 k;
+		int non_standard_names = 0;
+		for (uint16 i = 0; i < nnames; i++) {
+			k = dis.read_ushort ();
+			index.append (k);
+			
+			if (k >= 258) {
+				non_standard_names++;
+			}
+		}
+		
+		add_standard_names ();
+		
+		// read non standard names
+		for (int i = 0; i < non_standard_names; i++) {
+			uint8 len = dis.read_byte ();
+			StringBuilder name = new StringBuilder ();
+			
+			for (int j = 0; j < len; j++) {
+				name.append_c (dis.read_char ());
+			}
+
+			names.append (name.str);
+		}
+		
+		populate_available ();
+	}
+
+	void populate_available () {
+		for (int i = 0; i < index.length (); i++) {
+			available_names.append (get_name (i));
+		}
+	}
+		
+	public void print_all () {
+		print (@"PostScript glyph mapping:\n");
+		for (int i = 0; i < index.length (); i++) {
+			print (@"gid $i -> $(get_name (i))\n");
+		}
+	}
+	
+	public void print_all_names () {
+		print (@"Post table names:\n");
+		foreach (var n in names) {
+			print (@"$n\n");
+		}
+	}
+	
+	// the Macintosh standard order
+	private void add_standard_names () {
+		names.append (".notdef");
+		names.append (".null");
+		names.append ("nonmarkingreturn");
+		names.append ("space");
+		names.append ("exclam");
+		names.append ("quotedbl");
+		names.append ("numbersign");
+		names.append ("dollar");
+		names.append ("percent");
+		names.append ("ampersand");
+		names.append ("quotesingle");
+		names.append ("parenleft");
+		names.append ("parenright");
+		names.append ("asterisk");
+		names.append ("plus");
+		names.append ("comma");
+		names.append ("hyphen");
+		names.append ("period");
+		names.append ("slash");
+		names.append ("zero");
+		names.append ("one");
+		names.append ("two");
+		names.append ("three");
+		names.append ("four");
+		names.append ("five");
+		names.append ("six");
+		names.append ("seven");
+		names.append ("eight");
+		names.append ("nine");
+		names.append ("colon");
+		names.append ("semicolon");
+		names.append ("less");
+		names.append ("equal");
+		names.append ("greater");
+		names.append ("question");
+		names.append ("at");
+		names.append ("A");
+		names.append ("B");
+		names.append ("C");
+		names.append ("D");
+		names.append ("E");
+		names.append ("F");
+		names.append ("G");
+		names.append ("H");
+		names.append ("I");
+		names.append ("J");
+		names.append ("K");
+		names.append ("L");
+		names.append ("M");
+		names.append ("N");
+		names.append ("O");
+		names.append ("P");
+		names.append ("Q");
+		names.append ("R");
+		names.append ("S");
+		names.append ("T");
+		names.append ("U");
+		names.append ("V");
+		names.append ("W");
+		names.append ("X");
+		names.append ("Y");
+		names.append ("Z");
+		names.append ("bracketleft");
+		names.append ("backslash");
+		names.append ("bracketright");
+		names.append ("asciicircum");
+		names.append ("underscore");
+		names.append ("grave");
+		names.append ("a");
+		names.append ("b");
+		names.append ("c");
+		names.append ("d");
+		names.append ("e");
+		names.append ("f");
+		names.append ("g");
+		names.append ("h");
+		names.append ("i");
+		names.append ("j");
+		names.append ("k");
+		names.append ("l");
+		names.append ("m");
+		names.append ("n");
+		names.append ("o");
+		names.append ("p");
+		names.append ("q");
+		names.append ("r");
+		names.append ("s");
+		names.append ("t");
+		names.append ("u");
+		names.append ("v");
+		names.append ("w");
+		names.append ("x");
+		names.append ("y");
+		names.append ("z");
+		names.append ("braceleft");
+		names.append ("bar");
+		names.append ("braceright");
+		names.append ("asciitilde");
+		names.append ("Adieresis");
+		names.append ("Aring");
+		names.append ("Ccedilla");
+		names.append ("Eacute");
+		names.append ("Ntilde");
+		names.append ("Odieresis");
+		names.append ("Udieresis");
+		names.append ("aacute");
+		names.append ("agrave");
+		names.append ("acircumflex");
+		names.append ("adieresis");
+		names.append ("atilde");
+		names.append ("aring");
+		names.append ("ccedilla");
+		names.append ("eacute");
+		names.append ("egrave");
+		names.append ("ecircumflex");
+		names.append ("edieresis");
+		names.append ("iacute");
+		names.append ("igrave");
+		names.append ("icircumflex");
+		names.append ("idieresis");
+		names.append ("ntilde");
+		names.append ("oacute");
+		names.append ("ograve");
+		names.append ("ocircumflex");
+		names.append ("odieresis");
+		names.append ("otilde");
+		names.append ("uacute");
+		names.append ("ugrave");
+		names.append ("ucircumflex");
+		names.append ("udieresis");
+		names.append ("dagger");
+		names.append ("degree");
+		names.append ("cent");
+		names.append ("sterling");
+		names.append ("section");
+		names.append ("bullet");
+		names.append ("paragraph");
+		names.append ("germandbls");
+		names.append ("registered");
+		names.append ("copyright");
+		names.append ("trademark");
+		names.append ("acute");
+		names.append ("dieresis");
+		names.append ("notequal");
+		names.append ("AE");
+		names.append ("Oslash");
+		names.append ("infinity");
+		names.append ("plusminus");
+		names.append ("lessequal");
+		names.append ("greaterequal");
+		names.append ("yen");
+		names.append ("mu");
+		names.append ("partialdiff");
+		names.append ("summation");
+		names.append ("product");
+		names.append ("pi");
+		names.append ("integral");
+		names.append ("ordfeminine");
+		names.append ("ordmasculine");
+		names.append ("Omega");
+		names.append ("ae");
+		names.append ("oslash");
+		names.append ("questiondown");
+		names.append ("exclamdown");
+		names.append ("logicalnot");
+		names.append ("radical");
+		names.append ("florin");
+		names.append ("approxequal");
+		names.append ("Delta");
+		names.append ("guillemotleft");
+		names.append ("guillemotright");
+		names.append ("ellipsis");
+		names.append ("nonbreakingspace");
+		names.append ("Agrave");
+		names.append ("Atilde");
+		names.append ("Otilde");
+		names.append ("OE");
+		names.append ("oe");
+		names.append ("endash");
+		names.append ("emdash");
+		names.append ("quotedblleft");
+		names.append ("quotedblright");
+		names.append ("quoteleft");
+		names.append ("quoteright");
+		names.append ("divide");
+		names.append ("lozenge");
+		names.append ("ydieresis");
+		names.append ("Ydieresis");
+		names.append ("fraction");
+		names.append ("currency");
+		names.append ("guilsinglleft");
+		names.append ("guilsinglright");
+		names.append ("fi");
+		names.append ("fl");
+		names.append ("daggerdbl");
+		names.append ("periodcentered");
+		names.append ("quotesinglbase");
+		names.append ("quotedblbase");
+		names.append ("perthousand");
+		names.append ("Acircumflex");
+		names.append ("Ecircumflex");
+		names.append ("Aacute");
+		names.append ("Edieresis");
+		names.append ("Egrave");
+		names.append ("Iacute");
+		names.append ("Icircumflex");
+		names.append ("Idieresis");
+		names.append ("Igrave");
+		names.append ("Oacute");
+		names.append ("Ocircumflex");
+		names.append ("apple");
+		names.append ("Ograve");
+		names.append ("Uacute");
+		names.append ("Ucircumflex");
+		names.append ("Ugrave");
+		names.append ("dotlessi");
+		names.append ("circumflex");
+		names.append ("tilde");
+		names.append ("macron");
+		names.append ("breve");
+		names.append ("dotaccent");
+		names.append ("ring");
+		names.append ("cedilla");
+		names.append ("hungarumlaut");
+		names.append ("ogonek");
+		names.append ("caron");
+		names.append ("Lslash");
+		names.append ("lslash");
+		names.append ("Scaron");
+		names.append ("scaron");
+		names.append ("Zcaron");
+		names.append ("zcaron");
+		names.append ("brokenbar");
+		names.append ("Eth");
+		names.append ("eth");
+		names.append ("Yacute");
+		names.append ("yacute");
+		names.append ("Thorn");
+		names.append ("thorn");
+		names.append ("minus");
+		names.append ("multiply");
+		names.append ("onesuperior");
+		names.append ("twosuperior");
+		names.append ("threesuperior");
+		names.append ("onehalf");
+		names.append ("onequarter");
+		names.append ("threequarters");
+		names.append ("franc");
+		names.append ("Gbreve");
+		names.append ("gbreve");
+		names.append ("Idotaccent");
+		names.append ("Scedilla");
+		names.append ("scedilla");
+		names.append ("Cacute");
+		names.append ("cacute");
+		names.append ("Ccaron");
+		names.append ("ccaron");
+		names.append ("dcroat");
+	}
+	
+	// mapping with char code to standard order
+	int get_standard_index (unichar c) {
+		switch (c) {
+			// entry 0 is the .notdef
+			
+			case '\0':
+				return 1;
+
+			case '\r':
+				return 2;
+
+			case ' ': // space
+				return 3;
+
+			case '!':
+				return 4;
+
+			case '"':
+				return 5;
+
+			case '#':
+				return 6;
+
+			case '$':
+				return 7;
+
+			case '%':
+				return 8;
+
+			case '&':
+				return 9;
+
+			case '\'':
+				return 10;
+
+			case '(':
+				return 11;
+
+			case ')':
+				return 12;
+
+			case '*':
+				return 13;
+
+			case '+':
+				return 14;
+
+			case ',':
+				return 15;
+
+			case '-':
+				return 16;
+
+			case '.':
+				return 17;
+
+			case '/':
+				return 18;
+
+			case '0':
+				return 19;
+
+			case '1':
+				return 20;
+
+			case '2':
+				return 21;
+
+			case '3':
+				return 22;
+
+			case '4':
+				return 23;
+
+			case '5':
+				return 24;
+
+			case '6':
+				return 25;
+
+			case '7':
+				return 26;
+
+			case '8':
+				return 27;
+
+			case '9':
+				return 28;
+
+			case ':':
+				return 29;
+
+			case ';':
+				return 30;
+
+			case '<':
+				return 31;
+
+			case '=':
+				return 32;
+
+			case '>':
+				return 33;
+
+			case '?':
+				return 34;
+
+			case '@':
+				return 35;
+
+			case 'A':
+				return 36;
+
+			case 'B':
+				return 37;
+
+			case 'C':
+				return 38;
+
+			case 'D':
+				return 39;
+
+			case 'E':
+				return 40;
+
+			case 'F':
+				return 41;
+
+			case 'G':
+				return 42;
+
+			case 'H':
+				return 43;
+
+			case 'I':
+				return 44;
+
+			case 'J':
+				return 45;
+
+			case 'K':
+				return 46;
+
+			case 'L':
+				return 47;
+
+			case 'M':
+				return 48;
+
+			case 'N':
+				return 49;
+
+			case 'O':
+				return 50;
+
+			case 'P':
+				return 51;
+
+			case 'Q':
+				return 52;
+
+			case 'R':
+				return 53;
+
+			case 'S':
+				return 54;
+
+			case 'T':
+				return 55;
+
+			case 'U':
+				return 56;
+
+			case 'V':
+				return 57;
+
+			case 'W':
+				return 58;
+
+			case 'X':
+				return 59;
+
+			case 'Y':
+				return 60;
+
+			case 'Z':
+				return 61;
+
+			case '[':
+				return 62;
+
+			case '\\':
+				return 63;
+
+			case ']':
+				return 64;
+
+			case '^':
+				return 65;
+
+			case '_':
+				return 66;
+
+			case '`':
+				return 67;
+
+			case 'a':
+				return 68;
+
+			case 'b':
+				return 69;
+
+			case 'c':
+				return 70;
+
+			case 'd':
+				return 71;
+
+			case 'e':
+				return 72;
+
+			case 'f':
+				return 73;
+
+			case 'g':
+				return 74;
+
+			case 'h':
+				return 75;
+
+			case 'i':
+				return 76;
+
+			case 'j':
+				return 77;
+
+			case 'k':
+				return 78;
+
+			case 'l':
+				return 79;
+
+			case 'm':
+				return 80;
+
+			case 'n':
+				return 81;
+
+			case 'o':
+				return 82;
+
+			case 'p':
+				return 83;
+
+			case 'q':
+				return 84;
+
+			case 'r':
+				return 85;
+
+			case 's':
+				return 86;
+
+			case 't':
+				return 87;
+
+			case 'u':
+				return 88;
+
+			case 'v':
+				return 89;
+
+			case 'w':
+				return 90;
+
+			case 'x':
+				return 91;
+
+			case 'y':
+				return 92;
+
+			case 'z':
+				return 93;
+
+			case '{':
+				return 94;
+
+			case '|':
+				return 95;
+
+			case '}':
+				return 96;
+
+			case '~':
+				return 97;
+
+			case 'Ä':
+				return 98;
+
+			case 'Å':
+				return 99;
+
+			case 'Ç':
+				return 100;
+
+			case 'É':
+				return 101;
+
+			case 'Ñ':
+				return 102;
+
+			case 'Ö':
+				return 103;
+
+			case 'Ü':
+				return 104;
+
+			case 'á':
+				return 105;
+
+			case 'à':
+				return 106;
+
+			case 'â':
+				return 107;
+
+			case 'ä':
+				return 108;
+
+			case 'ã':
+				return 109;
+
+			case 'å':
+				return 110;
+
+			case 'ç':
+				return 111;
+
+			case 'é':
+				return 112;
+
+			case 'è':
+				return 113;
+
+			case 'ê':
+				return 114;
+
+			case 'ë':
+				return 115;
+
+			case 'í':
+				return 116;
+
+			case 'ì':
+				return 117;
+
+			case 'î':
+				return 118;
+
+			case 'ï':
+				return 119;
+
+			case 'ñ':
+				return 120;
+
+			case 'ó':
+				return 121;
+
+			case 'ò':
+				return 122;
+
+			case 'ô':
+				return 123;
+
+			case 'ö':
+				return 124;
+
+			case 'õ':
+				return 125;
+
+			case 'ú':
+				return 126;
+
+			case 'ù':
+				return 127;
+
+			case 'û':
+				return 128;
+
+			case 'ü':
+				return 129;
+
+			case '†':
+				return 130;
+
+			case '°':
+				return 131;
+
+			case '¢':
+				return 132;
+
+			case '£':
+				return 133;
+
+			case '§':
+				return 134;
+
+			case '•':
+				return 135;
+
+			case '¶':
+				return 136;
+
+			case 'ß':
+				return 137;
+
+			case '®':
+				return 138;
+
+			case '©':
+				return 139;
+
+			case '™':
+				return 140;
+
+			case '´':
+				return 141;
+
+			case '¨':
+				return 142;
+
+			case '≠':
+				return 143;
+
+			case 'Æ':
+				return 144;
+
+			case 'Ø':
+				return 145;
+
+			case '∞':
+				return 146;
+
+			case '±':
+				return 147;
+
+			case '≤':
+				return 148;
+
+			case '≥':
+				return 149;
+
+			case '¥':
+				return 150;
+
+			case 'µ':
+				return 151;
+
+			case '∂':
+				return 152;
+
+			case '∑':
+				return 153;
+
+			case '∏':
+				return 154;
+
+			case 'π':
+				return 155;
+
+			case '∫':
+				return 156;
+
+			case 'ª':
+				return 157;
+
+			case 'º':
+				return 158;
+
+			case 'Ω':
+				return 159;
+
+			case 'æ':
+				return 160;
+
+			case 'ø':
+				return 161;
+
+			case '¿':
+				return 162;
+
+			case '¡':
+				return 163;
+
+			case '¬':
+				return 164;
+
+			case '√':
+				return 165;
+
+			case 'ƒ':
+				return 166;
+
+			case '≈':
+				return 167;
+
+			case '∆':
+				return 168;
+
+			case '«':
+				return 169;
+
+			case '»':
+				return 170;
+
+			case '…':
+				return 171;
+
+			case ' ': // non breaking space
+				return 172;
+							
+			case 'À':
+				return 173;
+
+			case 'Ã':
+				return 174;
+
+			case 'Õ':
+				return 175;
+
+			case 'Œ':
+				return 176;
+
+			case 'œ':
+				return 177;
+
+			case '–':
+				return 178;
+
+			case '—':
+				return 179;
+
+			case '“':
+				return 180;
+
+			case '”':
+				return 181;
+
+			case '‘':
+				return 182;
+
+			case '’':
+				return 183;
+
+			case '÷':
+				return 184;
+
+			case '◊':
+				return 185;
+
+			case 'ÿ':
+				return 186;
+
+			case 'Ÿ':
+				return 187;
+
+			case '⁄':
+				return 188;
+
+			case '¤':
+				return 189;
+
+			case '‹':
+				return 190;
+
+			case '›':
+				return 191;
+
+			case 'fi':
+				return 192;
+
+			case 'fl':
+				return 193;
+
+			case '‡':
+				return 194;
+
+			case '·':
+				return 195;
+
+			case '‚':
+				return 196;
+
+			case '„':
+				return 197;
+
+			case '‰':
+				return 198;
+
+			case 'Â':
+				return 199;
+
+			case 'Ê':
+				return 200;
+
+			case 'Á':
+				return 201;
+
+			case 'Ë':
+				return 202;
+
+			case 'È':
+				return 203;
+
+			case 'Í':
+				return 204;
+
+			case 'Î':
+				return 205;
+
+			case 'Ï':
+				return 206;
+
+			case 'Ì':
+				return 207;
+
+			case 'Ó':
+				return 208;
+
+			case 'Ô':
+				return 209;
+				
+			// Machintosh apple goes here
+			// return 210;
+
+			case 'Ò':
+				return 211;
+
+			case 'Ú':
+				return 212;
+
+			case 'Û':
+				return 213;
+
+			case 'Ù':
+				return 214;
+
+			case 'ı':
+				return 215;
+
+			case 'ˆ':
+				return 216;
+
+			case '˜':
+				return 217;
+
+			case '¯':
+				return 218;
+
+			case '˘':
+				return 219;
+
+			case '˙':
+				return 220;
+
+			case '˚':
+				return 221;
+
+			case '¸':
+				return 222;
+
+			case '˝':
+				return 223;
+
+			case '˛':
+				return 224;
+
+			case 'ˇ':
+				return 225;
+
+			case 'Ł':
+				return 226;
+
+			case 'ł':
+				return 227;
+
+			case 'Š':
+				return 228;
+
+			case 'š':
+				return 229;
+
+			case 'Ž':
+				return 230;
+
+			case 'ž':
+				return 231;
+
+			case '¦':
+				return 232;
+
+			case 'Ð':
+				return 233;
+
+			case 'ð':
+				return 234;
+
+			case 'Ý':
+				return 235;
+
+			case 'ý':
+				return 236;
+
+			case 'Þ':
+				return 237;
+
+			case 'þ':
+				return 238;
+
+			case '−':
+				return 239;
+
+			case '×':
+				return 240;
+
+			case '¹':
+				return 241;
+				
+			case '²':
+				return 242;
+
+			case '³':
+				return 243;
+
+			case '½':
+				return 244;
+
+			case '¼':
+				return 245;
+
+			case '¾':
+				return 246;
+
+			case '₣':
+				return 247;
+
+			case 'Ğ':
+				return 248;
+
+			case 'ğ':
+				return 249;
+
+			case 'İ':
+				return 250;
+
+			case 'Ş':
+				return 251;
+
+			case 'ş':
+				return 252;
+
+			case 'Ć':
+				return 253;
+
+			case 'ć':
+				return 254;
+
+			case 'Č':
+				return 255;
+
+			case 'č':
+				return 256;
+
+			case 'đ':
+				return 257;
+		}
+		
+		return 0;
+	}
+	
+	public void process () throws Error {
+		FontData fd = new FontData ();
+		string n;
+		
+		fd.add_fixed (0x00020000); // Version
+		fd.add_fixed (0x00000000); // italicAngle
+		
+		fd.add_short (-2); // underlinePosition
+		fd.add_short (1); // underlineThickness
+
+		fd.add_ulong (0); // non zero for monospaced font
+		
+		// mem boundries may be omitted
+		fd.add_ulong (0); // min mem for type 42
+		fd.add_ulong (0); // max mem for type 42
+		
+		fd.add_ulong (0); // min mem for Type1
+		fd.add_ulong (0); // max mem for Type1
+
+		fd.add_ushort ((uint16) glyf_table.glyphs.length ());
+
+		// this part of the spec is so weird
+		
+		fd.add_ushort ((uint16) 0); // first index is .notdef
+		index.append (0);
+		
+		assert (names.length () == 0);
+		add_standard_names ();
+
+		int index;
+		Glyph g;
+		for (int i = 1; i < glyf_table.glyphs.length (); i++) {
+			g = (!) glyf_table.glyphs.nth (i).data;
+			index = get_standard_index (g.unichar_code);
+			
+			if (index != 0) {
+				fd.add_ushort ((uint16) index);  // use standard name
+			} else {
+				printd ("Adding non standard postscript name $(g.get_name ())\n");
+				
+				index = (int) names.length (); // use font specific name
+				fd.add_ushort ((uint16) index);
+				names.append (g.get_name ());
+			}
+			
+			this.index.append ((uint16) index);
+		}
+
+		for (int i = 258; i < names.length (); i++) {
+			n = (!) names.nth (i).data;
+			
+			if (n.length > 0xFF) {
+				warning ("too long name for glyph $n");
+			}
+						
+			fd.add ((uint8) n.length); // length of string
+			fd.add_str (n);
+		}		
+
+		fd.pad ();
+		
+		this.font_data = fd;
+	}
+
+}
+
+class GaspTable : Table {
+	
+	public GaspTable () {
+		id = "gasp";
+	}
+	
+	public override void parse (FontData dis) throws Error {
+	}
+	
+	public void process () throws GLib.Error {
+		FontData fd = new FontData ();
+
+		fd.add_ushort (0);
+		fd.add_ushort (0);
+
+		fd.pad ();
+	
+		this.font_data = fd;
+	}
+
+}
+
+class GdefTable : Table {
+	
+	public GdefTable () {
+		id = "GDEF";
+	}
+	
+	public override void parse (FontData dis) throws Error {
+	}
+	
+	public void process () throws GLib.Error {
+		FontData fd = new FontData ();
+
+		fd.add_ulong (0x00010002);
+		fd.add_ushort (0); // class def
+		fd.add_ushort (0); // attach list
+		fd.add_ushort (0); // ligature carret
+		fd.add_ushort (0); // mark attach
+		fd.add_ushort (0); // mark glyf
+		fd.add_ushort (0); // mark glyf set def
+		
+		fd.pad ();
+	
+		this.font_data = fd;
+	}
+
+}
+
+class CvtTable : Table {
+	
+	public CvtTable () {
+		id = "cvt ";
+	}
+	
+	public override void parse (FontData dis) throws Error {
+	}
+	
+	public void process () throws GLib.Error {
+		FontData fd = new FontData ();
+		
+		fd.add_ushort (0);
+		fd.pad ();
+	
+		this.font_data = fd;
+	}
+
+}
+
+class Kern : GLib.Object {
+	public uint16 left;
+	public uint16 right;
+	public int16 kerning;
+	
+	public Kern (uint16 l, uint16 r, int16 k) {
+		left = l;
+		right = r;
+		kerning = k;
+	}
+}
+
+class KernList : GLib.Object {
+	public List<Kern> kernings;
+	
+	public KernList () {
+		kernings = new List<Kern> ();
+	}
+}
+
+class KernTable : Table {
+	
+	public static const uint16 HORIZONTAL = 1;
+	public static const uint16 MINIMUM = 1 << 1;
+	public static const uint16 CROSS_STREAM = 1 << 2;
+	public static const uint16 OVERRIDE = 1 << 3;
+	public static const uint16 FORMAT = 1 << 8;
+	
+	GlyfTable glyf_table;
+	
+	List<Kern> kernings = new List<Kern> ();
+	
+	public int kerning_pairs = 0;
+	
+	public KernTable (GlyfTable gt) {
+		glyf_table = gt;
+		id = "kern";
+	}
+	
+	public KernList get_all_pairs (int gid) {
+		KernList kl = new KernList ();
+		
+		foreach (Kern k in kernings) {
+			if (k.left == gid) {
+				kl.kernings.append (k);
+			}
+		}
+		
+		return kl;
+	}
+	
+	public override void parse (FontData dis) throws GLib.Error {
+		uint16 version;
+		uint16 sub_tables;
+		
+		uint16 subtable_version;
+		uint16 subtable_length;
+		uint16 subtable_flags;
+
+		uint16 search_range;
+		uint16 entry_selector;
+		uint16 range_shift;
+		
+		uint16 n_pairs;
+			
+		dis.seek (offset);
+		
+		version = dis.read_ushort ();
+		warn_if_fail (version == 0);
+		sub_tables = dis.read_ushort ();
+		
+		for (uint16 i = 0; i < sub_tables; i++) {
+			subtable_version = dis.read_ushort ();			
+			subtable_length = dis.read_ushort ();			
+			subtable_flags = dis.read_ushort ();
+
+			n_pairs = dis.read_ushort ();
+			search_range = dis.read_ushort ();
+			entry_selector = dis.read_ushort ();
+			range_shift = dis.read_ushort ();
+						
+			// TODO: check more flags
+			if ((subtable_flags & HORIZONTAL) > 0 && (subtable_flags & CROSS_STREAM) == 0 && (subtable_flags & MINIMUM) == 0) {
+				parse_pairs (dis, n_pairs);
+			}
+		}
+	}
+	
+	public void parse_pairs (FontData dis, uint16 n_pairs) throws Error {
+		uint16 left;
+		uint16 right;
+		int16 kerning;
+		
+		for (int i = 0; i < n_pairs; i++) {
+			left = dis.read_ushort ();
+			right = dis.read_ushort ();
+			kerning = dis.read_short ();
+						
+			kernings.append (new Kern (left, right, kerning));
+		}		
+	}
+	
+	public void process () throws GLib.Error {
+		FontData fd = new FontData ();
+		uint16 n_pairs = 0;
+		
+		uint16 gid_left;
+		int gid_right;
+		
+		uint16 range_shift = 0;
+		uint16 entry_selector = 0;
+		uint16 search_range = 0;
+		
+		fd.add_ushort (0); // version 
+		fd.add_ushort (1); // n subtables
+
+		fd.add_ushort (0); // subtable version 
+
+		foreach (Glyph g in glyf_table.glyphs) {
+			foreach (Kerning k in g.kerning) {
+				n_pairs++;
+			}
+		}
+		
+		this.kerning_pairs = n_pairs;
+		
+		fd.add_ushort (6 * n_pairs + 14); // subtable length
+		fd.add_ushort (HORIZONTAL); // subtable flags
+
+		fd.add_ushort (n_pairs);
+		
+		search_range = 6 * largest_pow2 (n_pairs);
+		entry_selector = largest_pow2_exponent (n_pairs);
+		range_shift = 6 * n_pairs - search_range;
+		
+		fd.add_ushort (search_range);
+		fd.add_ushort (entry_selector);
+		fd.add_ushort (range_shift);
+
+		gid_left = 0;
+		foreach (Glyph g in glyf_table.glyphs) {
+			
+			foreach (Kerning k in g.kerning) {
+				gid_right = glyf_table.get_gid (k.glyph_right);
+				
+				if (gid_right == -1) {
+					warning ("right glyph not found in kerning table");
+				}
+				
+				fd.add_ushort (gid_left);
+				fd.add_ushort ((uint16)gid_right);
+				fd.add_short ((int16)k.val);
+			}
+			
+			gid_left++;
+		}
+
+		fd.pad ();
+		this.font_data = fd;
+	}
+
+}
+
+/** Table with list of tables sorted by table tag. */
+class DirectoryTable : Table {
+	
+	public CmapTable cmap_table;
+	public CvtTable  cvt_table;
+	public GaspTable gasp_table;
+	public GdefTable gdef_table;
+	public GlyfTable glyf_table;
+	public HeadTable head_table;
+	public HheaTable hhea_table;
+	public HmtxTable hmtx_table;
+	public KernTable kern_table;
+	public MaxpTable maxp_table;
+	public NameTable name_table;
+	public Os2Table  os_2_table;
+	public PostTable post_table;
+	public LocaTable loca_table;
+	
+	public OffsetTable offset_table;
+	
+	List<Table> tables;
+	
+	public DirectoryTable () {
+		offset_table = new OffsetTable (this);
+		
+		loca_table = new LocaTable ();
+		gasp_table = new GaspTable ();
+		gdef_table = new GdefTable ();
+		glyf_table = new GlyfTable (loca_table);
+		cmap_table = new CmapTable (glyf_table);
+		cvt_table  = new CvtTable ();
+		head_table = new HeadTable (glyf_table);
+		hmtx_table = new HmtxTable (head_table, glyf_table);
+		hhea_table = new HheaTable (glyf_table, head_table, hmtx_table);
+		kern_table = new KernTable (glyf_table);
+		maxp_table = new MaxpTable (glyf_table);
+		name_table = new NameTable ();
+		os_2_table = new Os2Table (); 
+		post_table = new PostTable (glyf_table);
+		
+		id = "Directory table";
+	}
+
+	public void process () throws GLib.Error {
+		// generate font data
+		glyf_table.process ();
+		gasp_table.process ();
+		gdef_table.process ();
+		cmap_table.process (glyf_table);
+		cvt_table.process ();
+		hmtx_table.process ();
+		hhea_table.process ();
+		maxp_table.process ();
+		name_table.process ();
+		os_2_table.process (glyf_table);
+		head_table.process ();
+		loca_table.process (glyf_table, head_table);
+		post_table.process ();
+		kern_table.process ();
+		
+		offset_table.process ();
+		process_directory (); // this table
+	}
+
+	public unowned List<Table> get_tables () {
+		if (tables.length () == 0) {
+			tables.append (offset_table);
+			tables.append (this);
+			
+			// tables.append (gdef_table); // invalid table
+			tables.append (os_2_table);
+			tables.append (cmap_table);
+			// tables.append (cvt_table);
+			// tables.append (gasp_table);
+			tables.append (glyf_table);
+			tables.append (head_table);
+			tables.append (hhea_table);
+			tables.append (hmtx_table);
+			
+			if (kern_table.kerning_pairs > 0) {
+				tables.append (kern_table);
+			}
+			
+			tables.append (loca_table);
+			tables.append (maxp_table);
+			tables.append (name_table);
+			tables.append (post_table);
+		}
+
+		return tables;
+	}
+
+	public void set_offset_table (OffsetTable ot) {
+		offset_table = ot;
+	}
+	
+	public new void parse (FontData dis, File file, OpenFontFormatReader reader_callback) throws Error {
+		StringBuilder tag = new StringBuilder ();
+		uint32 checksum;
+		uint32 offset;
+		uint32 length;
+		
+		return_if_fail (offset_table.num_tables > 0);
+		
+		for (uint i = 0; i < offset_table.num_tables; i++) {
+			tag.erase ();
+			
+			tag.append_unichar ((unichar) dis.read_byte ());
+			tag.append_unichar ((unichar) dis.read_byte ());
+			tag.append_unichar ((unichar) dis.read_byte ());
+			tag.append_unichar ((unichar) dis.read_byte ());
+			
+			checksum = dis.read_ulong ();
+			offset = dis.read_ulong ();
+			length = dis.read_ulong ();
+			
+			printd (@"$(tag.str) \toffset: $offset \tlength: $length \tchecksum: $checksum.\n");
+			
+			if (tag.str == "cvt") {
+				cvt_table.id = tag.str;
+				cvt_table.checksum = checksum;
+				cvt_table.offset = offset;
+				cvt_table.length = length;
+			} else if (tag.str == "hmtx") {
+				hmtx_table.id = tag.str;
+				hmtx_table.checksum = checksum;
+				hmtx_table.offset = offset;
+				hmtx_table.length = length;
+			} else if (tag.str == "hhea") {
+				hhea_table.id = tag.str;
+				hhea_table.checksum = checksum;
+				hhea_table.offset = offset;
+				hhea_table.length = length;	
+			} else if (tag.str == "loca") {
+				loca_table.id = tag.str;
+				loca_table.checksum = checksum;
+				loca_table.offset = offset;
+				loca_table.length = length;	
+			} else if (tag.str == "cmap") {
+				cmap_table.id = tag.str;
+				cmap_table.checksum = checksum;
+				cmap_table.offset = offset;
+				cmap_table.length = length;
+			} else if (tag.str == "maxp") {
+				maxp_table.id = tag.str;
+				maxp_table.checksum = checksum;
+				maxp_table.offset = offset;
+				maxp_table.length = length;
+			} else if (tag.str == "gasp") {
+				gasp_table.id = tag.str;
+				gasp_table.checksum = checksum;
+				gasp_table.offset = offset;
+				gasp_table.length = length;
+			} else if (tag.str == "gdef") {
+				gdef_table.id = tag.str;
+				gdef_table.checksum = checksum;
+				gdef_table.offset = offset;
+				gdef_table.length = length;
+			} else if (tag.str == "glyf") {
+				glyf_table.id = tag.str;
+				glyf_table.checksum = checksum;
+				glyf_table.offset = offset;
+				glyf_table.length = length;
+			} else if (tag.str == "head") {
+				head_table.id = tag.str;
+				head_table.checksum = checksum;
+				head_table.offset = offset;
+				head_table.length = length;
+			} else if (tag.str == "name") {
+				name_table.id = tag.str;
+				name_table.checksum = checksum;
+				name_table.offset = offset;
+				name_table.length = length;
+			} else if (tag.str == "OS/2") {
+				os_2_table.id = tag.str;
+				os_2_table.checksum = checksum;
+				os_2_table.offset = offset;
+				os_2_table.length = length;
+			} else if (tag.str == "post") {
+				post_table.id = tag.str;
+				post_table.checksum = checksum;
+				post_table.offset = offset;
+				post_table.length = length;
+			} else if (tag.str == "kern") {
+				kern_table.id = tag.str;
+				kern_table.checksum = checksum;
+				kern_table.offset = offset;
+				kern_table.length = length;
+			}
+		}
+
+		head_table.parse (dis);
+		
+		if (!validate_tables (dis, file)) {
+			warning ("Missing required table or bad checksum.");
+		}
+		
+		hhea_table.parse (dis);
+		reader_callback.set_limits ();
+		
+		name_table.parse (dis);
+		post_table.parse (dis);
+		os_2_table.parse (dis);
+		maxp_table.parse (dis);
+		loca_table.parse (dis, head_table, maxp_table);
+		hmtx_table.parse (dis, hhea_table, loca_table);
+		cmap_table.parse (dis);
+		
+		if (kern_table.has_data ()) {
+			kern_table.parse (dis);
+		}
+		
+		glyf_table.parse (dis, cmap_table, loca_table, hmtx_table, head_table, post_table, kern_table);
+		
+		if (kern_table.has_data ()) {
+			gasp_table.parse (dis);
+		}
+		
+		if (kern_table.has_data ()) {
+			cvt_table.parse (dis);
+		}
+	}
+	
+	public bool validate_tables (FontData dis, File file) {
+		bool valid = true;
+		
+		try {
+			dis.seek (0);
+			
+			if (!validate_checksum_for_entire_font (dis, file)) {
+				warning ("file has invalid checksum");
+			} else {
+				printd ("Font file has valid checksum.\n");
+			}
+
+			// Skip validation of head table for now it should be realy simple but seems to
+			// be broken in some funny way.
+
+			if (!glyf_table.validate (dis)) {
+				warning ("glyf_table has invalid checksum");
+				valid = false;
+			}
+			
+			if (!maxp_table.validate (dis)) {
+				warning ("maxp_table has is invalid checksum");
+				valid = false;
+			}
+			
+			if (!loca_table.validate (dis)) {
+				warning ("loca_table has invalid checksum");
+				valid = false;
+			}
+			
+			if (!cmap_table.validate (dis)) {
+				warning ("cmap_table has invalid checksum");
+				valid = false;
+			}
+			
+			if (!hhea_table.validate (dis)) {
+				warning ("hhea_table has invalid checksum");
+				valid = false;
+			}
+			
+			if (!hmtx_table.validate (dis)) {
+				warning ("hmtx_table has invalid checksum");
+				valid = false;
+			}
+
+			if (!name_table.validate (dis)) {
+				warning ("name_table has invalid checksum");
+				valid = false;
+			}
+
+			if (!os_2_table.validate (dis)) {
+				warning ("os_2_table has invalid checksum");
+				valid = false;
+			}
+
+			if (!post_table.validate (dis)) {
+				warning ("post_table has invalid checksum");
+				valid = false;
+			}
+			
+			if (kern_table.has_data () && !kern_table.validate (dis)) {
+				warning ("kern_table has invalid checksum");
+				valid = false;
+			}
+			
+		} catch (GLib.Error e) {
+			warning (e.message);
+			valid = false;
+		}
+		
+		return valid;
+	}
+	
+	bool validate_checksum_for_entire_font (FontData dis, File f) throws GLib.Error {
+		uint p = head_table.offset + head_table.get_checksum_position ();
+		uint32 checksum_font, checksum_head;
+
+		checksum_head = head_table.get_font_checksum ();
+		
+		dis.seek (0);
+		
+		// zero out checksum entry in head table before validating it
+		dis.write_at (p + 0, 0);
+		dis.write_at (p + 1, 0);
+		dis.write_at (p + 2, 0);
+		dis.write_at (p + 3, 0);
+		
+		checksum_font = (uint32) (0xB1B0AFBA - dis.check_sum ());
+
+		if (checksum_font != checksum_head) {
+			warning (@"Fontfile checksum in head table does not match calculated checksum. checksum_font: $checksum_font checksum_head: $checksum_head");
+			return false;
+		}
+		
+		return true;
+	}
+	
+	public long get_font_file_size () {
+		long length = 0;
+		
+		foreach (Table t in tables) {
+			length += t.get_font_data ().length_with_padding ();
+		}
+		
+		return length;
+	}
+	
+	public void process_directory () throws GLib.Error {
+		create_directory (); // create directory without offsets to calculate length of offset table and checksum for entre file
+		create_directory (); // generate a valid directory
+	}
+
+	// Check sum adjustment for the entire font
+	public uint32 get_font_file_checksum () {
+		uint32 check_sum = 0;
+		foreach (Table t in tables) {
+			t.get_font_data ().continous_check_sum (ref check_sum);
+		}
+		return check_sum;
+	}
+
+	public void create_directory () throws GLib.Error {
+		FontData fd;
+	
+		uint32 table_offset = 0;
+		uint32 table_length = 0;
+		
+		uint32 check_sum = 0;
+		
+		fd = new FontData ();
+
+		return_val_if_fail (offset_table.num_tables > 0, fd);
+		
+		table_offset += offset_table.get_font_data ().length_with_padding ();
+		
+		if (this.font_data != null) {
+			table_offset += this.get_font_data ().length_with_padding ();
+		}
+
+		head_table.set_check_sum_adjustment (0); // Set this to zero, calculate checksums and update the value
+		head_table.process ();
+		
+		// write the directory 
+		foreach (Table t in tables) {
+						
+			if (t is DirectoryTable || t is OffsetTable) {
+				continue;
+			}
+			
+			printd (@"c $(t.id)  offset: $(table_offset)  len with pad  $(t.get_font_data ().length_with_padding ())\n");
+
+			table_length = t.get_font_data ().length (); // without padding
+			
+			fd.add_tag (t.get_id ()); // name of table
+			fd.add_u32 (t.get_font_data ().check_sum ());
+			fd.add_u32 (table_offset);
+			fd.add_u32 (table_length);
+			
+			table_offset += t.get_font_data ().length_with_padding ();
+		}
+
+		// padding
+		fd.pad ();
+						
+		this.font_data = fd;
+
+		check_sum = get_font_file_checksum ();
+		head_table.set_check_sum_adjustment ((uint32)(0xB1B0AFBA - check_sum));
+		head_table.process (); // update the value		
+	}
+	
+}
+
+/** Largest power of two less than max. */
+internal static uint16 largest_pow2 (uint16 max) {
+	uint16 x = 1;
+	uint16 l = 0;
+	
+	while (x <= max) {
+		l = x;
+		x = x << 1;
+	}
+	
+	return l;
+}
+
+internal static uint16 largest_pow2_exponent (uint16 max) {
+	uint16 exp = 0;
+	uint16 l = 0;
+	uint16 x = 0;
+	
+	while (x <= max) {
+		l = exp;
+		exp++;
+		x = 1 << exp;
+	}	
+	
+	return l;
+}
+
+void printd (string s) {
+	// print (s);
+}
+
+}
diff --git a/libbirdfont/OverView.vala b/libbirdfont/OverView.valanew file mode 100644index 0000000..e8915c2--- /dev/null+++ b/libbirdfont/OverView.vala@@ -0,0 +1,1069 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++/** A display with all glyphs present in this font. */+public class OverView : FontDisplay {+	Allocation allocation;+	+	int rows = 0;+	int items_per_row = 0;+	uint32 first_character = 0;+	int64 first_visible = 0;+	uint32 selected = 0;+	+	double view_offset_y = 0;+	double view_offset_x = 0;+	+	int nail_height = 0;+	int nail_width = 0;++	double nail_zoom_height = 0;+	double nail_zoom_width = 0;+	+	public signal void open_glyph_signal (string c);++	GlyphRange glyph_range;++	List<GlyphCollection> visible_characters = new List<GlyphCollection> ();++	List<unichar> zoom_list = new List<unichar> ();+	int zoom_list_index = 0;++	List<GlyphCollection> deleted_glyphs = new List<GlyphCollection> ();++	Scrollbar scrollbar;+	+	bool all_avail = true;+	+	public OverView (GlyphRange? range = null) {+		GlyphRange gr;+		+		if (range == null) {+			gr = new GlyphRange ();+			set_glyph_range (gr);+		}+		+		reset_zoom ();++		this.open_glyph_signal.connect ((t, s) => {+			TabBar tabs = MainWindow.get_tab_bar ();+			Toolbox tools = MainWindow.get_toolbox ();+			bool selected = tabs.select_char (s);+			unichar new_char = s.get_char (0);+			Font f = Supplement.get_current_font ();+							+			if (!selected) {+				GlyphCollection? fg = f.get_glyph_collection (s);+				Glyph g = (fg == null) ? new Glyph (s, new_char) : ((!) fg).get_current ();+				ZoomTool z = (ZoomTool) tools.get_tool ("zoom_tool");+				+				stdout.printf (@"Open '%s' %u (%s)\n", s, new_char, Font.to_hex (new_char));+				+				z.store_current_view ();+				f.add_glyph (g);+				tabs.add_tab (g);+				+				MainWindow.get_glyph_canvas ().set_current_glyph (g);+				+				g.close_path ();+				g.default_zoom ();+				z.store_current_view ();+			}+		});+		+		scrollbar = new Scrollbar ();+		scrollbar.signal_scroll.connect ((delta, delta_last, absolute) => {+			int64 adjustement = (int64) (-delta_last * get_height ());+			int64 pixels = (int64) (-5.0 * delta_last * nail_height);+			+			if (absolute <= 0.002) {+				first_character = 0;+				first_visible = 0;+				selected = 0;+				scroll_top ();+			} else if (absolute >= 1) {+				scroll_bottom ();+			} else {+				scroll_to (absolute);+			}		+			+			redraw_area (0, 0, allocation.width, allocation.height);+		});+		+		update_scrollbar ();+	}+	+	void scroll_bottom () {+		Font f;+		+		if (all_avail) {+			f = Supplement.get_current_font ();+			scroll_to_position (f.length () - items_per_row * (rows - 1));+		} else {+			scroll_to_position (glyph_range.length () - items_per_row * (rows - 1));+		}+	}+	+	public double get_height () {+		double l;+		Font f;+		+		if (rows == 0) {+			return 0;+		}+				+		if (all_avail) {+			f = Supplement.get_current_font ();+			l = f.length ();+		} else {+			l = glyph_range.length ();+		}+				+		return 2.0 * nail_height * (l / rows);+	}++	public bool selected_char_is_visible () {+		return first_visible <= selected <= first_visible + items_per_row * rows;+	}++	public override void scroll_wheel_up (double x, double y) {+		key_up ();+		close_menus ();+		redraw_area (0, 0, allocation.width, allocation.height);+	}+	+	public override void scroll_wheel_down (double x, double y) {+		key_down ();+		close_menus ();+		redraw_area (0, 0, allocation.width, allocation.height);+	}+	+	public override void selected_canvas () {+		redraw_area (0, 0, allocation.width, allocation.height);+		KeyBindings.singleton.set_require_modifier (true);+	}+	+	public override void zoom_min () {+		int minw = 26 * 2;+		int minh = 46 * 2;+		+		if (zoom_value_in_range (minw, minh)) {+			nail_width = minw;+			nail_height = minh;+				+			nail_zoom_height = minw; +			nail_zoom_width = minh;		+		+			adjust_scroll ();+			redraw_area (0, 0, allocation.width, allocation.height);+		} else {+			warn_if_reached ();+		}	+	}+	+	public override void reset_zoom () {+		nail_height = 210;+		nail_width = 150;+		+		nail_zoom_height = nail_height; +		nail_zoom_width = nail_width;+		+		adjust_scroll ();+		redraw_area (0, 0, allocation.width, allocation.height);+	}++	public override void zoom_max () {+		if (zoom_value_in_range (allocation.width, allocation.height)) {+			nail_width = allocation.width;+			nail_height = allocation.height;+				+			nail_zoom_height = allocation.height; +			nail_zoom_width = allocation.width;		+		+			adjust_scroll ();+			redraw_area (0, 0, allocation.width, allocation.height);+		} else {+			warn_if_reached ();+		}+	}+	+	public override void zoom_in () {+		double w = (1.1 * nail_width);+		double h = (1.1 * nail_height);+		int nw = (int) w;+		int nh = (int) h;+		+		if (zoom_value_in_range (nw, nh)) {+			nail_width = nw;+			nail_height = nh;+			+			nail_zoom_height = h; +			nail_zoom_width = w;+			+			adjust_scroll ();+			redraw_area (0, 0, allocation.width, allocation.height);+		}+	}+	+	public override void zoom_out () {+		double w = (nail_width / 1.1);+		double h = (nail_height / 1.1);+		int nw = (int) w;+		int nh = (int) h;+		+		if (zoom_value_in_range (nw, nh)) {+			nail_width = nw;+			nail_height = nh;++			nail_zoom_height = h; +			nail_zoom_width = w;+			+			adjust_scroll ();		+			redraw_area (0, 0, allocation.width, allocation.height);+		}+	}+	+	private bool zoom_value_in_range (int w, int h) {+		return (25 < w <= allocation.width && 45 < h <= allocation.height);+	}++	public override void store_current_view () {+		if (zoom_list_index + 1 < zoom_list.length ()) {+			unowned List<unichar> n = zoom_list.nth (zoom_list_index);+			while (n != zoom_list.last ()) zoom_list.delete_link (zoom_list.last ());+		}+		+		zoom_list.append (selected);+		zoom_list_index = (int) zoom_list.length () - 1;+		+		if (zoom_list.length () > 50) zoom_list.delete_link (zoom_list.first ());+	}+	+	public override void restore_last_view () +		requires (zoom_list.length () > 0)+	{		+		if (zoom_list_index - 1 < 0 || zoom_list.length () == 0)+			return;+		+		zoom_list_index--;+	}++	public override void next_view () +		requires (zoom_list.length () > 0)+	{+		if (zoom_list_index + 1 >= zoom_list.length ())+			return;+		+		zoom_list_index++;+	}++	+	public override string get_name () {+		return "Overview";+	}+	+	public void display_all_available_glyphs () {+		all_avail = true;+		+		first_character = 0;+		first_visible = 0;+		selected = 0;+	}+	+	private unowned List<GlyphCollection> get_visible_glyphs () {+		return visible_characters;+	}+	+	public void draw_caption (int row, double width, Context cr, double y, uint64 index_begin) {+		string character_string;+		double left_margin, x, caption_y; // for glyph caption+		uint64 index = index_begin;+		int i = row * items_per_row;+		Font f = Supplement.get_current_font ();+		+		cr.save ();+		cr.set_line_width (1);+		+		x = 0;+		+		cr.set_font_size (14);++		left_margin = 0;+			+		for (int j = 0; j < items_per_row; j++) {+			+			if (all_avail) {+				if (! (0 <= index < f.length ())) {+					break;+				}+				+				character_string = ((!) f.get_glyph_indice ((uint32) index)).get_name ();+			} else {+				if (!(0 <= index < glyph_range.get_length ())) {+					break;+				}+				+				character_string = glyph_range.get_char ((uint32) index);+			}+			+			cr.save ();+			draw_thumbnail (cr, character_string, x, y);+			cr.restore ();++			cr.save ();+						+			if (index == selected) {+				cr.set_source_rgba (142/255.0, 158/255.0, 190/255.0, 1);+			} else {+				cr.set_source_rgba (164/255.0, 185/255.0, 215/255.0, 1);+			}+			+			cr.set_line_join (LineJoin.ROUND);+			cr.set_line_width(12);+			+			cr.rectangle (x, y + nail_height - 20, nail_width - 30, 10);			+			cr.stroke ();+			cr.restore ();+			+			draw_menu (cr, character_string, x, y);+			+			caption_y = y + 10 + nail_height - 20;+			+			cr.move_to (x, caption_y);+			cr.show_text (character_string);+			+			x += nail_width;+			index++;+			i++;+		}+		+		cr.stroke();+	}+		+	private void draw_menu (Context cr, string name, double x, double y) {+		GlyphCollection? gl = Supplement.get_current_font ().get_glyph_collection (name);+		GlyphCollection g;+		DropMenu menu;+		+		if (gl == null) {+			return;+		}+		+		g = (!) gl;+		+		menu = g.get_version_list ();+		menu.set_position (x + nail_width - 40, y + nail_height - 21);+		menu.draw_icon (cr);+		menu.draw_menu (cr);+		+		visible_characters.append (g);+	}+		+	private bool draw_thumbnail (Context cr, string name, double x, double y) {+		Glyph? gl = Supplement.get_current_font ().get_glyph (name);+		Glyph g;+		+		double gx, gy;+		double x1, x2, y1, y2;+		double scale = nail_zoom_width / 150.0;+		double w, h;++		w = nail_width  - 19;+		h = nail_height - 22;+		+		scale = nail_zoom_height / (210.0 - 15.0);+		+		if (gl == null) {+			return false;+		}+		+		g = (!) gl;+		g.boundries (out x1, out y1, out x2, out y2);++		gx = 0;+		gy = h / scale - 40;+		+		Surface s = new Surface.similar (cr.get_target (), Content.COLOR_ALPHA, (int) w, (int) h);+		Context c = new Context (s);+/*+		c.set_source_rgb (200/255.0, 200/255.0, 229/255.0);+		c.rectangle (0, 0, w, h);	+		c.fill ();+*/+		c.scale (scale, scale);				+		Svg.draw_svg_path (c, g.get_svg_data (), gx, gy, 1.0);+		+		cr.save ();+		cr.set_source_surface (s, x - 6, y);+		cr.paint ();+		cr.restore ();++		return true;+	}++	// 150 * 190 px+	public override void draw (Allocation allocation, Context cr) {+		double width;+		double y;+		uint64 t;+		int i;+		int n_items;+		+		while (visible_characters.length () > 9) {+			visible_characters.remove_link (visible_characters.first ());+		}++		this.allocation = allocation;+		+		cr.save ();+		cr.set_source_rgba (1, 1, 1, 1);+		cr.rectangle (0, 0, allocation.width, allocation.height + nail_height);+		cr.fill ();+		cr.restore ();+		+		if (Supplement.experimental) {+			scrollbar.draw (cr, allocation);+		}+		+		n_items = ((allocation.width - 40) / nail_width);+		rows = (allocation.height / nail_height);++		if (items_per_row != n_items) { 	+			if (items_per_row == 0) {+				items_per_row = n_items;+				first_visible = first_character;+			+				key_down ();+				key_up ();+			}	else {+				items_per_row = n_items;+				first_visible = first_character;+			}+		}++		width = nail_width * items_per_row;+		view_offset_x = (allocation.width - width) / 2.0;+		+		cr.translate(view_offset_x, view_offset_y);+	+		y = 0;+		t = first_visible;+		i = 0;+		for (int j = 0; j < rows + 1; j++) {+			draw_caption (i, width, cr, y, t);++			t += items_per_row;+			y += nail_height;+			i++;+		}+		+		draw_menus (allocation, cr);+	}++	private void draw_menus (Allocation allocation, Context cr) {+	}++	private void close_menus () {+	}+	+	public void scroll_rows (int row_adjustment) {+		for (int i = 0; i < row_adjustment; i++) {+			scroll (-nail_height);+		}+		+		for (int i = 0; i > row_adjustment; i--) {+			scroll (nail_height);+		}+	}+	+	public void scroll_adjustment (double pixel_adjustment) {+		double l;+		Font f;+				+		if (all_avail) {+			f = Supplement.get_current_font ();+			l = f.length ();+		} else {+			l = glyph_range.length ();+		}+		+		if (first_visible <= 0) {+			return;+		}++		if (first_visible + rows * items_per_row >= l) {+			return;+		}+		+		scroll ((int64) pixel_adjustment);+	}+	+	void default_position () {+		scroll_top ();+		scroll_rows (1);+	}+	+	void scroll_to_position (int64 r) {+		int64 l;+		Font f;+		+		if (all_avail) {+			f = Supplement.get_current_font ();+			l = f.length ();+		} else {+			l = glyph_range.length ();+		}+		+		// bottom+		if (r > l - items_per_row * rows) {+			return;+		}+		+		if (r < 0) {+			scroll_top ();+			return;+		}+		+		default_position ();++		first_visible = r;+		selected = (uint32) r;+		+		adjust_scroll ();+	}+	+	public void scroll_to (double percent) {+		int64 r;+		double l;+		Font f;+		+		if (all_avail) {+			f = Supplement.get_current_font ();+			l = f.length ();+		} else {+			l = glyph_range.length ();+		}+		+		l /= rows;+		r = (int64) (percent * l * items_per_row);+		scroll_to_position (r);+	}+		+	private void scroll (int64 pixel_adjustment) {++		if (first_visible < 0 && pixel_adjustment < 0) {+			scroll_top ();+			return;+		}+				+		view_offset_y += pixel_adjustment;+		+		if (view_offset_y >= 0) {+			while (view_offset_y > nail_height) {			+				view_offset_y -= nail_height;+				first_visible -= items_per_row;+			}++			first_visible -= items_per_row;+			view_offset_y -= nail_height;+		} else if (view_offset_y < -nail_height) {+			view_offset_y = 0;+			first_visible += items_per_row;+		}+		+	}+	+	public void scroll_top () {+		first_visible = first_character;+		view_offset_y = 0;+	}+	+	private int selected_offset_y (unichar item) {+		int64 di;+		int64 a, b;+		if (unlikely (items_per_row == 0)) return 0;+		+		a = (int64) item;+		b = (int64) first_visible;+		di = (a - b);+		+		return (int) (view_offset_y + ((di / items_per_row) * nail_height));+	}++	/** Make selected item fully visible if it partly off screen. */+	private void adjust_scroll ()  {+		int off =  selected_offset_y (selected);++		if (off < 0)  {+			close_menus ();+			scroll (-1 * off);+		} else if (off > allocation.height - nail_height) {+			scroll ((allocation.height - nail_height) - off);+		}+	}++	public void key_down () {+		double l;+		Font f;+				+		if (all_avail) {+			f = Supplement.get_current_font ();+			l = f.length ();+		} else {+			l = glyph_range.length ();+		}+		+		update_scrollbar ();+		+		if (l == 0) return;+	+		if (at_bottom ()) {+			int len = (int) l;+			int s = (int) selected;+			+			if (len - items_per_row >= s) {+				selected += items_per_row; +			} else if (s < len) {+				selected++;+			}+			+			if (selected >= len) {+				selected = len - 1;+			}+			+			adjust_scroll ();+			+			return;+		}+		+		selected += items_per_row;+		+		if (selected > rows * items_per_row + first_visible) {+			scroll (-nail_height);+		}+		+		adjust_scroll ();+	}++	public void key_up () {+		update_scrollbar ();++		if (selected < first_character) {		+			warn_if_reached ();+		}+		+		if (selected < first_character + items_per_row) {+			scroll_top ();+			return;+		}+		+		if (selected - items_per_row >= first_character) {+			selected -= items_per_row;+			if (selected < first_visible) {+				scroll (-nail_height);+			}+		} +		+		if (first_visible < first_character) {+			selected = first_character;+		}+		+		adjust_scroll ();++		if (first_visible < first_character) {+			scroll_top ();+		}+	}	+	+	public void open_current_glyph () {+		key_press (Key.ENTER);+	}++	public void select_next_glyph () {+		key_press (Key.RIGHT);+	}++	public void key_right () {		+		uint len;+		Font f;+		+		if (all_avail) {+			f = Supplement.get_current_font ();+			len = f.length ();+		} else {+			len = glyph_range.length ();+		}+		+		if (len == 0) return;+		+		selected++;+		+		if (selected >= len) {+			selected = len - 1;+		}++		if (selected_offset_y (selected) > allocation.height - nail_height) {+			selected -= items_per_row;+			key_down ();+		}+	}+	+	public void key_left () {+		if (selected > first_character) {+			selected--;					+			if (selected_offset_y (selected) < 0) {+				selected -= items_per_row;+				key_up ();+			}+		}+	}+	+	public string get_selected_char () {+		Font f;+		Glyph? g;+		+		if (all_avail) {+			f = Supplement.get_current_font ();+			g = f.get_glyph_indice (selected);+			return_val_if_fail (g != null, "".dup ());+			return ((!) g).get_name ();+		}+		+		return glyph_range.get_char (selected);+	}+	+	public override void key_press (uint keyval) {+		redraw_area (0, 0, allocation.width, allocation.height);+		close_menus ();++		if (KeyBindings.modifier == CTRL) {+			return;+		}++		switch (keyval) {+			case Key.ENTER:+				open_glyph_signal (get_selected_char ());+				return;+			+			case Key.UP:+				key_up ();+				return;+				+			case Key.RIGHT:+				key_right ();+				return;+				+			case Key.LEFT:+				key_left ();+				return;+				+			case Key.DOWN:+				key_down ();+				return;+				+			case Key.PG_UP:+				for (int i = 0; i++ < rows; i++) {+					key_up ();+				}+				return;+				+			case Key.PG_DOWN:+				for (int i = 0; i++ < rows; i++) {+					key_down ();+				}+				return;+				+			case Key.DEL:+				delete_selected_glyph ();+				return;+		}++		scroll_to_char (keyval);+	}++	public void delete_selected_glyph () {+		delete_glyph (glyph_range.get_char (selected));+	}+		+	public void delete_glyph (string g) {+		GlyphCollection? gc;+		Font f = Supplement.get_current_font ();+		+		gc = f.get_glyph_collection (g);++		if (gc != null) {+			deleted_glyphs.append ((!) gc);+		}++		f.delete_glyph (g);+		MainWindow.get_tab_bar ().close_by_name (g);+		set_glyph_range (glyph_range);+		f.touch ();+	}+	+	public override void undo () {+		Font f = Supplement.get_current_font ();+		+		if (deleted_glyphs.length () == 0) {+			return;+		}+			+		f.add_glyph_collection (deleted_glyphs.last ().data);+		deleted_glyphs.remove_link (deleted_glyphs.last ());+	}+	+	public void scroll_to_char (unichar c) {+		GlyphRange gr = glyph_range;+		GlyphRange full = new GlyphRange ();+		+		if (is_modifier_key (c)) {+			return;+		}+		+		full.use_full_unicode_range ();+		+		set_glyph_range (full);+		selected = c;+		set_glyph_range (gr);+	}+		+	public override void motion_notify (double x, double y) {+		if (x > allocation.x - 10) {+			scrollbar.motion_notify ((int) x, (int) y);+		}+	}+	+	public override void button_release (int button, double ex, double ey) {+		if (ex > allocation.x - 10) {+			scrollbar.button_release (button, ex, ey);+		}+	}+	+	private void selection_click (uint button, double ex, double ey) {+		double x = view_offset_x;+		double y = view_offset_y;++		unichar current = selected;++		// click in margin+		int n_items = (allocation.width - 40 / nail_width);+		double m = (allocation.width - 40 - (n_items * nail_width)) / 2.0;+		+		bool menu_action = false;+		+		Font f;+		uint len;++		redraw_area (0, 0, allocation.width, allocation.height);++		if (all_avail) {+			f = Supplement.get_current_font ();+			len = f.length ();+		} else {+			len = glyph_range.length ();+		}+		+		foreach (GlyphCollection g in get_visible_glyphs ()) {+			bool a;++			a = g.get_version_list ().menu_item_action (ex - view_offset_x, ey - view_offset_y); // select one item on the menu+			+			if (a) {+				return;+			}+						+			a = g.get_version_list ().menu_icon_action (ex - view_offset_x, ey - view_offset_y); // open menu+			+			if (a) {+				menu_action = true;+			}+		}+		+		if (ex < m) {+			ex = m + 1;+		}+		+		if (ex > allocation.width - m) {+			ex = allocation.width - m - 1;+		}+		+		if (ey < 5) {+			ey = 5;+		}+		+		if (ey > allocation.height - 5) {+			ey = allocation.height - 5;+		}+			+		// empty set+		if (len == 0) return;++		// scroll to the selected glyph+		selected = (uint32) first_visible;++		while (y < ey < allocation.height) {+			selected += items_per_row;+			y += nail_height;+		}+		selected -= items_per_row;++		while (x < ex < allocation.width) {+			selected++;+			x += nail_width;+		}+		+		selected--;+	+		if (at_bottom () && selected >= len) {+			selected = len - 1;+		}+		+		adjust_scroll ();+		+		if (current == selected && !menu_action) {+			open_glyph_signal (get_selected_char ());+		}+		+		adjust_scroll ();+		adjust_scroll ();+	}+	+	public override void button_press (uint button, double x, double y) {+		if (x > allocation.width - 10) {+			if (Supplement.experimental) {+				scrollbar.button_press (button, x, y);+			}+		} else {+			selection_click (button, x, y);+		}+	}++	/** Returns true if overview shows the last character. */+	private bool at_bottom () {+		Font f;+		double t = rows * items_per_row + items_per_row + first_visible;+		+		if (all_avail) {+			f = Supplement.get_current_font ();+			return t >= f.length ();+		}+		+		return t >= glyph_range.length ();+	}++	public void set_glyph_range (GlyphRange range) {+		GlyphRange? current = glyph_range;+		string c = "";+		bool done = false;+		uint32 i;+		uint32 len = 0;+		+		if (current != null) {+			c = glyph_range.get_char (selected);+		}+		+		all_avail = false;+		+		glyph_range = range;++		// Todo: optimized search when full range is selected.+		+		// scroll to the selected character+		if (c == "") {+			c = "a";+		}+		+		if (items_per_row == 0) {+			return;+		}+		+		scroll_top ();++		// Skip scroll if this is a unassigned character+		if (c.char_count () > 1) {+			return;+		}++		while (true) {+			for (i = (uint32) first_visible; i < first_visible + items_per_row * (rows + 1); i++) {+				if (range.get_char (i) == c) {+					selected = i;+					done = true;+					break;+				}+			}+			+			if (done) break;+			if (at_bottom ()) break;++			scroll_rows (rows);+		}++		if (unlikely (!done)) {+			MainWindow.get_toolbox ().select_tool_by_name ("custom_character_set");+			warning (@"No glyph \"$c\"\n");+			return;+		}+		+		adjust_scroll ();+		+		if (at_bottom ()) {+			while (c != glyph_range.get_char (selected) && glyph_range.get_length () >= items_per_row) {+				key_right ();	+				if (selected >= glyph_range.get_length () - items_per_row) {+					break;+				}+			}++			len = glyph_range.get_length ();+			for (i = selected; i < len; i++) {+				if (c == glyph_range.get_char (i)) {+					break;+				}+			}++			if (i == len) {+				selected = first_character;+				scroll_top ();+			}+		}++		update_scrollbar ();+		redraw_area (0, 0, allocation.width, allocation.height);+	}++	public void update_scrollbar () {+		double len;+		Font f;+		+		if (all_avail) {+			f = Supplement.get_current_font ();+			len = f.length ();+		} else {+			len = glyph_range.get_length ();+		}+		+		scrollbar.set_handle_size (items_per_row * rows / len);+		scrollbar.set_handle_position (first_visible / len);+	}+}++}diff --git a/libbirdfont/Path.vala b/libbirdfont/Path.valanew file mode 100644index 0000000..6c27d01--- /dev/null+++ b/libbirdfont/Path.vala@@ -0,0 +1,1879 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Math;++namespace Supplement {++public enum Direction {+	CLOCKWISE,+	COUNTER_CLOCKWISE+}++public class Path {+	+	public List<EditPoint> points = new List<EditPoint> ();++	EditPoint? last_point = null;+	EditPoint? second_last_point = null;++	/** Region boundries */+	public double xmax = double.MIN;+	public double xmin = double.MAX;+	public double ymax = double.MIN;+	public double ymin = double.MAX;++	bool edit = true;+	bool open = true;+	bool direction_is_set = false;++	bool no_derived_direction = false;+	bool clockwise_direction = true;++	public delegate bool RasterIterator (double x, double y, double step); // iterate over each pixel at a given zoom level+	+	bool selected = false;+	+	private static ImageSurface? edit_point_image = null;+	private static ImageSurface? active_edit_point_image = null;+	+	private static ImageSurface? edit_point_handle_image = null;+	private static ImageSurface? active_edit_point_handle_image = null;++	private static ImageSurface? selected_edit_point_image = null;+	private static ImageSurface? active_selected_edit_point_image = null;+	+	Path quadratic_path; // quadratic points for ttf export+	+	public Path () {+		if (edit_point_image == null) {+			edit_point_image = Icons.get_icon ("edit_point.png");+			active_edit_point_image = Icons.get_icon ("active_edit_point.png");+			edit_point_handle_image = Icons.get_icon ("edit_point_handle.png");+			active_edit_point_handle_image = Icons.get_icon ("active_edit_point_handle.png");+			selected_edit_point_image  = Icons.get_icon ("selected_edit_point.png");+			active_selected_edit_point_image = Icons.get_icon ("active_selected_edit_point.png");+		}+	}++	public bool has_direction () {+		return direction_is_set;+	}++	public bool empty () {+		return points.length () == 0;+	}++	public void draw_outline (Context cr, Allocation allocation, double view_zoom) {+		unowned List<EditPoint> ep = points;+		+		unowned EditPoint? n = null;+		unowned EditPoint en;+		unowned EditPoint em;+		+		if (ep.length () < 2) {+			return;+		}+		+		cr.new_path ();+		+		// draw lines+		foreach (EditPoint e in ep) {+			if (n != null) {+				en = (!) n;+				draw_next (e, en, cr);+			}+			+			n = e;+		}++		// close path+		if (!is_open () && n != null) {+			en = (!) n;+			em = ep.first ().data;+			+			draw_next (em, en, cr);+		}++		cr.stroke ();+	}+	+	public void draw_edit_points (Context cr, Allocation allocation, double view_zoom) {+		unowned List<EditPoint> ep = points;+		+		if (is_editable ()) {+			// control points for curvature+			foreach (EditPoint e in ep) {+				if (e.selected || e.selected_handle > 0)+					draw_edit_point_handles (e, cr);+			}+						+			// control points+			foreach (EditPoint e in ep) {+				draw_edit_point (e, cr);+			}+		}+	}++	public void fill_path (Context cr, Allocation allocation, double view_zoom) {+		unowned List<EditPoint> ep = points;+		+		unowned EditPoint? n = null;+		unowned EditPoint en;+		unowned EditPoint em;+		+		cr.new_path ();+					+		// draw lines+		foreach (EditPoint e in ep) {+			if (n != null) {+				en = (!) n;+				draw_next (e, en, cr);+			}+			+			n = e;+		}+		+		// close path+		if (!is_open () && ep.length () >= 2 && n != null) {+			en = (!) n;+			em = ep.first ().data;+			draw_next (em, en, cr);+		}++		// fill path+		cr.close_path ();+			+		if (is_clockwise ()) {+			cr.set_source_rgba (80/255.0, 95/255.0, 137/255.0, 0.5);+		} else {+			cr.set_source_rgba (144/255.0, 145/255.0, 236/255.0, 0.5);+		}+		+		cr.fill ();+	}++	private void draw_next (EditPoint e, EditPoint en, Context cr) {+		draw_curve (e, en, cr);+	}+	+	private static void draw_curve (EditPoint e, EditPoint en, Context cr, double alpha = 1) {+		Glyph g = MainWindow.get_current_glyph ();+		double xa, ya, xb, yb, xc, yc, xd, yd;+		+		get_bezier_points (e, en, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);++		cr.set_source_rgba (0, 0, 0, alpha);+		cr.set_line_width (1 / g.view_zoom);+		+		cr.line_to (xa, ya); // this point makes sense only if it is the first or last position, the other points are meaning less don't export them++		cr.curve_to (xb, yb, xc, yc, xd, yd);+	}+	+	/** Curve relative to window center. */+	public static void get_bezier_points (EditPoint e, EditPoint en, out double xa, out double ya, out double xb, out double yb, out double xc, out double yc, out double xd, out double yd) {+		Glyph g = MainWindow.get_current_glyph ();+		+		double center_x, center_y;+		+		center_x = g.allocation.width / 2.0;+		center_y = g.allocation.height / 2.0;+				+		xa = center_x + en.x;+		ya = center_y - en.y;++		xb = center_x + en.get_right_handle ().x ();+		yb = center_y - en.get_right_handle ().y ();+		+		xc = center_x + e.get_left_handle ().x ();+		yc = center_y - e.get_left_handle ().y ();+		+		xd = center_x + e.x;+		yd = center_y - e.y;		+	}++	/** Curve absolute glyph data. */+	public static void get_abs_bezier_points (EditPoint e, EditPoint en, out double xa, out double ya, out double xb, out double yb, out double xc, out double yc, out double xd, out double yd) {+		xa =  + en.x;+		ya =  - en.y;++		xb =  + en.get_right_handle ().x ();+		yb =  - en.get_right_handle ().y ();+		+		xc =  + e.get_left_handle ().x ();+		yc =  - e.get_left_handle ().y ();+		+		xd =  + e.x;+		yd =  - e.y;		+	}+		+	/** Line points relative to center. */+	public static void get_line_points (EditPoint e, EditPoint en, out double xa, out double ya, out double xb, out double yb) {+		double xc = Glyph.xc ();+		double yc = Glyph.yc ();+		+		xa = xc + en.x;+		ya = yc - en.y;+		+		xb = xc + e.x;+		yb = yc - e.y;+	}+		+	public void draw_line (EditPoint e, EditPoint en, Context cr, double alpha = 1) { +		Glyph g = MainWindow.get_current_glyph ();+		double ax, ay, bx, by;++		get_line_points (e, en, out ax, out ay, out bx, out by);+	+		cr.set_source_rgba (0/255.0, 0/255.0, 0/255.0, 1);+		cr.set_line_width (1.7 * (1/g.view_zoom));++		cr.line_to (ax, ay);+		cr.line_to (bx, by);+		+		cr.stroke ();+	}+	+	public void draw_edit_point  (EditPoint e, Context cr) {+		draw_edit_point_center (e, cr);+	}+	+	public void draw_edit_point_handles (EditPoint e, Context cr)+		requires (active_edit_point_handle_image != null && edit_point_handle_image != null)+	{+		ImageSurface img_right, img_left;++		EditPoint handle_right = e.get_right_handle ().get_point ();+		EditPoint handle_left = e.get_left_handle ().get_point ();++		cr.stroke ();+		+		img_right = (e.get_right_handle ().active) ? (!) active_edit_point_handle_image : (!) edit_point_handle_image;+		img_left = (e.get_left_handle ().active) ? (!) active_edit_point_handle_image : (!) edit_point_handle_image;+		+		if (!(is_open () && e == points.last ().data)) {+			draw_line (handle_right, e, cr, 0.15);+			draw_img_center (cr, img_right, e.get_right_handle ().x (), e.get_right_handle ().y ());+		}+		+		if (!(is_open () && e == points.first ().data)) {+			draw_line (handle_left, e, cr, 0.15);+			draw_img_center (cr, img_left, e.get_left_handle ().x (), e.get_left_handle ().y ());+		}+	}++	public static void draw_edit_point_center (EditPoint e, Context cr) +		requires (active_edit_point_image != null && edit_point_image != null)+	{	+		ImageSurface img;+		+		if (e.is_selected ()) {+			img = (e.active) ? (!) active_selected_edit_point_image : (!) selected_edit_point_image;+		} else {+			img = (e.active) ? (!) active_edit_point_image : (!) edit_point_image;+		}+		+		draw_img_center (cr, img, e.x, e.y);+	}+	+	public static void draw_img_center (Context cr, ImageSurface img, double x, double y) {+		Glyph g = MainWindow.get_current_glyph ();+		+		double ivz = 1 / g.view_zoom;++		double xc = g.allocation.width / 2.0;+		double yc = g.allocation.height / 2.0;++		cr.save ();++		cr.scale (ivz, ivz);+		+		x = xc + x - (img.get_width () / 2.0) * ivz; +		y = yc - y - (img.get_height () / 2.0) * ivz;+		+		cr.set_source_surface (img, x * g.view_zoom, y * g.view_zoom);+		cr.paint ();+		+		cr.restore ();		+	}+	+	/** Returns true if there is an edit point at (p.x, p.y). */+	private bool has_edit_point (EditPoint p) {+		foreach (var t in points) {+			if (p.x == t.x && p.y == t.y) {+				return true;+			}+		}+		return false;+	}++	private EditPoint? get_edit_point_at (double x, double y) {+		foreach (var p in points) {+			if (p.x == x && p.y == y) return p;+		}+				+		return null;+	}+		+	private void remove_all_edit_points () {+		while (points.length () > 0) {+			points.remove_link (points.first ());+		}+	}+	+	/** Replace edit points in this path with @param new_path.points. */+	public void replace_path (Path new_path) {+		while (points.length () > 0) {+			points.remove_link (points.first ());+		}+		+		foreach (var np in new_path.points) {+			add (np.x, np.y);+		}+		+		close ();+	}+	+	/** Returns true if this path can be merged with @param union */ +	private bool is_valid_union (Path union) {+		if (union == this) {+			warning ("Path can't union with it self.");+			return false;+		}+		+		if (union.points.length () <= 2 || points.length () <= 2) {+			return false;+		}++		if (!has_overlapping_boundry (union)) {+			return false;+		}++		return true;+	}+	+	/** Set direction for this path to clockwise for outline and +	 * counter clockwise for inline paths.+	 */+	public bool force_direction (Direction direction) {+		bool c = (direction == Direction.CLOCKWISE);+		direction_is_set = true;+		+		if (c != is_clockwise ()) {+			this.reverse ();+		}+		+		if (unlikely (is_clockwise () != c)) {+			warning ("Failed to set direction for path in force_direction.");+			return true;+		}+				+		return false;+	}++	/** Switch direction from clockwise path to counter clockwise path or vise versa. */+	public void reverse () {+		bool direction = is_clockwise ();+		+		if (no_derived_direction) {+			clockwise_direction = !clockwise_direction;+		}+		+		reverse_points ();+		+		if (unlikely (direction == is_clockwise ())) {+			stderr.printf ("Error: Direction did not change after reversing path.\n");+			stderr.printf (@"Length: $(points.length ())\n");+			stderr.printf (@"No particular direction can be derived: $no_derived_direction \n");+			warning ("Path.reverse () failed.\n");+		}+	}++	private void reverse_points () {+		points.reverse ();+		+		EditPointHandle t;+		foreach (var e in points) {+			t = e.right_handle;+			e.right_handle = e.left_handle;+			e.left_handle = t;+		}+		+		create_list ();+	}++	public void print_all_points () {+		int i = 0;+		foreach (var p in points) {+			++i;+			string t = (p.type == PointType.END) ? " endpoint" : "";+			stdout.printf (@"Point $i at ($(p.x), $(p.y)) $t \n");+		}+	}+	+	private bool validate_list () {+		unowned List<EditPoint> ep = points.first ();+		+		if (points.length () < 3) {+			warning ("points.length () < 3");+		}+		+		if (!is_open ()) {+			for (int i = 0; i < points.length (); i++) {+				if (ep.data.next == null) {+					return false;+				}++				if (ep.data.prev == null) {+					return false;+				}+								+				ep = (!) ep.data.next;+			}+		}++		if (is_open ()) {+			for (int i = 1; i < points.length () - 1; i++) {+				if (ep.data.next == null) {+					return false;+				}++				if (ep.data.prev == null) {+					return false;+				}+								+				ep = (!) ep.data.next;+			}+			+			if (ep.data.next != null) {+				return false;+			}+		}+		+		return true;+	}+	+	private double clockwise_sum () {+		return_if_fail (points.length () >= 3);+		+		double sum = 0;+		EditPoint prev = points.last ().data;+		foreach (EditPoint e in points) {+			sum += (e.x - prev.x) * (e.y + prev.y);+			prev = e;+		}+		+		return sum;+	}+	+	public bool is_clockwise () {+		double s;+		+		if (unlikely (points.length () <= 2)) {+			no_derived_direction = true;+			return clockwise_direction;+		}+		+		s = clockwise_sum ();+		+		if (s == 0) {+			no_derived_direction = true;+			return clockwise_direction;	+		}+		+		return s > 0;+	}+	+	public bool is_editable () {+		return edit;+	}++	/** Show control points on outline path. */+	public void set_editable (bool e) {+		edit = e;+	}+		+	public bool is_open () {+		return open;+	}+	+	private bool direction_x (EditPoint start, EditPoint stop) {+		return (start.x < stop.x);+	}++	private bool direction_y (EditPoint start, EditPoint stop) {+		return (start.y < stop.y);+	}+	+	public void thinner (double ratio) +		requires (points.length () > 2)+	{		+		update_region_boundries ();+		+		double w = Math.fabs(xmax - xmin);+		double h = Math.fabs(ymax - ymin);+		double cx = w / 2 + xmin; +		double cy = h / 2 + ymin;+		+		ratio = 1 - ratio;++		unowned List<EditPoint> i = points.first ();+		unowned List<EditPoint> prev = points.last ();+		+		bool dirx = true;+		bool ldirx = direction_x (prev.data, i.data);+		bool ndirx;++		bool diry = true;+		bool ldiry = direction_y (prev.data, i.data);+		bool ndiry;+		+		while (true) {+			ndirx = direction_x (prev.data, i.data);+			+			if (ndirx != ldirx) {+				dirx = !dirx;+			}+			+			ldirx = ndirx;++			ndiry = direction_y (prev.data, i.data);+			+			if (ndiry != ldiry) {+				diry = !diry;+			}+			+			ldiry = ndiry;+			+			EditPoint p = i.data;+			+			if (dirx) {+				if (p.x < cx)+					p.x += (cx - p.x) * ratio;+				else+					p.x -= (p.x - cx) * ratio;+			} else {+				if (p.x < cx)+					p.x += (cx - p.x) * ratio * 2;+				else+					p.x -= (p.x - cx) * ratio * 2;+			}++			// err borde vara -y+			if (p.y < cy && diry || p.y > cy && ! diry)+				p.y += (cy - p.y) * ratio;+			else+				p.y -= (p.y - cy) * ratio;	+			+			if (i == points.last ()) {+				break;+			}+			+			prev = i;+			i = i.next;+		}+	}+	+	public void resize (double ratio) {+		double h = ymax - ymin;+		double px, py, nx, ny;++		px = xmin;+		py = ymin;+		+		if (ratio < 1 && h < 2) {+			return;+		}+		+		foreach (var p in points) {+			p.x *= ratio;+			p.y *= ratio;+			p.right_handle.length *= ratio;+			p.left_handle.length *= ratio;+		}+		+		xmin *= ratio;	+		xmax *= ratio;+		ymin *= ratio;+		ymax *= ratio;++		nx = xmin;+		ny = ymin;+		+		move (px - nx, py - ny);+	}+	+	public Path copy () {+		Path new_path = new Path ();+		+		foreach (var ep in points) {+			new_path.add_point (ep.copy ());+		}+		+		new_path.edit = edit;+		new_path.open = open;+	+		new_path.create_list ();+		+		return new_path;+	}	+	+	public bool is_over (double x, double y) {+		Glyph g = MainWindow.get_current_glyph ();+		+		x = x * Glyph.ivz () + g.view_offset_x - Glyph.xc ();+		y = y * Glyph.ivz () + g.view_offset_y - Glyph.yc ();++		y *= -1;+		+		return is_over_coordinate (x, y);+	}+	+	public bool is_over_coordinate (double x, double y) {+		return is_over_coordinate_var (x, y, 0.1);+	}+	+	public static double distance (double ax, double bx, double ay, double by) {+		return Math.fabs (Math.sqrt (Math.pow (ax - bx, 2) + Math.pow (ay - by, 2)));+	}+	+	/** Estimate length of path. */+	private double get_length () {+		double len = 0;+		+		return_if_fail (points.length () > 2);+		+		EditPoint prev = points.last ().data;+		foreach (EditPoint p in points) {+			len += get_length_from (prev, p);+			prev = p;+		}+		+		return len;+	}+	+	private static double get_length_from (EditPoint a, EditPoint b) {+		double x, y;+		+		x = Math.fabs (a.x - a.get_right_handle ().x ());+		x += Math.fabs (a.get_right_handle ().x () - b.get_left_handle ().x ());+		x += Math.fabs (b.get_left_handle ().x () - b.x);++		y = Math.fabs (a.y - a.get_right_handle ().y ());+		y += Math.fabs (a.get_right_handle ().y () - b.get_left_handle ().y ());+		y += Math.fabs (b.get_left_handle ().y () - b.y);+		+		return Math.fabs (Math.sqrt (x * x + y * y));+	} +	+	/** Variable precision */+	public bool is_over_coordinate_var (double x, double y, double tolerance) {+		List<EditPoint> ycoordinates = new List<EditPoint> ();+		double last = 0;+		bool on_edge = false;+		double last_x = 0;++		if (points.length () < 3) {+			return false;+		}+		+		if (!is_over_boundry_precision (x, y, tolerance)) {+			return false;+		}++		foreach (EditPoint e in points) {+			if (distance (e.x, x, e.y, y) < tolerance) {+				return true;+			}+		}++		all_of_path ((cx, cy, ct) => {+			double distance = Math.fabs (Math.sqrt (Math.pow (cx - x, 2) + Math.pow (cy - y, 2)));+			+			if (distance < tolerance) {+				on_edge = true;+				return false;+			}+			+			if (Math.fabs (cx - x) < tolerance && Math.fabs (last - cy) > 2 * tolerance) {+				ycoordinates.append (new EditPoint (cx, cy));+				last = cy;+			}+			+			last_x = cx;+			return true;+		});++		if (on_edge) {+			return true;+		}++		if (ycoordinates.length () == 0) {+			warning ("No ycoordinates is empty");+			return true;+		}++		ycoordinates.sort ((a, b) => {+			return (a.y < b.y) ? 1 : -1;+		});+		+		for (int i = 0; i < ycoordinates.length () - 1; i++) {+			if (Math.fabs (ycoordinates.nth (i).data.y - ycoordinates.nth (i + 1).data.y) < 4 * tolerance) {+				ycoordinates.remove_link (ycoordinates.nth (i));+			}+		}+		+		if (unlikely (ycoordinates.length () % 2 != 0)) {+			warning (@"not an even number of coordinates ($(ycoordinates.length ()))");+			stderr.printf (@"(ymin <= y <= ymax) && (xmin <= x <= xmax);\n");+			stderr.printf (@"($ymin <= $y <= $ymax) && ($xmin <= $x <= $xmax);\n");+		+			stderr.printf (@"tolerance: $(tolerance)\n");+			+			stderr.printf ("ycoordinates:\n");+			foreach (EditPoint e in ycoordinates) {+				stderr.printf (@"$(e.y)\n");+			}++			ycoordinates.append (ycoordinates.last ().data.copy ());+			+			return true;+		}+		+		for (unowned List<EditPoint> e = ycoordinates.first (); true; e = e.next) {+			return_val_if_fail (!is_null (e), true);+			+			if (y <= e.data.y + tolerance) {+				return_val_if_fail (!is_null (e.next), true);+				+				e = e.next;++				if (y >= e.data.y - tolerance) {+					return true;+				}+			}+			+			if (e == ycoordinates.last ()) {+				break;+			}+		}++		return false;+	}+	+	public bool is_over_boundry_precision (double x, double y, double p) {+		if (unlikely (ymin == double.MAX || ymin == 10000)) {+			warning (@"no bounding box. length ($(points.length ()))");+			update_region_boundries ();+		}+		+		return (ymin - p <= y <= ymax + p) && (xmin - p <= x <= xmax + p);+	}+	+	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.");+		}++		return (ymin <= y <= ymax) && (xmin <= x <= xmax);+	}++	public bool has_overlapping_boundry (Path p) {+		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 void add_point (EditPoint p) {+		add_point_after (p, points.last ());+	}++	/** Insert a new point after @param previous_point and return a reference +	 * to the new item in list.+	 */+	public unowned List<EditPoint> add_after (double x, double y, List<EditPoint>? previous_point) {+		EditPoint p = new EditPoint (x, y, PointType.LINE);	+		return add_point_after (p, previous_point);+	}+	+	public unowned List<EditPoint> add_point_after (EditPoint p, List<EditPoint>? previous_point) {+		unowned List<EditPoint> np;+		int prev_index;++		if (points.length () > 0 && previous_point != null && ((!)previous_point).data.type == PointType.END) {+			points.delete_link ((!) previous_point);+		}++		if (unlikely (previous_point == null && points.length () != 0)) warning ("previous_point == null");++		if (points.length () == 0) {+			points.append (p);+			np = points.last ();+			p.prev = points.last ();+			p.next = points.last ();+		} else {+			p.prev = (!) previous_point;+			p.next = ((!) previous_point).next;++			points.insert_before (((!) previous_point).next, p);+			+			prev_index = points.position ((!) previous_point);+			np = points.nth (prev_index + 1);+		}+		+		second_last_point = last_point;+		last_point = p;+		+		PenTool.set_default_handle_positions ();+		+		return np;+	}++	public void close () {+		open = false;+		edit = false;++		create_list ();+		+		if (points.length () > 2) {+			points.first ().data.recalculate_linear_handles ();+			points.last ().data.recalculate_linear_handles ();+		}+	}+	+	public void reopen () {+		open = true;+		edit = true;+	}+	+	public EditPoint? get_last_point () {+		return last_point;+	}++	public EditPoint? get_second_last_point () {+		return second_last_point;+	}+	+	/** Move path. */+	public void move (double delta_x, double delta_y) {+		foreach (var ep in points) {+			ep.x += delta_x;+			ep.y += delta_y; // kanske -y+		}+		+		update_region_boundries ();+	}++	public void update_region_boundries () {+		if (points.length () == 0) {+			xmax = 0;+			xmin = 0;+			ymax = 0;+			ymin = 0;			+		}++		// inside and outside in vala lambda functions reveals a tricky problem+		// (look at c code). that's the reason for the !new_val expression++		xmax = -10000;+		xmin = 10000;+		ymax = -10000;+		ymin = 10000;++		double txmax = -10000;+		double txmin = 10000;+		double tymax = -10000;+		double tymin = 10000;++		bool new_val = false;+			+		all_of_path ((cx, cy) => {	+			if (!new_val) {+				txmax = cx;+				txmin = cx;+				tymax = cy;+				tymin = cy;+				new_val = true;+			}+			+			if (cx < txmin) {+				txmin = cx;+			}++			if (cx > txmax) {+				txmax = cx;+			}+			+			if (cy < tymin) {+				tymin = cy;+			}++			if (cy > tymax) {+				tymax = cy;+			}++			return true;+		});+				+		xmax = txmax;+		xmin = txmin;+		ymax = tymax;+		ymin = tymin;++		if (unlikely (!new_val)) {+			// only one point+		} else if (unlikely (!got_region_boundries ())) {+			warning (@"No new region boundries.\nPoints.length: $(points.length ())");+			print_boundries ();+		}+	}+		+	/** Test if @param path is a valid outline for this object. */	+	public bool test_is_outline (Path path) {+		assert (false);+		return this.test_is_outline_of_path (path) && path.test_is_outline_of_path (this);+	}+	+	private bool test_is_outline_of_path (Path outline)+		requires (outline.points.length () >= 2 || points.length () >= 2)+	{	+		// rather slow use it for testing, only+		unowned List<EditPoint> i = outline.points.first ();+		unowned List<EditPoint> prev = i.last ();++		double tolerance = 1;+		bool g = false;+		+		EditPoint ep = new EditPoint (0, 0);+		double min = double.MAX;+		+		while (true) {+			min = 10000;+			 +			all_of (prev.data, i.data, (cx, cy) => {+					get_closest_point_on_path (ep, cx, cy);++					double n = pow (ep.x - cx, 2) + pow (cy - ep.y, 2);+					+					if (n < min) min = n;+					+					if (n < tolerance) {+						g = true;+						return false;+					}++					return true;+				});+			+			if (!g) {+				critical (@"this path does not seem to be the outline. (min $min)");+			}+			+			g = false;+			+			if (i == i.last ()) {+				break;+			}+				+			i = i.next;+		}+		+		return true;+	}++	/** Convert quadratic bezier points to cubic representation of the glyph+	 * suitable for ttf-export.+	 */ +	public Path get_quadratic_points () {+		EditPoint middle = new EditPoint ();+		unowned List<EditPoint> e;+		EditPointHandle eh;+		+		quadratic_path = copy ();+		quadratic_path.close ();+				+		if (quadratic_path.points.length () < 2) {+			return quadratic_path;+		}++		// split all curves in as many regions as we need+		split_cubic_in_parts (quadratic_path);	++		// estimate quatdratic form+		middle.prev = quadratic_path.points.last ();+		middle.next = quadratic_path.points.first ();++		convert_to_quadratic (quadratic_path, middle);+		+		e = (!) quadratic_path.points.first ();+		while (e != quadratic_path.points.last ().prev) {+			middle.prev = e;+			middle.next = e.next;+							+			convert_to_quadratic (quadratic_path, middle);	+			+			e = (!) e.next;+		}+		+		eh = ((!)quadratic_path.points.last ().prev).data.get_left_handle ();+		eh.set_point_type (PointType.NONE);+		eh.length = 0;++		eh = ((!)quadratic_path.points.last ().prev).data.get_right_handle ();+		eh.set_point_type (PointType.CURVE);+		eh.length *= 1.6;++		return quadratic_path;+	}++	void convert_to_quadratic (Path cubic_path, EditPoint middle) {+		double curve_x, curve_y;	+		EditPointHandle eh;+		+		EditPoint start = middle.get_prev ().data;+		EditPoint stop = middle.get_next ().data;++		estimate_quadratic (start, stop, out curve_x, out curve_y);+		+		eh = start.get_left_handle ();+		eh.set_point_type (PointType.NONE);+		eh.length = 0;+		+		eh = start.get_right_handle ();+		eh.set_point_type (PointType.CURVE);+		eh.move_to_coordinate (curve_x, curve_y);+	}++	public void split_cubic_in_parts (Path cubic_path) {+		int j = 0;+		while (split_all_cubic_in_half (cubic_path)) {+			j++;+			+			if (j > 5) {+				warning ("too many iterations in split path");+				break;+			}+		}	+	}++	public bool split_all_cubic_in_half (Path cubic_path) {+		EditPoint middle = new EditPoint ();+		unowned List<EditPoint> e;+		bool need_split = false;+		uint len = cubic_path.points.length ();+	+		if (len < 2) {+			return false;+		}++		middle.prev = cubic_path.points.last ();+		middle.next = cubic_path.points.first ();+		+		if (split_cubic_in_half (cubic_path, middle)) {+			need_split = true;+		}+			+		e = (!) cubic_path.points.first ();+		for (uint i = 0; i < len - 1; i++) {+			middle.prev = e;+			middle.next = e.next;+			+			middle.get_prev ().data.recalculate_linear_handles ();+			middle.get_next ().data.recalculate_linear_handles ();+			+			if (split_cubic_in_half (cubic_path, middle)) {+				e = (!) e.next.next;+				need_split = true;+			} else {+				e = (!) e.next;+			}+		}+				+		return need_split;	+	}++	public void estimate_quadratic (EditPoint start, EditPoint stop, out double curve_x, out double curve_y) {+		curve_x = -0.25 * start.x + 0.75 *  stop.get_left_handle ().x () + 0.75 * start.get_right_handle ().x () - 0.25 * stop.x;+		curve_y = -0.25 * start.y + 0.75 *  stop.get_left_handle ().y () + 0.75 * start.get_right_handle ().y () - 0.25 * stop.y;		+	}++	bool split_cubic_in_half (Path cubic_path, EditPoint middle) {+		double curve_x, curve_y, cx, cy, qx, qy, nx, ny, distance;	+		+		EditPoint start = middle.get_prev ().data;+		EditPoint stop = middle.get_next ().data;+		+		estimate_quadratic (start, stop, out curve_x, out curve_y);+	+		qx = quadratic_bezier_path (0.75, start.x, curve_x, stop.x);+		qy = quadratic_bezier_path (0.75, start.y, curve_y, stop.y);++		cx = bezier_path (0.75, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x);+		cy = bezier_path (0.75, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y);++		distance = Math.sqrt (Math.pow (cx - qx, 2) + Math.pow (cy - qy, 2));++		nx = bezier_path (0.5, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x);+		ny = bezier_path (0.5, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y);++		if (Math.fabs (distance) > 0.01) {+			EditPoint new_edit_point = new EditPoint (nx, ny, PointType.CURVE);++			new_edit_point.next = middle.get_next ();+			new_edit_point.prev = middle.get_prev ();+			cubic_path.insert_new_point_on_path (new_edit_point);+			cubic_path.create_list ();++			return true;	+		}+		+		return false;+	}++	double distance_to_path (EditPoint start, EditPoint stop, double x, double y) {+		double min = double.MAX;+		+		all_of (start, stop, (cx, cy, t) => {+			double n = pow (x - cx, 2) + pow (y - cy, 2);+			+			if (n < min) {+				min = n;+			}+			+			return true;+		});+		+		return min;+	}++	public void insert_new_point_on_path (EditPoint? epp) {+		EditPoint start, stop;+		double x0, x1, y0, y1;+		double px, py;+		+		double position, t, d, min;+		double steps = 500;+		+		EditPoint ep;++		if (epp == null) {+			return;+		}++		ep = (!) epp;++		start = ep.get_prev ().data;+		stop = ep.get_next ().data;++		add_point_after (ep, ep.get_prev ());++		min = double.MAX;++		position = 0.5;++		for (int i = 0; i < steps; i++) {+			t = i / steps;+			+			px = bezier_path (t, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x);+			py = bezier_path (t, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y);+			+			d = Math.sqrt (Math.pow (ep.x - px, 2) + Math.pow (ep.y - py, 2));+			+			if (d < min) {+				min = d;+				position = t;+			}+		}+			+		bezier_vector (position, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out x0, out x1);+		bezier_vector (position, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out y0, out y1);++		ep.get_left_handle ().set_point_type (PointType.CURVE);+		ep.get_left_handle ().move_to_coordinate (x0, y0);+		ep.get_left_handle ().parent = ep;+		+		ep.get_right_handle ().set_point_type (PointType.CURVE);+		ep.get_right_handle ().move_to_coordinate (x1, y1);+		ep.get_right_handle ().parent = ep;++		stop.get_left_handle ().length *= 1 - position;+		start.get_right_handle ().length *= position;+	}+			+	/** Get a point on the this path closest to x and y coordinates. */+	public void get_closest_point_on_path (EditPoint edit_point, double x, double y) {+		return_if_fail (points.length () != 0);+		+		double min = double.MAX;+		double n = 0;+		bool g = false;+		+		double ox = 0;+		double oy = 0;+		+		double handle_x0, handle_x1;+		double handle_y0, handle_y1;+		+		unowned List<EditPoint> i = points.first ();+		unowned List<EditPoint> prev = i.next;++		bool done = false;+		bool exit = false;+		+		unowned List<EditPoint>? previous_point = null;+		unowned List<EditPoint>? next_point = null;++		EditPoint previous;+		EditPoint next;+		double step = 0;++		if (points.length () == 1) {+			edit_point.x = i.data.x;+			edit_point.y = i.data.y;+			+			edit_point.prev = i;+			edit_point.next = i;+			+			exit = true;+			+			return;+		}+		+		if (points.length () != 1) {+			edit_point.x = i.data.x;+			edit_point.y = i.data.y;+		}+		+		while (!exit) {+			+			if (i == points.last ()) {+				done = true;+			}+			+			if (!done) {+				i = i.next;+				prev = i.prev;+			}	else if (done && !is_open ()) {+				i = points.first ();+				prev = points.last ();+				exit = true;+			} else {+				break;+			}+			+			all_of (prev.data, i.data, (cx, cy, t) => {+				n = pow (x - cx, 2) + pow (y - cy, 2);+				+				if (n < min) {+					min = n;+					+					ox = cx;+					oy = cy;+				+					previous_point = prev;+					next_point = i;+					+					step = t;+					+					g = true;+				}+				+				return true;+			});+		}++		if (previous_point == null) {+			warning (@"previous_point == null, points.length (): $(points.length ())");+			return;+		}+		+		if (next_point == null) {+			warning ("next_point != null");+			return;+		}++		previous = ((!) previous_point).data;+		next = ((!) next_point).data;++		bezier_vector (step, previous.x, previous.get_right_handle ().x (), next.get_left_handle ().x (), next.x, out handle_x0, out handle_x1);+		bezier_vector (step, previous.y, previous.get_right_handle ().y (), next.get_left_handle ().y (), next.y, out handle_y0, out handle_y1);++		// position +		edit_point.set_position (ox, oy);+		edit_point.prev = previous_point;+		edit_point.next = next_point;++		// curve (angle)+		edit_point.get_right_handle ().move_to_coordinate (handle_x0, handle_y0);+		edit_point.get_left_handle ().move_to_coordinate (handle_x1, handle_y1);+		// FIXA: maybe just edit_point.set_tie_handle (true);++		if (unlikely (!g)) {+			warning (@"Error: Got no coordinates for point on path. Num points $(points.length ())\n");+		}+	}++	public static void all_of (EditPoint start, EditPoint stop, RasterIterator iter, int steps = -1) {+		if (steps == -1) {+			steps = (int) (10 * get_length_from (start, stop));+		}+		+		all_of_curve (start.x, start.y, start.get_right_handle ().x (), start.get_right_handle ().y (), stop.get_left_handle ().x (), stop.get_left_handle ().y (), stop.x, stop.y, iter, steps);+	}++	private static void all_of_curve (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, RasterIterator iter, double steps = 400) {+		double px = x1;+		double py = y1;+		+		double t;+		+		for (int i = 0; i < steps; i++) {+			t = i / steps;+			+			px = bezier_path (t, x0, x1, x2, x3);+			py = bezier_path (t, y0, y1, y2, y3);+			+			if (!iter (px, py, t)) {+				return;+			}+			+		}	+	}+	+	void point_on_bezier_path (double t, EditPoint start, EditPoint stop, out double x, out double y) {		+		double x0 = start.x;+		double y0 = start.y; +		double x1 = start.get_right_handle ().x ();+		double y1 = start.get_right_handle ().y ();+		double x2 = stop.get_left_handle ().x ();+		double y2 = stop.get_left_handle ().y (); +		double x3 = stop.x;+		double y3 = stop.y;++		x = bezier_path (t, x0, x1, x2, x3);+		y = bezier_path (t, y0, y1, y2, y3);+	}++	private void all_of_path (RasterIterator iter) {+		unowned List<EditPoint> i, next;+		+		if (points.length () < 2) {+			return;+		}++		i = points.first ();+		next = i.next;++		while (i != points.last ()) {+			all_of (i.data, next.data, iter);+			i = i.next;+			next = i.next;+		}+		+		if (!is_open ()) {+			all_of (points.last ().data, points.first ().data, iter);+		}+	}++	public static double bezier_path (double step, double p0, double p1, double p2, double p3) {+		double q0, q1, q2;+		double r0, r1;++		q0 = step * (p1 - p0) + p0;+		q1 = step * (p2 - p1) + p1;+		q2 = step * (p3 - p2) + p2;++		r0 = step * (q1 - q0) + q0;+		r1 = step * (q2 - q1) + q1;++		return step * (r1 - r0) + r0;+	}++	public static void bezier_vector (double step, double p0, double p1, double p2, double p3, out double a0, out double a1) {+		double q0, q1, q2;++		q0 = step * (p1 - p0) + p0;+		q1 = step * (p2 - p1) + p1;+		q2 = step * (p3 - p2) + p2;++		a0 = step * (q1 - q0) + q0;+		a1 = step * (q2 - q1) + q1;+	}++	public static double quadratic_bezier_path (double step, double p0, double p1, double p2) {+		double q0 = step * (p1 - p0) + p0;+		double q1 = step * (p2 - p1) + p1;+		+		return step * (q1 - q0) + q0;+	}+			+	public void plot (Context cr, Allocation allocation, double view_zoom) {+			double px = 0, py = 0;+			double xc = allocation.width / 2.0;+			double yc = allocation.height / 2.0;++			cr.save ();+			+			all_of_path ((x, y) => {+				//cr.set_source_rgba (0.3, 0.3, 0.3, 1);+				cr.move_to (px + xc, -py + yc);+				cr.line_to (x + xc, -y + yc);+				+				px = x;+				py = y;+				+				return true;+			});++			cr.stroke ();+			cr.restore ();+	}+	+	public void print_boundries () {+		stderr.printf (@"xmax $xmax \n");+		stderr.printf (@"xmin $xmin \n");+		stderr.printf (@"ymax $ymax \n");+		stderr.printf (@"ymin $ymin \n");		+	}+	+	public bool got_region_boundries () {+		return !(xmax == -10000 || xmin ==  10000 || ymax == -10000 || ymin ==  10000);+	}+	+	public void create_list () {+		unowned List<EditPoint> ep = points.first ();+		unowned List<EditPoint> prev = ep;+		+		if (points.length () == 0) {+			return;+		}+		+		if (points.length () == 1) {+			ep.data.next = null;+			ep.data.prev = null;+			return;+		}+		+		ep.data.next = ep.next;+		ep.data.prev = points.last ();+		prev = ep;+		+		assert (ep.data.next != null);+		assert (ep.data.prev != null);+		+		while (ep != ep.last ()) {+			ep.data.next = ep.next;+			ep.data.prev = prev;+			prev = ep;+			+			assert (ep.data.next != null);+			assert (ep.data.prev != null);+		+			ep = ep.next;+		}+		+		if (is_open ()) {+			ep.data.next = points.first ();+		} else {+			ep.data.next = points.first ();+			assert (ep.data.next != null);+		}+		+		ep.data.prev = prev;+		assert (ep.data.prev != null);+	}++	public void delete_edit_point (EditPoint ep) +		requires (points.length () > 0)+	{+			unowned List<EditPoint>? pl = null;+			EditPoint p;+			+			if (ep.prev != null) {+				set_new_start (ep);+			}+			+			p = points.first ().data;+			+			for (uint i = 0; i < points.length (); i++) {+				p = points.nth (i).data;+				+				if (p == ep) {+					p.prev = null;+					p.next = null;+					+					pl = points.nth (i);+					break;+				}+			}++			if (pl != null) {+				points.delete_link ((!) pl);+				reopen ();+				MainWindow.get_current_glyph ().add_active_path (this);+				create_list ();+			}+	}+		+	public void set_new_start (EditPoint ep) {+		List<EditPoint> list = new List<EditPoint> ();+		uint len = points.length ();+		unowned List<EditPoint> iter = points.first ();+		unowned List<EditPoint>? ni = null;+		bool found = false;+		+		foreach (EditPoint it in points) {+			if (it == ep) {+				found = true;+				break;+			}+			+			iter = iter.next;+			ni = (!) iter;+		}+		+		if (unlikely (!found)) {+			warning ("Could not find edit point.");+		}+		+		if (ni == null) {+			return;			+		}+		+		iter = (!) ni;+		+		for (uint i = 0; i < len; i++) {+			list.append (iter.data);+			+			if (iter == iter.last ())+				iter = iter.first ();+			else+				iter = iter.next;+		+		}+		+		while (points.length () > 0) {+			points.remove_link (points.first ());+		}+		+		foreach (EditPoint p in list) {+			points.append (p);+		}+		+	}+	+	public static PathList merge (Path p0, Path p1) {+		bool done;+		PathList path_list;+		+		done = try_merge (p0.copy(), p1.copy(), out path_list);+		+		if (!done) {+			warning ("failed to merge paths");+		}+		+		return path_list;+	}+	+	private static bool try_merge (Path p0, Path p1, out PathList path_list) {+		EditPoint e;+		IntersectionList il;+		int i;+		Path np;+		+		unowned List<Path> pi;+		+		il = IntersectionList.create_intersection_list (p0, p1);+		+		path_list = new PathList ();+		+		if (p0 == p1) {+			return false;+		}+		+		// add editpoints on intersections +		p0.update_region_boundries ();+		p1.update_region_boundries ();+		foreach (Intersection inter in il.points) {+			e = new EditPoint ();+			p0.get_closest_point_on_path (e, inter.x, inter.y);+			inter.editpoint_a = e;+			+			e = inter.editpoint_a;+			if (!p0.has_edit_point (e)) {+				p0.insert_new_point_on_path (e);+			}++			e = new EditPoint ();+			p1.get_closest_point_on_path (e, inter.x, inter.y);+			inter.editpoint_b = e;+			+			inter.editpoint_b.x = inter.editpoint_a.x;+			inter.editpoint_b.y = inter.editpoint_a.y;+			+			e = inter.editpoint_b;+			if (!p1.has_edit_point (e)) {+				p1.insert_new_point_on_path (e);+			}+		}+			+		if (il.points.length () < 2) {+			return false;+		}+		+		//path_list.paths.append (p0);+		//path_list.paths.append (p1);+		//return false;+		+		// get all parts+		foreach (Intersection inter in il.points) {+			p0.set_new_start (inter.editpoint_a);+			get_merge_part (il, p0, p1, out np);+			path_list.paths.append (np);+		}+		+		foreach (Path pp in path_list.paths) {+			pp.update_region_boundries ();+		}++		// remove duplicate paths+		for (i = 0; i < path_list.paths.length (); i++) {+			pi = path_list.paths.nth (i);++			if (is_duplicated (path_list, pi.data)) {+				path_list.paths.remove_link (pi);+				--i;			+			}+		}+		+		// remove paths contained in other paths+		for (i = 0; i < path_list.paths.length (); i++) {+			pi = path_list.paths.nth (i);+			+			if (pi.data.is_clockwise () && is_clasped (path_list, pi.data)) {+				path_list.paths.remove_link (pi);+				i--;+			}+		}+	+		return true;+	}+	+	private static bool is_duplicated (PathList pl, Path p) {+		bool duplicate = false;+		foreach (Path pd in pl.paths) {+			if (pd == p) {+				continue;+			}+			+			if (is_duplicated_path (p, pd)) {+				duplicate = true;+			}+		}+		+		return duplicate;+	}+	+	private static bool is_duplicated_path (Path p0, Path p1) {+		bool eq;+		+		assert (p1 != p0);	+		+		foreach (EditPoint ep in p0.points) {+			eq = false;+			+			foreach (EditPoint e in p1.points) {+				eq = (Math.fabs (ep.x - e.x) < 0.04 && Math.fabs (ep.y - e.y) < 0.04);+				+				if (eq) {+					break;+				}+			}+			+			if (eq) {+				continue;+			} else {+				return false;+			}+		}+		+		return true;+	}+	+	private static bool is_clasped (PathList pl, Path p) {+		foreach (Path o in pl.paths) {+			if (o == p) {+				continue;+			}+			+			if (is_clasped_path (o, p)) {+				return true;+			}+		}+		+		return false;+	}++	private static bool is_clasped_path (Path outside, Path inside) {+		bool i = true;+		foreach (EditPoint e in inside.points) {+			if (!outside.is_over_coordinate_var (e.x, e.y, 0.5)) { // high tolerance since point may be off curve in both paths+				i = false;+				break;+			}+		}+		return i;+	}+	+	private static bool get_merge_part (IntersectionList il, Path p0, Path p1, out Path new_path) {+		EditPoint ex;+		EditPoint ix;+		EditPoint en;+		uint offset_i = 0;+		uint offset_j;+		uint len_i;+		int i, j;+		uint len_j;+		Path np = new Path ();+		Intersection s = new Intersection (0, 0, 1);+		+		ex = p0.points.last ().data;+		ix = p0.points.last ().data;+		len_i = p0.points.length ();+		+		for (i = 0; i < p0.points.length (); i++) {+			ex = p0.points.nth ((i + offset_i) % len_i).data;++			if (ex == p0.points.first ().data && i != 0) {	+				s = (!) il.get_intersection (ex);	+				break;+			}++			// add new point for path a+			if (np.has_edit_point (ex)) {+				// SPLIT+				warning ("Merged path need split");+				np.close ();+				new_path = np;+				+				return false;+			} else {+				en = ex.copy ();+				np.add_point (en);+				en.recalculate_linear_handles ();+			}+			+			// swap paths+			if (il.has_edit_point (ex)) {+				s = (!) il.get_intersection (ex);+			+				en.type = PointType.CURVE;+				en.right_handle.type = PointType.CURVE;+				en.right_handle.angle  = s.editpoint_b.right_handle.angle;+				en.right_handle.length = s.editpoint_b.right_handle.length;+							+				// read until we find ex+				for (j = 0; j < p1.points.length (); j++) {+					ix = p1.points.nth (j).data;+					+					if (ix == s.editpoint_b) {+						break;+					}+				}+				+				offset_j = j + 1;+				len_j = p1.points.length ();+				for (j = 0; j < p1.points.length (); j++) {+					+					ix = p1.points.nth ((j + offset_j) % len_j).data;+					+					// add+					if (np.has_edit_point (ix)) {+						// SPLIT+						warning ("Merged path need split");+						np.close ();+						new_path = np;+						+						break;+					} else {+						en = ix.copy ();+						np.add_point (en);+						ix.recalculate_linear_handles ();+					}+					+					if (il.has_edit_point (ix)) {+						s = (!) il.get_intersection (ix);+						break;+					}+				}++				en.type = PointType.CURVE;+				en.right_handle.type = PointType.CURVE;+				en.right_handle.angle  = s.editpoint_a.right_handle.angle;+				en.right_handle.length = s.editpoint_a.right_handle.length;+								+				if (j == p0.points.length ()) {+					np.close ();+					new_path = np;+					return true;+				}++				// skip to next intersection+				int k;+				for (k = 0; k < p0.points.length (); k++) {+					ix = p0.points.nth (k).data; ++					if (ix == s.editpoint_a) {+						break;+					}+				}+				+				if (k == p0.points.length ()) {+					new_path = np;+					return true;+				}+				+				offset_i = 0;+				i = k;+			}+		}++		new_path = np;+		+		return true;	+	}++}++public class PathList : GLib.Object {+	public List<Path> paths = new List<Path> ();+	+	public PathList () {+	}+	+	public void clear () {+		while (paths.length () > 0) {+			paths.remove_link (paths.first ());+		}+	}+}++}diff --git a/libbirdfont/PenTool.vala b/libbirdfont/PenTool.valanew file mode 100644index 0000000..931b4fa--- /dev/null+++ b/libbirdfont/PenTool.vala@@ -0,0 +1,1001 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++class PenTool : Tool {++	private static const double CONTACT_SURFACE = 50;++	public static bool move_selected = false;+	public static bool begin_new_point_on_path = false;+	public static bool move_point_on_path = false;++	public static bool edit_active_corner = false;+	+	public static List<EditPoint> selected_points = new List<EditPoint> (); ++	public static EditPointHandle active_handle = new EditPointHandle.empty ();+	public static EditPointHandle selected_handle = new EditPointHandle.empty ();+	+	public static EditPoint? active_edit_point = new EditPoint ();++	/** Move handle vertical or horizontal. */+	public static bool tie_x_or_y_coordinates = false;++	private static bool move_selected_handle = false;++	private static double last_point_x = 0;+	private static double last_point_y = 0;++	public static double precision = 1;+	+	public PenTool (string name) {+		base (name, _("Right click to add new points, left click to move points") + " " + _("and double click to add new point on path."), ',', CTRL);+		+		select_action.connect ((self) => {+		});++		deselect_action.connect ((self) => {+			Glyph glyph = MainWindow.get_current_glyph ();+			+			CutTool.force_direction ();+			glyph.close_path ();+			+			move_point_on_path = false;+			begin_new_point_on_path = false;+		});+		+		press_action.connect ((self, b, x, y) => {+			last_point_x = x;+			last_point_y = y;++			press (b, x, y, false);+		});+		+		double_click_action.connect ((self, b, x, y) => {+			last_point_x = x;+			last_point_y = y;++			press (b, x, y, true);+		});++		release_action.connect ((self, b, ix, iy) => {+			double x = ix;+			double y = iy;+			Glyph g = MainWindow.get_current_glyph ();+			EditPoint selected_corner;+			+			if (move_selected && GridTool.is_visible () && selected_points.length () > 0) {+				selected_corner = selected_points.last ().data;+				GridTool.tie (ref x, ref y);+				g.move_selected_edit_point (selected_corner, x, y);+			}++			move_selected = false;+			move_selected_handle = false;+			edit_active_corner = false;+			+			active_handle = new EditPointHandle.empty ();+			+			move (x, y);+		});++		move_action.connect ((self, x, y) => {+			move (x, y);+		});+		+		key_press_action.connect ((self, keyval) => {+			Glyph g = MainWindow.get_current_glyph ();+			+			if (keyval == Key.DEL) {+				foreach (EditPoint p in selected_points) {+					g.delete_edit_point (p);+				}+				+				g.update_view ();+			}+		});+		+		key_release_action.connect ((self, keyval) => {+		});+	}+	+	public double get_precision () {+		return precision;+	}+	+	public void set_precision (double p) {+		precision = p;+		MainWindow.get_toolbox ().precision.set_value_round (p, false, false);+	}+	+	public void move (double x, double y) {+		Glyph glyph = MainWindow.get_current_glyph ();+		+		control_point_event (x, y);+		curve_active_corner_event (x, y);+		set_default_handle_positions ();++		// show new point on path+		if (glyph.new_point_on_path != null) {+			move_current_point_on_path (x, y);+		}+		+		// move curve handles+		if (move_selected_handle) {+			if (GridTool.is_visible ()) {+				GridTool.tie (ref x, ref y);+				selected_handle.set_point_type (PointType.CURVE);+				selected_handle.move_to (x, y);+			} else {+				selected_handle.set_point_type (PointType.CURVE);+				selected_handle.move_delta ((x - last_point_x) * precision, (y - last_point_y) * precision);+			}+			+			selected_handle.parent.recalculate_linear_handles ();+			+			// Fixa: redraw line only+			glyph.redraw_area (0, 0, glyph.allocation.width, glyph.allocation.height);+						+			last_point_x = x;+			last_point_y = y;+			+			return;+		}+		+		// move edit point+		if (move_selected) {+			foreach (EditPoint p in selected_points) {+				glyph.move_selected_edit_point_delta (p, (x - last_point_x) * precision, (y - last_point_y) * precision);+				+				if (tie_x_or_y_coordinates) {+					GridTool.tie_to_prev (p, x, y);+				}+				+				p.recalculate_linear_handles ();+			}+		}+		+		last_point_x = x;+		last_point_y = y;+	}+	+	public void press (int button, int x, int y, bool double_click) {+		Glyph? g = MainWindow.get_current_glyph ();+		Glyph glyph = (!) g;+		+		return_if_fail (g != null);++		if (button == 2) {+			if (glyph.is_open ()) {+				CutTool.force_direction ();+				glyph.close_path ();+			} else {+				glyph.open_path ();+			}+			+			return;+		}++		if (double_click) {+			glyph.insert_new_point_on_path (x, y);+			return;+		}++		if (is_new_point_from_path_selected ()) {+			new_point_on_path_at (x, y);+			return;	+		}++		// add new point on path +		if (is_new_point_from_path_selected ()) {+			move_selected = true;+			move_point_on_path = true;+			glyph.new_point_on_path = null;+			return;+		}+		+		// add new point+		if (button == 3) {+			remove_all_selected_points ();+			new_point_action (x, y);+			glyph.store_undo_state ();+			return;+		}+				+		control_point_event (x, y);+		curve_corner_event (x, y);+		+		if (!move_selected_handle) {+			select_active_point (x, y);+		}++		glyph.store_undo_state ();+	}+	+	public void select_active_point (double x, double y) {+		Glyph? g = MainWindow.get_current_glyph ();+		Glyph glyph = (!) g;+		+		if (KeyBindings.modifier != SHIFT) {+			if (active_edit_point != null) {+				((!)active_edit_point).set_active (false);+			}++			remove_all_selected_points ();+		}+		+		move_selected = true;+		move_point_on_path = true;++		if (active_edit_point != null) {+			((!)active_edit_point).set_selected (true);+		}++		if (!is_over_handle (x, y)) {+			edit_active_corner = true;+			set_default_handle_positions ();+			+			if (active_edit_point != null) {+				add_selected_point ((!) active_edit_point);+			}+		}+		+		// continue adding points from the selected one+		foreach (Path p in glyph.active_paths) {+			if (p.is_open () && p.points.length () > 0 && active_edit_point == p.points.first ().data) {+				p.reverse ();+			}+		}+	}+	+	public void move_current_point_on_path (double x, double y) {+		Glyph g = MainWindow.get_current_glyph ();++		EditPoint e;+		+		double rax, ray;+		double pax, pay;++		double distance, min;+		+		return_if_fail (g.new_point_on_path != null);+		+		if (GridTool.is_visible ()) {+			GridTool.tie (ref x, ref y);+		}++		min = double.MAX;++		foreach (Path p in g.path_list) {+			if (p.points.length () < 2) {+				continue;+			}+			+			e = new EditPoint ();+			+			pax = x * Glyph.ivz () + g.view_offset_x - Glyph.xc ();+			pay = y * Glyph.ivz () + g.view_offset_y - Glyph.yc ();++			pay *= -1;++			p.get_closest_point_on_path (e, pax, pay);+			+			distance = Math.sqrt (Math.pow (Math.fabs (pax - e.x), 2) + Math.pow (Math.fabs (pay - e.y), 2));+			+			if (distance < min) {+				min = distance;+				+				g.new_point_on_path = e;+				+				rax = (pax + g.view_offset_x - Glyph.xc ()) / Glyph.ivz ();+				ray = (pax + g.view_offset_x - Glyph.xc ()) / Glyph.ivz ();+				+				g.redraw_area (rax - 5, ray - 5, 10, 10);					+			}+		}+	}++	public void set_new_point_on_path (Path ap, int x, int y) {+		Glyph g = MainWindow.get_current_glyph ();+		+		return_if_fail (ap.is_editable ());+		+		g.clear_active_paths ();+		g.add_active_path (ap);++		if (g.new_point_on_path == null) {+			g.new_point_on_path = new EditPoint (0, 0, PointType.FLOATING);+		}+		+		move_current_point_on_path (x, y);+	}+		+	public static void set_active_edit_point (EditPoint? e) {+		Glyph g = MainWindow.get_current_glyph ();+		foreach (var p in g.path_list) {+			foreach (var ep in p.points) {+				ep.set_active (false);+			}+		}+		+		active_edit_point = e;+		+		if (e != null) {+			((!)e).set_active (true);+		}++		g.redraw_area (0, 0, g.allocation.width, g.allocation.height);+	}++	EditPoint? get_closest_point (double ex, double ey, out Path? path) {+		double x = Glyph.path_coordinate_x (ex);+		double y = Glyph.path_coordinate_y (ey);+		double d = double.MAX;+		double nd;+		EditPoint? ep = null;+		Glyph g = MainWindow.get_current_glyph ();+		+		path = null;+		+		foreach (Path current_path in g.path_list) {+			foreach (EditPoint e in current_path.points) {+				nd = e.get_distance (x, y);+				+				if (nd < d) {+					d = nd;+					ep = e;+					path = current_path;+				}+			}	+		}+		+		return ep;+	}++	public double get_distance_to_closest_edit_point (double event_x, double event_y) {+		Path? p;+		EditPoint e;+		EditPoint? ep = get_closest_point (event_x, event_y, out p);++		double x = Glyph.path_coordinate_x (event_x);+		double y = Glyph.path_coordinate_y (event_y);+		+		if (ep == null) {+			return double.MAX;+		}+		+		e = (!) ep;+		+		return e.get_distance (x, y);+	}++	public void control_point_event (double event_x, double event_y) {+		Path? p;+		EditPoint? ep = get_closest_point (event_x, event_y, out p);+		Glyph g = MainWindow.get_current_glyph ();+		double x = Glyph.path_coordinate_x (event_x);+		double y = Glyph.path_coordinate_y (event_y);+		double distance;+		EditPoint e;+		+		set_active_edit_point (null);+		+		if (ep == null) {+			return;	+		}+		+		e = (!) ep;+		distance = e.get_distance (x, y) * g.view_zoom;++		if (distance < CONTACT_SURFACE) {+			set_active_edit_point (ep);+			g.add_active_path (p);+		}+	}++	public static bool insert_new_point_on_path_selected () {+		Tool t = MainWindow.get_toolbox ().get_tool ("insert_point_on_path");+		return t.is_selected ();+	}++	public static bool is_new_point_from_path_selected () {+		if (!Supplement.experimental) {+			return false;+		}+		+		Tool t = MainWindow.get_toolbox ().get_tool ("new_point_on_path");+		return t.is_selected ();+	}++	public static bool is_erase_selected () {+		Tool t = MainWindow.get_toolbox ().get_tool ("erase_tool");+		return t.is_selected ();+	}+	+	public void new_point_action (int x, int y) {+		Glyph glyph;+		EditPoint new_point;+		glyph = MainWindow.get_current_glyph ();+		glyph.open_path ();+		+		new_point = glyph.add_new_edit_point (x, y);+		new_point.set_selected (true);+		+		if (KeyBindings.modifier != SHIFT) {+			remove_all_selected_points ();+		}+		+		add_selected_point (new_point);+		+		move_selected = true;+	}++	void set_linear_handles_for_last_point () {+		Glyph glyph;+		glyph = MainWindow.get_current_glyph ();+		return_if_fail (glyph.active_paths.length () > 0);+		return_if_fail (glyph.active_paths.last ().data.points.length () > 0);+		glyph.active_paths.last ().data.points.last ().data.right_handle.parent.recalculate_linear_handles ();+	}++	public static void set_default_handle_positions () {+		Glyph g = MainWindow.get_current_glyph ();+		foreach (var p in g.path_list) {+			if (p.is_editable ()) {+				p.create_list ();+				set_default_handle_positions_on_path (p);+			}+		}+	}++	static void set_default_handle_positions_on_path (Path path) {+		foreach (EditPoint e in path.points) {+			e.recalculate_linear_handles ();+		}+	}++	bool new_point_on_path_at (int x, int y) {+		EditPoint ep;+		Glyph glyph = MainWindow.get_current_glyph ();+		+		if (is_new_point_from_path_selected ()) {+			+			if (glyph.new_point_on_path != null) {+				ep = (!)glyph.new_point_on_path; +				+				begin_new_point_on_path = false;+				+				glyph.add_new_edit_point (glyph.reverse_path_coordinate_x (ep.x), glyph.reverse_path_coordinate_x (ep.y));+				glyph.new_point_on_path = null;+				+				MainWindow.get_toolbox ().select_tool_by_name ("new_point");++				return false;+			}+			+			start_from_new_point_on_path (x, y);+			begin_new_point_on_path = true;+			+			return false;+		} else {+			begin_new_point_on_path = false;+		}+		+		return true;+	}++	void start_from_new_point_on_path (double x, double y) {		+		Glyph g = MainWindow.get_current_glyph ();+		Path? p = null;+		Path pn;+		+		p = g.get_closeset_path (x, y);++		pn = (!)p;++		if (pn.points.length () >= 2) {+			pn.set_editable (true);+			set_new_point_on_path (pn, 0, 0);+			g.open_path ();+		}+		+		begin_new_point_on_path = false;+	}+	+	public void begin_from_new_point_on_path () {+		begin_new_point_on_path = true;+	}+	+	private bool is_over_handle (double event_x, double event_y) {+		double x = Glyph.path_coordinate_x (event_x);+		double y = Glyph.path_coordinate_y (event_y);+		Glyph g = MainWindow.get_current_glyph (); +		double d_point = g.view_zoom * get_distance_to_closest_edit_point (event_x, event_y);+		+		double dl, dr;+	+		foreach (EditPoint selected_corner in selected_points) {+			dl = g.view_zoom * selected_corner.get_left_handle ().get_point ().get_distance (x, y);+			dr = g.view_zoom * selected_corner.get_right_handle ().get_point ().get_distance (x, y);+			+			if (dl < CONTACT_SURFACE && dl < d_point) {+				return true;+			}++			if (dr < CONTACT_SURFACE && dr < d_point) {+				return true;+			}+		}+	+		return false;+	}++	EditPointHandle get_closest_handle (double event_x, double event_y) {+		EditPointHandle left, right;+		double x = Glyph.path_coordinate_x (event_x);+		double y = Glyph.path_coordinate_y (event_y);		+		EditPointHandle eh = new EditPointHandle.empty();++		double d = double.MAX;+		double dn;+				+		foreach (EditPoint selected_corner in selected_points) {+			left = selected_corner.get_left_handle ();+			right = selected_corner.get_right_handle ();++			dn = left.get_point ().get_distance (x, y);+			+			if (dn < d) {+				eh = left;+				d = dn;+			}++			dn = right.get_point ().get_distance (x, y);+			+			if (dn < d) {+				eh = right;+				d = dn;+			}+		}+		+		return eh;+	}++	private void curve_active_corner_event (double event_x, double event_y) {+		EditPointHandle eh;+		+		active_handle.active = false;+		+		if (!is_over_handle (event_x, event_y)) {+			return;+		}		+		+		eh = get_closest_handle (event_x, event_y);+		eh.active = true;+		active_handle = eh;+	}++	private void curve_corner_event (double event_x, double event_y) {+		MainWindow.get_current_glyph ().open_path ();++		if (!is_over_handle (event_x, event_y)) {+			return;+		}++		move_selected_handle = true;+		selected_handle = get_closest_handle (event_x, event_y);+	}++	void add_selected_point (EditPoint p) {+		foreach (EditPoint ep in selected_points) {+			if (p == ep) {+				return;+			}+		}+		+		selected_points.append (p);+	}+	+	void remove_all_selected_points () {+		foreach (EditPoint e in selected_points) {+			e.set_active (false);+			e.set_selected (false);+		}+			+		while (selected_points.length () > 0) {+			selected_points.remove_link (selected_points.first ());+		}+	}+	+	/** Draw a test glyph. */+	public override bool test () {+		test_select_action ();+		+		test_open_next_glyph ();+		+		// paint+		test_click_action (1, 30, 30); +		test_click_action (1, 60, 30);+		test_click_action (1, 60, 60);+		test_click_action (1, 30, 60);+		+		// close+		test_click_action (3, 0, 0);++		// reopen+		test_click_action (3, 35, 35);+		+		// move around+		test_move_action (100, 200);+		test_move_action (20, 300);+		test_move_action (0, 0);+		+		// add to path+		test_move_action (70, 50);+		+		test_click_action (1, 70, 50);+		test_click_action (1, 70, 50);+		test_click_action (1, 70, 100);+		test_click_action (1, 50, 100); +		test_click_action (1, 50, 50);+		+		// close+		test_click_action (3, 0, 0);+		+		// merge it+		+		+		this.yield ();+		+		return true;+	}++	public void test_active_edit_point () {+		Glyph g;+		EditPoint epa, epb;+		+		// paint+		test_select_action ();+		test_open_next_glyph ();++		g = MainWindow.get_current_glyph ();++		test_click_action (1, 130, 130); // open path+		test_click_action (1, 130, 130); // add point+		epa = g.get_last_edit_point ();+		+		test_click_action (1, 160, 130);+		test_click_action (1, 160, 160);+		epb = g.get_last_edit_point ();+		+		test_click_action (1, 130, 160);+		+		// validate active point+		test_move_action (130, 130);+		warn_if_fail (active_edit_point == epa);+		+		test_move_action (161, 161); // close but not on+		warn_if_fail (active_edit_point == epb);+		+		warn_if_fail (epa != epb);+		+		// TODO: Test move handle here.+	}++	/** Test path coordinates and reverse path coordinates. */+	public void test_coordinates () {+		int x, y, xc, yc;+		double px, py, mx, my;+		string n;+		+		Tool.test_open_next_glyph ();+		Glyph g = MainWindow.get_current_glyph ();+		+		xc = (int) (g.allocation.width / 2.0);+		yc = (int) (g.allocation.height / 2.0);++		g.default_zoom ();+		+		x = 10;+		y = 15;+		+		px = g.path_coordinate_x (x);+		py = g.path_coordinate_y (y);++		mx = x * g.ivz () - g.xc () + g.view_offset_x;+		my = g.yc () - y * g.ivz () - g.view_offset_y;+		+		if (mx != px || my != py) {+			warning (@"bad coordinate $mx != $px || $my != $py");+		}+			+		test_reverse_coordinate (x, y, px, py, "ten fifteen");+		test_click_action (1, x, y);+	+		// offset no zoom+		n = "Offset no zoom";+		g.reset_zoom ();+		+		px = g.path_coordinate_x (x);+		py = g.path_coordinate_y (y);+		+		test_reverse_coordinate (x, y, px, py, n);+		test_click_action (1, x, y);+		+		// close path+		test_click_action (3, x, y);+	}+	+	private void test_coordinate (double x, double y, double px, double py, string n) {+		if (!(px - 0.5 <= x <= px + 0.5|| py - 0.5 <= y <= py + 0.5)) {+			warning (@"Expecting (x == px || y == py) got ($x == $px || $y == $py) in \"$n\"\n");+		}+	}+	+	private void test_reverse_coordinate (int x, int y, double px, double py, string n) {+		Glyph g = MainWindow.get_current_glyph ();+		if (x != g.reverse_path_coordinate_x (px) || g.reverse_path_coordinate_y (py) != y) {+			warning (@"Reverse coordinates does not match current point for test case \"$n\".\n $x != $(g.reverse_path_coordinate_x (px)) || $(g.reverse_path_coordinate_y (py)) != $y (x != g.reverse_path_coordinate_x (px) || g.reverse_path_coordinate_y (py) != y)");+		}+	}++	private void test_last_is_counter_clockwise (string name) {+		bool d = ((!)MainWindow.get_current_glyph ().get_last_path ()).is_clockwise ();+		+		if (d) {+			critical (@"\nPath $name is clockwise, in test_last_is_counter_clockwise");+		}		+	}+	+	private void test_last_is_clockwise (string name) {+		bool d = ((!)MainWindow.get_current_glyph ().get_last_path ()).is_clockwise ();+		+		if (!d) {+				critical (@"\nPath $name is counter clockwise, in test_last_is_clockwise");+		}++	}+	+	private bool test_reverse_last (string name) +		requires (MainWindow.get_current_glyph ().get_last_path () != null)+	{+		Glyph g = MainWindow.get_current_glyph ();+		Path p = (!) g.get_last_path ();+		bool direction = p.is_clockwise ();++		p.reverse ();+		+		if (direction == p.is_clockwise ()) {+			critical (@"Direction did not change after reverseing path \"$name\"\n");+			stderr.printf (@"Path length: $(p.points.length ()) \n");+			return false;+		}++		this.yield ();+		return true;+	}+	+	+	class Point {+		+		public int x;+		public int y;+		+		public Point (int x, int y) {+			this.x = x;+			this.y = y;+		}+	}+	+	private Point p (int x, int y) {+		return new Point (x, y);+	}+	+	private void test_triangle (Point a, Point b, Point c, string name = "") {+		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");+		+		this.yield ();+		MainWindow.get_tab_bar ().select_overview ();++		this.yield ();+		MainWindow.get_overview ().open_current_glyph ();+		+		pen_tool.test_select_action ();+		+		pen_tool.test_click_action (1, 0, 0); // open path+		+		pen_tool.test_click_action (1, a.x, a.y);+		pen_tool.test_click_action (1, b.x, b.y);+		pen_tool.test_click_action (1, c.x, c.y);+		pen_tool.test_click_action (3, 0, 0);+		+		test_reverse_last (@"Triangle reverse \"$name\" ($(a.x), $(a.y)), ($(b.x), $(b.y)), ($(c.x), $(c.y)) failed.");+		+		this.yield ();+	}+	+	private void test_various_triangles () {+		test_triangle (p (287, 261), p (155, 81), p (200, 104), "First");+		test_triangle (p (65, 100), p (168, 100), p (196, 177), "Second");+		test_triangle (p (132, 68), p (195, 283), p (195, 222), "Third");+		test_triangle (p (144, 267), p (147, 27), p (296, 267), "Fourth");+	}+	+	public bool test_reverse_path () {+		// open a new glyph+		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");++		this.yield ();+		MainWindow.get_tab_bar ().select_overview ();++		this.yield ();+		MainWindow.get_overview ().open_current_glyph ();+		+		pen_tool.test_select_action ();+		+		// paint+		int x_offset = 10;+		int y_offset = 10;+		+		test_open_next_glyph ();+		test_various_triangles ();+		+		test_open_next_glyph ();+		// draw clockwise and check direction		++		y_offset += 160;+		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (1, 17 + x_offset, 17 + y_offset);+		pen_tool.test_click_action (1, 20 + x_offset, 0 + y_offset);+		pen_tool.test_click_action (3, 0, 0);+		test_last_is_clockwise ("Clockwise triangle 1.2");++		// draw paths clockwise / counter clockwise and reverse them+		+		pen_tool.test_click_action (1, 115, 137);+		pen_tool.test_click_action (1, 89, 74);+		pen_tool.test_click_action (1, 188, 232);+		pen_tool.test_click_action (3, 0, 0);+		test_reverse_last ("Triangle 0");++		// draw incomplete paths+		y_offset += 20;+		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (3, 0, 0);+		test_reverse_last ("Point");++		y_offset += 20;+		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (3, 0, 0);+		test_reverse_last ("Double point");++		y_offset += 20;+		pen_tool.test_click_action (1, 10 + x_offset, 30 + y_offset);+		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (3, 0, 0);+		test_reverse_last ("Vertical line");++		y_offset += 20;+		pen_tool.test_click_action (1, 30 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (3, 0, 0);+		test_reverse_last ("Horisontal line");++		// triangle 1+		y_offset += 20;+		pen_tool.test_click_action (1, 10 + x_offset, -10 + y_offset);+		pen_tool.test_click_action (1, 20 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (1, 30 + x_offset, 0 + y_offset);+		pen_tool.test_click_action (3, 0, 0);+		test_reverse_last ("Triangle reverse 1");++		// box+		y_offset += 20;+		pen_tool.test_click_action (1, 100 + x_offset, 150 + y_offset);+		pen_tool.test_click_action (1, 150 + x_offset, 150 + y_offset);+		pen_tool.test_click_action (1, 150 + x_offset, 100 + y_offset);+		pen_tool.test_click_action (1, 100 + x_offset, 100 + y_offset); +		pen_tool.test_click_action (3, 0, 0); // close+		test_reverse_last ("Box 1");+		+		return true;+	}+	+	private Tool select_pen () {+		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");+		pen_tool.test_select_action ();+		return pen_tool;+	}+	+	public bool test_delete_points () {+		PenTool pen;+		Tool delete_tool = MainWindow.get_toolbox ().get_tool ("erase_tool");+		+		test_open_next_glyph ();+				+		pen = (PenTool) select_pen ();+		pen.test_click_action (1, 0, 0); // open path+		+		// draw a line with ten points+		for (int i = 1; i <= 10; i++) {+			pen.test_click_action (1, 20*i, 20);+		}	+	+		// TODO: it would be nice to test if points were created here+		+		// delete points+		delete_tool.test_select_action ();+		for (int i = 1; i <= 10; i++) {+			pen.move (20*i, 20);+			pen.test_click_action (1, 20*i, 20);+		}+		+		return true;+	}+	+	public bool test_reverse_random_triangles () {+		Tool pen;+		+		int ax, bx, cx;+		int ay, by, cy;++		bool r = true;++		test_open_next_glyph ();+		pen = select_pen ();++		for (int i = 0; i < 30; i++) {+			this.yield ();+			+			ax = Random.int_range (0, 300);+			bx = Random.int_range (0, 300);+			cx = Random.int_range (0, 300);++			ay = Random.int_range (0, 300);+			by = Random.int_range (0, 300);+			cy = Random.int_range (0, 300);++			pen.test_click_action (1, ax, ay);+			pen.test_click_action (1, bx, by);+			pen.test_click_action (1, cx, cy);+			pen.test_click_action (3, 0, 0);+		+			r = test_reverse_last (@"Random triangle № $(i + 1) ($ax, $ay), ($bx, $by), ($cx, $cy)");+			if (!r) {+				test_open_next_glyph ();+				pen = select_pen ();++				pen.test_click_action (1, ax, ay);+				pen.test_click_action (1, bx, by);+				pen.test_click_action (1, cx, cy);+		+				return false;+			}+			+			test_open_next_glyph ();+		}+		+		if (r) test_open_next_glyph ();+		+		return true;+	}+}++}diff --git a/libbirdfont/Preferences.vala b/libbirdfont/Preferences.valanew file mode 100644index 0000000..6435d8e--- /dev/null+++ b/libbirdfont/Preferences.vala@@ -0,0 +1,175 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/+namespace Supplement {++public class Preferences {+		+	static HashTable<string, string> data;++	public Preferences () {+		data = new HashTable<string, string> (str_hash, str_equal);+	}++	public static void set_last_file (string fn) {+		set ("last_file", fn);+	}++	public static string @get (string k) {+		string? s;+		+		lock (data) {+			s = data.lookup (k);+		}+		+		return (s != null) ? (!) s : "";+	}++	public static void @set (string k, string v) {+		lock (data) {+			data.replace (k, v);+			save (); // Fixa: save in separate io thread instead.+		}+	}++	public static string[] get_recent_files () {+		string recent = get ("recent_files");+		string[] files = recent.split ("\t");+		+		for (uint i = 0; i < files.length; i++) {+			files[i] = files[i].replace ("\\t", "\t");+		}+		+		return files;+	}++	public static void add_recent_files (string file) {+		string escaped_string = file.replace ("\t", "\\t");+		StringBuilder recent = new StringBuilder ();++		foreach (string f in get_recent_files ()) {+			if (f != file) {+				recent.append (f.replace ("\t", "\\t"));+				recent.append ("\t");+			}+		}++		recent.append (escaped_string);++		set ("recent_files", @"$(recent.str)");+	}++	public static int get_window_width() {+		string wp = get ("window_width");+		int w = int.parse (wp);+		return (w == 0) ? 860 : w;+	}++	public static int get_window_height() {+		int h = int.parse (get ("window_height"));+		return (h == 0) ? 500 : h;+	}+	+	public static void load () {+		File app_dir = Supplement.get_settings_directory ();+		File settings = app_dir.get_child ("settings");++		data = new HashTable<string, string> (str_hash, str_equal);++		if (!settings.query_exists ()) {+			stderr.printf ("No settings file.\n");+			return;+		}++		FileStream? settings_file = FileStream.open ((!) settings.get_path (), "r");+		+		if (settings_file == null) {+			stderr.printf ("Failed to load settings from file %s.\n", (!) settings.get_path ());+			return;+		}+		+		return_if_fail (settings_file != null);+		+		unowned FileStream b = (!) settings_file;+		+		string? l;+		l = b.read_line ();+		while ((l = b.read_line ())!= null) {+			string line;+			+			line = (!) l;+			+			if (line.get_char (0) == '#') {+				continue;+			}+			+			int i = 0;+			int s = 0;+			+			i = line.index_of_char(' ', s);+			string key = line.substring (s, i - s);++			s = i + 1;+			i = line.index_of_char('"', s);+			s = i + 1;+			i = line.index_of_char('"', s);+			string val = line.substring (s, i - s);+			+			// (key, val, description);+			+			data.insert (key, val);+		}+	}+	+	public static void save () {+		try {+			File app_dir = Supplement.get_settings_directory ();+			File settings = app_dir.get_child ("settings");++			return_if_fail (app_dir.query_exists ());+		+			if (settings.query_exists ()) {+				settings.delete ();+			}++			DataOutputStream os = new DataOutputStream(settings.create(FileCreateFlags.REPLACE_DESTINATION));+			uint8[] d;+			long written = 0;+			+			StringBuilder sb = new StringBuilder ();+			+			sb.append ("# Supplement settings\n");+			sb.append ("# Version: 1.0\n");+			+			foreach (var k in data.get_keys ()) {+				sb.append (k);+				sb.append (" \"");+				sb.append (data.lookup (k));+				sb.append ("\"\n");+			}+			+			d = sb.str.data;+				+			while (written < d.length) { +				written += os.write (d[written:d.length]);+			}+		} catch (Error e) {+			stderr.printf ("Can not save key settings. (%s)", e.message);	+		}	+	}+}++}diff --git a/libbirdfont/Preview.vala b/libbirdfont/Preview.valanew file mode 100644index 0000000..39f5da8--- /dev/null+++ b/libbirdfont/Preview.vala@@ -0,0 +1,55 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using WebKit;++namespace Supplement {++public class Preview : FontDisplay {	+	+	public Preview () {+	}+	+	public override string get_name () {+		return "Preview";+	}+	+	public override bool is_html_canvas () {+		return true;+	}++	public override void selected_canvas () {+	}++	public File get_html_file () {+		Font font = Supplement.get_current_font ();+		string path = @"$(font.get_name ()).html";+		File dir = font.get_folder ();+		File file = dir.get_child (path);+		+		if (!file.query_exists ()) {+			ExportTool.generate_html_document ((!)file.get_path (), font);				+		}+		+		return file;	+	}++	public override string get_uri () {+		return path_to_uri ((!) get_html_file ().get_path ());+	}+}+}diff --git a/libbirdfont/ResizeTool.vala b/libbirdfont/ResizeTool.valanew file mode 100644index 0000000..a63a312--- /dev/null+++ b/libbirdfont/ResizeTool.vala@@ -0,0 +1,249 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++class ResizeTool : Tool {++	ImageSurface move_icon;+	ImageSurface move_icon_active;++	ImageSurface rotate_icon;+	ImageSurface rotate_icon_active;+		+	int active_handle = -1;+	+	Path selected_path = new Path ();+	+	bool resize = false;+	double resize_begin_x = 0;+	double resize_begin_y = 0;+	Path? original_path = null;+	+	public ResizeTool (string n) {+		base (n, "resize paths");++		select_action.connect((self) => {+			active_handle = -1;+			redraw_handle ();+		});+			+		deselect_action.connect((self) => {+			active_handle = -1;+			redraw_handle ();+		});+		+		press_action.connect((self, b, x, y) => {+			if (get_handle_at (x, y) > -1) {+				resize_begin_x = x;+				resize_begin_y = y;+				resize = true;+				original_path = null;+			}+		});++		release_action.connect((self, b, x, y) => {+			resize = false;+			original_path = null;+		});+		+		move_action.connect ((self, x, y)	 => {+			high_light (x, y);+			+			if (resize) {+				resize_process (x, y);+			}+		});+		+		draw_action.connect ((self, cairo_context, glyph) => {+				draw_handle (cairo_context, glyph);+			});+			+		move_icon = (!) Icons.get_icon ("move_handle.png");+		move_icon_active = (!) Icons.get_icon ("move_handle_active.png");++		rotate_icon = (!) Icons.get_icon ("rotate_icon.png");+		rotate_icon_active = (!) Icons.get_icon ("rotate_icon_active.png");		+		+	}++	private void resize_process (double x, double y) {+		double a, b, c, nc, tx, ty;+		+		Path p;+		+		// TODO: this function resizes only one path not a group of paths+		Path po = selected_path;+		+		if (po.empty ()) return;+				+		if (original_path == null) original_path = po.copy ();+		+		p = ((!)original_path).copy ();+		+		a = p.xmax - p.xmin;+		b = p.ymax - p.ymin;+		+		tx = x - resize_begin_x;+		ty = y - resize_begin_y;+		+		c  =  Math.sqrt (Math.pow (a, 2) + Math.pow (b, 2));+		nc =  Math.sqrt (Math.pow (a + tx, 2) + Math.pow (b + ty, 2));++		p.resize (nc / c);+		po.replace_path (p);++		MainWindow.get_current_glyph ().queue_redraw_path (po);+		MainWindow.get_current_glyph ().queue_redraw_path (p);+	}++	private void high_light (double x, double y) {+		int i = get_handle_at (x, y);++		if (active_handle != i) {+			redraw_handle ();+			active_handle = i;+		}+	}++	private int get_handle_at (double x, double y) {+		Glyph g = MainWindow.get_current_glyph ();+		int hx, hy;+		double ivz = 1/g.view_zoom;+				+		int i = 0;+		foreach (var p in g.path_list) {+			hx = (int) (p.xmax * ivz + g.view_offset_x + (g.allocation.width / 2) * ivz);+			hy = (int) (p.ymax * ivz + g.view_offset_y + (g.allocation.height / 2) * ivz);+		+			if (p.points.length () > 0 && hx <= x <= hx + move_icon.get_width () && hy <= y <= hy + move_icon.get_height ()) {+				selected_path = p;+				return i;+			}+			+			i++;+		}+		+		selected_path = new Path ();+		return -1;+	}++	private void draw_handle (Context cr, Glyph g) {+		double x, y;+		int i = 0;	+		double ivz = 1/g.view_zoom;++		if (resize) return;+						+		foreach (var p in g.path_list) {+			x = p.xmax * ivz + g.view_offset_x + (g.allocation.width / 2) * ivz;+			y = p.ymax * ivz + g.view_offset_y + (g.allocation.height / 2) * ivz;+			+			if (p.points.length () > 0) {+				ImageSurface s = (i == active_handle) ? move_icon_active : move_icon;+				cr.set_source_surface (s, x, y);+				cr.paint ();++				ImageSurface r = (i == active_handle) ? rotate_icon_active : rotate_icon;+				cr.set_source_surface (r, x, y - 20);+				cr.paint ();+			}+			+			i++;+		}+	}++	private void redraw_handle () {+		Glyph g = MainWindow.get_current_glyph ();+		int x, y;+		double ivz = 1/g.view_zoom;+		+		foreach (var p in g.path_list) {+			x = (int) (p.xmax * ivz + g.view_offset_x + (g.allocation.width / 2) * ivz);+			y = (int) (p.ymax * ivz + g.view_offset_y + (g.allocation.height / 2) * ivz);++			g.redraw_area (x, y, (int) move_icon.get_width (), (int) move_icon.get_height ());+			g.redraw_area (0, 0, g.allocation.width, g.allocation.height);+		}+	}++	public override bool test () {+		// draw and resize glyphs +		+		draw_test_glyph (0, 100); // 1:1++		draw_test_glyph (0, 89); +		resize_test_glyph (0.99); // 99%+				+		for (int i = 2; i < 10; i++) {+			draw_test_glyph (10 * (i - 1), 100);+			resize_test_glyph (1.0 / i);+		}++		int m = 100;+		for (int i = 2; i < 6; i++) {+			m += ((i - 1) * 30) + 5;+			draw_test_glyph (0, m);+			resize_test_glyph (i);+		}++		this.yield ();+		+		return true;+	}++	private void resize_test_glyph (double ratio_percent) {+		test_select_action ();+		+		this.yield ();+		+		Glyph g = MainWindow.get_current_glyph ();+		Path? pn = g.get_last_path ();+		return_if_fail (pn != null);++		Path p = (!) pn;		+		p.resize (ratio_percent);++	}+	+	private void draw_test_glyph (int x_offset, int y_offset) {+		// open a new glyph+		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");++		this.yield ();+		MainWindow.get_tab_bar ().select_overview ();++		this.yield ();+		MainWindow.get_overview ().open_current_glyph ();+		+		pen_tool.test_select_action ();+		+		// paint+		pen_tool.test_click_action (1, 10 + x_offset, 10 + y_offset); +		pen_tool.test_click_action (1, 20 + x_offset, 10 + y_offset);+		pen_tool.test_click_action (1, 20 + x_offset, 20 + y_offset);+		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);+		+		// close+		pen_tool.test_click_action (3, 0, 0);+	}+	+}++}diff --git a/libbirdfont/SaveDialog.vala b/libbirdfont/SaveDialog.valanew file mode 100644index 0000000..2a8ac81--- /dev/null+++ b/libbirdfont/SaveDialog.vala@@ -0,0 +1,98 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++public class SaveDialog : FontDisplay {++	public signal void finished ();++	public SaveDialog () {+		add_html_callback ("save_dialog", (val) => {+			if (val == "save") {+				if (MenuTab.save ()) {+					finished ();+				}+			}+			+			if (val == "save_as") {+				if (MenuTab.save_as ()) {+					finished ();+				}+			}+			+			if (val == "discard") {+				// discard it+				finished ();+			}+			+			if (val == "cancel") {+				MainWindow.get_tab_bar ().close_display (this);+			}			+			+		});+	}++	public override string get_name () {+		return "Save?";+	}++	public override bool is_html_canvas () {+		return true;+	}++	public override string get_html () {+		Font f = Supplement.get_current_font ();+		string fn = f.get_file_name ();+		+		if (fn == "") {+			fn = f.get_name () + ".ffi";+		}+				+		return """+<html>+<head>+	<script type="text/javascript" src="supplement.js"></script>+	<style type="text/css">@import url("style.css");</style>+</head>+<body>+	+	<div style="width:300px; margin: 50px auto 0 auto;">+		<div class="heading"><h2>""" + _("Save?") + """</h2></div>+		+		<p>""" + fn + """</p>+		+		<form>+			<input class="button" type="button" value=""" + "\"" + _("Save") + "\"" + """    onclick="call ('save_dialog:save');"/>+			<input class="button" type="button" value=""" + "\"" + _("Save as") + "\"" + """ onclick="call ('save_dialog:save_as');"/>+			<input class="button" type="button" value=""" + "\"" + _("Discard") + "\"" + """ onclick="call ('save_dialog:discard');"/>+			<input class="button" type="button" value=""" + "\"" + _("Cancel") + "\"" + """  onclick="call ('save_dialog:cancel');"/>+			<br />+		</form>+	</div>+</body>+</html>""";++		// FIXME:+		// tell xgettext to extract this:+		_("Save?");+	}+}+	+}diff --git a/libbirdfont/Scrollbar.vala b/libbirdfont/Scrollbar.valanew file mode 100644index 0000000..8ddd580--- /dev/null+++ b/libbirdfont/Scrollbar.vala@@ -0,0 +1,141 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++class Scrollbar : GLib.Object {+	Allocation alloc;++	double pos = 0;+	double height = 0.1;+	+	double y = 0;+	double h = 0;+	+	double y_begin = 0;+	double y_last = 0;+	bool move = false;+	bool visible = true;++	public signal void signal_scroll (double delta, double delta_last, double absolute);++	public Scrollbar () {+	}+	+	public void set_handle_size (double s) {+		height = (s < 0.01) ? 0.01 : s;+		+		if (height > 1) {+			height = 1;+		}+		+		h = height * alloc.height;+	}++	public void set_handle_position (double p) {+		pos = (p > 0) ? p : 0;+		y = pos * alloc.height;+	}+	+	public void button_press (uint button, double x, double y) {+		move = true;+		y_begin = y;+		y_last = y;+	}+	+	public void button_release (int button, double ex, double ey) {+		move = false;+	}+	+	public void motion_notify (double ex, double ey) {+		double d = 0;+		double dl = 0;+		double m;+		+		h = height * alloc.height;+		m = alloc.height;+				+		if (move && 0 < ey < m) {+			d = ey - y_begin;+			dl = ey - y_last;+			y += dl;+			y_last = ey;+		}++		if (y < 0) {+			y = 0;+		}++		if (y > alloc.height - h) {+			y = alloc.height - h;+			d = 0;+			dl = 0;+		}+		+		if (move) {+			pos = y / alloc.height;+		}+		+		if (move) {+			signal_scroll (d / (m - h), dl / (m -h), y / (m - h));+			pos = y / m;+		}++	}+		+	public void draw (Context cr, Allocation alloc) {+		double ax, ay, aw, ah;++		this.alloc = alloc;++		ax = alloc.width - 10;+		ay = 0;+		aw = alloc.width;+		ah = alloc.height;+						+		if (!visible) {+			cr.save ();+			cr.rectangle (ax, ay, aw, ah);+			cr.set_line_width (0);+			cr.set_source_rgba (194/255.0, 194/255.0, 194/255.0, 1);+			cr.fill_preserve ();+			cr.stroke ();+			cr.restore ();+			return;+		}+		+		cr.save ();+		cr.rectangle (ax, ay, aw, ah);+		cr.set_line_width (0);+		cr.set_source_rgba (183/255.0, 200/255.0, 223/255.0, 1);+		cr.fill_preserve ();+		cr.stroke ();+		cr.restore ();	+		+		cr.save ();+		cr.rectangle (ax, pos * alloc.height, alloc.width, ah * height);+		cr.set_line_width (0);+		cr.set_source_rgba (133/255.0, 143/255.0, 174/255.0, 1);+		cr.fill_preserve ();+		cr.stroke ();+		cr.restore ();			+	}+}++}diff --git a/libbirdfont/ShrinkTool.vala b/libbirdfont/ShrinkTool.valanew file mode 100644index 0000000..cea88be--- /dev/null+++ b/libbirdfont/ShrinkTool.vala@@ -0,0 +1,113 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++// TODO contrast++class ShrinkTool : Tool {++	public ShrinkTool (string n) {+		base (n, "shrink path");++		select_action.connect((self) => {+			Glyph g = MainWindow.get_current_glyph ();+		+			foreach (Path p in g.path_list) {+				shrink_copy (p, 0.25);+			}+			+			MainWindow.get_glyph_canvas ().redraw ();+		});++		select_action.connect((self) => {+		});+			+		press_action.connect((self, b, x, y) => {+		});++		move_action.connect((self, x, y) => {+		});+	}+	+	public static int path_index (Path p) {+		Glyph g = MainWindow.get_current_glyph ();+		int i = 0;+		+		foreach (Path pt in g.path_list) {+			if (p == pt) {+				return i;+			}+			i++;+		}+		+		return -1;+	}+	+	public static void shrink_copy (Path px, double step) {		+		Path p = px.copy ();+		shrink (px, step);+		p.set_editable (true);+	}	+	+	public static void shrink (Path p, double step) {		+		if (p.points.length () < 3) return;++		p.create_list ();+		+		EditPoint ep;++		p.create_list ();++		foreach (EditPoint pep in p.points) {+			unowned List<EditPoint>? lep = pep.get_next ();+			+			if (unlikely (lep == null)) {+				warning ("Bad list.");+				stderr.printf (@"No links in path $(path_index (p)). Length: $(p.points.length ())\n");+				p.print_boundries ();+				continue;+			}+			+			ep = ((!)lep).data;+			move_point (p, pep, ep, step);+		}+		+		move_point (p, p.points.last ().data, p.points.first ().data, step);+		+		p.set_editable (true);+	}+	+	static void move_point (Path p, EditPoint pep, EditPoint ep, double step) {	+		double istep = 1 - step;+		+		double mx = ep.get_right_handle ().x () / 2 + ep.get_left_handle ().x () / 2;+		double my = ep.get_right_handle ().y () / 2 + ep.get_left_handle ().y () / 2;+			+		ep.x = mx * step + ep.x * istep;+		ep.y = my * step + ep.y * istep;++	}+	+	public override bool test () {+		test_select_action ();+		return true;+	}++}+	+}diff --git a/libbirdfont/SpinButton.vala b/libbirdfont/SpinButton.valanew file mode 100644index 0000000..fb417b6--- /dev/null+++ b/libbirdfont/SpinButton.vala@@ -0,0 +1,228 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++public class SpinButton : Tool {++	public signal void new_value_action (SpinButton selected);+	+	public int8 deka = 2;+	public int8 deci = 0;+	public int8 centi = 0;+	public int8 milli = 0;+	+	bool value_from_motion = false;+	double begin_x = 0;+	double begin_y = 0;+	double begin_value = 0;+	+	double max = 9.999;+	double min = 0;+	+	public SpinButton (string? name = null, string tip = "", unichar key = '\0', uint modifier_flag = 0) {+		base (null , tip, key, modifier_flag);+		+		if (name != null) {+			base.name = (!) name;+		}+		+		set_icon ("spin_button");+		+		panel_press_action.connect ((selected, button, tx, ty) => {+			double py = Math.fabs (y - ty);+			int n;+			+			if (is_selected ()) {+				n = (button == 3) ? 10 : 1;+					+				for (int i = 0; i < n; i++) {+					if (py < 28) increase ();+					if (py > 35) decrease ();+				}+			}+			+			value_from_motion = true;+			begin_x = tx;+			begin_y = ty;+			+			begin_value = double.parse (get_display_value ());+		});++		panel_move_action.connect ((selected, button, tx, ty) => {+			double d;+			if (value_from_motion) {+			 d = (begin_y - ty) / 200;+			 d = (d < 0) ? -Math.pow (d, 2) : Math.pow (d, 2);+			 set_value_round (begin_value + d);+			}+		});++		panel_release_action.connect ((selected, button, tx, ty) => {+			value_from_motion = false;+		});+	}+	+	public void set_max (double max) {+		this.max = max;+	}++	public void set_min (double min) {+		this.min = min;+	}+	+	public void increase () {	+		if (deka == 9 && deci == 9 && centi == 9 && milli == 9) {+			return;+		}+		+		milli++;++		if (milli == 10) {+			centi++;+			milli = 0;+		}+				+		if (centi == 10) {+			deci++;+			centi = 0;+		}++		if (deci == 10) {+			deka++;+			deci = 0;+		}+		+		if (get_value () > max) {+			set_value_round (max, false);+		}++		new_value_action (this);+	}++	public void decrease () {+		if (deka == 0 && deci == 0 && centi == 0) {+			return;+		}+		+		milli--;+		+		if (milli == -1) {+			centi--;+			milli = 9;+		}+		+		if (centi == -1) {+			deci--;+			centi = 9;+		}++		if (deci == -1) {+			deka--;+			deci = 9;+		}+		+		if (get_value () < 0.05) { // lower limit+			set_value_round (0.05);+		}++		if (get_value () < min) {+			set_value_round (min, false);+		}+				+		new_value_action (this);+	}++	public void set_value (string new_value, bool check_boundries = true, bool emit_signal = true) {+		string v = new_value;+		+		while (!(v.char_count () >= 5)) {+			if (v.index_of (".") == -1) {+				v += ".";+			} else {+				v += "0";+			}+			+			return;+		}+		+		deka = (int8) int.parse (v.substring (0, 1));+		deci = (int8) int.parse (v.substring (2, 1));+		centi = (int8) int.parse (v.substring (3, 1));+		milli = (int8) int.parse (v.substring (4, 1));+		+		if (emit_signal) {+			new_value_action (this);+		}+		+		if (check_boundries && get_value () > max) {+			set_value_round (max, false);+		}++		if (check_boundries && get_value () < min) {+			set_value_round (min, false);+		}+	}++	public void set_value_round (double v, bool check_boundries = true, bool emit_signal = true) {+		int8 m;+		+		v += 0.005;+		+		if (v > 10) v = 9.999;+		if (v < 0.001) v = 0.001;+				+		m = milli; // ignore milli value+		set_value (@"$v", check_boundries, emit_signal);+		milli = m;+	}+	+	public double get_value () {+		return deka + (deci / 10.0) + (centi / 100.0) + (milli / 1000.0);+	}++	public string get_display_value () {+		return @"$deka.$deci$centi$milli";+	}++	public override void draw (Context cr) {+		double xt = x + 3 + w;+		double yt = y + h + 17;++		double text_x = -4.5;+		double text_y = 11;++		base.draw (cr);+	+		cr.save ();+	+		cr.set_source_rgba (99/255.0, 99/255.0, 99/255.0, 1);+		+		cr.set_font_size (10);+		cr.select_font_face ("Cantarell", FontSlant.NORMAL, FontWeight.NORMAL);++		cr.move_to (xt + text_x, yt + text_y);+		+		cr.show_text (get_display_value ());+		+		cr.restore ();+	}+}++}diff --git a/libbirdfont/Supplement.vala b/libbirdfont/Supplement.valanew file mode 100644index 0000000..2a5a290--- /dev/null+++ b/libbirdfont/Supplement.vala@@ -0,0 +1,223 @@+/*
+    Copyright (C) 2012 Johan Mattsson
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using Supplement;
+using Birdfont;
+
+const string GETTEXT_PACKAGE = "birdfont"; 
+
+namespace Supplement {
+
+public class Supplement {
+	public static Argument args;
+	internal static bool experimental = false;
+	internal static bool show_coordinates = false;
+	internal static bool fatal_wanings = false;
+	public static bool win32 = false;
+	internal static string exec_path = "";
+
+	internal static Font current_font;
+	public static Glyph current_glyph;
+	
+	public void init (string[] arg) {
+		int err_arg;
+		int i;
+		File font_file;
+		IdleSource idle;
+		string exec_path;
+
+		stdout.printf ("birdfont version %s\n", VERSION);
+		stdout.printf ("built on %s\n", BUILD_TIMESTAMP);
+		
+		init_gettext ();		
+		
+		args = new Argument.command_line (arg);
+
+		if (args.has_argument ("--help")) {
+			args.print_help ();
+			Process.exit (0);
+		}
+
+		err_arg = args.validate ();
+		if (err_arg != 0) {
+			stdout.printf (@"Unknown parameter $(arg [err_arg])\n\n");
+			args.print_help ();
+			Process.exit (0);
+		}
+
+		current_font = new Font ();
+		current_glyph = new Glyph ("");
+
+		experimental = args.has_argument ("--test");
+		show_coordinates = args.has_argument ("--show-coordinates");
+		fatal_wanings = args.has_argument ("--fatal-warning");
+		win32 = (arg[0].index_of (".exe") > -1) || arg[0] == "wine";
+		exec_path = "";
+
+		if (win32) {
+			// wine hack to get "." folder in win32 environment
+			i = arg[0].last_index_of ("\\");
+			
+			if (i != -1) {	
+				exec_path = arg[0];
+				exec_path = exec_path.substring (0, i);
+				exec_path = wine_to_unix_path (exec_path);			
+			}
+		} else {
+			exec_path = "./";
+		}
+		
+		Preferences preferences = new Preferences ();
+		preferences.load ();
+		
+		if (args.get_file () != "") {
+			font_file = File.new_for_path (args.get_file ());
+			
+			if (!font_file.query_exists ()) {
+				stderr.printf (@"File $(args.get_file ()) not found.");
+				Process.exit (-1);
+			}
+		}
+
+		if (fatal_wanings) {
+			LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
+			Log.set_handler (null, levels, fatal_warning);
+		}
+		
+		preferences.set_last_file (get_current_font ().get_path ());
+	}
+
+	static void init_gettext () {
+		File f;
+
+		Intl.setlocale (LocaleCategory.MESSAGES, "");
+		Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "utf-8");
+
+		f = File.new_for_path ("./build/locale/sv/LC_MESSAGES/birdfont.mo");
+		if (f.query_exists ()) {
+			Intl.bindtextdomain (GETTEXT_PACKAGE, "./build/locale");
+			return;
+		}
+		
+		f = File.new_for_path ("./locale/sv/LC_MESSAGES/birdfont.mo");
+		if (f.query_exists ()) {
+			Intl.bindtextdomain (GETTEXT_PACKAGE, "./locale");
+			return;
+		}
+
+		f = File.new_for_path ("../locale/sv/LC_MESSAGES/birdfont.mo");
+		if (f.query_exists ()) {
+			Intl.bindtextdomain (GETTEXT_PACKAGE, "../locale");
+			return;
+		}
+
+		f = File.new_for_path ("/usr/local/share/locale/sv/LC_MESSAGES/birdfont.mo");
+		if (f.query_exists ()) {
+			Intl.bindtextdomain (GETTEXT_PACKAGE, "/usr/local/share/locale");
+			return;
+		}		
+
+		f = File.new_for_path ("/usr/share/locale/sv/LC_MESSAGES/birdfont.mo");
+		if (f.query_exists ()) {
+			Intl.bindtextdomain (GETTEXT_PACKAGE, "/usr/share/locale");
+			return;
+		}
+		
+		warning ("translations not found");
+	}
+	
+	public static Font get_current_font () {
+		return current_font;
+	}
+
+	internal static void fatal_warning (string? log_domain, LogLevelFlags log_levels, string message) {		
+		bool fatal = true;
+		
+		if (log_domain != null) {
+			stderr.printf ("%s: \n", (!) log_domain);
+		}
+		
+		stderr.printf ("\n%s\n\n", message);
+		assert (!fatal);
+	}
+	
+	internal static void new_font () {
+		current_font = new Font ();
+	}
+
+	public static File get_preview_directory () {
+		File settings = get_settings_directory ();
+		File backup = settings.get_child ("preview");
+		
+		if (!backup.query_exists ()) {
+			DirUtils.create ((!) backup.get_path (), 0xFFFFFF);
+		}
+			
+		return backup;
+	}
+
+	internal static File get_thumbnail_directory () {
+		File thumbnails = get_settings_directory ().get_child ("thumbnails");
+		
+		if (!thumbnails.query_exists ()) {
+			DirUtils.create ((!) thumbnails.get_path (), 0xFFFFFF);
+		}
+		
+		return thumbnails;
+	}
+		
+	internal static File get_settings_directory () {
+		File home = File.new_for_path (Environment.get_home_dir ());
+		File settings = home.get_child (".birdfont");
+		
+		if (!settings.query_exists ()) {
+			DirUtils.create ((!) settings.get_path (), 0xFFFFFF);
+		}
+			
+		return settings;
+	}
+
+	internal static File get_backup_directory () {
+		File settings = get_settings_directory ();
+		File backup = settings.get_child ("backup");
+		
+		if (!backup.query_exists ()) {
+			DirUtils.create ((!) backup.get_path (), 0xFFFFFF);
+		}
+			
+		return backup;
+	}
+
+	internal static bool has_argument (string param) {
+		if (is_null (args)) {
+			warning ("args is null");
+			return false;
+		}
+		
+		return args.has_argument (param);
+	}
+	
+	internal static string? get_argument (string param) {
+		return args.get_argument (param);
+	}	
+}
+
+internal bool is_null (void* n) {
+	return n == null;
+}
+
+}
diff --git a/libbirdfont/Svg.vala b/libbirdfont/Svg.valanew file mode 100644index 0000000..50dca9a--- /dev/null+++ b/libbirdfont/Svg.vala@@ -0,0 +1,345 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++class Svg {++	/** Export to svg glyph data. */+	public static string to_svg_glyph (Glyph g, double scale = 1) {	+		StringBuilder svg = new StringBuilder ();++		foreach (Path pl in g.path_list) {+			write_path_as_glyph (pl, svg, g, scale);+		}+		+		return svg.str;+	}++	/** Export to regular svg path data. */+	public static string to_svg_path (Path pl, Glyph g, double scale = 1) {	+		StringBuilder svg = new StringBuilder ();+		pl.create_list ();+		write_path (pl, svg, g, false, scale);+		return svg.str;+	}++	private static void write_path_as_glyph (Path pl, StringBuilder svg, Glyph g, double scale = 1) {+		write_path (pl, svg, g, true, scale);+	}++	private static void write_path (Path pl, StringBuilder svg, Glyph g, bool do_glyph, double scale = 1) {+		int i = 0;+		EditPoint? n = null;+		EditPoint m;+		+		if (pl.points.length () <= 2) {+			return;+		}+		+		pl.create_list ();+		+		foreach (var e in pl.points) {+			if (i == 0) {+				add_abs_start (e, svg, g, do_glyph, scale);+				i++;+				n = e;+				continue;+			}+			+			m = (!) n;++			add_abs_next (m, e, svg, g, do_glyph, scale);+			+			n = e;+			i++;+		}++		m = pl.points.first ().data;+		+		add_abs_next ((!) n, m, svg, g, do_glyph, scale);++		close_path (svg);+	}++	private static void add_abs_next (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool do_glyph, double scale = 1) {+		if (start.right_handle.type == PointType.LINE && end.left_handle.type == PointType.LINE) {+			add_abs_line_to (end, start, svg, g, do_glyph, scale);+		} else if (end.get_left_handle ().type == PointType.NONE) {+			add_quadratic_abs_path (end, start, svg, g, do_glyph, scale);+		} else {+			add_cubic_abs_path (end, start, svg, g, do_glyph, scale);+		}+	}++	private static void add_abs_start (EditPoint ep, StringBuilder svg, Glyph g, bool do_glyph, double scale = 1) {		+		double left = g.left_limit;+		double baseline = Supplement.get_current_font ().base_line;+		+		svg.append_printf ("M");++		svg.append_printf ("%s ",  round ((ep.x - left) * scale));+		svg.append_printf ("%s ",  round ((ep.y + baseline) * scale));+	}+		+	private static void close_path (StringBuilder svg) {+		svg.append ("z");+	}	+	+	private static void add_abs_line_to (EditPoint start, EditPoint stop, StringBuilder svg, Glyph g, bool to_glyph, double scale = 1) {+		double baseline = Supplement.get_current_font ().base_line;+		double left = g.left_limit;+		+		double xa, ya, xb, yb;+		+		Path.get_line_points (start, stop, out xa, out ya, out xb, out yb);++		double height = Supplement.get_current_font ().get_height (); // no probably not++		double center_x = Glyph.xc ();+		double center_y = Glyph.yc ();+	+		if (!to_glyph) {+			svg.append ("L");+						+			svg.append_printf ("%s ", round ((xa - center_x - left) * scale));+			svg.append_printf ("%s ", round ((ya - center_y - baseline + height) * scale));		++			svg.append ("L");+			+			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));+			svg.append_printf ("%s ", round ((yb - center_y - baseline + height) * scale));	++		} else {+			svg.append ("L");+			+			svg.append_printf ("%s ", round ((xa - center_x - left) * scale));+			svg.append_printf ("%s ", round ((-ya + center_y + baseline) * scale));++			svg.append ("L");+			+			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));+			svg.append_printf ("%s ", round ((-yb + center_y + baseline) * scale));				+		}+	}+	+	private static void add_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph, double scale = 1) {+		double left = g.left_limit;+		double baseline = Supplement.get_current_font ().base_line;+		double height = Supplement.get_current_font ().get_height (); // no probably not+		+		double xa, ya, xb, yb, xc, yc, xd, yd;+		+		Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);++		double center_x = Glyph.xc ();+		double center_y = Glyph.yc ();+		+		// cubic path+		if (!to_glyph) {+			svg.append_printf ("Q");++			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));+			svg.append_printf ("%s ", round ((yb - center_y - baseline + height) * scale));+			+			svg.append_printf ("%s ", round ((xc - center_x - left) * scale));+			svg.append_printf ("%s ", round ((yc - center_y - baseline + height) * scale));++		} else {		+			svg.append_printf ("Q");++			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));+			svg.append_printf ("%s ", round ((-yb + center_y + baseline) * scale));+			+			svg.append_printf ("%s ", round ((xc - center_x - left) * scale));+			svg.append_printf ("%s ", round ((-yc + center_y + baseline) * scale));	+		}+	}+			+	private static void add_cubic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph, double scale = 1) {+		double left = g.left_limit;+		double baseline = Supplement.get_current_font ().base_line;+		double height = Supplement.get_current_font ().get_height (); // no probably not+		+		double xa, ya, xb, yb, xc, yc, xd, yd;+		+		Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);++		double center_x = Glyph.xc ();+		double center_y = Glyph.yc ();+		+		// cubic path+		if (!to_glyph) {+			svg.append_printf ("C");++			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));+			svg.append_printf ("%s ", round ((yb - center_y - baseline + height) * scale));+			+			svg.append_printf ("%s ", round ((xc - center_x - left) * scale));+			svg.append_printf ("%s ", round ((yc - center_y - baseline + height) * scale));+			+			svg.append_printf ("%s ", round ((xd - center_x - left) * scale));+			svg.append_printf ("%s ", round ((yd - center_y - baseline + height) * scale));	++		} else {		+			svg.append_printf ("C");++			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));+			svg.append_printf ("%s ", round ((-yb + center_y + baseline) * scale));+			+			svg.append_printf ("%s ", round ((xc - center_x - left) * scale));+			svg.append_printf ("%s ", round ((-yc + center_y + baseline) * scale));	+			+			svg.append_printf ("%s ", round ((xd - center_x - left) * scale));+			svg.append_printf ("%s ", round ((-yd + center_y + baseline) * scale));	+		}+	}+	+	/** Draw path from svg font data. */+	public static void draw_svg_path (Context cr, string svg, double x, double y, double scale = 1) {+		double x1, x2, x3;+		double y1, y2, y3;++		string[] d = svg.split (" ");+		+		x /= scale;+		y /= scale;++		if (d.length == 0) {+			return;+		}+		+		cr.save ();++		cr.set_line_width (1);+		cr.set_source_rgba (0, 0, 0, 1);+		+		if (svg == "") {+			return;+		}+		+		for (int i = 0; i < d.length; i++) {+			+			// trim off leading white space	+			while (d[i].index_of (" ") == 0) { +				d[i] = d[i].substring (1);+			}+			+			if (d[i].index_of ("L") == 0) {+				x1 = double.parse (d[i].substring (1)) + x;+				y1 = -double.parse (d[i+1]) + y;+				+				x1 *= scale;+				y1 *= scale;+				+				cr.line_to (x1, y1);+				continue;+			}++			if (d[i].index_of ("Q") == 0) {+				x1 = double.parse (d[i].substring (1)) + x;+				y1 = -double.parse (d[i+1]) + y;++				x2 = double.parse (d[i+2]) + x;+				y2 = -double.parse (d[i+3]) + y;++				x1 *= scale;+				y1 *= scale;++				x2 *= scale;+				y2 *= scale;+																+				cr.curve_to (x1, y1, x2, y2, x2, y2);+				continue;+			}+			+			if (d[i].index_of ("C") == 0) {+				x1 = double.parse (d[i].substring (1)) + x;+				y1 = -double.parse (d[i+1]) + y;++				x2 = double.parse (d[i+2]) + x;+				y2 = -double.parse (d[i+3]) + y;++				x3 = double.parse (d[i+4]) + x;+				y3 = -double.parse (d[i+5]) + y;++				x1 *= scale;+				y1 *= scale;++				x2 *= scale;+				y2 *= scale;+				+				x3 *= scale;+				y3 *= scale;+																+				cr.curve_to (x1, y1, x2, y2, x3, y3);+				continue;+			}++			if (d[i].index_of ("M") == 0) {+				x1 = double.parse (d[i].substring (1)) + x;+				y1 = -double.parse (d[i+1]) + y;+				+				x1 *= scale;+				y1 *= scale;+				+				cr.move_to (x1, y1);+				continue;+			}+								+			if (d[i].index_of ("zM") == 0) {+				cr.close_path ();+				+				x1 = double.parse (d[i].substring (2)) + x;+				y1 = -double.parse (d[i+1]) + y;+				+				x1 *= scale;+				y1 *= scale;+				+				cr.move_to (x1, y1);+				continue;+			}++			if (d[i].index_of ("z") == 0) {+				cr.close_path ();+				continue;+			}+		+		}+		+		cr.fill ();+		cr.restore ();+	}++}++internal static string round (double p) {+	string v = p.to_string ();+	char[] c = new char [501];+	+	v = p.format (c, "%3.15f");+	+	if (v.index_of ("e") != -1) {	+		return "0.0";+	}+	+	return v;+}+	+}diff --git a/libbirdfont/SvgFontFormatWriter.vala b/libbirdfont/SvgFontFormatWriter.valanew file mode 100644index 0000000..2b8c2a0--- /dev/null+++ b/libbirdfont/SvgFontFormatWriter.vala@@ -0,0 +1,132 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++class SvgFontFormatWriter : Object  {++	DataOutputStream os;++	public SvgFontFormatWriter () {+	}++	public void open (File file) throws Error {+		if (file.query_exists ()) {+			throw new FileError.EXIST ("SvgFontFormatWriter: file exists.");+		}+		+		os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));+	}++	public void close () throws Error {+		os.close ();+	}+	+	public void write_font_file (Font font) throws Error {+		string font_name = font.get_name ();+		+		int units_per_em = 100;+		+		int ascent = 80;+		int descent = -20;+		+		StringBuilder b;+		+		Glyph? g;+		Glyph glyph;+		unichar indice = 0;+		+		string uni;+		+		put ("""<?xml version="1.0" standalone="no"?>""");+		put ("""<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >""");+		put ("""<svg xmlns="http://www.w3.org/2000/svg">""");++		// (metadata goes here)++		put ("<defs>");++		put (@"<font id=\"$font_name\" horiz-adv-x=\"250\" >");+		put (@"<font-face units-per-em=\"$units_per_em\" ascent=\"$ascent\" descent=\"$descent\" />");++		// (missing-glyph goes here)++		while (true) {+			g = font.get_glyph_indice (indice++);+			+			if (g == null) {+				break;+			}+			+			glyph = (!) g;+			+			b = new StringBuilder ();+			b.append_unichar (glyph.get_unichar ());++			if (glyph.get_unichar () >= ' ' && b.str.validate ()) {+				if (b.str == "\"" || b.str == "&" || b.str == "<") {+					uni = Font.to_hex_code (glyph.get_unichar ());+					put (@"<glyph unicode=\"&#x$(uni);\" horiz-adv-x=\"$(glyph.get_width ())\" d=\"$(glyph.get_svg_data ())\" />");			+				} else {+					put (@"<glyph unicode=\"$(b.str)\" horiz-adv-x=\"$(glyph.get_width ())\" d=\"$(glyph.get_svg_data ())\" />");+				}+			}+		}++		while (true) {+			g = font.get_glyph_indice (indice++);+			+			if (g == null) {+				break;+			}+			+			glyph = (!) g;+			+			foreach (Kerning k in glyph.kerning) {+				string l, r;+				Font f = Supplement.get_current_font ();+				Glyph? gr = f.get_glyph (k.glyph_right);+				Glyph glyph_right;+				+				if (gr == null) {+					warning ("kerning glyph that does not exist.");+				}+				+				glyph_right = (!) gr;+				+				l = Font.to_hex_code (glyph.unichar_code);+				r = Font.to_hex_code (glyph_right.unichar_code);+								+				os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(k.val)\"/>\n");+			}+		}		++		put ("</font>");+		put ("</defs>");+		put ("</svg>");+	}++	/** Write a new line */+	private void put (string line) throws Error {+		os.put_string (line);+		os.put_string ("\n");+	}++}+++}diff --git a/libbirdfont/Tab.vala b/libbirdfont/Tab.valanew file mode 100644index 0000000..fc9a582--- /dev/null+++ b/libbirdfont/Tab.vala@@ -0,0 +1,49 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++public class Tab : GLib.Object {++	bool always_open;+	double width; +	FontDisplay display;++	public Tab (FontDisplay glyph, double tab_width, bool always_open) {+		width = tab_width;+		this.display = glyph;+		this.always_open = always_open;+	}++	public bool has_close_button () {+		return !always_open;+	}++	public FontDisplay get_display () {+		return display;+	}+	+	public string get_label () {+		return display.get_name ();+	}+	+	public double get_width () {+		return width;+	}+}++}diff --git a/libbirdfont/TabBar.vala b/libbirdfont/TabBar.valanew file mode 100644index 0000000..fb10a5f--- /dev/null+++ b/libbirdfont/TabBar.vala@@ -0,0 +1,583 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++public class TabBar : GLib.Object {+	+	int width = 0;+	int height = 0;+	+	public List<Tab> tabs = new List<Tab> ();++	static const int NO_TAB = -1;+	static const int NEXT_TAB = -2;+	static const int PREVIOUS_TAB = -3;++	int first_tab = 0;+	int selected = 0;+	int over = NO_TAB;+	int over_close = NO_TAB;+	+	public signal void signal_tab_selected (Tab selected_tab);++	Tab? previous_tab = null;+	Tab? current_tab = null;++	ImageSurface? tab1_left = null;+	ImageSurface? tab1_right = null;++	ImageSurface? tab2_left = null;+	ImageSurface? tab2_right = null;++	ImageSurface? tab3_left = null;+	ImageSurface? tab3_right = null;+	+	ImageSurface? bar_background = null;+	ImageSurface next_tab;+	ImageSurface to_previous_tab;+			+	public TabBar () {+		tab1_left = Icons.get_icon ("tab1_left.png");+		tab1_right = Icons.get_icon ("tab1_right.png");++		tab2_left = Icons.get_icon ("tab2_left.png");+		tab2_right = Icons.get_icon ("tab2_right.png");++		tab3_left = Icons.get_icon ("tab3_left.png");+		tab3_right = Icons.get_icon ("tab3_right.png");+		+		bar_background = Icons.get_icon ("tabbar_background.png");+		+		next_tab = (!) Icons.get_icon ("next_tab.png");+		to_previous_tab = (!) Icons.get_icon ("previous_tab.png");+		+		return_if_fail (!is_null (next_tab));+	}+	+	public void motion (double x, double y) {+		TooltipArea status = MainWindow.get_tool_tip ();+		is_over_close (x, y, out over, out over_close);+	}+	+	private bool is_over_next (double x) {+		return x > width - 19 && has_scroll ();+	}++	private bool is_over_previous (double x) {+		return x < 19  && has_scroll ();+	}++	private void is_over_close (double x, double y, out int over, out int over_close) {+		int i = 0;+		double offset = 19;+		+		if (x < 19 && has_scroll ()) {+			over_close = NO_TAB;+			over = PREVIOUS_TAB;+			return;+		}+		+		foreach (Tab t in tabs) {+			+			if (i < first_tab) {+				i++;+				continue;+			}+			+			if (offset + t.get_width () + 3 > width && has_scroll ()) {+				over_close = NO_TAB;+				over = NEXT_TAB;+				return;+			}+			+			if (offset < x < offset + t.get_width ()) {+				over = i;+				+				if (8 < y < 20 && x > offset + t.get_width () - 16) {+					over_close =  i;+				} else {+					over_close =  NO_TAB;+				}+				+				return;+			}+			+			offset += t.get_width () + 3;+			i++;+		}++		over_close = NO_TAB;		+		over = NO_TAB;+	}	+	+	+	/** Select tab for a glyph by charcode or name.+	 * @return true if the tab was found+	 */+	public bool select_char (string s) {+		int i = 0;+		foreach (Tab t in tabs) {+			if (t.get_label () == s) {+				select_tab (i);+				return true;+			}+			i++;+		}+		+		return false;+	}++	public bool select_tab_name (string s) {+		return select_char (s);+	}++	public void select_overview () {+		select_tab_name ("Overview");+	}++	private void select_previous_tab () {+		Tab t;+		bool open;+		+		if (previous_tab == null) {+			return;+		}+		+		t = (!) previous_tab;+		open = selected_open_tab (t);+		+		if (!open) {+			select_tab ((int) tabs.length () - 1);+		}+	}+		+	public void close_display (FontDisplay f) {+		int i = -1;+		foreach (var t in tabs) {+			++i;+			+			if (t.get_display () == f) {+				close_tab (i) ;+				return;+			}+		}+		+		return_if_fail (i != -1);+	} ++	public bool close_tab (int index, bool background_tab = false) {	+		unowned List<Tab?>? lt;+		Tab t;+		+		if (!(0 <= index < tabs.length ())) {+			return false;+		}+		+		lt = tabs.nth(index);+		+		if (lt == null || ((!) lt).data == null) {+			return false;+		}++		if (first_tab > 0) {+			first_tab--;+		}++		t = (!) ((!) lt).data;++		if (t.has_close_button ()) {+			tabs.delete_link (tabs.nth(index));+			+			if (!background_tab) {+				select_previous_tab ();+			}+			+			return true;+		}+		+		select_tab (index);+		return false;+	}+	+	public bool close_by_name (string name, bool background_tab = false) {+		int i = 0;+		+		foreach (var t in tabs) {+			if (t.get_label () == name) {+				return close_tab (i, background_tab);+			}+			+			i++;+		}+		+		return false;+	}+	+	public void close_background_tab_by_name (string name) {+		close_by_name (name, true);+	}+	+	/** Select a tab and return true if it is open. */+	public bool selected_open_tab (Tab t) {+		int i = 0;+		foreach (var n in tabs) {+			if (n == t) {+				select_tab (i);+				return true;+			}+			+			i++;+		}+		+		return false;+	}++	public Tab? get_tab (string name) {+		foreach (var n in tabs) {+			if (n.get_label () == name) {+				return n;+			}+		}+		+		return null;+	}++	public bool selected_open_tab_by_name (string t) {+		int i = 0;+		foreach (var n in tabs) {+			if (n.get_label () == t) {+				select_tab (i);+				return true;+			}+			+			i++;+		}+		+		return false;+	}+		+	public Tab get_selected_tab () {+		return tabs.nth (get_selected ()).data;+	}+	+	public uint get_length () {+		return tabs.length ();+	}++	public int get_selected () {+		return selected;+	}+	+	public void select_tab (int index) {+		Tab t;++		if (index == NEXT_TAB) {+			selected++;+			+			if (selected >=  tabs.length ()) {+				selected = (int) tabs.length () - 1;+			}+			+			scroll_to_tab (selected);+			return;+		}+		+		if (index == PREVIOUS_TAB) {++			if (selected > 0) {+				selected--;+			}+			+			scroll_to_tab (selected);+			return;+		}+		+		if (!(0 <= index < tabs.length ())) {+			return;+		}++		selected = index;+		+		unowned List<Tab?>? lt = tabs.nth(index);+		+		return_if_fail(lt != null);+		t = (!) ((!) lt).data;+		+		previous_tab = current_tab;+		current_tab = t;++		scroll_to_tab (selected);+	}+	+	private bool has_scroll () {+		int i = 0;+		double offset = 19;+		+		if (first_tab > 0) {+			return true;+		}+		+		foreach (Tab t in tabs) {	+			if (i < first_tab) {+				i++;+				continue;+			}+			+			if (offset + t.get_width () + 3 > width - 19) {+				return true;+			}++			offset += t.get_width () + 3;+			i++;+		}+		+		return false;		+	}+	+	private void signal_selected (int index) {+		unowned List<Tab?>? lt;+		Tab t;+		+		lt = tabs.nth(index);+		return_if_fail(lt != null);		+		t = (!) ((!) lt).data;+		+		signal_tab_selected (t);		+	}+	+	private void scroll_to_tab (int index) {+		double offset = 19;+		int i = 0;+		+		if (index < first_tab) {+			first_tab = index;+			signal_selected (index);+			return;+		}+		+		foreach (Tab t in tabs) {+			+			if (i < first_tab) {+				i++;+				continue;+			}+			+			// out of view+			if (offset + t.get_width () + 3 > width - 19) {+				first_tab++;+				scroll_to_tab (index);+				return;+			}++			// in view+			if (i == index) {+				signal_selected (index);+				return;+			}++			offset += t.get_width () + 3;+			i++;+		}+		+		warning ("");+	}+	+	public void select_tab_click (double x, double y, int width, int height) {+		int over, close;+		+		this.width = width;+		this.height = height;+		+		is_over_close (x, y, out over, out close);++		if (over_close >= 0 && over == selected) {+			close_tab (over_close);+		} else {+			select_tab (over);+		}+	}+	+	public void add_tab (FontDisplay display_item, double tab_width = -1, bool always_open = false) {+		int s = (tabs.length () == 0) ? 0 : selected + 1;+		+		if (tab_width < 0) {+			//cr.text_extents (display_item.get_name (), out te); // this is not a good estimation, pango might solve it+			//tab_width = te.width + 30;+			+			tab_width = 9 * display_item.get_name ().char_count ();+			tab_width += 30;+		}+				+		tabs.insert (new Tab (display_item, tab_width, always_open), s);+		select_tab (s);+	}+	+	/** Returns true if the new item was added to the bar. */+	public bool add_unique_tab (FontDisplay display_item, double tab_width = -1, bool always_open = false) {+		bool i = select_tab_name (display_item.get_name ());++		if (!i) {+			add_tab (display_item, tab_width, always_open);+			return true;+		}+		+		return false;+	}+	+	public void draw (Context cr, int width, int height) {+		this.width = width;+		this.height = height;+		+		cr.save ();+		cr.rectangle (0, 0, width, height);+		cr.set_line_width (0);+		cr.set_source_rgba (183/255.0, 200/255.0, 223/255.0, 1);+		cr.fill_preserve ();+		cr.stroke ();+		cr.restore ();++		for (int j = 0; j < width; j++) {+			cr.set_source_surface ((!) bar_background, j, 0);+			cr.paint ();+		}++		if (has_scroll ()) {+			// left arrow+			cr.set_source_surface (to_previous_tab, 3, (height - to_previous_tab.get_height ()) / 2.0);+			cr.paint ();++			// right arrow+			cr.set_source_surface (next_tab, width - 19, (height - next_tab.get_height ()) / 2.0);+			cr.paint ();+		}+		+		draw_tabs (cr);+		+	}+	+	// this can be removed when tabs are not referenced by name+	string translate (string s) {+		switch (s) {+			case "Overview":+				return _("Overview");+				break;+			case "Kerning":+				return _("Kerning");+				break;+			case "Menu":+				return _("Menu");+				break;+			default:+				return s;+				break;+		}+	}+	+	private void draw_tabs (Context cr) {+		double close_opacity;+		double offset = 19;+		int i = 0;+		+		foreach (Tab t in tabs) {+			+			if (i < first_tab) {+				i++;+				continue;+			}++			cr.save ();+			cr.translate (offset, 0);+						+			if (offset + t.get_width () + next_tab.get_width () + 3 > width) {+				break;+			}+		+			// background+			if (i == selected) {+				for (int j = 0; j < t.get_width (); j++) {+					cr.set_source_surface ((!) tab3_right, j, 2);+					cr.paint ();+				}							+			} else if (i == over) {+				for (int j = 0; j < t.get_width (); j++) {+					cr.set_source_surface ((!) tab2_right, j, 2);+					cr.paint ();+				}				+			} else {+				for (int j = 0; j < t.get_width (); j++) {+					cr.set_source_surface ((!) tab1_right, j, 2);+					cr.paint ();+				}+			}+			+			// close+			if (t.has_close_button ()) {+				cr.set_line_width (1);+				+				close_opacity = (over_close == i) ? 1 : 0.2; +				cr.set_source_rgba (0, 0, 0, close_opacity);+				+				cr.move_to (t.get_width () - 5, 11);+				cr.line_to (t.get_width () - 10, 16);++				cr.move_to (t.get_width () - 10, 11);+				cr.line_to (t.get_width () - 5, 16);+				+				cr.stroke ();	+			}++			cr.set_source_rgba (0, 0, 0, 1);+			cr.set_font_size (14);+			cr.move_to (8, 18);+			cr.show_text (translate (t.get_label ()));+			cr.stroke ();+			+			// edges+			if (tab1_left != null  && tab1_right != null && tab2_left != null  && tab2_right != null && tab3_left != null  && tab3_right != null) {+				if (i == selected) {+					cr.set_source_surface ((!) tab3_left, 0, 2);+					cr.paint ();+					+					cr.set_source_surface ((!) tab3_right, t.get_width () - 2, 2);+					cr.paint ();+				} else if (i == over) {+					cr.set_source_surface ((!) tab2_left, 0, 2);+					cr.paint ();++					cr.set_source_surface ((!) tab2_right, t.get_width () - 2, 2);+					cr.paint ();+				} else {+					cr.set_source_surface ((!) tab1_left, 0, 2);+					cr.paint ();++					cr.set_source_surface ((!) tab1_right, t.get_width () - 2, 2);+					cr.paint ();+				}+			}++			cr.restore ();+			+			offset += t.get_width () + 3;+			i++;+		}+	}+}++}diff --git a/libbirdfont/TestCases.vala b/libbirdfont/TestCases.valanew file mode 100644index 0000000..29730a9--- /dev/null+++ b/libbirdfont/TestCases.vala@@ -0,0 +1,527 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++/** All the things we want to test listed is here. */+class TestCases {+	+	public List<Test> test_cases;++	public TestCases () {+		add (test_overview, "Overview");+		add (test_data_reader, "Font data reader");+		add (test_argument, "Argument list");+		add (test_glyph_ranges, "Glyph ranges");+		add (test_glyph_table, "Glyph table");+		add (test_active_edit_point, "Active edit point");+		add (test_hex, "Unicode hex values");+		add (test_reverse_path, "Reverse path");+		add (test_reverse_random_paths, "Reverse random paths");+		add (test_coordinates, "Coordinates");+		add (test_drawing, "Pen tool");+		add (test_delete_points, "Delete edit points");+		add (test_save_backup, "Save backup");+		add (test_convert_to_quadratic_bezier_path, "Convert to quadratic path");+		add (test_notdef, "Notdef");+		add (test_merge, "Merge");+		add (test_over_path, "Over path");+		add (test_preview, "Preview");+		add (test_export, "Export");+		add (test_background_coordinates, "Background coordinates");+	}++	public static void test_background_coordinates () {+		GlyphBackgroundImage bg = new GlyphBackgroundImage ("");+		+		bg.set_position (100, 100);+		bg.set_img_offset (bg.img_offset_x, bg.img_offset_y);+		warn_if_fail (bg.img_x == 100 && bg.img_y == 100);++		bg.set_img_offset (100, 100);+		bg.set_position (bg.img_x, bg.img_y);+		warn_if_fail (bg.img_offset_x == 100 && bg.img_offset_y == 100);+	}++	public static void test_export () {+		Glyph g;+		Path p = new Path ();+		+		Tool.test_open_next_glyph ();+		g = MainWindow.get_current_glyph ();+				+		p.add (-10, -10);+		p.add (50, 50);+		p.add (10, -10);+		+		p.close ();+		g.add_path (p);	++		MenuTab.preview ();++		// TODO: run this many times on big font+		for (int i = 0; i < 10; i++) {+			ExportTool.export_all ();+			Tool.yield ();+		}		+	}+++	public static void test_preview () {+		Glyph g;+		Path p = new Path ();+		+		Tool.test_open_next_glyph ();+		g = MainWindow.get_current_glyph ();+				+		p.add (-10, 10);+		p.add (10, 10);+		p.add (10, -10);+		p.add (-10, -10);+		p.close ();+		g.add_path (p);	++		MenuTab.preview ();++		// TODO: run this many times on big font+		for (int i = 0; i < 100; i++) {+			MainWindow.get_tab_bar ().select_tab_name ("Menu");+			Tool.yield ();+			+			MainWindow.get_tab_bar ().select_tab_name ("Preview");+			Tool.yield ();+		}+		+	}++	public static void test_over_path () {+		Glyph g;+		Path p = new Path ();+		Tool pen_tool;+		+		pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");+		pen_tool.test_select_action ();+		Tool.test_open_next_glyph ();+		+		g = MainWindow.get_current_glyph ();++		pen_tool.test_click_action (1, 10, 10);+		pen_tool.test_click_action (1, 10, 10);+		pen_tool.test_click_action (1, 100, 10);+		pen_tool.test_click_action (1, 100, 100);+		pen_tool.test_click_action (1, 10, 100);+		pen_tool.test_click_action (3, 0, 0);++		warn_if_fail (g.active_paths.length () == 0);++		g.select_path (50, 50);++		warn_if_fail (g.active_paths.length () == 1);+		+		p.add (-10, 10);+		p.add (10, 10);+		p.add (10, -10);+		p.add (-10, -10);+		p.update_region_boundries ();+		g.add_path (p);+		g.close_path ();++		if (!p.is_over_coordinate (0, 0)) {+			warning ("Coordinate 0, 0 is not in path.");+		}+		+		if (!p.is_over_coordinate (-10, 10)) {+			warning ("Corner corrdinate -10, 10 is not in path.");+		}+		+		warn_if_fail (!p.is_over_coordinate (-20, -20));+		+		for (double x = -10; x <= 10; x += 0.1) {+			for (double y = 10; y <= 10; y += 0.1) {+				warn_if_fail (p.is_over_coordinate (x, y));+			} +		} +		+	}++	public static void test_merge () {+		Glyph g;+		Path p = new Path ();+		Path p2 = new Path ();+		+		Tool.test_open_next_glyph ();+		g = MainWindow.get_current_glyph ();+				+		p.add (-10, 10);+		p.add (10, 10);+		p.add (10, -10);+		p.add (-10, -10);+		p.close ();+		g.add_path (p);++		p2.add (10, 10);+		p2.add (10, -10);		+		p2.add (20, -10);+		p2.add (20, 10);+		p2.reverse ();+		p2.close ();+		g.add_path (p2);+		+		g.add_active_path (p);+		g.add_active_path (p);+		+		g.add_active_path (p);+		g.merge_all	();+	}++	public static void test_notdef () {+		Font f = Supplement.get_current_font ();+		Glyph n = f.get_not_def_character ();+		Glyph g;+		Path pn;+		+		f.add_glyph (n);+		+		Tool.test_open_next_glyph ();+		g = MainWindow.get_current_glyph ();+		foreach (Path p in n.path_list) {+			pn = p.copy ().get_quadratic_points ();+			g.path_list.append (pn);+			pn.move (50, 0);+			g.path_list.append (p.copy ());+		}+	}++	public static void test_convert_to_quadratic_bezier_path () {+		Glyph g;+		Path gqp;+		Path gqp_points;+		+		Path p = new Path ();+		Path p1 = new Path ();+		List<Path> qpl = new List<Path> (); +		+		EditPoint e0, e1, e2, e3;+		+		g = MainWindow.get_current_glyph ();+		+		// split_all_cubic_in_half+		foreach (Path gp in g.path_list) {+			gqp_points = gp.copy ();+			gqp_points.split_cubic_in_parts (gqp_points);+			gqp_points.move (50, 0);+			qpl.append (gqp_points);+		}+		+		// convert to quadratic points+		foreach (Path gp in g.path_list) {+			gqp = gp.get_quadratic_points ();+			gqp.move (100, 0);+			qpl.append (gqp);+		}++		foreach (Path gp in qpl) {+			g.add_path (gp);+		}++		Tool.test_open_next_glyph ();+		+		g = MainWindow.get_current_glyph ();+		+		p.add (-10, 10);+		p.add (10, 10);+		p.add (10, -10);+		p.add (-10, -10);+		p.close ();+		g.add_path (p);+		g.add_path (p1.get_quadratic_points ());++		e0 = new EditPoint (20, 40);+		e1 = new EditPoint (40, 40);+		e2 = new EditPoint (40, 20);+		e3 = new EditPoint (20, 20);++		p1.add_point (e0);+		p1.add_point (e1);+		p1.add_point (e2);+		p1.add_point (e3);+		p1.close ();++		e0.set_tie_handle (true);+		e1.set_tie_handle (true);+		e2.set_tie_handle (true);+		e3.set_tie_handle (true);++		e0.process_tied_handle ();+		e1.process_tied_handle ();+		e2.process_tied_handle ();+		e3.process_tied_handle ();++		g.add_path (p1);+		g.add_path (p1.get_quadratic_points ());+	}++	public static void test_overview () {+		OverView o = MainWindow.get_overview ();++		warn_if_fail (o.selected_char_is_visible ());+	+		for (int i = 0; i < 10; i++) {+			o.key_down ();+			warn_if_fail (o.selected_char_is_visible ());+		}++		for (int i = 0; i < 15; i++) {+			o.key_up ();+			warn_if_fail (o.selected_char_is_visible ());+		}++		for (int i = 0; i < 6; i++) {+			o.key_down ();+			warn_if_fail (o.selected_char_is_visible ());+		}++		for (int i = 0; i < 3; i++) {+			o.key_down ();+			warn_if_fail (o.selected_char_is_visible ());+		}+		+		for (int i = 0; i < 2000; i++) {+			o.scroll_adjustment (5);+		}++		for (int i = 0; i < 2000; i++) {+			o.scroll_adjustment (-5);+		}+	}++	public static void test_data_reader () {+		FontData fd = new FontData ();+		uint len;++		try {+			fd.add (7);+			fd.add_ulong (0x5F0F3CF5);+			fd.add_ulong (9);+			+			warn_if_fail (fd.table_data[0] == 7);+			warn_if_fail (fd.read () == 7);+			warn_if_fail (fd.read_ulong () == 0x5F0F3CF5);+			warn_if_fail (fd.read_ulong () == 9);+			+			fd = new FontData ();+			for (int16 i = 0; i < 2048; i++) {+				fd.add_short (i);+			}+			+			fd.seek (2 * 80);+			warn_if_fail (fd.read_short () == 80);+			+			fd.seek (100);+			fd.add_short (7);+			fd.seek (100);+			warn_if_fail (fd.read_short () == 7);+			+			fd.seek_end ();+			len = fd.length ();+			fd.add (0);+			warn_if_fail (len + 1 == fd.length ());+		} catch (GLib.Error e) {+			warning (e.message);+		}+	}++	public static void test_argument () {+		Argument arg = new Argument ("supplement -t \"Argument list\" --unknown -unknown --help -s");+		+		return_if_fail (arg.has_argument ("--test"));+		return_if_fail ((!) arg.get_argument ("--test") == "\"Argument list\"" );+		return_if_fail (arg.has_argument ("--unknown"));+		return_if_fail (arg.has_argument ("--help"));+		return_if_fail (arg.has_argument ("--slow"));+		return_if_fail (arg.validate () != 0);++		arg = new Argument ("supplement --test \"Argument list\"");+		return_if_fail ((!) arg.get_argument ("--test") == "\"Argument list\"" );+		return_if_fail (!arg.has_argument ("--help"));+		return_if_fail (!arg.has_argument ("--slow"));+		return_if_fail (arg.validate () == 0);+		+	}++	public static void test_glyph_ranges () {+		GlyphRange gr = new GlyphRange ();+		+		gr.add_range ('b', 'c');+		gr.add_single ('d');+		gr.add_range ('e', 'h');+		gr.add_range ('k', 'm');+		gr.add_range ('o', 'u');+		gr.add_range ('a', 'd');+		gr.add_range ('f', 'z');+		gr.add_range ('b', 'd');+			+		return_if_fail (gr.length () == 'z' - 'a' + 1);+		return_if_fail (gr.get_ranges ().length () == 1);+		return_if_fail (gr.get_ranges ().first ().data.length () == 'z' - 'a' + 1);+		+		for (unichar i = 'a'; i <= 'z'; i++) {+			uint index = i - 'a';+			string c = gr.get_char (index);+			StringBuilder s = new StringBuilder ();+			s.append_unichar (i);+			+			if (c != s.str) {+				warning (@"wrong glyph in glyph range got \"$c\" expected \"$(s.str)\" for index $(index).");+			}+		}+		+		gr = new GlyphRange ();+		gr.add_single ('a');+		gr.add_range ('c', 'e');+		gr.add_single ('◊');+		return_if_fail (gr.get_char (0) == "a");+		return_if_fail (gr.get_char (1) == "c");+		return_if_fail (gr.get_char (2) == "d");+		return_if_fail (gr.get_char (3) == "e");+		return_if_fail (gr.get_char (4) == "◊");+	}++	public static void test_glyph_table () {+		GlyphTable table = new GlyphTable ();+		List<GlyphCollection> gc = new List<GlyphCollection> ();+		List<unowned GlyphCollection> gc_copy;+		GlyphCollection g;+		+		return_if_fail (table.length () == 0);+		return_if_fail (table.get ("Some glyph") == null);+		+		// generate test data+		for (uint i = 0; i < 1000; i++) {+			gc.append (new GlyphCollection (new Glyph (@"TEST $i", i + 'a')));+		}++		// insert in random order+		gc_copy = gc.copy ();+		for (uint i = 0; gc_copy.length () > 0; i++) {+			int t = (int) ((gc_copy.length () - 1) * Random.next_double ());+			g = gc_copy.nth (t).data;+			+			if (!table.insert (g)) {+				warning (@"Failed to insert $(g.get_name ())");+				return;+			}+			+			gc_copy.remove_all (g);+			+			if (!table.validate_index ()) {+				table.print_all ();+				warning ("index is invalid");+				return;+			}+		}+		+		return_if_fail (table.length () == gc.length ());+		+		// validate table+		for (uint i = 0; i > 1000; i++) {+			g = (!) table.get (gc.nth (i).data.get_name ());+			return_if_fail (gc.nth (i).data == g);+		}+		+		// search +		for (int i = 0; i < 2000; i++) {+			int t = (int) (999 * Random.next_double ());+			if (table.get (@"TEST $t") == null) {+				table.print_all ();+				warning (@"Did't find TEST $t in glyph table.");+				return;+			}+		}++		// remove+		table.remove ("TEST 0");+		table.remove ("TEST 53");+		return_if_fail (table.get ("TEST 0") == null);+		return_if_fail (table.get ("TEST 53") == null);+		+		// search +		return_if_fail (table.get ("TEST 52") != null);+		return_if_fail (table.get ("TEST 54") != null);+	}++	public static void test_delete_points () {+		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+		tool.test_delete_points ();+	}++	public static void test_active_edit_point () {+		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+		tool.test_active_edit_point ();+	}+	+	public static void test_save_backup () {+		// TODO draw various things and assert that they are restored correctly+		Supplement.get_current_font ().save_backup ();+		Supplement.get_current_font ().restore_backup ();+	}++	public static void test_hex () {+		test_hex_conv ('H', "U+48", 72);+		test_hex_conv ('1', "U+31", 49);+		test_hex_conv ('å', "U+e5", 229);+		test_hex_conv ('◊', "U+25ca", 9674);+	}++	private static void test_hex_conv (unichar h, string sr, int r) {+		string s = Font.to_hex (h);+		unichar t = Font.to_unichar (sr);+		+		if (s != sr) warning (@"($s != \"$sr\")");+		if ((int)t != r || t != h) warning (@"$((int)t) != $r || $t != '$h'");+	}++	public static void test_coordinates () {+		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+		tool.test_coordinates ();		+	}++	public static void test_reverse_random_paths () {+		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+		tool.test_reverse_random_triangles ();		+	}++	public static void test_reverse_path () {+		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+		tool.test_reverse_path ();+	}++	public static void test_drawing () {+		Tool tool = MainWindow.get_toolbox ().get_tool ("pen_tool");+		tool.test ();+	}+		+	private void add (Callback callback, string name) {+		test_cases.append (new Test (callback, name));+	}+	+	public unowned List<Test> get_test_functions () {+		return test_cases;+	}+}	+	+}diff --git a/libbirdfont/TestSupplement.vala b/libbirdfont/TestSupplement.valanew file mode 100644index 0000000..e03b659--- /dev/null+++ b/libbirdfont/TestSupplement.vala@@ -0,0 +1,262 @@+/*
+    Copyright (C) 2012 Johan Mattsson
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace Supplement {
+
+class Test : Object {
+	public Callback callback;
+	public string name;
+	
+	public Test (Callback callback, string name) {
+		this.callback = callback;
+		this.name = name;
+	}
+}
+
+/** Class for executing tests cases. */
+class TestSupplement : GLib.Object {
+
+	const int NOT_STARTED = 0;
+	const int RUNNING = 1;
+	const int PAUSED = 2;
+	const int DONE = 3;
+	
+	static int state = NOT_STARTED;
+
+	TestCases tests;
+	
+	unowned List<Test> test_cases;
+	unowned List<Test?>? current_case = null;
+
+	unowned List<Test> passed;
+	unowned List<Test> failed;
+	unowned List<Test> skipped;
+
+	static TestSupplement? singleton = null;
+
+	bool has_failed = false;
+	bool has_skipped = false;
+		
+	static bool slow_test = false;
+	
+	string test_cases_to_run; // name of specific test case or all to run all test cases
+	
+	public TestSupplement () {
+		assert (singleton == null);
+		tests = new TestCases ();
+		test_cases = tests.get_test_functions ();
+		current_case = test_cases.first ();
+		test_cases_to_run = "All";
+		
+		from_command_line ();
+	}
+	
+	public static bool is_slow_test () {
+		return slow_test;
+	}
+	
+	public static void set_slow_test (bool s) {
+		slow_test = s;
+	}
+	
+	public static TestSupplement get_singleton () {
+		if (singleton == null) {
+			singleton = new TestSupplement ();
+		}
+		
+		return (!) singleton;
+	}
+
+	private bool has_test_case (string s) {
+		foreach (var t in test_cases) {
+			if (t.name == s) return true;
+		}	
+		
+		if (s == "" || s == "All") {
+			print ("No specific tescase given run all test cases.\n");
+			return true;
+		}
+		
+		return false;
+	}
+
+	/** Run only test specified on the command line. */
+	private void from_command_line () {
+		string? stn = Supplement.get_argument ("--test");
+	
+		if (stn != null) {
+			string st = (!) stn;
+			
+			if (!has_test_case (st)) {
+				stderr.printf (@"Test case \"$st\" does not exist.\n");
+				stderr.printf ("\nAvaliable test cases:\n");
+				
+				foreach (var t in test_cases) {
+					stderr.printf (t.name);
+					stderr.printf ("\n");
+				}
+				
+				Process.exit(1);
+			}
+
+			if (st == "All" || st == "") {
+				return;
+			} else {
+				stderr.printf  (@"Run only test case \"$st\" \n");
+			}
+			
+			test_cases_to_run = st;
+		}
+	}
+
+	public static void log (string? log_domain, LogLevelFlags log_levels, string message) {		
+		Test t = (!)((!)get_singleton ().current_case).data;
+		
+		if (log_domain != null) {
+			stderr.printf ("%s: \n", (!) log_domain);
+		}
+		
+		stderr.printf ("Testcase \"%s\" failed because:\n", t.name);
+		stderr.printf ("%s\n\n", message);	
+		
+		get_singleton ().has_failed = true;
+		
+		assert (!Supplement.fatal_wanings);
+	}
+
+	public static void @continue () {
+		int s = AtomicInt.get (ref state);
+		if (s == DONE) {
+			singleton = null;
+		}
+
+		TestSupplement t = get_singleton ();
+		
+		LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
+		Log.set_handler (null, levels, log);
+				
+		AtomicInt.set (ref state, RUNNING);
+		t.run_all_tests ();
+	}
+
+	public static bool is_running () {
+		int r = AtomicInt.get (ref state);
+		return (r == RUNNING);
+	}
+
+	public static void pause () {
+		AtomicInt.set (ref state, PAUSED);
+		Log.set_handler (null, LogLevelFlags.LEVEL_MASK, Log.default_handler);
+	}
+
+	private static void pad (int t) {
+		for (int i = 0; i < t; i++) stdout.printf (" ");
+	}
+
+	public void print_result () {
+		stdout.printf ("\n");
+		stdout.printf ("Testcase results:\n");
+
+		foreach (var t in skipped) {
+			stdout.printf ("%s", t.name);
+			pad (40 - t.name.char_count());
+			stdout.printf ("Skipped\n");
+		}
+		
+		if (skipped.length () > 0) {
+			stdout.printf ("\n");
+		}
+		
+		foreach (var t in passed) {
+			stdout.printf ("%s", t.name);
+			pad (40 - t.name.char_count());
+			stdout.printf ("Passed\n");
+		}
+		
+		foreach (var t in failed) {
+			stdout.printf ("%s", t.name);
+			pad (40 - t.name.char_count());
+			stdout.printf ("Failed\n");
+		}
+		
+		stdout.printf ("\n");
+		
+		stdout.printf ("Total %u test cases executed, %u passed and %u failed.\n", (passed.length () + failed.length ()), passed.length (), failed.length ());
+	}
+
+	/** Run tests in main loop. */
+	public void run_all_tests () {
+		var idle = new TimeoutSource (20);
+		
+		idle.set_callback(() => {
+			int s = AtomicInt.get (ref state);
+			
+			if (s != RUNNING || current_case == null) {
+				return false;
+			}
+
+			Test test = (!)((!) current_case).data;
+					
+			has_failed = false;
+			has_skipped = false;
+
+			if (test_cases_to_run != "All" && test_cases_to_run != test.name) {
+				has_skipped = true;
+			} else {
+				test.callback ();
+			}
+			
+			if (has_failed) {
+				failed.append ((!) test);
+				
+				if (Supplement.has_argument ("--exit")) {
+					print_result ();
+					Process.exit (1);
+				}
+	
+			} else if (has_skipped) {
+				skipped.append ((!) test);
+			} else {
+				passed.append ((!) test);
+			}
+
+			if (unlikely (current_case == test_cases.last ())) {
+				stdout.printf ("Finished running test suite.\n");			
+				
+				AtomicInt.set (ref state, DONE);
+				Log.set_handler (null, LogLevelFlags.LEVEL_MASK, Log.default_handler);
+
+				print_result ();
+				
+				if (Supplement.has_argument ("--exit")) {
+					print_result ();
+					Process.exit ((failed.length () == 0) ? 0 : 1);
+				}
+				return false;
+			}
+			
+			current_case = ((!) current_case).next;
+			
+			return true;
+		});
+    
+		idle.attach (null);
+	}
+
+}
+
+}
diff --git a/libbirdfont/Tool.vala b/libbirdfont/Tool.valanew file mode 100644index 0000000..6d3e31f--- /dev/null+++ b/libbirdfont/Tool.vala@@ -0,0 +1,353 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;++namespace Supplement {++public class Tool : GLib.Object {+	+	public double x = 0;+	public double y = 0;+	public double w = 0;+	public double h = 0;++	protected bool active = false;+	protected bool selected = false;+	+	double r_default_selected = 131/255.0;+	double g_default_selected = 179/255.0;+	double b_default_selected = 231/255.0;+	double a_default_selected = 1;+	+	double r_default = 185/255.0;+	double g_default = 207/255.0;+	double b_default = 231/255.0;+	double a_default = 1;+	+	double r;+	double g;+	double b;+	double a;+	+	ImageSurface? icon = null;+		+	public signal void select_action (Tool selected);+	public signal void deselect_action (Tool selected);+	+	public signal void press_action (Tool selected, int button, int x, int y);+	public signal void double_click_action (Tool selected, int button, int x, int y);+	public signal void move_action (Tool selected, int x, int y);+	public signal void release_action (Tool selected, int button, int x, int y);+	+	public signal void key_press_action (Tool selected, uint32 keyval);+	public signal void key_release_action (Tool selected, uint32 keyval);+	+	public signal void panel_press_action (Tool selected, uint button, double x, double y);+	public signal void panel_release_action (Tool selected, uint button, double x, double y);+	public signal void panel_move_action (Tool selected, double x, double y);+	+	public signal void draw_action (Tool selected, Context cr, Glyph glyph);+	+	protected string name = "";+	+	static int next_id = 1;+	+	int id;+	+	public bool new_selection = false;+	+	bool show_bg = true;+	+	string tip = "";++	// keyboard bindings+	public uint modifier_flag;+	public unichar key;+	+	private static ImageSurface? selected_button = null;+	private static ImageSurface? active_selected_button = null;+	private static ImageSurface? deselected_button = null;+	private static ImageSurface? active_deselected_button = null;+	+	/** Create tool with a certain name and load icon "name".png */+	public Tool (string? name = null, string tip = "", unichar key = '\0', uint modifier_flag = 0) {+		this.tip = tip;+		+		if (selected_button == null) {+			selected_button = Icons.get_icon ("tool_button_selected.png");+			active_selected_button = Icons.get_icon ("tool_button_selected_active.png");+			deselected_button = Icons.get_icon ("tool_button_deselected.png");+			active_deselected_button = Icons.get_icon ("tool_button_deselected_active.png");+		}+		+		if (name != null) {+			set_icon ((!) name);+			this.name = (!) name;+		}+		+		this.key = key;+		this.modifier_flag = modifier_flag;+				+		id = next_id;+		next_id++;+		+		panel_press_action.connect ((self, button, x, y) => {+			MainWindow.get_tool_tip ().set_text_from_tool ();+		});+		+		r = r_default;+		g = g_default;+		b = b_default;+		a = a_default;+	}+	+	public void set_icon (string name) {+		StringBuilder n = new StringBuilder ();+		n.append ((!) name);+		n.append (".png");+		icon = Icons.get_icon (n.str);+	}+	+	public bool is_active () {+		return active;+	}+	+	public void set_show_background (bool bg) {+		show_bg = bg;+	}+	+	public int get_id () {+		return id;+	}++	public string get_name () {+		return name;+	}++	public bool is_selected () {+			return selected;+	}+		+	public virtual void set_properties (double tx, double ty, double tw, double th) {+		x = tx;+		y = ty;+		w = tw;+		h = th;+	}++	public string get_tip () {+		return tip;+	}++	public bool is_over (double xp, double yp) {	+		return (x <= xp <= x + w  + 12 + 15 && y + 15 <= yp <= y + w + 12 + 30);  +	}+	+	public bool set_selected (bool a) {+		TooltipArea? tpa = null;					+		tpa = MainWindow.get_tool_tip ();+							+		new_selection = true;+		selected = a;+		set_active (a);+		+		if (!a) {+			deselect_action (this);+		} else {+			((!)tpa).update_text ();+		}+		+		return true;+	}+	+	public bool set_active (bool ac) {+		bool ret = (active != ac);++		if (selected) {+			if (ac) {+				r = r_default_selected;+				g = g_default_selected;+				b = b_default_selected;+				a = a_default_selected * 0.5;+			} else {+				r = r_default_selected;+				g = g_default_selected;+				b = b_default_selected;+				a = a_default_selected;+			}			+		} else {+			if (ac) {+				r = r_default;+				g = g_default;+				b = b_default;+				a = a_default * 0.2;+			} else {+				r = r_default;+				g = g_default;+				b = b_default;+				a = a_default;+			}+		}++		active = ac;+		return ret;+	}+		+	public virtual void draw (Context cr) {+		double xt = x + 3 + w;+		double yt = y + h + 17;++		double bgx, bgy;+		+		bgx = xt - 6;+		bgy = yt - 7;++		cr.save ();++		// Button in four states+		if (selected && selected_button != null) {+			cr.set_source_surface ((!) selected_button, bgx, bgy);+			cr.paint ();+		}++		if (selected && active && active_selected_button != null) {+			cr.set_source_surface ((!) active_selected_button, bgx, bgy);+			cr.paint ();+		}++		if (!selected && deselected_button != null) {+			cr.set_source_surface ((!) deselected_button, bgx, bgy);+			cr.paint ();+		}++		if (!selected && active && active_deselected_button != null) {+			cr.set_source_surface ((!) active_deselected_button, bgx, bgy);+			cr.paint ();+		}+				+		if (icon != null) {+			ImageSurface i = (!) icon;+			+			if (likely (i.status () == Cairo.Status.SUCCESS)) {+				cr.set_source_surface (i, xt + (15 - i.get_width () / 2) - 5.7, yt + (15 - i.get_height ()) / 2.0);+				cr.paint ();+			} else {+				warning (@"Falied to load icon for $name");+			}+		}+		+		cr.restore ();+	}++	/** Run a test case for this tool. */+	public virtual bool test () {+		stderr.printf (@"$(get_name ()) does not have a test case, implement one by overriding test method in Tool base class.");+		return false;+	}+	+	/** Help function to test button press actions. */+	public void test_click_action (int b, int x, int y) {+		Tool.yield ();+		press_action (this, b, x, y);+		+		Tool.yield ();+		release_action (this, b, x, y);+	}++	/** Help function to test select action for this tool. */+	public void test_select_action () {+		Tool.yield ();+		MainWindow.get_toolbox ().select_tool (this);+	}++	public void test_move_action (int x, int y) {+		Tool.yield ();+		move_action (this, x, y);+	}++	public void test_press_action (int b, int x, int y) {+		Tool.yield ();+		press_action (this, b, x, y);+	}++	public void test_release_action (int b, int x, int y) {+		Tool.yield ();+		release_action (this, b, x, y);+	}++	public static void test_open_next_glyph () {+		Tool.yield ();+		OverView o = MainWindow.get_overview ();+		+		MainWindow.get_tab_bar ().select_overview ();+		MainWindow.get_toolbox ().select_tool_by_name ("utf_8");+		+		o.select_next_glyph ();+		Tool.yield ();+		+		o.open_current_glyph ();+		Tool.yield ();+	}+	+	/** Run pending events in main loop before continue. */+	public static void @yield () {+		int t = 0;+		var time = new TimeoutSource(500);+		bool timeout;++		if (TestSupplement.is_slow_test ()) {+			timeout = false;+			+			time.set_callback(() => {+				timeout = true;+				return false;+			});++			time.attach(null);		+		} else {+			timeout = true;+		}+    +		unowned MainContext c = MainContext.default ();+		bool a = c.acquire ();+		+		while (unlikely (!a)) {+			warning ("Failed to acquire main loop.\n");+			return;+		}++		while (c.pending () || TestSupplement.is_slow_test ()) {+			c.iteration (true);+			t++;++			if (!c.pending () && TestSupplement.is_slow_test ()) {+				if (timeout) break;+			}+						+			if (unlikely (t > 100000)) {+				assert (false);+				break;+			}+		}+		+		c.release ();+	}+	+}++}diff --git a/libbirdfont/Toolbox.vala b/libbirdfont/Toolbox.valanew file mode 100644index 0000000..b5c6089--- /dev/null+++ b/libbirdfont/Toolbox.vala@@ -0,0 +1,868 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Math;++namespace Supplement {++public class Toolbox : GLib.Object  {+	GlyphCanvas glyph_canvas;+	+	bool expanded = true;+	+	ToolboxExpander toolbox_expander;+	+	public List<Expander> expanders;+	+	Tool current_tool = new Tool ("no_icon");+	+	Expander draw_tools;+	Expander grid_expander;+		+	BackgroundTool move_background;+	CutBackgroundTool cut_background;+	+	public Tool press_tool = new Tool (null); // activate the pressed button on release+	+	public SpinButton background_scale = new SpinButton ();+	public SpinButton precision;+	+	public signal void redraw (int x, int y, int w, int h);+	+	public int allocation_width = 0;+	public int allocation_height = 0;+	+	ImageSurface? toolbox_background = null;+	+	public Toolbox (GlyphCanvas main_glyph_canvas) {+		glyph_canvas = main_glyph_canvas;++		toolbox_background = Icons.get_icon ("toolbox_background.png");++		toolbox_expander = new ToolboxExpander ();+		+		add_expander (toolbox_expander);+		+		draw_tools = new Expander ();+			+		Expander path_tool_modifiers = new Expander ();+		Expander draw_tool_modifiers = new Expander ();+		Expander characterset_tools = new Expander ();+		Expander test_tools = new Expander ();+		Expander guideline_tools = new Expander ();+		Expander view_tools = new Expander ();+		Expander grid = new Expander ();+		Expander background_tools = new Expander ();+		Expander trace = new Expander ();+		+		grid_expander = grid;++		// Draw tools+		PenTool pen_tool = new PenTool ("pen_tool");+		draw_tools.add_tool (pen_tool);+	+		ZoomTool zoom_tool = new ZoomTool ("zoom_tool");+		draw_tools.add_tool (zoom_tool);++		CutTool cut_tool = new CutTool ("cut");+		draw_tools.add_tool (cut_tool);	++		Tool move_tool = new MoveTool ("move");+		draw_tools.add_tool (move_tool);++		Tool shrink_tool = new ShrinkTool ("shrink");+		// Fixa: draw_tools.add_tool (shrink_tool);	+		+		// Draw tool modifiers+		// DELETE tools:+		+		Tool new_point = new Tool ("new_point", _("Right click to add new point, left click to move points") + " " + _("and double click to add new point on path"), 'a');+		new_point.select_action.connect ((self) => {+				select_draw_tool ();+			});+		+		// draw_tool_modifiers.add_tool (new_point);++		Tool insert_point_on_path = new Tool ("insert_point_on_path", _("Add new point on path"), 'n');+		insert_point_on_path.select_action.connect ((self) => {+			select_draw_tool ();+			pen_tool.begin_from_new_point_on_path ();+		});+		// draw_tool_modifiers.add_tool (insert_point_on_path);+		+		if (Supplement.experimental) {+			Tool new_point_on_path = new Tool ("new_point_on_path", _("Begin new line from point on path"), 'n');+			new_point_on_path.select_action.connect ((self) => {+					select_draw_tool ();+					pen_tool.begin_from_new_point_on_path ();+				});+			draw_tool_modifiers.add_tool (new_point_on_path);	+		}++		Tool tie_editpoint_tool = new Tool ("tie_point", _("Tie curve handles for selected edit point"), 'w');+		tie_editpoint_tool.select_action.connect ((self) => {+			bool tie;+			+			select_draw_tool ();+			+			foreach (EditPoint ep in pen_tool.selected_points) {+				tie = !ep.tie_handles;++				if (tie) {+					ep.process_tied_handle ();+				}++				ep.set_tie_handle (tie);+				+				MainWindow.get_current_glyph ().update_view ();				+			}+		});+		draw_tool_modifiers.add_tool (tie_editpoint_tool);	++		Tool tie_x_or_y = new Tool ("tie_x_or_y", _("Tie coordinates to previous edit point"), 'q');+		tie_x_or_y.select_action.connect((self) => {+			select_draw_tool ();+			pen_tool.tie_x_or_y_coordinates = !pen_tool.tie_x_or_y_coordinates;+		});+		// Fixa: draw_tool_modifiers.add_tool (tie_x_or_y);	++		Tool erase_tool = new Tool ("erase_tool", _("Delete edit points"));+		erase_tool.select_action.connect((self) => {+			select_draw_tool ();+		});+		// draw_tool_modifiers.add_tool (erase_tool);	++		// adjust precision+		string precision_value = Preferences.get ("precision");+		+		precision = new SpinButton ("precision", _("Set precision"));+		+		if (precision_value != "") {+			precision.set_value (precision_value);+		} else {+			precision.set_value_round (1);+		}+		+		precision.new_value_action.connect ((self) => {+			select_tool (precision);+			+			Preferences.set ("precision", self.get_display_value ());+			redraw ((int) precision.x, (int) precision.y, 70, 70);+		});++		precision.select_action.connect((self) => {+			pen_tool.set_precision (((SpinButton)self).get_value ());+		});+		+		precision.set_min (0.001);+		precision.set_max (1);+		+		draw_tool_modifiers.add_tool (precision);+				+		// path tools+		Tool union_paths_tool = new MergeTool ("union_paths");+		path_tool_modifiers.add_tool (union_paths_tool);+		+		Tool reverse_path_tool = new Tool ("reverse_path", _("Create counter from outline"), 'r');+		reverse_path_tool.select_action.connect ((self) => {+			Glyph g = MainWindow.get_current_glyph ();+			+			foreach (Path p in g.active_paths) {+				p.reverse ();+			}+		+			g.redraw_area (0, 0, g.allocation.width, g.allocation.height);+		});+		path_tool_modifiers.add_tool (reverse_path_tool);++		Tool move_layer = new Tool ("move_layer", _("Move to path to the bottom layer"), 'd');+		move_layer.select_action.connect ((self) => {+			Glyph g = MainWindow.get_current_glyph ();++			foreach (Path p in g.active_paths) {+				g.path_list.remove (p);+				g.path_list.prepend (p);+			}+		});+		path_tool_modifiers.add_tool (move_layer);+		+		// Character set tools+		Tool full_unicode = new Tool ("utf_8", _("Show full unicode characters set"), 'f', CTRL);+		full_unicode.select_action.connect ((self) => {+				MainWindow.get_tab_bar ().add_unique_tab (new OverView (), 100, false);	+				OverView o = MainWindow.get_overview ();+				GlyphRange gr = new GlyphRange ();+				gr.use_full_unicode_range ();+				o.set_glyph_range (gr);+				MainWindow.get_tab_bar ().select_tab_name ("Overview");+			});+		characterset_tools.add_tool (full_unicode);++		Tool custom_character_set = new Tool ("custom_character_set", _("Show default characters set"), 'r', CTRL);+		custom_character_set.select_action.connect ((self) => {+			MainWindow.get_tab_bar ().add_unique_tab (new OverView (), 100, false);+			OverView o = MainWindow.get_overview ();+			GlyphRange gr = new GlyphRange ();+			gr.use_default_range ();+			o.set_glyph_range (gr);+			MainWindow.get_tab_bar ().select_tab_name ("Overview");+		});+		characterset_tools.add_tool (custom_character_set);++		Tool avalilable_characters = new Tool ("available_characters", _("Show all characters in font"), 'd', CTRL);+		avalilable_characters.select_action.connect ((self) => {+			MainWindow.get_tab_bar ().add_unique_tab (new OverView (), 100, false);+			OverView o = MainWindow.get_overview ();+			o.display_all_available_glyphs ();+			MainWindow.get_tab_bar ().select_tab_name ("Overview");+		});+		characterset_tools.add_tool (avalilable_characters);++		Tool delete_glyph = new Tool ("delete_selected_glyph", _("Delete selected glyph"));+		delete_glyph.select_action.connect ((self) => {+			OverView o = MainWindow.get_overview ();+			+			if (MainWindow.get_current_display () is OverView) {+				o.delete_selected_glyph ();+			}+			+			MainWindow.get_tab_bar ().select_tab_name ("Overview");+		});+		characterset_tools.add_tool (delete_glyph);+						+		if (Supplement.has_argument ("--test")) {+			Tool test_case = new Tool ("test_case");+			test_case.select_action.connect((self) => {+					if (self.is_selected ()) {+						if (TestSupplement.is_running ()) {+							TestSupplement.pause ();+						} else {+							TestSupplement.continue ();+						}+					}+				});+			test_tools.add_tool (test_case);++			Tool slow_test = new Tool ("slow_test");+			slow_test.select_action.connect((self) => {+					bool s = TestSupplement.is_slow_test ();+					TestSupplement.set_slow_test (!s);+					s = TestSupplement.is_slow_test ();+					self.set_selected (s);+				});+		+			test_tools.add_tool (slow_test);+					+			// Run from commad line+			string? st = Supplement.get_argument ("--test");+			if (st != null && ((!)st).char_count () > 0) {+				IdleSource idle = new IdleSource ();++				idle.set_callback (() => {			+					select_tool (test_case);+					return false;+				});+				+				idle.attach (null);+			}+			+			if (Supplement.has_argument ("--slow")) {+				select_tool (slow_test);+			}+			+		}+		+		// help lines, grid and other guidlines+		Tool help_lines = new Tool ("help_lines", "Show help lines", 'l');+		help_lines.select_action.connect ((self) => {+				bool h;+				h = glyph_canvas.get_current_glyph ().get_show_help_lines ();+				glyph_canvas.get_current_glyph ().set_show_help_lines (!h);+				self.set_selected (!h);+				glyph_canvas.get_current_glyph ().redraw_help_lines ();+			});	++		guideline_tools.add_tool (help_lines);++		Tool xheight_help_lines = new Tool ("show_xheight_helplines", _("Show help lines for x-height and baseline"), 'x');+		xheight_help_lines.select_action.connect ((self) => {+			Glyph g = MainWindow.get_current_glyph ();+			bool v = !g.get_xheight_lines_visible ();+			g.set_xheight_lines_visible (v);+			self.set_selected (v);+			MainWindow.get_glyph_canvas ().redraw ();+			+			if (v && !help_lines.is_selected ()) {+				select_tool (help_lines);+			}+			+		});+		guideline_tools.add_tool (xheight_help_lines);++		Tool background_help_lines = new Tool ("background_help_lines", _("Show help lines at top and bottom margin"), 't');+		background_help_lines.select_action.connect ((self) => {+			Glyph g = MainWindow.get_current_glyph ();+			bool v = !g.get_margin_lines_visible ();+			g.set_margin_lines_visible (v);+			self.set_selected (v);+			MainWindow.get_glyph_canvas ().redraw ();+			+			if (v && !help_lines.is_selected ()) {+				select_tool (help_lines);+			}+		});+		guideline_tools.add_tool (background_help_lines);++		Tool new_grid = new GridTool ("new_grid");+		guideline_tools.add_tool (new_grid);++		// Zoom tools +		Tool zoom_in = new Tool ("zoom_in", _("Zoom in"), '+', CTRL);+		zoom_in.select_action.connect ((self) => {+			zoom_tool.store_current_view ();+			glyph_canvas.get_current_display ().zoom_in ();+		});+		view_tools.add_tool (zoom_in);++		Tool zoom_out = new Tool ("zoom_out", _("Zoom out"), '-', CTRL);+		zoom_out.select_action.connect ((self) => {+			zoom_tool.store_current_view ();+			glyph_canvas.get_current_display ().zoom_out ();+		});+		view_tools.add_tool (zoom_out);++		Tool reset_zoom = new Tool ("zoom_1_1", _("Zoom to scale 1:1"), '0', CTRL);+		reset_zoom.select_action.connect ((self) => {+				zoom_tool.store_current_view ();+				glyph_canvas.get_current_display ().reset_zoom ();+				glyph_canvas.redraw_area(0, 0, glyph_canvas.allocation.width, glyph_canvas.allocation.height);+			});+		view_tools.add_tool (reset_zoom);++		Tool full_glyph = new Tool ("full_glyph", _("Show full glyph"));+		full_glyph.select_action.connect((self) => {+			zoom_tool.store_current_view ();+			zoom_tool.zoom_full_glyph ();+		});+		view_tools.add_tool (full_glyph);++		Tool zoom_boundries = new Tool ("zoom_boundries", _("Zoom in at region boundries"), 'v');+		zoom_boundries.select_action.connect((self) => {+			zoom_tool.store_current_view ();+			glyph_canvas.get_current_display ().zoom_max ();+		});+		view_tools.add_tool (zoom_boundries);++		Tool zoom_bg = new Tool ("zoom_background_image", _("Zoom in background image"), 'b');+		zoom_bg.select_action.connect((self) => {+			if (MainWindow.get_current_glyph ().get_background_image () != null) {+				zoom_tool.store_current_view ();					+				glyph_canvas.get_current_display ().reset_zoom ();+				+				zoom_tool.zoom_full_background_image ();+				+				glyph_canvas.redraw_area(0, 0, glyph_canvas.allocation.width, glyph_canvas.allocation.height);+			}+		});+		view_tools.add_tool (zoom_bg);++		Tool zoom_prev = new Tool ("prev", _("Previous view"), 'j', CTRL);+		zoom_prev.select_action.connect((self) => {+			zoom_tool.previous_view ();+		});+		view_tools.add_tool (zoom_prev);++		Tool zoom_next = new Tool ("next", _("Next view"), 'l', CTRL);+		zoom_next.select_action.connect((self) => {+			zoom_tool.next_view ();+		});+		view_tools.add_tool (zoom_next);+				+		// background tools+		background_scale = new SpinButton ("scale_background", _("Set size for background image"));+		background_scale.set_value_round (1);+		+		background_scale.new_value_action.connect((self) => {+			background_scale.select_action (self);+		});+		+		background_scale.select_action.connect((self) => {+			SpinButton sb = (SpinButton) self;+			Glyph g = MainWindow.get_current_glyph ();+			GlyphBackgroundImage? img = g.get_background_image ();+			double s = sb.get_value ();+			+			if (img != null) {+				((!)img).set_img_scale (s, s);+			}+			+			MainWindow.get_glyph_canvas ().redraw ();+		});+		+		background_tools.add_tool (background_scale);		++		BackgroundTool move_background = new BackgroundTool ("move_background");			+		background_tools.add_tool (move_background);+		this.move_background = move_background;+		+		CutBackgroundTool cut_background = new CutBackgroundTool ("cut_background");+		background_tools.add_tool (cut_background);+		this.cut_background = cut_background;+		+		Tool show_bg = new Tool ("show_background", _("Show/hide background image"));+		show_bg.select_action.connect ((self) => {+			Glyph g = MainWindow.get_current_glyph ();+			g.set_background_visible (!g.get_background_visible ());+			MainWindow.get_glyph_canvas ().redraw ();+		});+		background_tools.add_tool (show_bg);++		Tool bg_selection = new Tool ("insert_background", _("Insert a new background image"));+		bg_selection.select_action.connect((self) => {+			Glyph? g = null;+			FontDisplay fd = MainWindow.get_current_display ();+			TooltipArea tp = MainWindow.get_tool_tip ();+			tp.show_text (_("Creating thumbnails"));+			+			bg_selection.yield ();+		+			if (fd is Glyph) {+				g = (Glyph) fd;+			} else {+				return;+			}+			+			BackgroundSelection bgs = new BackgroundSelection ();+			MainWindow.get_tab_bar ().add_unique_tab (bgs , 120, false);+			+			tp.show_text ("");+		});+		+		bg_selection.set_show_background (true);+		background_tools.add_tool (bg_selection);+		+		SpinButton background_contrast = new SpinButton ("background_contrast", _("Set contrast for background image"));+		background_contrast.set_value_round (1);++		background_contrast.new_value_action.connect ((self) => {+			background_contrast.select_action (self);+		});+		+		background_contrast.select_action.connect ((self) => {		+			Glyph g = MainWindow.get_current_glyph ();+			GlyphBackgroundImage? bg = g.get_background_image ();+			GlyphBackgroundImage b;+			+			if (bg != null) {+				b = (!) bg;+				b.set_contrast (background_contrast.get_value ());+			}+		});+		+		// Fixa: background_tools.add_tool (background_contrast);++		SpinButton background_threshold = new SpinButton ("background_threshold", _("Set threshold"));+		background_threshold.set_value_round (1);++		background_threshold.new_value_action.connect ((self) => {+			background_threshold.select_action (self);+		});+		+		background_threshold.select_action.connect ((self) => {		+			Glyph g = MainWindow.get_current_glyph ();+			GlyphBackgroundImage? bg = g.get_background_image ();+			GlyphBackgroundImage b;+			+			if (bg != null) {+				b = (!) bg;+				b.set_threshold (background_threshold.get_value ());+			}+			+		});+		trace.add_tool (background_threshold);+		+		draw_tools.set_open (true);+		draw_tool_modifiers.set_open (true);+		path_tool_modifiers.set_open (true);+		view_tools.set_open (true);+		grid.set_open (true);+		characterset_tools.set_open (true);+		test_tools.set_open (true);+		guideline_tools.set_open (true);+		background_tools.set_open (true);+		trace.set_open (true);+		+		add_expander (draw_tools);+		add_expander (draw_tool_modifiers);+		add_expander (path_tool_modifiers);	+		+		add_expander (characterset_tools);+		add_expander (guideline_tools);+		add_expander (grid);+		add_expander (view_tools);+		add_expander (background_tools);+		+		// Fixa: add_expander (trace);+		+		add_expander (test_tools);+		+		draw_tools.set_persistent (true);+		draw_tools.set_unique (true);+		+		draw_tool_modifiers.set_persistent (true);+		draw_tool_modifiers.set_unique (true);++		path_tool_modifiers.set_persistent (false);+		path_tool_modifiers.set_unique (false);++		characterset_tools.set_persistent (true);+		characterset_tools.set_unique (true);+		+		test_tools.set_persistent (true);+	+		guideline_tools.set_persistent (true);+		guideline_tools.set_unique (false);+		+		grid.set_persistent (true);+		grid.set_unique (true);+		grid.set_open (false);+		+		background_tools.set_persistent (true);+		background_tools.set_unique (true);+		+		trace.set_persistent (false);+		trace.set_unique (false);++		update_expanders ();+		reset_active_tool ();++		// Default selection+		var idle = new IdleSource();+		idle.set_callback (() => {+			new_point.set_selected (true);+			pen_tool.set_selected (true);+			select_draw_tool ();			+			+			if (glyph_canvas.get_current_glyph ().get_show_help_lines ()) {+				help_lines.set_selected (true);+				help_lines.set_active (false);+			}++			add_new_grid ();+			add_new_grid ();+			+			move (0, 0);+			+			return false;+		});+		+		idle.attach (null);+	}+	+	public void key_press (uint keyval) {+		print ("key_press\n");+		foreach (var exp in MainWindow.get_toolbox ().expanders) {+			foreach (Tool t in exp.tool) {+				t.set_active (false);+				+				if (t.key == keyval && t.modifier_flag == NONE) {+					MainWindow.get_toolbox ().select_tool (t);+				}+			}+		}+	}+	+	public void press (uint button, double x, double y) {+		foreach (var exp in expanders) {+			foreach (Tool t in exp.tool) {+				if (t.is_over (x, y)) {+					t.panel_press_action (t, button, x, y);+					press_tool = t;+				}+			}+		}+	}+	+	public void release (uint button, double x, double y) {+		foreach (var exp in expanders) {+			if (exp.is_over (x, y)) {+				exp.set_open (! exp.is_open ());					+				update_expanders ();			+				redraw ((int) exp.x - 10, (int) exp.y - 10, allocation_width, (int) (allocation_height - exp.y + 10));+			}+			+			if (exp.is_open ()) {+				foreach (Tool t in exp.tool) {+					bool active = t.is_over (x, y);+					+					if (active) {+						if (press_tool == t) {+							select_tool (t);+						}+					}+					+					t.panel_release_action (t, button, x, y);+				}+			}+		}+	}+	+	private void select_draw_tool () {+		select_tool_by_name ("pen_tool");+	}+	+	public void move (double x, double y) {+		bool update;+		bool a;+		foreach (var exp in expanders) {+			a = exp.is_over (x, y);+			update = exp.set_active (a);+			+			if (update) {+				MainWindow.get_toolbox ().redraw ((int) exp.x - 10, (int) exp.y - 10, (int) (exp.x + exp.w + 10), (int) (exp.y + exp.h + 10));+			}+			+			if (exp.is_open ()) {+				foreach (Tool t in exp.tool) {+					bool active = t.is_over (x, y);+					TooltipArea? tpa = null;+					+					update = t.set_active (active);+					tpa = MainWindow.get_tool_tip ();+					+					if (active && tpa != null) {+						((!)tpa).update_text ();+					}+					+					if (update) {+						MainWindow.get_toolbox ().redraw (0, 0, allocation_width, allocation_height);+					}+					+					t.panel_move_action (t, x, y);+				}+			}+		}+	}+	+	public void remove_all_grid_buttons () {+		while (grid_expander.tool.length () > 0) {+			grid_expander.tool.remove_link (grid_expander.tool.last ());+		}+		+		while (GridTool.sizes.length () > 0) {+			GridTool.sizes.remove_link (GridTool.sizes.first ());+		}+		+		grid_expander.set_open (true);+		update_expanders ();+		redraw (0, 0, allocation_width, allocation_height);		+	}+	+	public void parse_grid (string spin_button_value) {+		SpinButton sb = add_new_grid ();+		sb.set_value (spin_button_value);+		select_tool (sb);+	}++	public SpinButton add_new_grid () {+		SpinButton grid_width = new SpinButton ("grid_width", _("Set size for grid"));+		+		grid_width.new_value_action.connect((self) => {+			grid_width.select_action (grid_width);+		});++		grid_width.select_action.connect((self) => {+			SpinButton sb = (SpinButton) self;+			GridTool.set_grid_width (sb.get_value ());+			MainWindow.get_glyph_canvas ().redraw ();+		});+				+		grid_expander.add_tool (grid_width);++		GridTool.sizes.append (grid_width);++		grid_expander.set_open (true);+		update_expanders ();+		+		redraw (0, 0, allocation_width, allocation_height);+		+		select_tool (grid_width);+		grid_width.set_active (false);+		+		return grid_width;+	}++	public void remove_current_grid () {+		Tool grid_width;+		+		foreach (Tool t in grid_expander.tool) {+			if (t.is_selected () && t is SpinButton) {+				GridTool.sizes.remove ((SpinButton)t);+				grid_expander.tool.remove (t);+				break;+			}+		}+		+		if (grid_expander.tool.length () > 0) {+			grid_width = grid_expander.tool.last ().data;+			select_tool (grid_width);+			grid_width.set_active (false);+		}+		+		update_expanders ();+		redraw (0, 0, allocation_width, allocation_height);+	}++	public void reset_active_tool () {+		foreach (var exp in expanders) {+			foreach (Tool t in exp.tool) {+				t.set_active (false);+			}+		}+	}++	public Tool? get_active_tool () {+		foreach (var exp in expanders) {+			foreach (Tool t in exp.tool) {+				if (t.is_active ()) {+					return t;+				}+			}+		}+		+		return null;+	}++	public Tool get_current_tool () {+		return current_tool;+	}+	+	public void select_tool (Tool tool) {+		foreach (var exp in expanders) {+			+			foreach (Tool t in exp.tool) {+				if (tool.get_id () == t.get_id ()) {+					exp.set_open (true);+					+					bool update;+					+					update = tool.set_selected (true);+					update = tool.set_active (true);+					+					tool.select_action (tool); // execute command+					+					if (update) {							+						redraw ((int) exp.x - 10, (int) exp.y - 10, allocation_width, (int) (allocation_height - exp.y + 10));+					}+					+					if (exp == draw_tools || t == move_background || t == cut_background) {+						current_tool = tool;+					}+				}+			}+			+		}+	}+	+	public Tool get_tool (string name) {+		foreach (var e in expanders) {+			foreach (var t in e.tool) {+				if (t.get_name () == name) {+					return t;+				}+			}+		}++		if (name == cut_background.get_name ()) {+			return cut_background;+		}+				+		warning ("No tool found for name \"%s\".\n", name);+		+		return new Tool ("no_icon");+	}+	+	public static void select_tool_by_name (string name) {+		Toolbox b = MainWindow.get_toolbox ();+		b.select_tool (b.get_tool (name));+	}+		+	private void update_expanders () {+		Expander? p = null; +		Expander pp;+		foreach (var e in expanders) {+			if (p != null) {+				pp = (!) p;+				e.set_offset (pp.y + pp.margin + 12);+			}+			+			p = e;+		}+	}+	+	private void add_expander (Expander e) {+		expanders.append (e);+	}+	+	private void draw_expanders (int w, int h, Context cr) {+		foreach (Expander e in expanders) {+			e.draw (w, h, cr);+			if (e.is_open ()) {+				e.draw_content (w, h, cr);+			}+		}+	}+	+	public void draw (int w, int h, Context cr) { +		cr.save ();+		+		cr.rectangle(0, 0, w, h);+		cr.set_line_width(0);+		cr.set_source_rgba(255/255.0, 255/255.0, 255/255.0, 1);+		cr.fill();++		for (int i = 0; i < w; i++) {+			cr.set_source_surface ((!) toolbox_background, i, 0);+			cr.paint ();+		}++		cr.rectangle(0, 0, 1, h);+		cr.set_line_width(0);+		cr.set_source_rgba(0/255.0, 0/255.0, 0/255.0, 1);+		cr.fill();+		+		draw_expanders (w, h, cr);+		+		cr.restore ();+	}+}++class ToolboxExpander : Expander {+	+	public ToolboxExpander () {+	}+	+	public override bool set_open (bool o) {+		bool r = (open != o);+		rotation = (o) ? 0 : Math.PI;+		open = o;+		return r;+	}+}++}diff --git a/libbirdfont/TooltipArea.vala b/libbirdfont/TooltipArea.valanew file mode 100644index 0000000..c70d7c5--- /dev/null+++ b/libbirdfont/TooltipArea.vala@@ -0,0 +1,122 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Gdk;+using Gtk;++namespace Supplement {++public class TooltipArea : GLib.Object {++	string tool_tip;+	public signal void redraw ();++	public TooltipArea () {+		set_text_from_tool ();+	}++	public void update_text () {+		set_text_from_current_tool ();+		redraw ();+	}+	+	public void show_text (string text) {+		tool_tip = text;+		redraw ();+	}+	+	public void set_text_from_tool () {+		set_text_from_current_tool ();+		redraw ();+	}+	+	private void set_text_from_current_tool () {+		Toolbox? tb = MainWindow.get_toolbox ();+		Tool? t;+		Tool tool;+		StringBuilder sb;+		+		if (tb == null) {+			return;+		}+		+		t = ((!)tb).get_active_tool ();+		+		if (t == null) {+			return;+		}+		+		tool = (!) t;+		+		if (tool.key != '\0') {+			sb = new StringBuilder ();++			sb.append ("(");+			+			if (tool.modifier_flag == CTRL) {+				sb.append ("Ctrl+");+			} else if (tool.modifier_flag == SHIFT) {+				sb.append ("Shift+");+			} ++			sb.append_unichar (tool.key);+			sb.append (") ");+			sb.append (tool.get_tip ());+			+			show_text (sb.str);+		} else {+			show_text (tool.get_tip ());+		}+		+	}+	+	public void draw (Context cr, Allocation alloc) {+		cr.save ();+		cr.rectangle (0, 0, alloc.width, alloc.height);+		cr.set_line_width (0);+		cr.set_source_rgba (183/255.0, 200/255.0, 223/255.0, 1);+		cr.fill_preserve ();+		cr.stroke ();+		cr.restore ();++		cr.save ();+		cr.rectangle (0, 0, alloc.width, 1);+		cr.set_line_width (0);+		cr.set_source_rgba (107/255.0, 127/255.0, 168/255.0, 1);+		cr.fill_preserve ();+		cr.stroke ();+		cr.restore ();+		+		cr.save ();+		cr.rectangle (0, 1, alloc.width, 1);+		cr.set_line_width (0);+		cr.set_source_rgba (145/255.0, 160/255.0, 190/255.0, 1);+		cr.fill_preserve ();+		cr.stroke ();+		cr.restore ();+		+		cr.save ();+		cr.set_font_size (14);+		cr.move_to (5, 15);+		cr.show_text (tool_tip);+		cr.restore ();++	}+}++}diff --git a/libbirdfont/UniRange.vala b/libbirdfont/UniRange.valanew file mode 100644index 0000000..fcfdc11--- /dev/null+++ b/libbirdfont/UniRange.vala@@ -0,0 +1,49 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {+	+public class UniRange : GLib.Object {+	+	public unichar start;+	public unichar stop;+	+	public UniRange (unichar start, unichar stop) {+		this.start = start;+		this.stop = stop;+	}+	+	public unichar length () {+		return stop - start + 1;+	}++	public unichar get_char (unichar index) {+		unichar result = start + index;+		+		if (unlikely (index < 0)) {+			warning ("Index is negative in UniRange.");+		}+		+		if (unlikely (!(start <= result <= stop))) {+			warning ("Index out of range in UniRange (%u <= %u <= %u) (index: %u)\n", start, result, stop, index);+		}+		+		return result;+	}+}++}diff --git a/libbirdfont/UserActions.vala b/libbirdfont/UserActions.valanew file mode 100644index 0000000..d7946eb--- /dev/null+++ b/libbirdfont/UserActions.vala@@ -0,0 +1,52 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++/** User interface action for unit testing and undo command.+ */+interface UserActions {+	public abstract void do_action ();+	public abstract void undo ();+	public abstract void reundo ();+	public abstract void test ();+}++interface UserClickActions {+	public abstract void do_click (int button, int x, int y);+}++class NewPointAction : UserActions, UserClickActions {++	public void do_action () {+	}++	public void undo () {+	}+	+	public void reundo () {+	}+	+	public void do_click (int button, int x, int y) {+	}+	+	public void test () {+	}+	+}++}diff --git a/libbirdfont/VersionList.vala b/libbirdfont/VersionList.valanew file mode 100644index 0000000..3f11a88--- /dev/null+++ b/libbirdfont/VersionList.vala@@ -0,0 +1,113 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Cairo;+using Math;++namespace Supplement {++public class VersionList : DropMenu {+	int versions = 1;+	int current_version = 0;+	public List<Glyph> glyphs = new List<Glyph> ();+	public static bool suppress_signal = false;++	public VersionList (Glyph? g = null) {+		base ("version");+		set_direction (MenuDirection.POP_UP);+		+		MenuAction ma = add_item (_("New version"));+		ma.action = (self) => {+			return_if_fail (self.parent != null);+			return_if_fail (glyphs.length () > 0);+			+			Supplement.get_current_font ().touch ();+			+			add_new_version ();+			current_version = (int) glyphs.length () - 1;+		};+		+		if (g != null) {+			add_glyph ((!) g);+		}+	}+	+	public static void set_suppress_signal (bool s) {+		suppress_signal = s;+	}+	+	public Glyph get_current () {+		if (unlikely (current_version >= glyphs.length ())) {+			warning (@"current_version >= glyphs.length ($current_version >= $(glyphs.length ()))");+			return new Glyph ("");+		}+		+		return glyphs.nth (current_version).data;+	}++	public void add_new_version () {+		Glyph g = get_current ();+		Glyph new_version = g.copy ();+		add_glyph (new_version);+	}+	+	private void set_selected_item (MenuAction ma) {+		int i = ma.index;+				+		return_if_fail (0 <= i < glyphs.length ());++		current_version = i;+		+		return_if_fail (ma.parent != null);+		+		((!)ma.parent).deselect_all ();+		ma.set_selected (true);		+		+		if (!is_null (MainWindow.get_tab_bar ())) {+			MainWindow.get_tab_bar ().close_by_name (get_current ().name);+		}	+	}+	+	public void add_glyph (Glyph new_version, bool selected = true) {+		MenuAction ma;+		+		versions++;+		glyphs.append (new_version);++		ma = add_item (_("Version") + @" $(versions - 1)");+		ma.index = (int) glyphs.length () - 1;+		+		ma.action = (self) => {+			Font font;+			+			if (suppress_signal) {+				return;+			}+			+			font = Supplement.get_current_font ();+			set_selected_item (self);+			font.touch ();+		};++		if (selected && !suppress_signal) {+			set_selected_item (ma);+		}+	}++}++}diff --git a/libbirdfont/ZoomTool.vala b/libbirdfont/ZoomTool.valanew file mode 100644index 0000000..25996cc--- /dev/null+++ b/libbirdfont/ZoomTool.vala@@ -0,0 +1,157 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++using Gtk;++namespace Supplement {++class ZoomTool : Tool {++	int zoom_area_begin_x = -1;+	int zoom_area_begin_y = -1;+	+	int view_index = 0;+	List<Tab> views = new List<Tab> ();+	+	public ZoomTool (string n) {+		base (n, "Zoom");++		select_action.connect((self) => {+		});++		select_action.connect((self) => {+		});+			+		press_action.connect((self, b, x, y) => {+			if (b == 1) {+				zoom_area_begin_x = x;+				zoom_area_begin_y = y;+			}+		});++		move_action.connect((self, x, y) => {+			if (zoom_area_begin_x > 0) {+				Glyph g =	MainWindow.get_current_glyph ();+				g.show_zoom_area (zoom_area_begin_x, zoom_area_begin_y, x, y);+			}+		});+		+		release_action.connect((self, b, x, y) => {+			Glyph g;+						+			if (b == 1) {+				store_current_view ();+				+				g =	MainWindow.get_current_glyph ();+				+				if (zoom_area_begin_x == x && zoom_area_begin_y == y) { // zero width center this point but don't zoom in+					g.set_center (x, y);+				} else { +					g.set_zoom_from_area ();+				}+				+				zoom_area_begin_x = -1;+				zoom_area_begin_y = -1;+			}+		});+	}++	public void zoom_full_background_image () {+		GlyphBackgroundImage bg;+		Glyph g = MainWindow.get_current_glyph ();+		int x, y;+		+		if (g.get_background_image () == null) {+			return;+		}+		+		bg = (!) g.get_background_image ();+		+		x = (int) (bg.img_offset_x);+		y = (int) (bg.img_offset_y);+		+		g.set_zoom_area (x, y, (int) (x + bg.size_margin * bg.img_scale_x), (int) (y + bg.size_margin * bg.img_scale_y));+		g.set_zoom_from_area ();+	}++	public void zoom_full_glyph () {+		store_current_view ();+		+		MainWindow.get_current_display ().zoom_min ();+	}+	+	public override bool test () {+		test_select_action ();+		return true;+	}+	+	/** Add an item to zoom view list. */+	public void store_current_view () {	+		if (views.length () - 1 > view_index) {+			unowned List<Tab> i = views.nth (view_index + 1);+			while (i != i.last ()) {+				i.delete_link (i.next);+			}+		}+		+		views.append (MainWindow.get_current_tab ());+		view_index = (int)views.length () - 1;+		MainWindow.get_current_display ().store_current_view ();+	}++	/** Redo last zoom.*/+	public void next_view () {+		if (view_index + 1 >= (int) views.length ()) {+			return;+		}+		+		view_index++;+	+		MainWindow.select_tab (views.nth (view_index).data);+		MainWindow.get_current_display ().next_view ();+		MainWindow.get_glyph_canvas ().redraw ();+	}+	+	/** Undo last zoom. */+	public void previous_view () {+		if (view_index == 0) {+			return;+		}+		+		view_index--;+	+		MainWindow.select_tab (views.nth (view_index).data);+		MainWindow.get_current_display ().restore_last_view ();+		MainWindow.get_glyph_canvas ().redraw ();+	}+}++class ZoomView : GLib.Object {+	public double x;+	public double y;+	public double zoom;+	public Allocation allocation;+	+	public ZoomView (double x, double y, double zoom, Allocation allocation) {+		this.x = x;+		this.y = y;+		this.zoom = zoom;+		this.allocation = allocation;+	}+}+	+}diff --git a/libbirdfont/wscript b/libbirdfont/wscriptnew file mode 100644index 0000000..f0466ab--- /dev/null+++ b/libbirdfont/wscript@@ -0,0 +1,92 @@+BIRDFONT = [+	'Allocation.vala',+	'Argument.vala',+	'Font.vala',+	'BackgroundTool.vala',+	'BackgroundSelection.vala',+	'Birdfont.vala',+	'EotWriter.vala',+	'Expander.vala',+	'ExportTool.vala',+	'FontDisplay.vala',+	'CharDatabase.vala',+	'Config.vala',+	'Context.vala',+	'CutBackgroundTool.vala',+	'CutTool.vala',+	'ClipTool.vala',+	'DropMenu.vala',+	'EditPoint.vala',+	'EditPointHandle.vala',+	'Glyph.vala',+	'GlyphBackgroundImage.vala',+	'GlyphCanvas.vala',+	'GlyphCollection.vala',+	'GlyphRange.vala',+	'GlyphTable.vala',+	'GridTool.vala',+	'Icons.vala',+	'Kerning.vala', +	'KeyBindings.vala', +	'Line.vala',+	'MainWindow.vala',+	'MenuAction.vala',+	'MenuTab.vala',+	'MergeTool.vala',+	'MoveTool.vala',+	'NativeWindow.vala',+	'Intersection.vala',+	'IntersectionList.vala',+	'IOThread.vala',+	'OpenFontFormatWriter.vala',+	'OpenFontFormatReader.vala',+	'OverView.vala',+	'Path.vala',+	'PenTool.vala',+	'Preview.vala',+	'ResizeTool.vala',+	'SaveDialog.vala',+	'Scrollbar.vala',+	'ShrinkTool.vala',+	'SpinButton.vala',+	'Supplement.vala',+	'Svg.vala',+	'SvgFontFormatWriter.vala',+	'Preferences.vala',+	'Tab.vala',+	'TabBar.vala',+	'TestCases.vala', +	'TestSupplement.vala', +	'Tool.vala',+	'Toolbox.vala',+	'TooltipArea.vala',+	'UniRange.vala',+	'VersionList.vala',+	'ZoomTool.vala',+]++PARAM = [+	'-X -D\'GETTEXT_PACKAGE="birdfont"\' '+	'--thread',+	'--disable-warnings',+	'--enable-experimental',+	'--enable-experimental-non-null',+	'--vapidir=./',+]++def build (bld):++	bld.shlib (+		target        = 'birdfont',+		param         = PARAM,+		packages      = 'libxml-2.0',+		uselib        = 'CAIRO GLIB GIO PIXBUF XML',+		source        = BIRDFONT,+		name          = "libbirdfont",+	)++	bld (+		rule          = "mv libbirdfont/birdfont.h ./",+		depends       = "libbirdfont",+		name          = "birdfont_h"+	)diff --git a/src/Allocation.vala b/src/Allocation.valadeleted file mode 100644index efcc36d..0000000--- a/src/Allocation.vala+++ /dev/null@@ -1,27 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--public struct Allocation {-	public int width;-	public int height;-	public int x;-	public int y;-}--}diff --git a/src/Argument.vala b/src/Argument.valadeleted file mode 100644index 623a50b..0000000--- a/src/Argument.vala+++ /dev/null@@ -1,241 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {-	-public class Argument : GLib.Object {-	-	List<string> args;-	-	public Argument (string line) {-		args = new List<string> ();-		set_argument (line);-	}-	-	public Argument.command_line (string[] arg) {	-		args = new List<string> ();--		foreach (string a in arg) {-			args.append (a);-		}-	}-	-	/** returns 0 if valid or index the bad parameter. */ -	public int validate () {-		string prev = "";-		int i = 0;-		foreach (string a in args) {-			-			// program name-			if (i == 0) {-				prev = a;-				i++;-				continue;-			}-			-			// file name-			if (i == 1 && !a.has_prefix ("-")) {-				prev = a;-				i++;-				continue;-			}--			// a single character, like -t-			if (!a.has_prefix ("--") && a.has_prefix ("-")) {-				a = expand_param (a);-			}-			-			// valid parameter-			if (a == "--exit" || a == "--slow" || a == "--help" || a == "--test" || a == "--fatal-warning" || a == "--show-coordinates") {-				prev = a;-				i++;-				continue;-			} else if (a.has_prefix ("--")) {-				return i;-			}-			-			// not argument to parameter-			if (!(prev == "--test")) {-				return i;-			}-			-			prev = a;-			i++;-		}-		-		return 0;-	}-	-	/** Return the font file parameter. */-	public string get_file () {-		string f = "";-		-		if (args.length () >= 2) {-			f = args.nth (1).data;-		}--		if (f.has_prefix ("-")) {-			return "";-		}-		-		return f;-	}-	-	public void print_all () {-		print (@"$(args.length ()) arguments:\n");-		-		foreach (string p in args) {-			print (@"$p\n");-		}-	}-	-	public bool has_argument (string param) {-		return (get_argument (param) != null);-	}-	-	/** Get commandline argument. */-	public string? get_argument (string param) {-		int i = 0;-		string? n;-		string p;--		if (param.substring (0, 1) != "-") {-			warning (@"parameters must begin with \"-\" got $param");-			return null;-		}--		foreach (string s in args) {--			// this is content not a parameter -			if (s.substring (0, 1) != "-") continue;--			// we might need to expand -t to test fo instance-			if (s.substring (0, 2) != "--") {-				p = expand_param (s);-			} else {				-				p = s;-			}-			-			if (param == p) {-				if (i + 2 >= args.length ()) {-					return "";-				}-				-				n = args.nth (i + 2).data;-				if (n == null) {-					return "";-				}-				-				if (args.nth (i + 2).data.substring (0, 1) == "-") {-					return "";-				}-				-				return args.nth (i + 2).data;-			}-			-			i++;-		}-		-		return null;-	}--	private void print_padded (string cmd, string desc) {-		int l = 25 - cmd.char_count ();--		stdout.printf (cmd);-		-		for (int i = 0; i < l; i++) {-				stdout.printf (" ");-		}-		-		stdout.printf (desc);-		stdout.printf ("\n");-	}--	/** Return full command line parameter for the abbrevation.-	 * -t becomes --test.-	 */-	private string expand_param (string? param) {-		if (param == null) return "";-		var p = (!) param;-		-		if (p.length == 0) return "";-		if (p.get_char (0) != '-') return "";-		if (p.char_count () != 2) return "";-		-		switch (p.get_char (1)) {-			case 'c':-				return "--show-coordinates";-			case 'e': -				return "--exit";-			case 'f': -				return "--fatal-warning";-			case 'h': -				return "--help";-			case 's': -				return "--slow";-			case 't': -				return "--test";-		}-		-		return "";-	}--	private void set_argument (string arg) {-		int i = 0;-		int a;-		string n;-		-		if (arg.char_count () <= 1) {-			return;-		}-		-		do {-			a = arg.index_of (" ", i + 1);-			n = arg.substring (i, a - i);-			-			if (n.index_of ("\"") == 0) {-				a = arg.index_of ("\"", i + 1);-				n = arg.substring (i, a - i + 1);-			}-					-			args.append (n);-			-			i += n.char_count () + 1;-		} while (i < arg.char_count ());-	}--	public void print_help () -		requires (args.length () > 0)-	{-		stdout.printf (_("Usage") + ": ");-		stdout.printf (args.nth (0).data);-		stdout.printf (" [" + _("FILE") + "] [" + _("OPTION") + " ...]\n");--		print_padded ("-c, --show-coordinates", _("show coordinate in glyph view"));-		print_padded ("-e, --exit", _("exit if a test case failes"));-		print_padded ("-f, --fatal-warning", _("treat warnings as fatal"));-		print_padded ("-h, --help", _("show this message"));-		print_padded ("-s, --slow", _("sleep between each command in test suite"));-		print_padded ("-t, --test [TEST]", _("run test case"));-		-		stdout.printf ("\n");-	}--}-	-}diff --git a/src/BackgroundSelection.vala b/src/BackgroundSelection.valadeleted file mode 100644index 84f5bf5..0000000--- a/src/BackgroundSelection.vala+++ /dev/null@@ -1,302 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Gdk;--namespace Supplement {--class BackgroundSelection : FontDisplay {--	int active_box = -1;-	ImageSurface add_icon;-	List <string> background_images = new List <string> ();-	Allocation allocation;-	Glyph glyph;-	-	public BackgroundSelection () {-		ImageSurface? i;-		glyph = MainWindow.get_current_glyph ();-		-		i = Icons.get_icon ("add_background_image.png");-		return_if_fail (i != null);-		add_icon = (!) i;-	}--	public override string get_name () {-		return "Backgrounds";-	}--	public override void draw (Allocation allocation, Context cr) {		-		double x, y, zoom;-		int box_index = 0;-		Font font;--		this.allocation = allocation;--		// bg color-		cr.save ();-		cr.set_source_rgba (255/255.0, 255/255.0, 255/255.0, 1);-		cr.rectangle (0, 0, allocation.width, allocation.height);-		cr.fill ();-		cr.restore ();-		-		draw_box (box_index, 20, 20, cr);-		cr.set_source_surface (add_icon, 62, 62);-		cr.paint ();-			-		cr.save ();-		x = 160;-		y = 20;-		zoom = 0.04;-		-		font = Supplement.get_current_font ();-		foreach (string file in background_images) {-			box_index++;--			if (x > allocation.width - 140) {-				x = 20;-				y += 140;-			}--			draw_box (box_index, x, y, cr);-			draw_thumbnail (file, x, y, cr, box_index);-			-			x += 140;	-		}-		cr.restore ();-			-	}-	-	private ImageSurface create_thumbnail (string file) throws GLib.Error {-		Pixbuf pixbuf;-		-		Font font = Supplement.get_current_font ();-		File folder = font.get_backgrounds_folder ();-		string fn = Checksum.compute_for_string (ChecksumType.SHA1, file);-		File original = File.new_for_path (file);-		File thumbnail_path = folder.get_child (@"thumbnail");-		File png_image = thumbnail_path.get_child (@"$(fn).png");-		-		if (!thumbnail_path.query_exists ()) {-			DirUtils.create ((!) thumbnail_path.get_path (), 0xFFFFFF);-		}-		-		if (!png_image.query_exists ()) {-			pixbuf = new Pixbuf.from_file_at_scale ((!) original.get_path (), 100, 100, true);-			if (!pixbuf.save ((!) png_image.get_path (), "png")) {-				warning (@"can't save $((!) png_image.get_path ())");-			}-		}-		-		return new ImageSurface.from_png ((!) png_image.get_path ());-	}-	-	private bool draw_thumbnail (string file, double x, double y, Context cr, int box_index) {-		ImageSurface img;-	-		try {-			img = create_thumbnail (file);-			-			if (img.status () == Cairo.Status.SUCCESS) {-				cr.set_source_surface (img, x + 10, y + 10);-				cr.paint ();-			}-		} catch (GLib.Error e) {-			warning (e.message);-			return false;-		}-		-		return true;	-	}-	-	private void draw_box (int index, double x, double y, Context cr) {-		cr.save ();-		cr.set_line_join (LineJoin.ROUND);-		cr.set_line_width (7);-		-		if (index == active_box) {-			cr.set_source_rgba (203/255.0, 220/255.0, 249/255.0, 1);-		} else {-			cr.set_source_rgba (183/255.0, 200/255.0, 223/255.0, 1);-		}-		-		cr.rectangle (x, y, 120, 120);-		cr.fill_preserve ();-		cr.stroke ();-		-		cr.restore ();		-	}-	-	public override void button_release (int button, double ex, double ey) {-		motion (ex, ey);-		-		if (active_box == 0) {-			add_image ();-		} else {-			select_image ();-		}-	}--	private void add_image () {-		string? file;--		file = MainWindow.file_chooser ("Add image");--		if (file != null) {-			Supplement.get_current_font ().add_background_image ((!) file);-		}-		-		MainWindow.get_glyph_canvas ().redraw ();				-		-		collect_background_images ();-	}--	private void select_image () {-		Font font;-		string file;-		GlyphBackgroundImage bg;-		TabBar tb;-		Tool zoom_background;--		font = Supplement.get_current_font ();-				-		if (!(0 <= active_box - 1 < background_images.length ())) {-			return;-		}-		-		file = background_images.nth (active_box - 1).data; // Fixa: check bounds-		bg = new GlyphBackgroundImage (file);-		tb = MainWindow.get_tab_bar ();-		-		bg.reset_scale (glyph);-		-		glyph.set_background_visible (true);-		-		MainWindow.get_current_glyph ().set_background_image (bg);-		tb.select_tab_name (MainWindow.get_current_glyph ().get_name ());--		zoom_background = MainWindow.get_tool ("zoom_background_image");-		zoom_background.select_action (zoom_background);--		MainWindow.get_toolbox ().select_tool_by_name ("cut_background");-	}--	private void add_unique_background (string file) {-		foreach (var f in background_images) {-			if (f == file) {-				return;-			}-		}-		-		background_images.prepend (file);-	}--	public override void selected_canvas () {-		Font font = Supplement.get_current_font ();-		File folder = font.get_backgrounds_folder ();-		-		if (!folder.query_exists ()) {-			DirUtils.create ((!) folder.get_path (), 0xFFFFFF);-		}-		-		collect_background_images ();-	}--	bool is_image (string file_name) {-		return file_name.index_of (".png") != -1 -			|| file_name.index_of (".jpg") != -1-			|| file_name.index_of (".jpeg") != -1;-	}--	void collect_background_images () {-		Font font = Supplement.get_current_font ();-		File directory = font.get_folder ();-		FileEnumerator enumerator;-		FileInfo? file_info;-		FileInfo fi;-		File img_file;--		while (background_images.length () > 0) {-			background_images.delete_link (background_images.first ());-		}-		-		foreach (string file in font.background_images) {-			add_unique_background (file);-		}-				-		try {-			enumerator = directory.enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, 0);-			-			while ((file_info = enumerator.next_file ()) != null) {-				fi = (!) file_info;-				-				if (is_image (fi.get_name ())) {-					img_file = directory.get_child (fi.get_name ());					-					add_unique_background ((!)img_file.get_path ());-				}-				-			}-		} catch (GLib.Error e) {-			warning (e.message);-		}-	}-	-	public override void key_press (uint e) {-	}--	public override void key_release (uint e) {-	}-	-	public override void motion_notify (double x, double y) {-		motion (x, y);-	}-	-	public void motion (double x, double y) {-		int active;-		-		active = (int) (x / 140); // column-		active += (int) (y / 140) * (int) (allocation.width / 140.0); // row-		-		if (active != active_box) {-			MainWindow.get_glyph_canvas ().redraw ();-			active_box = active;-		}-	}--	public override void button_press (uint button, double ex, double ey) {-		motion (ex, ey);-	}--	public override void store_current_view () {-	}-	-	public override void restore_last_view () {-	}--	public override void next_view () {-	}-	-	public override void scroll_wheel_up (double x, double y) {-	}--	public override void scroll_wheel_down (double x, double y) {-	}--}--}diff --git a/src/BackgroundTool.vala b/src/BackgroundTool.valadeleted file mode 100644index 2e505e9..0000000--- a/src/BackgroundTool.vala+++ /dev/null@@ -1,158 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--class BackgroundTool : Tool {--	double begin_x = 0;-	double begin_y = 0;--	public double img_offset_x = 0;-	public double img_offset_y = 0;--	public double img_width = 0;-	public double img_height = 0;-	-	public double img_scale_x = 0;--	public static double top_limit;-	public static double bottom_limit;-		-	bool move_bg;-	bool resize_bg; // DEL-	-	public BackgroundTool (string name) {-		base (name, _("Move, resize and rotate background image"));-		-		top_limit = 0;-		bottom_limit = 0;--		select_action.connect((self) => {-		});--		deselect_action.connect ((self) => {-		});-		-		press_action.connect((self, b, x, y) => {-			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? bg = g.get_background_image ();-			GlyphBackgroundImage background;-			-			if (bg == null) {-				return;-			}-			-			background = (!) bg;-			-			g.store_undo_state ();-			-			background.handler_press (x, y);-			-			begin_x = x;-			begin_y = y;-			-			img_offset_x = background.img_offset_x;-			img_offset_y = background.img_offset_y;-			-			img_scale_x = background.img_scale_x;-			-			img_width = background.get_img ().get_width () * background.img_scale_x;-			img_height = background.get_img ().get_height () * background.img_scale_y;-			-			move_bg = true;-		});--		release_action.connect((self, b, x, y) => {-			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? bg = g.get_background_image ();-			-			if (bg == null) {-				return;-			}-			-			img_offset_x = ((!)bg).img_offset_x;-			img_offset_y = ((!)bg).img_offset_y;-			-			((!)bg).handler_release (x, y);-			-			move_bg = false;-			resize_bg = false;-		});--		move_action.connect((self, x, y) => {			-			move (x, y);-		});-		-		draw_action.connect ((self, cairo_context, glyph) => {-			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? background_image = g.get_background_image ();-			if (background_image == null) return;-			-			((!) background_image).draw_handle (cairo_context, glyph);-		});-	}-		-	void move (double x, double y) {-		Glyph g = MainWindow.get_current_glyph ();-		GlyphBackgroundImage? background_image = g.get_background_image ();-		GlyphBackgroundImage bg = (!) background_image;-		-		double xscale, yscale, dx, dy;--		if (background_image == null) return;-		 		-		bg.handler_move (x, y);-	-		if (!(move_bg || resize_bg)) return;-		-		dx = x - begin_x;-		dy = y - begin_y;-		-		dx *= 1/g.view_zoom;-		dy *= 1/g.view_zoom;-	-		dx *= PenTool.precision;-		dy *= PenTool.precision;-		-		if (bg.selected_handle == 2) {-			bg.set_img_rotation_from_coordinate (x, y);-		}-		-		if (bg.selected_handle == 1) {-			xscale = img_scale_x * ((img_width - dx) / img_width);	-			yscale = xscale;--			bg.set_img_scale (xscale, yscale);-			bg.set_img_offset (this.img_offset_x + dx, this.img_offset_y);-		} -		-		if (move_bg && bg.selected_handle <= 0) {-			bg.set_img_offset (this.img_offset_x + dx, this.img_offset_y + dy);-		}--		MainWindow.get_glyph_canvas ().redraw ();-	}-	-	public override bool test () {-		return false;-	}--}--}-diff --git a/src/Birdfont.vala b/src/Birdfont.valadeleted file mode 100644index 60c15b5..0000000--- a/src/Birdfont.vala+++ /dev/null@@ -1,169 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/-using Supplement;--namespace Birdfont {--static void print_export_help (string[] arg) {-	stdout.printf (_("Usage:"));-	stdout.printf (arg[0]);-	stdout.printf (" [OPTION ...] FILE\n");-	stdout.printf ("-h, --help                      " + _("print this message\n"));-	stdout.printf ("-o, --output [DIRECTORY]        " + _("write files to this directory\n"));-	stdout.printf ("-s, --svg                       " + _("write svg file\n"));-	stdout.printf ("-t, --ttf                       " + _("write ttf and eot files\n"));-	stdout.printf ("\n");-}--public static int run_export (string[] arg) {-	string output_directory = ".";-	string file_name = "";-	bool specific_formats = false;	-	bool write_ttf = false;-	bool write_svg = false;-	Supplement.Supplement supplement = new Supplement.Supplement ();-	-	File directory;-		-	if (arg.length < 2) {-		print_export_help (arg);-	}-	-	for (int i = 1; i < arg.length; i++) {--		if (arg[i] == "-f" || arg[i] == "--fatal-warnings") {-			supplement.fatal_wanings = true;-			return 0;-		}--		if (arg[i] == "-h" || arg[i] == "--help") {-			print_export_help (arg);-			return 0;-		}-		-		if ((arg[i] == "-o" || arg[i] == "--output") && i + 1 < arg.length) {-			output_directory = arg[i + 1];-			i++;-			continue;-		}--		if (arg[i] == "-s" || arg[i] == "--svg") {-			write_svg = true;-			specific_formats = true;-			continue;-		}-		-		if (arg[i] == "-t" || arg[i] == "--ttf") {-			write_ttf = true;-			specific_formats = true;-			continue;-		}-		-		if (arg[i].has_prefix ("-")) {-			print_export_help (arg);-			return 1;-		}-		-		if (!arg[i].has_prefix ("-")) {-			file_name = arg[i];-						-			if (i != arg.length - 1) {-				print_export_help (arg);-				return 1;-			}-			-			break;-		}-	}--	if (supplement.fatal_wanings) {-		LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;-		Log.set_handler (null, levels, supplement.fatal_warning);		-	}--	Preferences preferences = new Preferences ();-	preferences.load ();-		-	supplement.args = new Argument ("");-	supplement.current_font = new Font ();-	supplement.current_glyph = new Glyph ("");-	-	if (!supplement.get_current_font ().load (file_name, false)) {-		stderr.printf (@"Failed to load font $file_name.\n");-		-		if (!file_name.has_suffix (".ffi")) {-			stderr.printf (@"Is it a .ffi file?\n");-		}-		-		return 1;-	}--	directory = File.new_for_path (output_directory);-	-	if (!directory.query_exists ()) {-		stderr.printf (_("Can't find output directory") + @"$((!)directory.get_path ())\n");-		return 1;-	}--	if (!specific_formats || write_svg) {-		print (_("Writing") + @" $(supplement.current_font.get_name ()).svg to $output_directory\n");-		ExportTool.export_svg_font_path (File.new_for_path (output_directory));-	}--	if (!specific_formats || write_ttf) {-		print (_("Writing") + @" $(supplement.current_font.get_name ()).ttf to $output_directory\n");-		ExportTool.export_ttf_font_path (File.new_for_path (output_directory), false);-	}-	-	return 0;-}--public static string wine_to_unix_path (string exec_path) {-	bool drive_c, drive_z;-	int i;-	string p, q;--	p = exec_path;-	p = p.replace ("\\", "/");-	-	drive_c = exec_path.index_of ("C:") == 0;-	drive_z = exec_path.index_of ("Z:") == 0;-	-	i = p.index_of (":");-	-	if (i != -1) {-		p = p.substring (i + 2);-	}--	if (drive_c) {-		q = @"/home/$(Environment.get_user_name ())/.wine/drive_c/" + p;-		-		if (File.new_for_path (q).query_exists ()) {-			return q;-		} else {-			return p;-		}-	}-	-	if (drive_z) {-		return ("/" + p).dup ();-	}--	return exec_path.dup ();-}--}diff --git a/src/BirdfontExport.vala b/src/BirdfontExport.valadeleted file mode 100644index c434bd8..0000000--- a/src/BirdfontExport.vala+++ /dev/null@@ -1,23 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Birdfont;--/** Stand alone command line utility for exporting fonts. */-public static int main (string[] arg) {				-	return run_export (arg);-}diff --git a/src/CharDatabase.vala b/src/CharDatabase.valadeleted file mode 100644index 54b71ce..0000000--- a/src/CharDatabase.vala+++ /dev/null@@ -1,62 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--class CharDatabase {--	public CharDatabase () {-	}--	public static bool has_ascender (unichar c) {-		if (!c.islower()) return true;-		-		// todo: their derrivatives-		switch (c) {-			case 'b': return true;-			case 'd': return true;-			case 'f': return true;-			case 'h': return true;-			case 'i': return true;-			case 'j': return true;-			case 'k': return true;-			case 'l': return true;	-		}-		-		if ('à' <= c <= 'å') return true;-		if ('è' <= c <= 'ö') return true;-		if ('ù' <= c <= 'ă') return true;-		if ('ć' <= c <= 'ė') return true;--		return false;-	}--	public static bool has_descender (unichar c) {-		// todo: their derrivatives-		switch (c) {-			case 'g': return true;-			case 'j': return true;-			case 'p': return true;-			case 'q': return true;-		}-		-		return false;		-	}-	-}--}diff --git a/src/ClipTool.vala b/src/ClipTool.valadeleted file mode 100644index 0472373..0000000--- a/src/ClipTool.vala+++ /dev/null@@ -1,102 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--public class ClipTool : Tool {--	static Glyph? glyph = null; -	static List<Path> path = new List<Path> ();--	public static void copy () {-		Font font = Supplement.get_current_font ();-		OverView overview  = MainWindow.get_overview ();-		FontDisplay fd = MainWindow.get_current_display ();-		string gc;-		Glyph g = MainWindow.get_current_glyph ();--		glyph = null;--		while (path.length () > 0) {-			path.remove_link (path.first ());-		}-					-		if (fd is OverView) {-			gc = overview.get_selected_char ();-			glyph = font.get_glyph (gc);-		} -		-		if (fd is Glyph) {-			foreach (Path p in g.active_paths) {-				path.append (p.copy ());-			}--			if (path.length () == 0) {-				glyph = g.copy ();-			}-		}--	}--	public static void paste () {-		Font font = Supplement.get_current_font ();-		OverView overview  = MainWindow.get_overview ();-		FontDisplay fd = MainWindow.get_current_display ();-		string gc;-		Glyph? destination = null;-		unichar new_char;-		Path inserted;-		-		if (fd is OverView) {-			gc = overview.get_selected_char ();-			destination = font.get_glyph (gc);-		-			if (destination == null) {-				new_char = gc.get_char (0);-				destination = new Glyph (gc, new_char) ;-				font.add_glyph ((!) destination);-			}-		}-		-		if (fd is Glyph) {-			destination = (Glyph) fd;--			if (destination == glyph) {-				return;-			}-			-			((!)destination).store_undo_state ();-			-			if (glyph != null) {-				foreach (Path p in ((!)glyph).path_list) {-					inserted = p.copy ();-					((!)destination).add_path (inserted);-				}-			}--			foreach (Path p in path) {-				inserted = p.copy ();-				((!)destination).add_path (inserted);-			}-			-			((!)destination).update_view ();-		}-	}--}--}diff --git a/src/Context.vala b/src/Context.valadeleted file mode 100644index 1061488..0000000--- a/src/Context.vala+++ /dev/null@@ -1,335 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--/** Kerning context. */-public class ContextDisplay : FontDisplay {--	List <Word> row;-	int active_handle = -1;-	int selected_handle = -1;-	-	double begin_handle_x = 0;-	double begin_handle_y = 0;-	-	double last_handle_x = 0;-	-	public ContextDisplay () {-		Word w = new Word ();-		row = new List <Word> ();-		row.append (w);-	}--	public override string get_name () {-		return "Kerning";-	}-	-	public override void draw (Allocation allocation, Context cr) {-		Glyph? g;-		Glyph glyph;-		double x, y, w, kern, alpha;-		double x2;-		int i, wi;-		string prev;-		-		i = 0;-		-		// bg color-		cr.save ();-		cr.set_source_rgba (1, 1, 1, 1);-		cr.rectangle (0, 0, allocation.width, allocation.height);-		cr.fill ();-		cr.restore ();-	-		alpha = 1;-		y = 100;-		x = 20;-		w = 0;-		prev = "";-		-		foreach (Word word in row) {-			wi = 0;-			-			foreach (string s in word.glyph) {				-				g = (!) Supplement.get_current_font ().get_glyph (s);	-				-				if (s == "") {-					continue;-				}-				-				kern = (prev != "") ? get_kerning (prev, s) : 0;-				-				// draw glyph				-				if (g == null) {-					w = 50;-					alpha = 1;-				} else {-					alpha = 0;-					glyph = (!) g;-					Svg.draw_svg_path (cr, glyph.get_svg_data (), x + kern, y);-					w = glyph.get_width ();-				}--				// handle-				if (active_handle == i) {-					//x2 = x + kern;-					x2 = x + kern / 2.0;-					-					cr.save ();-					cr.set_source_rgba (153/255.0, 153/255.0, 173/255.0, 1);-					cr.move_to (x2 - 5, y + 20);-					cr.line_to (x2 + 0, y + 20 - 5);-					cr.line_to (x2 + 5, y + 20);-					cr.fill ();-					-					cr.set_font_size (10);-					cr.show_text (s);-					cr.restore ();-				}	-				-				x += w + kern;-	-				// caption-				if (g == null || ((!)g).is_empty ()) {-					cr.save ();-					cr.set_source_rgba (153/255.0, 153/255.0, 153/255.0, alpha);-					cr.move_to (x - w / 2.0 - 5, y + 20);-					cr.set_font_size (10);-					cr.show_text (s);-					cr.restore ();-				}-							-				prev = s;-				-				wi++;-				i++;-			}-			-			if (y > allocation.height && i > 10) {-				row.remove (word);-			}-						-			y += MainWindow.get_current_glyph ().get_height () + 20;-			x = 20;			-		}-	}-	-	private void set_kerning (int handle, double val) {-		string a, b;-		Font font;-		int wi = 0;-		double kern;-		-		font = Supplement.get_current_font ();--		font.touch ();--		a = "";-		b = "";--		foreach (Word word in row) {-			foreach (string s in word.glyph) {-				-				if (s == "") {-					continue;-				}-				-				b = s;-				-				if (handle == wi) {-					kern = font.get_kerning (a, b) + val;-					font.set_kerning (a, b, kern);-				}-				-				wi++;-				-				a = b;-			}-		}-		-		-	}-	-	private double get_kerning (string a, string b) {-		Font font = Supplement.get_current_font ();-		return font.get_kerning (a, b);-	}-	-	public override void selected_canvas () {-		Glyph g;-		Word w;-		unowned List<Word>? r;-		unowned List<string>? rw;-		StringBuilder s = new StringBuilder ();-		bool append_char = false;-		-		KeyBindings.singleton.set_require_modifier (true);-		-		g = MainWindow.get_current_glyph ();-		s.append_unichar (g.get_unichar ());--		r = row;-		return_if_fail (r != null);--		if (row.length () == 0) {-			append_char = true;-		}-		-		if (row.length () > 0) {-			rw = row.first ().data.glyph;-			return_if_fail (rw != null);-			append_char = (row.first ().data.glyph.data.index_of (s.str) == -1);-		}-		-		if (append_char) {-			w = new Word ();-			row.append (w);-			w.glyph.prepend (s.str);-		}					-	}-	-	public override void key_press (uint keyval) {-		unichar c = (unichar) keyval;-		Font f = Supplement.get_current_font ();-		string name;-		-		if (KeyBindings.singleton.modifier == NONE || KeyBindings.singleton.modifier == SHIFT) {-					-			if (keyval == Key.BACK_SPACE && row.length () > 0) {	-				row.first ().data.glyph.remove_link (row.first ().data.glyph.last ());-			}-			-			if (row.length () == 0 || c == Key.ENTER) {-				row.prepend (new Word ());-			}-			-			if (!is_modifier_key (c) && c.validate ()) {-				name = f.get_name_for_character (c);-				row.first ().data.glyph.append (name);-			}-		}-		-		MainWindow.get_glyph_canvas ().redraw ();-	}-	-	public override void motion_notify (double ex, double ey) {-		double k, y;-		-		if (selected_handle == -1) {-			set_active_handle (ex, ey);-		} else {-			y = 1;-			-			if (Math.fabs (ey - begin_handle_y) > 20) {-				y = ((Math.fabs (ey - begin_handle_y) / 100) + 1);-			}-			-			k = (ex - last_handle_x) / y; // y-axis is variable precision-			set_kerning (selected_handle, k);-			MainWindow.get_glyph_canvas ().redraw ();-		}-		-		last_handle_x = ex;-	}-	-	public void set_active_handle (double ex, double ey) {-		double y = 100;-		double x = 20;-		double w = 0;-		double d, kern;-		double min = double.MAX;-		int i = 0;-		int row_index = 0;-		int col_index = 0;-		Glyph? g;-		Glyph glyph = new Glyph.no_lines ("");-		-		string prev = "";-		string gl_name = "";-		-		foreach (Word word in row) {-			col_index = 0;-			foreach (string s in word.glyph) {-				-				// draw glyph-				g = (!) Supplement.get_current_font ().get_glyph (s);	-				kern = (prev != "") ? get_kerning (prev, s) : 0;-				-				if (g == null) {-					w = 50;	-				} else {-					glyph = (!) g;-					w = glyph.get_width ();-					-				}-				-				d = Math.pow (x + kern - ex, 2) + Math.pow (y - ey, 2);-				-				if (d < min) {-					min = d;-					gl_name = glyph.get_name ();-					-					if (active_handle != i - row_index) {-						active_handle = i - row_index;-						MainWindow.get_glyph_canvas ().redraw ();-					}--					if (col_index == word.glyph.length () || col_index == 0) {-						active_handle = -1;-					}-				}-				-				prev = s;-				x += w + kern;-				i++;-				col_index++;-			}-			-			row_index++;-			y += MainWindow.get_current_glyph ().get_height () + 20;-			x = 20;-		}-	}-	-	public override void button_release (int button, double ex, double ey) {-		set_active_handle (ex, ey);-		selected_handle = -1;-	}-	-	public override void button_press (uint button, double ex, double ey) {-		set_active_handle (ex, ey);-		selected_handle = active_handle;-		begin_handle_x = ex;-		begin_handle_y = ey;-		last_handle_x = ex;-	}--}--class Word {-	public List <string> glyph;-	-	public Word () {-		glyph = new List <string> ();-		glyph.append ("");-	}-}--}diff --git a/src/CutBackgroundTool.vala b/src/CutBackgroundTool.valadeleted file mode 100644index db17ddd..0000000--- a/src/CutBackgroundTool.vala+++ /dev/null@@ -1,246 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Math;--namespace Supplement {--class CutBackgroundTool : Tool {--	double x1 = 0;-	double y1 = 0;-	double x2 = 0;-	double y2 = 0;-	-	bool is_visible = false;-	bool is_set = false;-	bool is_done = false;-		-	public signal void new_image (GlyphBackgroundImage file);-	-	public CutBackgroundTool (string name) {-		base (name, _("Crop background image"));--		select_action.connect((self) => {-		});--		deselect_action.connect ((self) => {-		});-		-		press_action.connect((self, b, x, y) => {				-			if (!is_over_rectangle (x, y) || !is_visible) {-				x1 = x;-				y1 = y;-	-				x2 = x;-				y2 = y;-				-				is_visible = true;-				is_set = false;-				is_done = false;-			}-			-			if (is_set && is_visible && is_over_rectangle (x, y)) {-				do_cut ();-				is_set = false;-				is_visible = false;-				is_done = true;-				-				x1 = 0;-				y1 = 0;-				x2 = 0;-				y2 = 0;-			}-		});--		release_action.connect((self, b, x, y) => {-			if (!is_set && !is_done) {-				x2 = x;-				y2 = y;-				is_set = true;-			}-			-			is_done = false;-		});--		move_action.connect((self, x, y) => {-			if (is_visible && !is_set) {-				x2 = x;-				y2 = y;-				-				MainWindow.get_glyph_canvas ().redraw ();-			}-		});-		-		draw_action.connect ((self, cr, glyph) => {-			if (is_visible) {-				cr.save ();-				cr.set_line_width (2.0);-				cr.set_source_rgba (0, 0, 1, 0.3);-				cr.rectangle (fmin (x1, x2), fmin (y1, y2), fabs (x1 - x2), fabs (y1 - y2));-				cr.stroke ();-				cr.restore ();-			}-		});--		new_image.connect ((file) => {-			Glyph glyph = MainWindow.get_current_glyph ();-			TabBar tb = MainWindow.get_tab_bar ();-			-			glyph.store_undo_state ();-			-			glyph.set_background_image (file);-			tb.select_tab_name (glyph.get_name ());-			-			glyph.set_background_visible (true);-		});--	}--	bool is_over_rectangle (double x, double y) {-		return fmin (x1, x2) + 1 < x < fmax (x1, x2) - 1 && fmin (y1, y2) + 1 < y < fmax (y1, y2) - 1;-	}--	double get_width () {-		return fabs (x1 - x2);-	}--	double get_height () {-		return fabs (y1 - y2);-	}--	void do_cut () {-		double x, y;-		int h, w;-		double wc, hc;-		-		Glyph g = MainWindow.get_current_glyph ();-		GlyphBackgroundImage? b = g.get_background_image ();-		GlyphBackgroundImage bg = (!) b;-		-		ImageSurface img;-		-		Surface sr;-		Context cr;-		-		double tx, ty, vx, vy;	-		if (b == null) {-			return;-		}--		// Add margin-		Surface sg = new Surface.similar (bg.get_img (), bg.get_img ().get_content (), bg.size_margin, bg.size_margin);-		Context cg = new Context (sg);-		-		wc = bg.get_margin_width ();-		hc = bg.get_margin_height ();-		-		cg.set_source_rgba (1, 1, 1, 1);-		cg.rectangle (0, 0, bg.size_margin, bg.size_margin);-		cg.fill ();-		-		cg.set_source_surface (bg.get_img (), wc, hc);-		cg.paint ();--		// find start-		tx = bg.img_offset_x - g.view_offset_x;-		ty = bg.img_offset_y - g.view_offset_y;-				-		ty *= g.view_zoom;-		tx *= g.view_zoom;--		vx = g.path_coordinate_x (tx) - g.path_coordinate_x (fmin (x1, x2));			-		vy = g.path_coordinate_y (ty) - g.path_coordinate_y (fmin (y1, y2));-		-		x = (int) (vx / bg.img_scale_x);-		y = (int) (-vy / bg.img_scale_y);--		// do the cut-		img = bg.get_img ();--		w = (int) (get_width () / g.view_zoom);-		h = (int) (get_height () / g.view_zoom);--		sr = new Surface.similar (sg, img.get_content (), w, h);-		cr = new Context (sr);--		cr.set_source_rgba (1, 1, 1, 1);-		cr.rectangle (0, 0, w, h);-		cr.fill ();-			-		cr.scale (bg.img_scale_x, bg.img_scale_y);-		-		cr.set_source_surface (sg, x, y);-		cr.paint ();-		-		save_img (sr, g);-		-		cr.restore ();-	}--	void save_img (Surface sr, Glyph g) {-		GlyphBackgroundImage newbg;-		Font f = Supplement.get_current_font ();-		File img_dir;-		File img_file;-		File img_file_next;-		string fn;-		double wc, hc;-		-		img_dir =  f.get_backgrounds_folder ().get_child ("parts");--		if (!img_dir.query_exists ()) {-			DirUtils.create ((!) img_dir.get_path (), 0xFFFFFF);-		}-	-		img_file = img_dir.get_child (@"NEW_BACKGROUND.png");-		fn = (!) img_file.get_path ();-		-		sr.write_to_png (fn);-		-		newbg = new GlyphBackgroundImage (fn);-		-		fn = newbg.get_sha1 () + ".png";--		img_file_next = img_dir.get_child (fn);-		-		try {-			if (img_file_next.query_exists ()) {-				img_file_next.delete ();-			}--			img_file.set_display_name (fn);-		} catch (GLib.Error e) {-			warning (e.message);-			return;-		}-		-		newbg = new GlyphBackgroundImage ((!) f.get_backgrounds_folder ().get_child ("parts").get_child (fn).get_path ());-			-		// set position for the new background-		wc = newbg.get_margin_width ();-		hc = newbg.get_margin_height ();-		-		newbg.img_x = g.path_coordinate_x (fmin (x1, x2)) - wc - 0.5;-		newbg.img_y = g.path_coordinate_y (fmin (y1, y2)) + hc + 0.5;--		new_image (newbg);-	}-}--}diff --git a/src/CutTool.vala b/src/CutTool.valadeleted file mode 100644index 40aab82..0000000--- a/src/CutTool.vala+++ /dev/null@@ -1,61 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--class CutTool : Tool {--	PenTool pen;-	-	public CutTool (string n) {-		base (n, "Create counter path", 'u', CTRL);-		-		select_action.connect((self) => {-			pen = (PenTool) MainWindow.get_tool ("pen_tool");-		});-		-		press_action.connect((self, b, x, y) => {-			pen.press_action (pen, b, x, y);-		});--		release_action.connect((self, b, x, y) => {-			pen.release_action (pen, b, x, y);-		});-		-		move_action.connect ((self, x, y)	 => {-			pen.move_action (pen, x, y);-		});-	}--	public static void force_direction () {-		Glyph g = MainWindow.get_current_glyph ();-		Tool current_tool = MainWindow.get_toolbox ().get_current_tool ();-		-		foreach (Path p in g.active_paths) {-			if (p.is_open () && !p.has_direction ()) {-				if (current_tool is PenTool) {-					p.force_direction (Direction.CLOCKWISE);-				} else if (current_tool is CutTool) {-					p.force_direction (Direction.COUNTER_CLOCKWISE);-				}				-			}-		}-	}-	-}--}diff --git a/src/DropMenu.vala b/src/DropMenu.valadeleted file mode 100644index f7387c9..0000000--- a/src/DropMenu.vala+++ /dev/null@@ -1,201 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--public enum MenuDirection {-	DROP_DOWN,-	POP_UP;-}--public class DropMenu : GLib.Object {--	public delegate void Selected (MenuAction self);-	public signal void selected (DropMenu self);-	-	double x = -1;-	double y = -1;-	bool menu_visible = false;--	List <MenuAction> actions = new List <MenuAction> ();--	const int item_height = 25;-	-	MenuDirection direction = MenuDirection.DROP_DOWN;-	-	ImageSurface? icon = null;-	-	public DropMenu (string icon_file = "") {-		if (icon_file != "") {-			icon = Icons.get_icon (@"$icon_file.png");-		}-	}-	-	public void deselect_all () {-		foreach (MenuAction m in actions) {-			m.set_selected (false);-		}-	}-	-	public void set_direction (MenuDirection d) {-		direction = d;-	}-	-	public void close () {-		menu_visible = false;-	}-	-	public MenuAction add_item (string label) {-		MenuAction m = new MenuAction (label);-		add_menu_item (m);-		return m;-	}-	-	public void add_menu_item (MenuAction m) {-		m.parent = this;-		actions.append (m);-	}-		-	public bool is_over_icon (double px, double py) {-		if (x == -1 || y == -1) {-			return false;-		}-		-		return x - 5 < px < x + 12 + 5 && y - 5 < py < y + 12 + 5;-	}--	public bool menu_item_action (double px, double py) {-		MenuAction? action;--		if (menu_visible) {-			action = get_menu_action_at (px, py);-			-			if (action != null) {-				((!) action).action ((!)action);-				selected (this);-				menu_visible = false;-				return true;-			}-		}-		-		return false;-	}-	-	public bool menu_icon_action (double px, double py) {		-		menu_visible = is_over_icon (px, py);-		return menu_visible;-	}-	-	MenuAction? get_menu_action_at (double px, double py) {-		double n = 0;-		double ix, iy;-		-		foreach (MenuAction item in actions) {-			ix = x - 100 + 12;-			-			if (direction == MenuDirection.DROP_DOWN) {-				iy = y + 12 + n * item_height;-			} else {-				iy = y - 24 - n * item_height;-			}-	-			if (ix <= px <= ix + 100 && iy <= py <= iy + item_height) {-				return item;-			}-			-			n++;			-		}--		return null;-	}-	-	public void set_position (double px, double py) {-		x = px;-		y = py;-	}-	-	public void draw_menu (Context cr) {-		double ix, iy;-		int n;-		-		if (likely (!menu_visible)) {-			return;-		}-		-		cr.save ();-		cr.set_source_rgba (122/255.0, 150/255.0, 169/255.0, 1);-		cr.set_line_join (LineJoin.ROUND);-		cr.set_line_width (12);-			-		if (direction == MenuDirection.DROP_DOWN)-			cr.rectangle (x - 100 + 18, y + 18, 88, actions.length () * item_height - 12);-		else-			cr.rectangle (x - 100 + 18, y + 6 - actions.length () * item_height, 88, actions.length () * item_height - 12);-			-		cr.fill_preserve ();-		cr.stroke ();-		cr.restore ();-		-		cr.save ();-		-		n = 0;-		foreach (MenuAction item in actions) {-			-			if (direction == MenuDirection.DROP_DOWN)-				iy = y + 33 + n * item_height - 5;-			else -				iy = y - 8 - n * item_height;-			-			ix = x - 100 + 20;-			-			item.draw (ix, iy, cr);-			n++;-		}-		-		cr.restore ();-	}-	-	public void draw_icon (Context cr) {-		double alpha = 1;-		ImageSurface i = (!) icon;-		-		if (!menu_visible) {-			alpha = 0;-		}-		-		cr.save ();-		-		cr.set_source_rgba (122/255.0, 150/255.0, 169/255.0, alpha);-		-		cr.rectangle (x, y, 12, 12);-		cr.fill_preserve ();-		cr.stroke ();-		-		if (likely (icon != null && i.status () == Cairo.Status.SUCCESS)) {-			cr.set_source_surface (i, x, y + (12 - i.get_height ()) / 2.0);-			cr.paint ();-		} else {-			warning ("Failed to load icon.");-		}-		-		cr.restore ();-	}-}--}diff --git a/src/EditPoint.vala b/src/EditPoint.valadeleted file mode 100644index 24ae2d0..0000000--- a/src/EditPoint.vala+++ /dev/null@@ -1,343 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Math;--namespace Supplement {--public enum PointType {-	NONE,-	NORMAL,-	LINE,-	CURVE,-	DOUBLE_CURVE,-	END,-	FLOATING-}--public class EditPoint {-	-	public double x;-	public double y;-	public PointType type;-	-	public double r = 1;-	public double g = 0;-	public double b = 0;-	public double a = 1;-	-	public unowned List<EditPoint>? prev = null;-	public unowned List<EditPoint>? next = null;--	public bool active = false;-	public bool selected = false;-		-	public int selected_handle = 0;-	-	public EditPointHandle right_handle;-	public EditPointHandle left_handle;-	-	public bool tie_handles = false;-	-	public EditPoint (double nx = 0, double ny = 0, PointType nt = PointType.LINE) {-		x = nx;-		y = ny;-		type = nt;-		active = false;-		-		set_active (true);-		-		if (nt == PointType.FLOATING) {-			r = 1;-			g = 1;-			b = 0;-			a = 1;-			active = false;-		}-	-		right_handle = new EditPointHandle (this, 0, 7);-		left_handle = new EditPointHandle (this, PI, 7);--		if (unlikely (nx.is_nan () || ny.is_nan ())) {-			warning (@"Invalid point at ($nx,$ny).");-			x = 0;-			y = 0;-		}-	}--	/** Flip handles if next point on path is in the other direction. -	 *  Used to recalculate handles after new point is inserted on a path.-	 */-	public void recalculate_handles (double px, double py) {-		double dr, dl;-		EditPointHandle t;-		-		if (next == null || ((!)next).length () < 2) {-				return;-		}-		-		px = get_next ().nth (1).data.x;-		py = get_next ().nth (1).data.y;-		-		dr = Math.sqrt (Math.pow (px - right_handle.x (), 2) + Math.pow (py - right_handle.y (), 2));-		dl = Math.sqrt (Math.pow (px - left_handle.x (), 2) + Math.pow (py - left_handle.y (), 2));--		// flip handles-		if (dl < dr) {-			t = right_handle;-			right_handle = left_handle;-			left_handle = t;-		}		-	}-		-	/** Set bezier points for linear paths. */-	public void recalculate_linear_handles () {-		unowned EditPointHandle h;-		unowned EditPoint n;-		double nx, ny;--		if (prev == null && next != null) {-			prev = get_next ().last ();-		}--		// left handle-		if (prev != null) {-			n = get_prev ().data;-			h = get_left_handle ();-			-			if (h.type == PointType.LINE) {-				nx = x + ((n.x - x) / 3);-				ny = y + ((n.y - y) / 3);-			-				h.move_to_coordinate (nx, ny);-			}--			h = n.get_right_handle ();-			-			// on the other side-			h = n.get_right_handle ();-			-			if (h.type == PointType.LINE) {-				nx = n.x + ((x - n.x) / 3);-				ny = n.y + ((y - n.y) / 3);-			-				h.move_to_coordinate (nx, ny);-			}-		}--		// right handle-		if (next != null) {-			n = get_next ().data;-			h = get_right_handle ();-			-			if (h.type == PointType.LINE) {-				nx = x + ((n.x - x) / 3);-				ny = y + ((n.y - y) / 3);-				-				h.move_to_coordinate (nx, ny);-			}--			h = n.get_left_handle ();-			-			if (h.type == PointType.LINE) {-				nx = n.x + ((x - n.x) / 3);-				ny = n.y + ((y - n.y) / 3);--				h.move_to_coordinate (nx, ny);-			}-		}-	}-	-	public void set_tie_handle (bool t) {-		tie_handles = t;-	}--	public void process_tied_handle () {-		double a, b, c, length, angle;-		EditPointHandle eh;-		-		eh = right_handle;-		-		a = left_handle.x () - right_handle.x ();-		b = left_handle.y () - right_handle.y ();-		c = a * a + b * b;-		-		if (c == 0) {-			return;-		}-		-		length = sqrt (fabs (c));-		-		if (right_handle.y () < left_handle.y ()) {-			angle = acos (a / length) + PI;-		} else {-			angle = -acos (a / length) + PI;-		}-		-		left_handle.type = PointType.CURVE;-		right_handle.type = PointType.CURVE;-		-		right_handle.angle = angle;-		left_handle.angle = angle;--		set_tie_handle (true);-		eh.move_to_coordinate (right_handle.x (), right_handle.y ());-	}--	public EditPoint copy () {-		EditPoint new_point = new EditPoint ();-		-		new_point.x = x;-		new_point.y = y;-		-		new_point.type = type;-		new_point.set_color (r, g, b, a);-		-		new_point.tie_handles = tie_handles;-		-		new_point.right_handle.angle = right_handle.angle;-		new_point.right_handle.length = right_handle.length;-		new_point.right_handle.type = right_handle.type;--		new_point.left_handle.angle = left_handle.angle;-		new_point.left_handle.length = left_handle.length;		-		new_point.left_handle.type = left_handle.type;--		return new_point;-	}--	public double get_distance (double x, double y) {-		return Path.distance (this.x, x, this.y, y);-	}--	public unowned EditPointHandle get_left_handle () {-		return left_handle;-	}-	-	public unowned EditPointHandle get_right_handle () {-		return right_handle;-	}-	-	public void set_color (double r, double g, double b, double a) {-		this.r = r;-		this.g = g;-		this.b = b;-		this.a = a;-	}-	-	public unowned List<EditPoint> get_prev () {-		if (prev == null && next != null && get_next ().prev == get_next ().first ()) {-			prev = get_next ().last ();-		}-		-		if (unlikely (prev == null)) {-			warning ("EditPoint.prev is null");-		}-		-		return (!) prev;-	}--	public unowned List<EditPoint> get_next () {-		if (unlikely (next == null)) {-			warning ("EditPoint.next is null");-		}-		-		return (!) next;-	}-	-	public unowned List<EditPoint> get_list () {-		return get_next ().first ();-	}-	-	public void set_position (double tx, double ty) {-		x = tx;-		y = ty;--		if (unlikely (tx.is_nan () || ty.is_nan ())) {-			warning (@"Invalid point at ($tx,$ty).");-			x = 0;-			y = 0;-		}-	}-	-	public static void to_coordinate (ref double x, ref double y) {-		double xc, yc, xt, yt, ivz;-		Glyph g = MainWindow.get_current_glyph ();-		-		ivz = 1 / g.view_zoom;-		-		xc = (g.allocation.width / 2.0);-		yc = (g.allocation.height / 2.0);-				-		x *= ivz;-		y *= ivz;--		xt = x - xc + g.view_offset_x;-		yt = yc - y - g.view_offset_y;		-		-		x = xt;-		y = yt;-	}-	-	public bool is_selected () {-		return selected;-	}-	-	public void set_selected (bool s) {-		selected = s;-	}-	-	public bool set_active (bool active) {-		bool update = (this.active != active);-		-		if (update) {-			this.active = active;-		-			if (active) {-				r = 0.5;-				g = 0;-				b = 1;-				a = 1;-			} else {-				r = 0;-				g = 1;-				b = 0.5;-				a = 1;-			}-		}-		-		return update;-	}-	-	public int get_index () {-		int i = -1;-		-		if (next == null) return i;-	-		foreach (var d in get_next ().first ()) {-			i++;-			-			if (this == d) {-				return i;-			}-		}-		-		return i;-	}-}--}diff --git a/src/EditPointHandle.vala b/src/EditPointHandle.valadeleted file mode 100644index c03fcc1..0000000--- a/src/EditPointHandle.vala+++ /dev/null@@ -1,167 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Math;--namespace Supplement {-		-public class EditPointHandle  {-	-	public double angle;-	public double length;-	-	public EditPoint parent;-	-	public PointType type;-	-	EditPoint? visual_handle = null;--	static EditPoint none = new EditPoint ();--	public bool active;--	public EditPointHandle.empty() {-		this.parent = none;-		this.angle = 0;-		this.length = 10;-		this.type = PointType.LINE;-		this.active = false;-	}-	-	public EditPointHandle (EditPoint parent, double angle, double length) {-		this.parent = parent;-		this.angle = angle;-		this.length = length;-		this.type = PointType.LINE;-		this.active = false;-	}--	public EditPointHandle copy () {-		EditPointHandle n = new EditPointHandle.empty ();-		n.angle = angle;-		n.length = length;-		n.parent = parent;-		n.type = type;-		n.active = active;-		return n;-	}--	public void set_point_type (PointType point_type) {-		type = point_type;-	}--	public double x () {-		double r = px ();-		-		if (unlikely (r <= -100000)) {-			print_position ();-			move_to (0, 0);-		}-	-		return r;-	}-	-	public double y () {-		double r = py ();-		-		if (unlikely (r <= -100000)) {-			print_position ();-			move_to (0, 0);-		}-		-		return r;-	}-	-	double px () {-		assert ((EditPoint?) parent != null);-		return cos (angle) * length + parent.x;-	}--	double py () {-		assert ((EditPoint?) parent != null);-		return sin (angle) * length + parent.y;-	}-		-	void print_position () {-			warning (@"\nEdit point handle at position $(px ()),$(py ()) is not valid.\n"-				+ @"Type: $(parent.type), "-				+ @"Index: $(parent.get_index ()) of $(parent.get_list ().length ())\n"-				+ @"Angle: $angle, Length: $length.");	-	}-	-	public EditPoint get_point () {-		EditPoint p;-		-		if (visual_handle == null) {-			visual_handle = new EditPoint (0, 0);-		}-	-		p = (!) visual_handle;-		p.x = x ();-		p.y = y ();-		-		return p;-	}-	-	public void move_to_coordinate (double x, double y) {-		double a, b, c;--		a = parent.x - x;-		b = parent.y - y;-		c = a * a + b * b;-		-		if (c == 0) {-			return;-		}-		-		length = sqrt (fabs (c));	-		-		if (c < 0) length = -length;-	-		if (y < parent.y) {-			angle = acos (a / length) + PI;-		} else {-			angle = -acos (a / length) + PI;-		}-		-		if (parent.tie_handles) {-			tie_handle ();-		}-	}--	private void tie_handle () {-		if (this == parent.get_left_handle ()) {-			parent.get_right_handle ().angle = angle - PI;-		} else {-			parent.get_left_handle ().angle = angle - PI;-		}-	}-	-	public void move_delta (double dx, double dy) {-		double px = px () + dx * Glyph.ivz ();-		double py = py () - dy * Glyph.ivz ();-		-		move_to_coordinate (px, py);-	}-	-	public void move_to (double x, double y) {-		EditPoint.to_coordinate (ref x, ref y);-		move_to_coordinate (x, y);-	}-}--}diff --git a/src/EotWriter.vala b/src/EotWriter.valadeleted file mode 100644index 2f32e05..0000000--- a/src/EotWriter.vala+++ /dev/null@@ -1,174 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Math;--namespace Supplement {--class EotWriter : GLib.Object {-	-	string ttf_file_name;-	string eot_file_name;-	-	public EotWriter (string ttf_file, string eot_file) {-		ttf_file_name = ttf_file;-		eot_file_name = eot_file;-	}-	-	public void write () throws Error {-		FontData fd = new FontData ();-		FontData ttf_data = new FontData ();-		OpenFontFormatReader input; -		uint32 ttf_length;-		File ttf_file;-		File eot_file;-		FileInfo file_info;-		OtfInputStream dis;-		DataOutputStream os;-		uint8* data;-		uint32 l;-		NameTable names;-		string tn;-		-		ttf_file = File.new_for_path (ttf_file_name);-		-		if (!ttf_file.query_exists ()) {-			warning (@"EotWriter: file does not exist. $((!) ttf_file.get_path ())");-			return;-		}--		eot_file = File.new_for_path (eot_file_name);-		-		if (eot_file.query_exists ()) {-			warning ("File exists in eot export.");-			return;-		}-		-		os = new DataOutputStream(eot_file.create (FileCreateFlags.REPLACE_DESTINATION));--		file_info = ttf_file.query_info ("*", FileQueryInfoFlags.NONE);-		ttf_length = (uint32) file_info.get_size ();-		 -		if (ttf_length == 0) {-			warning ("TTF file is of zero length.");-			return;-		}--		dis = new OtfInputStream (ttf_file.read ());-		ttf_data.write_table (dis, 0, ttf_length);--		// parse file to find head checksum-		input = new OpenFontFormatReader ();-		input.parse_index (ttf_file_name);- -		names = input.directory_table.name_table;-		-		fd.add_littleendian_u32 (0); // table length-		fd.add_littleendian_u32 (ttf_length);-	-		fd.add_littleendian_u32 (0x00020001); // table format version-		fd.add_littleendian_u32 (0); // flags-		-		// panose-		for (int i = 0; i < 10; i++) {-			fd.add (0);-		}-		-		// according to specification should charset and italic be added the -		// other way around, but it does not work-				-		fd.add (1); // default charset-		fd.add (0); // italic set to regular-				-		fd.add_littleendian_u32 (400); // weight-		fd.add_littleendian_u16 (0); // 0 drm, designer must know that notified-		fd.add_littleendian_u16 (0x504C); // magic number--		// FIXA:-		// unicode ranges -		fd.add_littleendian_u32 (0);-		fd.add_littleendian_u32 (0);-		fd.add_littleendian_u32 (0);-		fd.add_littleendian_u32 (0);-		-		// FIXA:-		// code page range-		fd.add_littleendian_u32 (0);-		fd.add_littleendian_u32 (0);-		-		fd.add_littleendian_u32 (input.get_head_checksum ()); // head checksum adjustment-		-		// reserved-		fd.add_littleendian_u32 (0);-		fd.add_littleendian_u32 (0);-		fd.add_littleendian_u32 (0);-		fd.add_littleendian_u32 (0);-	-		fd.add_littleendian_u16 (0); // padding-		-		tn = names.get_name (names.FONT_NAME);-		fd.add_littleendian_u16 ((uint16) (2 * tn.char_count ())); // strlen of family name-		fd.add_str_littleendian_utf16 (tn);-		-		fd.add_littleendian_u16 (0); // padding-		-		tn = names.get_name (names.SUBFAMILY_NAME);-		fd.add_littleendian_u16 ((uint16) (2 * tn.char_count ())); // strlen of family name-		fd.add_str_littleendian_utf16 (tn);--		fd.add_littleendian_u16 (0); // padding-		-		// FIXA version or name + version-		tn = names.get_name (names.VERSION);-		fd.add_littleendian_u16 ((uint16) (2 * tn.char_count ())); // strlen of family name-		fd.add_str_littleendian_utf16 (tn);-		-		fd.add_littleendian_u16 (0); // padding-		-		tn = names.get_name (names.FULL_FONT_NAME);-		fd.add_littleendian_u16 ((uint16) (2 * tn.char_count ())); // strlen of family name-		fd.add_str_littleendian_utf16 (tn);-		-		fd.add_littleendian_u16 (0); // padding-		-		fd.add_littleendian_u16 (0); // length of root string-		// the root string goes here.-		-		// update length of this table-		fd.seek (0);-		fd.add_littleendian_u32 (fd.length () + ttf_length);-		-		l = fd.length_with_padding ();-		data = fd.table_data;-		for (int i = 0; i < l; i++) {-			os.put_byte (data[i]);-		}--		l = ttf_data.length_with_padding ();-		data = ttf_data.table_data;-		for (int i = 0; i < l; i++) {-			os.put_byte (data[i]);-		}-		-		dis.close ();-		os.close ();-		input.close ();-	}-	-}-		-}diff --git a/src/Expander.vala b/src/Expander.valadeleted file mode 100644index 7e2bd50..0000000--- a/src/Expander.vala+++ /dev/null@@ -1,225 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Math;--namespace Supplement {--public class Expander : GLib.Object {-	-	public double x = 7;-	public double y = 5;--	public double w = 6;-	public double h = 5;-	-	public double margin = 0;-	-	protected double rotation = 0;-	protected double opacity = 0.5;-	-	protected bool active = false;-	protected bool open = false;--	public List<Tool> tool;--	bool persist = false;-	bool unique = false;-	-	public Expander () {-	}--	/** Returns true if tools can be used with the current canvas after-	 * they have been selectes and false if they are a commands to be executed.-	 */-	public bool is_persistent () {-		return persist;-	}--	/** Returns true if all other tools in thid expander should be deselected -	 * when a tool is selected.-	 */-	public bool is_unique () {-		return unique;-	}--	public void set_persistent (bool p) {-		persist = p;-	}--	public void set_unique (bool u) {-		unique = u;-	}-	-	private void update_tool_position () {-		int i = 0;-		double xt = x + 10;-		double yt = y - 4;-		foreach (var t in tool) {-			t.set_properties (xt, yt, w, h);-			-			if (i == 3) {-				yt += 31;-				i = 0;-				xt = x + 10;-			} else {-				i++;-				xt += w + 3 + 25;-			}-		}-	}-	-	public void set_offset (double ty) {-		y = ty;-		-		if (open) {-			update_tool_position ();-		}-	}-	-	public void add_tool (Tool t) {-		tool.append (t);-		update_tool_position ();-		-		t.select_action.connect ((selected) => {-				if (!selected.new_selection) {-					if (is_persistent ()) {-						selected.set_selected (true);-					} else {-						selected.set_selected (false);-					}-				}-			-				MainWindow.get_toolbox ().redraw ((int) x, (int) y, (int) w  + 300, (int) (h + margin));-			-				if (is_unique ()) {-					foreach (var deselected in tool) {-						if (selected.get_id () != deselected.get_id ()) {-							deselected.set_selected (false);-						}-					}-				}--				if (!is_persistent ()) {-						var time = new TimeoutSource(200);-						time.set_callback(() => {-							selected.set_selected (false);-							MainWindow.get_toolbox ().redraw ((int) x, (int) y, (int) w  + 300, (int) (h + margin));-							return false;-						});-						time.attach(null);-				}--				selected.new_selection = false;-			});-	}-	-	public bool is_over (double xp, double yp) {	-		return (x - 7/2.0 <= xp <= x + w  + 7/2.0 && y - 7/2.0<= yp <= y + w + 7/2.0);  -	}-	-	public bool set_active (bool a) {-		bool r = (active != a);-		opacity = (a) ? 1 : 0;-		active = a;-		return r;-	}-	-	public bool is_open () {-		return open;-	}-	-	public virtual bool set_open (bool o) {-		bool r = (open != o);-		rotation = (o) ? Math.PI_2 : 0;-		-		if (o) {-			margin = 35 * (int)((tool.length () / 4.0) + 1) ;-			rotation = Math.PI_2;			-			if (tool.length () % 4 == 0) {-				margin -= 35;-			}-		} else {-			margin = 0;-			rotation = 0; -		}-		-		open = o;-		return r;-	}-	-	public void draw (int wd, int hd, Context cr) {-		double lx, ly;-		double ih2 = 5.4 / 2;-		double iw2 = 5.4 / 2;-		-		// box-		cr.save ();-		cr.set_line_join (LineJoin.ROUND);-		cr.set_line_width(7);-		cr.set_source_rgba (176/255.0, 211/255.0, 230/255.0, opacity);-		cr.rectangle (x, y, w, h);-		cr.stroke ();-		cr.restore ();-		-		// arrow-		cr.save ();-		-		cr.translate (x + w/2, y + h/2);-		cr.rotate (rotation);--		cr.set_line_width (1);-		cr.set_source_rgba (0, 0, 0, opacity);--		cr.new_path ();-		cr.move_to (-iw2, -ih2);-		cr.line_to (iw2, 0);	-		cr.line_to (-iw2, +ih2);--		cr.close_path();-		cr.stroke ();-		cr.restore ();-		-		// separator-		cr.save ();-		lx = x + w + 7;-		ly = y + ih2;-		if (lx < wd) {-			cr.set_line_width(1);-			cr.set_source_rgba (0, 0, 0, 0.2);-			cr.move_to (lx, ly);-			cr.line_to (wd - w - x + 4, ly);	-			cr.stroke ();-		}-		cr.restore ();--	}-	-	public void draw_content (int w, int h, Context cr) {-		if (open) {-			cr.save ();-			foreach (var t in tool) {-				t.draw (cr);-			}-			cr.restore ();-		}-	}-	-}--}diff --git a/src/ExportTool.vala b/src/ExportTool.valadeleted file mode 100644index 8d7d4d4..0000000--- a/src/ExportTool.vala+++ /dev/null@@ -1,538 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--public class ExportTool : Tool {-	private static string? prefered_browser = null;-	private static ExportThread export_thread;-	-	private static unowned Thread<void*> export_thread_handle;--	private static bool stop_export_thread = false;--	public ExportTool (string n) {-		base (n, _("Export glyph to svg file"), 'e', CTRL);-	-		select_action.connect((self) => {-			export_svg_font ();-			// Fixa: add export_glyph_to_svg (); as tool in toolbox-		});-		-		press_action.connect((self, b, x, y) => {-		});--		release_action.connect((self, b, x, y) => {-		});-		-		move_action.connect ((self, x, y)	 => {-		});-	}--	public static bool export_all () {-		Font font = Supplement.get_current_font ();-		bool f;-		-		if (font.get_ttf_export ()) {-			f = export_ttf_font ();-			-			if (!f) {-				warning ("Failed to export font");-				return false;-			}-		}-		-		if (font.get_svg_export ()) {-			f = export_svg_font ();--			if (!f) {-				warning ("Failed to export font");-				return false;-			}-		}-		-		return true;-	}--	public void export_glyph_to_svg () {-		Glyph glyph = MainWindow.get_current_glyph ();-		Font font = Supplement.get_current_font ();-		FontDisplay fd = MainWindow.get_current_display ();-		string glyph_svg;-		string svg_file = @"$(glyph.get_name ()).svg";-		File file = font.get_folder ().get_child (svg_file);-		DataOutputStream os;-		string name = glyph.get_name ();-		int pid = 0;-		-		if (!(fd is Glyph)) {-			return;-		}-		-		try {-			-			if (file.query_exists ()) {-				file.delete ();-			}-			-			os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));	-	-			_("Alphabet");-					-			os.put_string (-"""<?xml version="1.0" standalone="no"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >-<svg """);--			os.put_string (@"xmlns=\"http://www.w3.org/2000/svg\"\n");-			os.put_string (@"\twidth=\"$(glyph.get_width () / Glyph.SCALE)\"\n");-			os.put_string (@"\theight=\"$(glyph.get_height () / Glyph.SCALE)\"\n");-			os.put_string (@"\tid=\"glyph_$(name)\"\n");-			os.put_string (@"\tversion=\"1.1\">\n");-			-			os.put_string (@"\n");-			-			os.put_string (@"<g id=\"$(name)\">\n");-			-			foreach (Path p in glyph.path_list) {-				-				glyph_svg = Svg.to_svg_path (p, glyph);-				-				os.put_string (@"<path ");-				os.put_string (@"style=\"fill:#000000;stroke-width:0px\" ");-				os.put_string (@"d=\"$(glyph_svg)\" id=\"path_$(name)$(pid++)\" />\n");-				-			}-			-			os.put_string ("""</g>-			-</svg>""");-	-		} catch (Error e) {-			stderr.printf (@"Export \"$svg_file\" \n");-			critical (@"$(e.message)");-			status (_("Can't export") + @" $svg_file.");-		}-		-		status (_("Wrote") + @" $svg_file.");-	}--	public static void generate_html_document (string html_file, Font font) {-		File file = File.new_for_path (html_file);-		DataOutputStream os;-		string name = font.get_name ();--		try {-			os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));--os.put_string (-"""<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">-<html>-<head>--	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">-	<title>Lorem ipsum</title>-	-	<style type="text/css">--		@font-face {-			font-family: '"""); os.put_string (@"$(name)");              os.put_string ("""SVG';-			src: url('""");     os.put_string (@"$(name).svg#$(name)");  os.put_string ("""') format('svg');-		}-	""");--	os.put_string ("""-		@font-face {-			font-family: '"""); os.put_string (@"$(name)");              os.put_string ("""TTF';-			src: url('""");     os.put_string (@"$(name).ttf"); os.put_string ("""') format('truetype');-		} -		""");--	os.put_string ("""-		@font-face {-			font-family: '"""); os.put_string (@"$(name)");              os.put_string ("""EOT';-			src: url('""");     os.put_string (@"$(name).eot"); os.put_string ("""');-		} -		-		""");-		-	os.put_string ("""	-		h1 {-			font-weight:normal;-			margin: 0 0 5px 0;-			color: #afafaf;-		}--		h2 {-			font-weight:normal;-			margin: 0 0 5px 0;-			color: #afafaf;-		}-		-		h3 {-			font-weight:normal;-			margin: 0 0 5px 0;-			color: #afafaf;-		}-		-		h4 {-			font-weight:normal;-			margin: 0 0 5px 0;-			color: #afafaf;-		}-		-		p {-			margin: 0 0 5px 0;-			color: #000000;-		}-		-		body {-			margin: 30px 0 0 30px;-		}-		-		div {-			font-family: """);--			os.put_string ("'");-			os.put_string (@"$(name)");-			os.put_string ("EOT'");-			-			os.put_string (", '");-			os.put_string (@"$(name)");-			os.put_string ("SVG'");-			-			os.put_string (", '");-			os.put_string (@"$(name)");-			os.put_string ("TTF'");--			os.put_string (";");-			os.put_string ("""-			margin: 0 0 30px 0;-		}--		h1.bigger {-			font-size: 58pt;-		}--		h2.big {-			font-size: 40pt;-		}-		-		h3.small {-			font-size: 32pt;-		}--		h4.smaller {-			font-size: 18pt;-		}--		h1.percent {-			font-size: 100%;-		}		-		-		p.bigger {-			font-size: 32pt;-		}--		p.big {-			font-size: 24pt;-		}-		-		p.small {-			font-size: 18pt;-		}--		p.smaller {-			font-size: 12pt;-		}--	</style>-""");-	-	os.put_string (-"""	-</head>-<body>--<div>-	<h1 class="bigger">Lorem ipsum</h1>-	<p class="bigger">Dolor sit amet!</p>-</div>--<div>-	<h2 class="big">Hamburgerfonstiv</h2>-	<p class="big">Inspirerande</p>-</div>--<div>-	<h3 class="small">Handgloves & Mittoms</h3>-	<p class="small">Wind blows in Hauges poetry.</p>-</p>-</div>--<div>-	<h4 class="smaller">Headline 16pt</h4>-	<p class="smaller">Ett litet stycke svalhjärta.</p>	-</div>--<div>-	<h4 class="smaller">""" +  _("Alphabet") + """</h4>-	<p class="smaller">""" + _("a b c d e f g h i j k l m n o p q r s t u v w x y z") + """</p>-	<p class="smaller">""" + _("A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") + """</p>-	<p class="smaller">0 1 2 3 4 5 6 7 8 9</p>-</div>--</body>-</html>-""");--		} catch (GLib.Error e) {-			warning (e.message);-		}-	}--	public static bool export_ttf_font () {-		Font font = Supplement.get_current_font ();-		File file = font.get_folder ();-		return export_ttf_font_path (file);-	}-	-	/** Stop a running export thread.*/-	public static void stop_export () {-		lock (stop_export_thread) {-			stop_export_thread = true;-		}-	}-	-	public static bool should_stop () {-		bool r;-		lock (stop_export_thread) {-			r = stop_export_thread;-		}-		return r;-	} -	-	public static string get_birdfont_export () {-		File f;-		-		f = File.new_for_path ("birdfont-export.sh");-		if (f.query_exists ()) {-			return "sh birdfont-export.sh";-		}--		f = File.new_for_path ("../birdfont-export.sh");	-		if (f.query_exists ()) {-			return "sh ../birdfont-export.sh";-		}--		f = File.new_for_path ("birdfont-export.exe");	-		if (f.query_exists ()) {-			return (!) f.get_path ();-		}--		f = File.new_for_path (@"$PREFIX/birdfont-export");-		if (f.query_exists ()) {-			return @"$PREFIX/bin/birdfont-export";-		}--		warning ("Can't find birdfont-export.");-		-		return "birdfont-export";-	}-	-	public static bool export_ttf_font_path (File folder, bool async = true) {-		Font current_font = Supplement.get_current_font ();-		File ttf_file;-		File eot_file;-		string temp_file;-		bool done = true;-		string export_command;-		-		if (Supplement.win32) {-			VersionList.set_suppress_signal (true);-			async = false;-		}-		-		try {-			// create a copy of current font and use it in a separate -			// export thread-			temp_file = current_font.save_backup ();-			ttf_file = folder.get_child (current_font.get_name () + ".ttf");-			eot_file = folder.get_child (current_font.get_name () + ".eot");--			if (ttf_file.query_exists ()) {-				ttf_file.delete ();-			}--			if (eot_file.query_exists ()) {-				eot_file.delete ();-			}-			-			assert (!is_null (temp_file));-			assert (!is_null (ttf_file.get_path ()));-			assert (!is_null (eot_file.get_path ()));--			export_thread = new ExportThread (temp_file, (!) ttf_file.get_path (), (!) eot_file.get_path ());--			try {-				if (async) {-					export_command = @"$(get_birdfont_export ()) --ttf -o $((!) folder.get_path ()) $temp_file";-					-					try {-						Process.spawn_command_line_async (export_command);-					} catch (Error e) {-						stderr.printf (@"Failed to execute \"$export_command\" \n");-						critical (@"$(e.message)");-					}-				}-				-				if (!async) {-					export_thread.run ();-				}-			} catch (ThreadError e) {-				warning (e.message);-				done = false;-			}	-			-		} catch (Error e) {-			critical (@"$(e.message)");-			done = false;-		}--		if (Supplement.win32) {-			VersionList.set_suppress_signal (false);-		}-		-		return done;		-	}-	-	public static bool export_svg_font () {-		Font font = Supplement.get_current_font ();-		return export_svg_font_path (font.get_folder ());-	}-		-	public static bool export_svg_font_path (File folder) {-		Font font = Supplement.get_current_font ();-		string file_name = @"$(font.get_name ()).svg";-		File file;-		SvgFontFormatWriter fo;-		-		try {-			file = folder.get_child (file_name);-			-			if (file.query_exists ()) {-				file.delete ();-			}-			-			fo = new SvgFontFormatWriter ();-			fo.open (file);-			fo.write_font_file (font);-			fo.close ();-		} catch (Error e) {-			critical (@"$(e.message)");-			status (e.message);-			return false;-		}-		-		status (_ ("Wrote font files"));-		return true;-	}--	public class ExportThread : GLib.Object {-		private static string ffi;-		private static string ttf;-		private static string eot;--		public ExportThread (string ffi, string ttf, string eot) {-			this.ffi = ffi.dup ();-			this.ttf = ttf.dup ();-			this.eot = eot.dup ();-		}-		-		public void* run () {-			assert (!is_null (ffi));-			assert (!is_null (ttf));-			assert (!is_null (eot));-			-			if (!should_stop ()) { -				write_ttf ();-			}-			-			if (!should_stop ()) { -				write_eof ();-			}--			return null;-		}-		-		void write_ttf () {-			OpenFontFormatWriter fo = new OpenFontFormatWriter ();-			Font f = new Font ();-			File file = (!) File.new_for_path (ttf);--			return_if_fail (!is_null (ffi));			-			return_if_fail (!is_null (ttf));-			return_if_fail (!is_null (file));-			return_if_fail (!is_null (f));-			return_if_fail (!is_null (fo));-			-			try {-				if (!f.load (ffi, false)) {-					warning (@"Can't read $ffi");-				}-				-				fo.open (file);-				fo.write_ttf_font (f);-				fo.close ();-			} catch (Error e) {-				critical (@"$(e.message)");-			}-		}-		-		void write_eof () {-			EotWriter fo;--			return_if_fail (!is_null (this));-			return_if_fail (!is_null (ttf));-			return_if_fail (!is_null (eot));-			-			fo = new EotWriter (ttf, eot);--			return_if_fail (!is_null (fo));--			try {-				fo.write ();-			} catch (Error e) {-				warning ("EOF conversion falied.");-				critical (@"$(e.message)");-			}-		}--	}-	-	private static void status (string s) {-		TooltipArea status = MainWindow.get_tool_tip ();-		-		if (is_null (status)) {-			return;-		}-		-		status.show_text (s);-	}-}--}diff --git a/src/Font.vala b/src/Font.valadeleted file mode 100644index f21499f..0000000--- a/src/Font.vala+++ /dev/null@@ -1,1439 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Xml;--namespace Supplement {--public class Font : GLib.Object {-	-	GlyphTable glyph_cache = new GlyphTable ();-	-	/** Glyphs that not are tied to a unichar value. */-	GlyphTable unassigned_glyphs = new GlyphTable ();--	List<string> glyph_names = new List<string> ();--	/** Last unassigned index */-	int next_unindexed = 0;-	-	public List <string> background_images = new List <string> ();-	public string background_scale = "1";-		-	/** Top margin */-	public double top_limit;-		-	/** Height of upper case letters. */-	public double top_position;--	/** x-height upper bearing from origo. */-	public double xheight_position;--	/** Base line coordinate from origo. */-	public double base_line;-	-	/** Descender position */-	public double bottom_position;-	-	/** Bottom margon */-	public double bottom_limit;-	-	public string? backup_file = null;-	public string? font_file = null;-	-	bool modified = false;-	-	private string name = "typeface";-	-	bool ttf_export = true;-	bool svg_export = true;--	OpenFontFormatReader otf;-	bool otf_font = false;-	-	public List<string> grid_width = new List<string> ();-	-	public Font () {-		// positions in pixels at first zoom level-		// default x-height should be 60 in 1:1-		top_limit = -66 * Glyph.SCALE;-		top_position = -54 * Glyph.SCALE;-		xheight_position = -38 * Glyph.SCALE;-		base_line = 18 * Glyph.SCALE;-		bottom_position = 38 * Glyph.SCALE;-		bottom_limit = 45 * Glyph.SCALE;-	}--	public void touch () {-		modified = true;-	}--	public bool get_ttf_export () {-		return ttf_export;-	}--	public bool get_svg_export () {-		return svg_export;-	}--	public File get_backgrounds_folder () {-		string fn = @"$(get_name ()) backgrounds";-		File f = Supplement.get_settings_directory ().get_child (fn);-		return f;-	}--	/** Retuns true if the current font has be modified */-	public bool is_modified () {-		return modified;-	}--	/** Full path to this font file. */-	public string get_path () {-		int i = 0;-		-		if (font_file != null) {-			return (!) font_file;-		}-		-		StringBuilder sb = new StringBuilder ();-		sb.append (Environment.get_home_dir ());-		sb.append (@"/$(get_name ()).ffi");-		-		File f = File.new_for_path (sb.str);--		while (f.query_exists ()) {-			sb.erase ();-			sb.append (Environment.get_home_dir ());-			sb.append (@"/$(get_name ())$(++i).ffi");-			f = File.new_for_path (sb.str);-		}-		-		return sb.str;-	}--	public string get_file_name () {-		string p = get_path ();-		int i = p.last_index_of ("/");-		-		if (i == -1) {-			i = p.last_index_of ("\\");-		}-		-		p = p.substring (i + 1);-		-		return p;-	}-		-	public File get_folder () {-		string p = get_path ();-		int i = p.last_index_of ("/");-		-		if (i == -1) {-			i = p.last_index_of ("\\");-		}-		-		p = p.substring (0, i);-		-		return File.new_for_path (p);-	}-	-	public double get_height () {-		double r = base_line - top_position;-		return (r > 0) ? r : -r;-	}-		-	public void set_name (string name) {-		this.name = name;-	}-	-	public string get_name () {-		return name;-	}--	public void print_all () {-		stdout.printf ("Assigned:\n");		-		glyph_cache.for_each((g) => {-			stdout.printf (@"$(g.get_name ())\n");-		});--		stdout.printf ("\n");-		stdout.printf ("Unssigned:\n");		-		unassigned_glyphs.for_each ((g) => {-			stdout.printf (@"$(g.get_name ())\n");-		});--	}--	public bool has_glyph (string n) {-		return get_glyph (n) != null;-	}--	public Glyph get_nonmarking_return () {-		Glyph ret;-		-		if (has_glyph ("nonmarkingreturn")) {-			return (!) get_glyph ("nonmarkingreturn");-		}-				-		ret = new Glyph ("nonmarkingreturn", '\r');-		ret.set_unassigned (false);-		ret.left_limit = 0;-		ret.right_limit = 0;-		ret.remove_empty_paths ();-		-		assert (ret.path_list.length () == 0);-		-		return ret;-	}-		-	public Glyph get_null_character () {-		Glyph n;-		-		if (has_glyph ("null")) {-			return (!) get_glyph ("null");-		}-		-		n = new Glyph ("null", '\0');-		n.set_unassigned (false);-		n.left_limit = 0;-		n.right_limit = 0;-		n.remove_empty_paths ();-		-		assert (n.path_list.length () == 0);-		-		return n;-	}-	-	public Glyph get_space () {-		Glyph n;-		-		if (has_glyph (" ")) {-			return (!) get_glyph (" ");-		}--		if (has_glyph ("space")) {-			return (!) get_glyph ("space");-		}-				-		n = new Glyph ("space", ' ');-		n.set_unassigned (false);-		n.left_limit = 0;-		n.right_limit = 27;-		n.remove_empty_paths ();-		-		assert (n.path_list.length () == 0);-		-		return n;		-	}-	-	public Glyph get_not_def_character () {-		Glyph g;--		Path p;-		Path i;-		-		if (has_glyph (".notdef")) {-			return (!) get_glyph (".notdef");-		}-		-		g = new Glyph (".notdef", 0);-		p = new Path ();-		i = new Path ();-		-		g.set_unassigned (true);-		g.left_limit = -33;-		g.right_limit = 33;-		-		p.add (-20, -top_position - 5);-		p.add (20, -top_position - 5);-		p.add (20, -base_line + 5);-		p.add (-20, -base_line + 5);-		p.close ();-		-		i.add (-15, -top_position - 10);-		i.add (15, -top_position - 10);-		i.add (15, -base_line + 10);-		i.add (-15, -base_line + 10);-		i.reverse ();-		i.close ();--		g.add_path (i);-		g.add_path (p);--		return g;-	}-		-	public void add_glyph (Glyph glyph) {-		GlyphCollection? gc = get_glyph_collection (glyph.get_name ());--		if (gc == null) {-			add_glyph_collection (new GlyphCollection (glyph));-		}-	}--	public void add_glyph_collection (GlyphCollection glyph_collection) {-		GlyphCollection? gc = get_cached_glyph_collection (glyph_collection.get_name ());-		-		if (glyph_collection.get_name () == "") {-			warning ("Refusing to insert glyph with name \"\", null character should be named null.");-			return;-		}-		-		if (gc != null) {-			warning ("glyph has already been added");-			return;-		}-		-		if (glyph_collection.get_current ().is_unassigned ()) {-			unassigned_glyphs.insert (glyph_collection);-		} else {-			glyph_cache.insert (glyph_collection);-		}--		if (!has_name (glyph_collection.get_name ())) {-			glyph_names.append (glyph_collection.get_name ());-			glyph_names.sort (strcmp);-		}-	}-	-	public string get_name_for_character (unichar c) {-		// if some glyph is already mapped to unichar, return it's name-		uint i = 0;-		Glyph? gl;-		Glyph g;-		StringBuilder sb;-		-		while ((gl = get_glyph_indice (i++)) != null) {-			g = (!) gl;-			-			if (g.unichar_code == c) {-				return g.name;-			}-		}-						-		// otherwise return some default name, possibly from unicode database-		if (c == 0) {-			return ".null".dup ();-		}-		-		sb = new StringBuilder ();-		sb.append_unichar (c);-		return sb.str;		-	}-	-	public bool has_name (string name) {-		foreach (string n in glyph_names) {-			if (n == name) {-				return true;-			}-		}-		-		return false;-	}-	-	public void delete_glyph (string glyph) {-		glyph_names.remove_all (glyph);-		glyph_cache.remove (glyph);-	}-	-	/** Obtain all versions and alterntes for this glyph. */-	public GlyphCollection? get_glyph_collection (string glyph) {-		GlyphCollection? gc = get_cached_glyph_collection (glyph);-		Glyph? g;-		-		if (gc == null && otf_font) {-			// load it from otf file if we need to-			g = otf.read_glyph (glyph);-			-			if (g != null) {-				add_glyph_callback ((!) g);-				return get_cached_glyph_collection (glyph);-			}-		}-			-		return gc;-	}--	public GlyphCollection? get_cached_glyph_collection (string glyph) {-		GlyphCollection? gc = null;-		-		gc = glyph_cache.get (glyph);-		-		if (gc == null) {-			gc = unassigned_glyphs.get (glyph);-		}-		-		return gc;-	}-	-	public Glyph? get_glyph (string glyph) {-		GlyphCollection? gc = get_glyph_collection (glyph);-		-		if (gc == null) {-			return null;-		}-		-		return ((!)gc).get_current ();-	}-	-	public Glyph? get_glyph_indice (unichar glyph_indice) {-		GlyphCollection? gc;-		string n;-		-		if (!(0 <= glyph_indice < glyph_names.length ())) {-			return null;-		}-		-		n = glyph_names.nth (glyph_indice).data;-		gc = get_glyph_collection (n);-		-		if (gc != null) {-			return ((!) gc).get_current ();-		}-		-		return null;-	}-	-	public void add_background_image (string file) {-		background_images.append (file);-	}--	public double get_kerning (string a, string b) {-		Glyph? gl = get_glyph (a);-		Glyph g;-		-		if (gl == null) {-			warning (@"glyph \"$a\" does not exist cannot obtain kerning");-			return 0;-		}-		-		g = (!) gl;-		-		return g.get_kerning (b);-	}--	public void set_kerning (string a, string b, double val) {-		Glyph? gl;-		Glyph g;-		-		gl = get_glyph (a);-		-		if (gl == null) {-			warning ("glyph is not parsed yet cannot add kerning");-			return;-		}-		-		g = (!) gl;-		g.add_kerning (b, val);-	}-		-	/** Returns path to backup file. */-	public string save_backup () {-		File dir = Supplement.get_backup_directory ();-		File? temp_file = null;-		int i = 0;--		if (backup_file == null) {-			temp_file = dir.get_child (@"current_font_$i.ffi");-			-			while (((!) temp_file).query_exists ()) {-				i++;-				temp_file = dir.get_child (@"current_font_$i.ffi");-			}-			-			backup_file = ((!) temp_file).get_path ();-		}--		assert (backup_file != null);		-		write_font_file ((!) backup_file);-		-		return (!) backup_file;-	}-	-	public bool save (string path) {-		bool r = write_font_file (path);-		-		if (r) {-			font_file = path;-		}-		-		modified = false;-		add_thumbnail ();-		Preferences.add_recent_files (get_path ());-		-		return r;-	}--	public bool write_font_file (string path) {-		try {-			File file = File.new_for_path (path);--			if (file.query_file_type (0) == FileType.DIRECTORY) {-				stderr.printf (@"Can not save font. $path is a directory.");-				return false;-			}-			-			if (file.query_exists ()) {-				file.delete ();-			}-			-			DataOutputStream os = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION));-			-			os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>""");-			os.put_string ("\n");-				-			os.put_string ("<font>\n");-			-			os.put_string ("\n");-			os.put_string (@"<name>$(get_name ())</name>\n");-			-			os.put_string ("\n");-			os.put_string (@"<ttf-export>$(ttf_export)</ttf-export>\n");--			os.put_string ("\n");-			os.put_string (@"<svg-export>$(svg_export)</svg-export>\n");-			-			os.put_string ("\n");-			os.put_string ("<lines>\n");-			-			os.put_string (@"\t<top_limit>$top_limit</top_limit>\n");-			os.put_string (@"\t<top_position>$top_position</top_position>\n");-			os.put_string (@"\t<x-height>$xheight_position</x-height>\n");-			os.put_string (@"\t<base_line>$base_line</base_line>\n");-			os.put_string (@"\t<bottom_position>$bottom_position</bottom_position>\n");-			os.put_string (@"\t<bottom_limit>$bottom_limit</bottom_limit>\n");-			-			os.put_string ("</lines>\n\n");--			foreach (string gv in grid_width) {-				os.put_string (@"<grid width=\"$(gv)\"/>\n");-			}-			-			if (GridTool.sizes.length () > 0) {-				os.put_string ("\n");-			}-			-			os.put_string (@"<background scale=\"$(background_scale)\" />\n");-			os.put_string ("\n");-			-			if (background_images.length () > 0) {-				os.put_string (@"<images>\n");-				-				foreach (string f in background_images) {-					os.put_string (@"\t<img src=\"$f\"/>\n");-				}-			-				os.put_string (@"</images>\n");-				os.put_string ("\n");-			}-			-			glyph_cache.for_each ((gc) => {-				try {-					bool selected;-					foreach (Glyph g in gc.get_version_list ().glyphs) {-						selected = (g == gc.get_current ());-						write_glyph (g, selected, os);-					}-				} catch (GLib.Error ef) {-					stderr.printf (@"Failed to save $path \n");-					stderr.printf (@"$(ef.message) \n");-				}-			});-		-			glyph_cache.for_each ((gc) => {-				try {-					Glyph glyph = gc.get_current ();-					-					foreach (Kerning k in glyph.kerning) {-						string l, r;-						Glyph? gr = get_glyph (k.glyph_right);-						Glyph glyph_right;-						-						if (gr == null) {-							warning ("kerning glyph that does not exist.");-						}-						-						glyph_right = (!) gr;-						-						l = Font.to_hex_code (glyph.unichar_code);-						r = Font.to_hex_code (glyph_right.unichar_code);-										-						os.put_string (@"<hkern left=\"U+$l\" right=\"U+$r\" kerning=\"$(k.val)\"/>\n");-					}-				} catch (GLib.Error e) {-					warning (e.message);-				}-			});--			glyph_cache.for_each ((gc) => {-				GlyphBackgroundImage bg;-				-				try {-					string data;-					-					foreach (Glyph g in gc.get_version_list ().glyphs) {--						if (g.get_background_image () != null) {-							bg = (!) g.get_background_image ();-							data = bg.get_png_base64 ();-							-							if (!bg.is_valid ()) {-								continue;-							}-							-							os.put_string (@"<background-image sha1=\"");-							os.put_string (bg.get_sha1 ());-							os.put_string ("\" ");-							os.put_string (" data=\"");-							os.put_string (data);-							os.put_string ("");-							os.put_string ("\" />\n");	-						}-					}-				} catch (GLib.Error ef) {-					stderr.printf (@"Failed to save $path \n");-					stderr.printf (@"$(ef.message) \n");-				}-			});-						-			os.put_string ("</font>");-			-		} catch (GLib.Error e) {-			stderr.printf (@"Failed to save $path \n");-			stderr.printf (@"$(e.message) \n");-			return false;-		} catch (GLib.IOError e) {-			stderr.printf (@"Failed to save $path \n");-			stderr.printf (@"$(e.message) \n");-			return false;-		}-		-		return true;-	}--	private void write_glyph (Glyph g, bool selected, DataOutputStream os) throws GLib.Error {-		os.put_string (@"<glyph unicode=\"$(to_hex (g.unichar_code))\" selected=\"$selected\" left=\"$(g.left_limit)\" right=\"$(g.right_limit)\">\n");--		foreach (var p in g.path_list) {-			if (p.points.length () == 0) {-				continue;-			}-			-			os.put_string ("\t<object>");--			foreach (var ep in p.points) {-				os.put_string (@"<point x=\"$(ep.x)\" y=\"$(ep.y)\" ");-				-				if (ep.right_handle.type == PointType.CURVE) {-					os.put_string (@"right_angle=\"$(ep.right_handle.angle)\" ");-					os.put_string (@"right_length=\"$(ep.right_handle.length)\" ");-				}-				-				if (ep.left_handle.type == PointType.CURVE) {-					os.put_string (@"left_angle=\"$(ep.left_handle.angle)\" ");-					os.put_string (@"left_length=\"$(ep.left_handle.length)\" ");						-				}-				-				if (ep.right_handle.type == PointType.CURVE || ep.left_handle.type == PointType.CURVE) {-					os.put_string (@"tie_handles=\"$(ep.tie_handles)\" ");-				}-				-				os.put_string ("/>");-				-			}-			-			os.put_string ("</object>\n");-		}-		-		GlyphBackgroundImage? bg = g.get_background_image ();-		-		if (bg != null) {-			GlyphBackgroundImage background_image = (!) bg;--			double pos_x = background_image.img_x;-			double pos_y = background_image.img_y;-			-			double scale_x = background_image.img_scale_x;-			double scale_y = background_image.img_scale_y;-			-			double rotation = background_image.img_rotation;-			-			if (background_image.is_valid ()) {-				os.put_string (@"\t<background sha1=\"$(background_image.get_sha1 ())\" x=\"$pos_x\" y=\"$pos_y\" scale_x=\"$scale_x\" scale_y=\"$scale_y\" rotation=\"$rotation\"/>\n");-			}-		}-		-		os.put_string ("</glyph>\n\n"); --	}--	public void set_font_file (string path) {-		font_file = path;-		modified = false;-	}--	public uint length () {-		return glyph_names.length ();-	}--	public bool is_empty () {-		return (glyph_names.length () == 0);-	}--	public bool load (string path, bool recent = true) {-		bool loaded = false;-		-		try {-			otf_font = false;--			while (grid_width.length () > 0) {-				grid_width.remove_link (grid_width.first ());-			}--			while (glyph_names.length () > 0) {-				glyph_names.remove_link (glyph_names.first ());-			}-			-			glyph_cache.remove_all ();-			unassigned_glyphs.remove_all ();-			-			if (path.has_suffix (".ffi")) {-				loaded = parse_file (path);-				font_file = path;-			}-			-			if (Supplement.experimental) {-				if (path.has_suffix (".ttf")) {-					loaded = parse_otf_file (path);-					font_file = path;-				}-			}-			-			if (recent) {-				add_thumbnail ();-				Preferences.add_recent_files (get_path ());-			}-		} catch (GLib.Error e) {-			warning (e.message);-			return false;-		}-		-		return loaded;-	}--	private void add_thumbnail () {-		File f = Supplement.get_thumbnail_directory ().get_child (@"$((!) get_file_name ()).png");-		Glyph? gl = get_glyph ("a");-		Glyph g;-		ImageSurface img;-		ImageSurface img_scale;-		Context cr;-		double scale;-		-		if (gl == null) {-			gl = get_glyph_indice (4);-		}		-		-		if (gl == null) {-			gl = get_not_def_character ();-		}-		-		g = (!) gl;--		img = g.get_thumbnail ();-		scale = 70.0 / img.get_width ();-		-		if (scale > 70.0 / img.get_height ()) {-			scale = 70.0 / img.get_height ();-		}-		-		if (scale > 1) {-			scale = 1;-		}--		img_scale = new ImageSurface (Format.ARGB32, (int) (scale * img.get_width ()), (int) (scale * img.get_height ()));-		cr = new Context (img_scale);-		-		cr.scale (scale, scale);--		cr.save ();-		cr.set_source_surface (img, 0, 0);-		cr.paint ();-		cr.restore ();-		-		img_scale.write_to_png ((!) f.get_path ());-	}--	/** Callback function for loading glyph in a separate thread. */-	public void add_glyph_callback (Glyph g) {-		GlyphCollection? gcl;-		GlyphCollection gc;-		-		gcl = get_cached_glyph_collection (g.get_name ());-		-		if (gcl != null) {-			warning (@"glyph \"$(g.get_name ())\" does already exist");-		}-		-		if (g.is_unassigned ()) {-			gc = new GlyphCollection (g);-			-			if (g.name == "") {-				warning ("refusing to insert glyph without name");-				g.name = @"($(++next_unindexed))";-				return;-			}-			-			add_glyph_collection ((!) gc);-		} else if (gcl == null) {-			gc = new GlyphCollection (g);-			add_glyph_collection ((!) gc);-		} else {-			stderr.printf (@"Glyph collection does already have an entry for $(g.get_name ()) char $((uint64) g.unichar_code).\n");-		}-				-		// take xheight from appropriate lower case letter-		// xheight_position = estimate_xheight ();-	}--	public bool parse_otf_file (string path) throws GLib.Error {-		otf = new OpenFontFormatReader ();-		otf_font = true;-		-		otf.parse_index (path);-		-		foreach (string n in otf.get_all_names ()) {-			glyph_names.append (n);-		}-		-		return true;-	}-	-	public bool parse_file (string path) throws GLib.Error {-		Parser.init ();-		-		Xml.Doc* doc;-		Xml.Node* root;-		Xml.Node* node;--		// set this path as file for this font-		font_file = path;-		-		// empty cache and fill it with new glyphs from disk-		glyph_cache.remove_all ();-		unassigned_glyphs.remove_all ();--		while (background_images.length () > 0) {-			background_images.remove_link (background_images.first ());-		}--		create_background_files (path);--		doc = Parser.parse_file (path);-		root = doc->get_root_element ();-		-		if (root == null) {-			delete doc;-			return false;-		}--		node = root;-		-		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {-		-			if (iter->name == "glyph") {-				parse_glyph (iter);-			}--			if (iter->name == "lines") {-				parse_font_boundries (iter);-			}--			if (iter->name == "grid") {-				parse_grid (iter);-			}--			if (iter->name == "background") {-				parse_background (iter);-			}-			-			if (iter->name == "images") {-				parse_background_selection (iter);-			}--			if (iter->name == "name") {-				name = iter->children->content;-			}--			if (iter->name == "hkern") {-				parse_kerning (iter);-			}--			if (iter->name == "ttf-export") {-				ttf_export = bool.parse (iter->children->content);-			}			--			if (iter->name == "svg-export") {-				svg_export = bool.parse (iter->children->content);-			}-			-		}-    -		delete doc;-		Parser.cleanup ();--		return true;-	}-	-	private void create_background_files (string path) {-		Xml.Doc* doc = Parser.parse_file (path);-		Xml.Node* root;-		Xml.Node* node;-		-		root = doc->get_root_element ();-		-		if (root == null) {-			delete doc;-			return;-		}--		node = root;-		-		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {-			if (iter->name == "name") {-				name = iter->children->content;-			}-			-			if (iter->name == "background-image") {-				parse_background_image (iter);-			}-		}-    -		delete doc;-		Parser.cleanup ();-	}-	-	private void parse_kerning (Xml.Node* node) {-		string attr_name;-		string attr_content;-		string left = "";-		string right = "";-		string kern = "";--		StringBuilder b;-		-		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {-			attr_name = prop->name;-			attr_content = prop->children->content;-			-			if (attr_name == "left") {-				b = new StringBuilder ();-				b.append_unichar (to_unichar (attr_content));-				left = @"$(b.str)";-			}--			if (attr_name == "right") {-				b = new StringBuilder ();-				b.append_unichar (to_unichar (attr_content));-				right = @"$(b.str)";-			}-			-			if (attr_name == "kerning") {-				kern = attr_content;-			}-		}-		-		set_kerning (left, right, double.parse (kern));-	}-	-	private void parse_background_image (Xml.Node* node) -		requires (node != null)-	{-		string attr_name;-		string attr_content;-		-		string file = "";-		string data = "";-		-		File img_dir;-		File img_file;-		FileOutputStream file_stream;-		DataOutputStream png_stream;-		-		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {-			return_if_fail (!is_null (prop->name));-			return_if_fail (!is_null (prop->children));-			return_if_fail (!is_null (prop->children->content));-			-			attr_name = prop->name;-			attr_content = prop->children->content;-			-			if (attr_name == "sha1") {-				file = attr_content;-			}-			-			if (attr_name == "data") {-				data = attr_content;-			}-		}-		-		img_dir = get_backgrounds_folder ().get_child ("parts");--		if (!img_dir.query_exists ()) {-			DirUtils.create ((!) img_dir.get_path (), 0xFFFFFF);-		}-	-		img_file = img_dir.get_child (@"$(file).png");-		-		if (img_file.query_exists ()) {-			return;-		}-		-		try {-			file_stream = img_file.create (FileCreateFlags.REPLACE_DESTINATION);-			png_stream = new DataOutputStream (file_stream);--			png_stream.write (Base64.decode (data));-			png_stream.close ();	-		} catch (GLib.Error e) {-			warning (e.message);-		}-	}-	-	private void parse_background (Xml.Node* node) -		requires (node != null)-	{-		string attr_name;-		string attr_content;-				-		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {-			-			return_if_fail (!is_null (prop->name));-			return_if_fail (!is_null (prop->children));-			return_if_fail (!is_null (prop->children->content));-			-			attr_name = prop->name;-			attr_content = prop->children->content;-			-			if (attr_name == "scale") {-				background_scale = attr_content;-			}-		}-	}-	-	private void parse_background_selection (Xml.Node* node) {-		string attr_name = "";-		string attr_content;-		-		return_if_fail (node != null);-				-		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {-			if (iter->name == "img") {-				for (Xml.Attr* prop = iter->properties; prop != null; prop = prop->next) {-					attr_name = prop->name;-					attr_content = prop->children->content;-					-					if (attr_name == "src") {-						background_images.append (attr_content);-					}-				}-			}-		}-	}-	-	private void parse_grid (Xml.Node* node) {-		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {-			string attr_name = prop->name;-			string attr_content = prop->children->content;-			-			if (attr_name == "width") {-				grid_width.append (attr_content);-			}-		}		-	}-	-	private void parse_font_boundries (Xml.Node* node) {-		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {-			if (iter->name == "top_limit") top_limit = parse_double_from_node (iter);-			if (iter->name == "top_position") top_position = parse_double_from_node (iter);-			if (iter->name == "x-height") xheight_position = parse_double_from_node (iter);-			if (iter->name == "base_line") base_line = parse_double_from_node (iter);-			if (iter->name == "bottom_position") bottom_position = parse_double_from_node (iter);-			if (iter->name == "bottom_limit") bottom_limit = parse_double_from_node (iter);-		}			-	}-	-	private double parse_double_from_node (Xml.Node* iter) {-		double d;-		bool r = double.try_parse (iter->children->content, out d);-		-		if (unlikely (!r)) {-			string? s = iter->content;-			if (s == null) warning (@"Content is null for node $(iter->name)\n");-			else warning (@"Failed to parse double for \"$(iter->content)\"\n");-		}-		-		return (r) ? d : 0;-	}-	-	/** Parse one glyph. */-	public void parse_glyph (Xml.Node* node) {-		string name = "";-		int left = 0;-		int right = 0;-		unichar uni = 0;-		int version = 0;-		bool selected = false;-		Glyph g;-		GlyphCollection? gc;-		-		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {-			string attr_name = prop->name;-			string attr_content = prop->children->content;-			StringBuilder b;-			-			if (attr_name == "unicode") {-				uni = to_unichar (attr_content);-				b = new StringBuilder ();-				b.append_unichar (uni);-				name = b.str;-			}--			if (attr_name == "left") {-				left = int.parse (attr_content);-			}-			-			if (attr_name == "right") {-				right = int.parse (attr_content);-			}-			-			if (attr_name == "version") {-				version = int.parse (attr_content);-			}-			-			if (attr_name == "selected") {-				selected = bool.parse (attr_content);-			}-		}--		g = new Glyph (name, uni);-		-		g.left_limit = left;-		g.right_limit = right;--		parse_content (g, node);-		-		// todo: use disk thread and idle add this:-		-		gc = get_glyph_collection (g.get_name ());-		-		if (g.get_name () == "") {-			warning ("No name set for glyph.");-		}-				-		if (gc == null) {-			gc = new GlyphCollection (g);-			add_glyph_collection ((!) gc);-		} else {-			((!)gc).insert_glyph (g, selected);-		}-		-	}-	-	/** Parse visual objects and paths */-	private void parse_content (Glyph g, Xml.Node* node) {-		Xml.Node* i;-		return_if_fail (node != null);-		-		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {-			if (iter->name == "object") {-				Path p = new Path ();-				-				for (i = iter->children; i != null; i = i->next) {-					if (i->name == "point") {-						parse_point (p, i);-					}					-				}--				p.close ();-				g.add_path (p);-			}-			-			if (iter->name == "background") {-				parse_background_scale (g, iter);-			}-		}-	}-	-	private void parse_background_scale (Glyph g, Xml.Node* node) {-		GlyphBackgroundImage img;-		GlyphBackgroundImage? new_img = null;-		-		string attr_name = "";-		string attr_content;-		-		File img_file = get_backgrounds_folder ().get_child ("parts");-		-		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {-			attr_name = prop->name;-			attr_content = prop->children->content;-			-			if (attr_name == "sha1") {-				img_file = img_file.get_child (attr_content + ".png");--				if (!img_file.query_exists ()) {-					warning (@"Background file has not been created yet. $((!) img_file.get_path ())");-				}-				-				new_img = new GlyphBackgroundImage ((!) img_file.get_path ());-				g.set_background_image ((!) new_img);-			}-		}-		-		if (unlikely (new_img == null)) {-			warning (@"No source for image found for $attr_name in $(g.name)");-			return;-		}-	-		img = (!) new_img;-	-		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {-			attr_name = prop->name;-			attr_content = prop->children->content;-							-			if (attr_name == "x") {-				img.img_x = double.parse (attr_content);-			}-			-			if (attr_name == "y") {-				img.img_y = double.parse (attr_content);-			}	--			if (attr_name == "scale_x") {-				img.img_scale_x = double.parse (attr_content);-			}--			if (attr_name == "scale_y") {-				img.img_scale_y = double.parse (attr_content);-			}-						-			if (attr_name == "rotation") {-				img.img_rotation = double.parse (attr_content);-			}-		}-		-		img.set_position(img.img_x, img.img_y);	-	}-	-	private void parse_point (Path p, Xml.Node* iter) {-		double x = 0;-		double y = 0;-		-		PointType type = PointType.LINE;-		-		double angle_right = 0;-		double angle_left = 0;-		-		double length_right = 0;-		double length_left = 0;-		-		PointType type_right = PointType.LINE;-		PointType type_left = PointType.LINE;-		-		bool tie_handles = false;-		-		EditPoint ep;-		-		for (Xml.Attr* prop = iter->properties; prop != null; prop = prop->next) {-			string attr_name = prop->name;-			string attr_content = prop->children->content;-						-			if (attr_name == "x") x = double.parse (attr_content);-			if (attr_name == "y") y = double.parse (attr_content);-			-			if (attr_name == "right_angle") {-				type = PointType.CURVE; // FIXA: delete, maybe-			}	-			-			if (attr_name == "right_angle") {-				type_right = PointType.CURVE;-			}	--			if (attr_name == "left_angle") {-				type_left = PointType.CURVE;-			}-						-			if (attr_name == "right_angle") angle_right = double.parse (attr_content);-			if (attr_name == "right_length") length_right = double.parse (attr_content);-			if (attr_name == "left_angle") angle_left = double.parse (attr_content);-			if (attr_name == "left_length") length_left = double.parse (attr_content);-			-			if (attr_name == "tie_handles") tie_handles = bool.parse (attr_content);-		}-		-		ep = new EditPoint (x, y);-		-		ep.type = type;-		-		ep.right_handle.angle = angle_right;-		ep.right_handle.length = length_right;-		ep.right_handle.type = type_right;-		-		ep.left_handle.angle = angle_left;-		ep.left_handle.length = length_left;-		ep.left_handle.type = type_left;-		-		ep.tie_handles = tie_handles;-		-		p.add_point (ep);-	}-	-	public bool restore_backup () {-		string? b = present_backup_file ();-		-		if (b == null) return false;-		-		return parse_file ((!)b);-	}-	-	private string? present_backup_file () {-		try {-			File dir = Supplement.get_settings_directory ();-			var files = dir.enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, 0);--			// What if we have more than one backup file left?-			FileInfo? file_info;-			while ((file_info = files.next_file ()) != null) {-				FileInfo fi = (!) file_info;-				if (fi.get_name ().index_of ("current_font_") != -1) {-					File f = dir.get_child (fi.get_name ());-					backup_file = f.get_path ();-					return backup_file;-				}-			}-		} catch (GLib.Error e) {-			stderr.printf (@"Failed to load backup\n");-			stderr.printf (@"$(e.message) \n");-		}-		-		return null;-	}-	-	/** Delete temporary rescue files. */-	public void delete_backup () {-		if (backup_file == null) return;--		try {-			File f = File.new_for_path ((!) backup_file);-			f.delete ();		-		} catch (GLib.Error e) {-			stderr.printf (@"Failed to delete backup\n");-			stderr.printf (@"$(e.message) \n");-		}-	}-		-	public static string to_hex (unichar ch) {-		StringBuilder s = new StringBuilder ();-		s.append ("U+");-		s.append (to_hex_code (ch));-		return s.str;-	}--	public static string to_hex_code (unichar ch) {-		StringBuilder s = new StringBuilder ();-		-		uint8 a = (uint8)(ch & 0x00000F);-		uint8 b = (uint8)((ch & 0x0000F0) >> 4 * 1);-		uint8 c = (uint8)((ch & 0x000F00) >> 4 * 2);-		uint8 d = (uint8)((ch & 0x00F000) >> 4 * 3);-		uint8 e = (uint8)((ch & 0x0F0000) >> 4 * 4);-		uint8 f = (uint8)((ch & 0xF00000) >> 4 * 5);-		-		if (e != 0 || f != 0) {-			s.append (oct_to_hex (f));-			s.append (oct_to_hex (e));-		}-		-		if (c != 0 || d != 0) {-			s.append (oct_to_hex (d));-			s.append (oct_to_hex (c));-		}-				-		s.append (oct_to_hex (b));-		s.append (oct_to_hex (a));-		-		return s.str;-	}--	public static unichar to_unichar (string unicode) {-		int index = 2;-		int i = 0;-		unichar c;-		unichar rc = 0;-		bool r;--		if (unlikely (unicode.index_of ("U+") != 0)) {-			warning ("All unicode values must begin with U+");-		}-		-		while (r = unicode.get_next_char (ref index, out c)) {-			rc <<= 4;-			rc += hex_to_oct (c);-			-			return_val_if_fail (++i <= 6, '\0');-		}--		return rc;-	}-	-	private static string oct_to_hex (uint8 o) {-		switch (o) {-			case 10: return "a";-			case 11: return "b";-			case 12: return "c";-			case 13: return "d";-			case 14: return "e";-			case 15: return "f";-		}--		return_val_if_fail (0 <= o <= 9, "-".dup ());-		-		return o.to_string ();-	}--	private static uint8 hex_to_oct (unichar o) {-		StringBuilder s = new StringBuilder ();-		s.append_unichar (o);-	-		switch (o) {-			case 'a': return 10;-			case 'b': return 11;-			case 'c': return 12;-			case 'd': return 13;-			case 'e': return 14;-			case 'f': return 15;-		}-		-		return_val_if_fail ('0' <= o <= '9', 0);-		-		return (uint8) (o - '0');-	}--}--}diff --git a/src/FontDisplay.vala b/src/FontDisplay.valadeleted file mode 100644index 820bbee..0000000--- a/src/FontDisplay.vala+++ /dev/null@@ -1,239 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Birdfont;--namespace Supplement {--/** Javascripts in webkit do use this callback. */-public class PropertyFunction {-	public delegate void PropertyCallback (string val);-	public PropertyCallback call;-}--public abstract class FontDisplay : GLib.Object {-	-	List<string> property = new List<string> ();-	List<PropertyFunction> call = new List<PropertyFunction> ();-	-	/** Queue redraw area */-	public signal void redraw_area (double x, double y, double w, double h);-	-	/** Name of symbol or set of symbols in display selections like the tab panel. */-	public virtual string get_name () {-		return "";-	}-	-	public virtual bool is_html_canvas () {-		return false;-	}--	public static string path_to_uri (string path) {-		string uri = path;-		string wp;-		-		// wine uri hack-		if (Supplement.win32) {-			wp = wine_to_unix_path (uri);-			-			if (find_file (wp, "").query_exists ()) {-				uri = wp;-			}--			if (uri.index_of ("\\") > -1) {-				uri = uri.replace ("\\", "/");-			}-		}--		if (uri.index_of ("/") == 0) {-			uri = @"file://$uri";-		} else {-			uri = @"file:///$uri";-		}-		-		return uri;-	}--	public virtual string get_uri () {-		return "";-	}-	-	public virtual string get_html_file () {-		return "index.html";-	}-	-	public virtual string get_html () {-		return "";-	}-	-	public virtual void draw (Allocation allocation, Context cr) {-	}-	-	public virtual void selected_canvas () {-	}-	-	public virtual void key_press (uint keyval) {-	}-	-	public virtual void key_release (uint keyval) {-	}-	-	public virtual void motion_notify (double x, double y) {-	}-	-	public virtual void button_release (int button, double x, double y) {-	}-	-	public virtual void button_press (uint button, double x, double y) {-	}--	public virtual void double_click (uint button, double ex, double ey) {-	}	--	public virtual void zoom_in () {-	}-	-	public virtual void zoom_out () {-	}-	-	public virtual void zoom_max () {-	}-	-	public virtual void zoom_min () {-	}-	-	public virtual void reset_zoom () {-	}-	-	public virtual void store_current_view () {-		warning ("store_current_view not implemented for this class.");-	}-	-	public virtual void restore_last_view () {-		warning ("restore_last_view not implemented for this class.");-	}--	public virtual void next_view () {-		warning ("next_view not implemented for this class.");-	}-	-	public virtual void scroll_wheel_up (double x, double y) {-	}-	-	public virtual void scroll_wheel_down (double x, double y) {-	}--	public virtual void undo () {-	}--	public static File find_layout_dir () {-		return find_file (null, "layout");-	}--	public void add_html_callback (string prop, PropertyFunction.PropertyCallback cb) {-		PropertyFunction pf = new PropertyFunction ();-		pf.call = cb;-		property.append (prop);-		call.append (pf);-	}-	-	public void process_property (string prop) {-		string k, v;-		int i, j;-		PropertyFunction cb;-		-		if (prop == "" || prop == "done") {-			return;-		}-		-		i = prop.index_of (":");-		-		if (i <= 0) {-			return;-		}-		-		k = prop.substring (0, i);-		v = prop.substring (i + 1);-		-		j = 0;-		-		foreach (string p in property) {-			if (p == k) {-				break;-			}-			j++;-		}-		-		if (j >= property.length ()) {-			warning (@"key  \"$k\" not found in property list, value: $v");-			return;-		}-		-		cb = call.nth (j).data;-		cb.call (v);-	}--	public static File find_file (string? dir, string name) {-		File f;-		string d = (dir == null) ? "" : (!) dir;-		-		f = get_file (Supplement.exec_path + "/", name + "/");-		if (likely (f.query_exists ())) return f;-		-		f = get_file (Supplement.exec_path + "/" + d + "/", name);-		if (likely (f.query_exists ())) return f;-		-		f = get_file ("./" + d + "/", name);-		if (likely (f.query_exists ())) return f;		--		f = get_file ("../" + d + "/", name);-		if (likely (f.query_exists ())) return f;--		f = get_file (".\\" + d + "\\", name);-		if (likely (f.query_exists ())) return f;--		f = get_file ("", name);-		if (likely (f.query_exists ())) return f;--		f = get_file (d + "\\", name);-		if (likely (f.query_exists ())) return f;--		f = get_file ("/usr/local/share/birdfont/" + d + "/", name);-		if (likely (f.query_exists ())) return f;--		f = get_file ("/usr/share/birdfont/" + d + "/", name);-		if (likely (f.query_exists ())) return f;--		f = get_file (@"$PREFIX/share/birdfont/" + d + "/", name);-		if (likely (f.query_exists ())) return f;-				-		warning (@"Did not find file $name in $d");-			-		return f;		-	}--	public static File get_file (string? path, string name) {-		StringBuilder fn = new StringBuilder ();-		string p = (path == null) ? "" : (!) path;-		fn.append (p);-		fn.append ((!) name);-		return File.new_for_path (fn.str);-	}-}--}diff --git a/src/Glyph.vala b/src/Glyph.valadeleted file mode 100644index 2440a0b..0000000--- a/src/Glyph.vala+++ /dev/null@@ -1,1719 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Math;--namespace Supplement {--public class Glyph : FontDisplay {--	public static const double SCALE = 1.0;--	// Background image-	GlyphBackgroundImage? background_image = null;-	bool background_image_visible = true;-	-	// Glyph zoom level-	public double view_zoom = 0.01;-	public double view_offset_x = 0;-	public double view_offset_y = 0;-	List<ZoomView> zoom_list = new List<ZoomView> ();-	int zoom_list_index = 0;-	-	// Paths-	public List<Path> path_list;-	public List<Path> active_paths = new List<Path> ();--	// Control points-	public EditPoint? new_point_on_path = null;-	public EditPoint? flipping_point_on_path = null;-	public EditPoint? last_added_edit_point = null;-		-	// The point where edit event begun -	double pointer_begin_x = 0;-	double pointer_begin_y = 0;--	// Current pointer position-	double motion_x = 0;-	double motion_y = 0;-		-	// Zoom area-	double zoom_x1 = 0;-	double zoom_y1 = 0;-	double zoom_x2 = 0;-	double zoom_y2 = 0;-	bool zoom_area_is_visible = false;-	-	bool move_view = false;-	public double move_offset_x = 0;-	public double move_offset_y = 0;--	public Allocation allocation;-	-	public string name;--	public double left_limit;-	public double right_limit;-	-	// x-height, lsb, etc.-	public List<Line> vertical_help_lines = new List<Line> ();-	public List<Line> horizontal_help_lines = new List<Line> ();-	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 unassigned = false;-	public unichar unichar_code = 0;-	-	List<Glyph> undo_list = new List<Glyph> ();-	-	public List<Kerning> kerning = new List<Kerning> ();-	-	string glyph_sequence = "";-	bool open = true;-	-	public Glyph (string name, unichar unichar_code = 0) {-		this.name = name;-		this.unichar_code = unichar_code;-		-		path_list.append (new Path ());-		-		add_help_lines ();--		left_limit = -28 * SCALE;-		right_limit = 28 * SCALE;-	}--	public Glyph.no_lines (string name, unichar unichar_code = 0) {-		this.name = name;-		this.unichar_code = unichar_code;--		path_list.append (new Path ());-	}-	-	public void clear_active_paths () {-		while (active_paths.length () > 0) {-			active_paths.remove_link (active_paths.first ());-		}-	}-	-	public void add_active_path (Path? p) {-		if (p != null) {-			-			foreach (Path pi in active_paths) {-				if (pi == p) {-					return;-				}-			}-			-			active_paths.append ((!) p);-		}-	}-	-	public Path? get_active_path () {-		return_if_fail (!is_null(active_paths));-		-		if (!is_null(active_paths.last ())) {-			return null;-		}-		-		return active_paths.last ().data;-	}-	-	-	public void add_kerning (string right_glyph, double val) {-		Kerning? kl = null;--		foreach (Kerning k in kerning) {-			if (k.glyph_right == right_glyph) {-				kl = k;-			}-		}-		-		if (kl == null) {-			kerning.append (new Kerning (right_glyph, val));-		} else {-			((!)kl).val = val;-		}-	}--	public double get_kerning (string right_glyph) {-		foreach (Kerning k in kerning) {-			if (k.glyph_right == right_glyph) {-				return k.val;-			}-		}-		-		return 0;-	}-		-	public void set_unassigned (bool u) {-		unassigned = u;-	}-	-	public bool is_unassigned () {-		return unassigned;-	}--	public void boundries (out double x1, out double y1, out double x2, out double y2) {-		if (path_list.length () == 0) {-			x1 = 0;-			y1 = 0;-			x2 = 0;-			y2 = 0;-			return;-		}--		x1 = path_list.first ().data.xmin;-		y1 = path_list.first ().data.ymin;-		x2 = path_list.first ().data.xmax;-		y2 = path_list.first ().data.ymax;-				-		foreach (Path p in path_list) {-			p.update_region_boundries ();-						-			if (p.xmin < x1) x1 = p.xmin;-			if (p.xmax > x2) x2 = p.xmax;-			if (p.ymin < y1) y1 = p.ymin;-			if (p.ymax > y2) y2 = p.ymax;-		}-	}-	-	/** @return centrum pixel for x coordinates. */-	public static double xc () {-		double c = MainWindow.get_current_glyph ().allocation.width / 2.0;-		return c;	-	}--	/** @return centrum pixel for y coordinates. */-	public static double yc () {-		double c = MainWindow.get_current_glyph ().allocation.height / 2.0;-		return c;-	}--	/** @return 1/view_zoom */-	public static double ivz () {-		return 1 / MainWindow.get_current_glyph ().view_zoom;-	}--	public void resized () {-		double a, b, c, d;-		-		if (view_zoom > 1) {	-			a = vertical_help_lines.first ().data.get_coordinate ();-			c = horizontal_help_lines.first ().data.get_coordinate ();-			-			b = vertical_help_lines.first ().data.get_coordinate ();-			d = horizontal_help_lines.first ().data.get_coordinate ();-		-			view_offset_x -= a - b;-			view_offset_y -= c - d;-		}-	}--	public void set_background_image (GlyphBackgroundImage? b) {-		GlyphBackgroundImage bg;-		-		if (b == null) {-			background_image = null;-		}-		-		bg = (!) b;-		-		background_image = bg;-		-		Supplement.get_current_font ().touch ();-	}-	-	public GlyphBackgroundImage? get_background_image () {-		return (!) background_image;-	}-		-	public override void scroll_wheel_up (double x, double y) {-		if (KeyBindings.has_ctrl ()) {-			zoom_in_at_point (x, y);-		} else if (KeyBindings.has_alt ()) { -			view_offset_x -= 10 / view_zoom;-		} else {-			view_offset_y -= 10 / view_zoom;-		}-		-		redraw_area (0, 0, allocation.width, allocation.height);-	}-	-	public override void scroll_wheel_down (double x, double y) {-		if (KeyBindings.has_ctrl ()) {-			zoom_out_at_point (x, y);-		} else	if (KeyBindings.has_alt ()) { -			view_offset_x += 10 / view_zoom;-		} else {-			view_offset_y += 10 / view_zoom;-		}-		-		redraw_area (0, 0, allocation.width, allocation.height);-	}-	-	public void add_path (Path p) {-		path_list.append (p);-	}-	-	public override void selected_canvas () {-		add_help_lines ();-		KeyBindings.singleton.set_require_modifier (false);-		glyph_sequence = Preferences.get ("glyph_sequence");-	}-	-	private void remove_lines () {-		while (vertical_help_lines.length () != 0) {-			vertical_help_lines.remove_link (vertical_help_lines.first ());-		}--		while (horizontal_help_lines.length () != 0) {-			horizontal_help_lines.remove_link (horizontal_help_lines.first ());-		}		-	}-	-	public void add_help_lines () {-		remove_lines ();--		assert (!is_null (Supplement.get_current_font ()));--		double bgt = Supplement.get_current_font ().top_limit;-		Line top_margin_line = new Line ("top margin", bgt, false);-		top_margin_line.set_color (0.7, 0, 0, 0.5);-		top_margin_line.position_updated.connect ((pos) => {-			Supplement.get_current_font ().top_limit = pos;-		});-						-		double thp = Supplement.get_current_font ().top_position;-		Line top_line = new Line ("top", thp, false);-		top_line.position_updated.connect ((pos) => {-				Font f = Supplement.get_current_font ();-				f.top_position = pos;-			});-		-		double xhp = Supplement.get_current_font ().xheight_position;-		Line xheight_line = new Line ("x-height", xhp, false);-		xheight_line.set_color (33 / 255.0, 68 / 255.0, 120 / 255.0, 166 / 255.0);-		xheight_line.position_updated.connect ((pos) => {				-				Font f = Supplement.get_current_font ();-				f.xheight_position = pos;-			});--		double xbl = Supplement.get_current_font ().base_line;-		Line base_line = new Line ("baseline", xbl, false);-		base_line.position_updated.connect ((pos) => {-				Font f = Supplement.get_current_font ();-				f.base_line = pos;-			});--		-		double bp = Supplement.get_current_font ().bottom_position;-		Line bottom_line = new Line ("bottom", bp, false);-		bottom_line.set_color (33 / 255.0, 68 / 255.0, 120 / 255.0, 166 / 255.0);-		bottom_line.position_updated.connect ((pos) => {-				Supplement.get_current_font ().bottom_position = pos;-			});--		double bgb = Supplement.get_current_font ().bottom_limit;-		Line bottom_margin_line = new Line ("bottom margin", bgb, false);-		bottom_margin_line.set_color (0.7, 0, 0, 0.5);-		bottom_margin_line.position_updated.connect ((pos) => {-			Supplement.get_current_font ().bottom_limit = pos;-		});-							-		Line left_line = new Line ("left", left_limit, true);-		left_line.position_updated.connect ((pos) => {-				left_limit = pos;-			});-		-		Line right_line = new Line ("right", right_limit, true);-		right_line.position_updated.connect ((pos) => {-				right_limit = pos;-			});-		-		// lists of help lines are sorted and lines are added only if -		// they are important for a particular glyph.-		-		// left to right-		add_line (left_line);-		add_line (right_line);--		bool glyph_has_top = has_top_line ();--		// top to bottom-		add_line (top_margin_line);-		top_margin_line.set_visible (margin_boundries_visible);-		-		add_line (top_line);-		top_line.set_visible (glyph_has_top || xheight_lines_visible);-		-		add_line (xheight_line);-		xheight_line.set_visible (!glyph_has_top || xheight_lines_visible);-		-		add_line (base_line);-		-		add_line (bottom_line);-		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);-	}-	-	bool has_top_line () {-		return !unichar_code.islower () || CharDatabase.has_ascender (unichar_code);-	}-	-	/** Show both x-height and top lines. */-	public bool get_xheight_lines_visible () {-		return xheight_lines_visible;-	}--	/** Show both x-height and top lines. */-	public void set_xheight_lines_visible (bool x) {-		xheight_lines_visible = x;-		add_help_lines ();-	}-		-	public void set_margin_lines_visible (bool m) {-		margin_boundries_visible = m;-		add_help_lines ();-	}-	-	public bool get_margin_lines_visible () {-		return margin_boundries_visible;-	}-	-	public void remove_empty_paths () {-		foreach (var p in path_list) {-			if (p.points.length () < 2) {-				delete_path (p);-			}-		}-	}-	-	public void delete_path (Path p) {-		path_list.remove_all (p);-	}-	-	public string get_svg_data () {-		return Svg.to_svg_glyph (this, 1 / SCALE);-	}-	-	public int get_height () -		requires (vertical_help_lines.length () >= 2)-	{-		unowned List<Line> a = horizontal_help_lines.first ();-		unowned List<Line> b = horizontal_help_lines.last ();-		-		return (int) Math.fabs (a.data.pos / SCALE - b.data.pos / SCALE); -	}-	-	public double get_width () {-		return Math.fabs (right_limit / SCALE - left_limit / SCALE);-	}--	public unichar get_unichar () {-		return unichar_code;-	}--	public bool get_show_help_lines () {-		return show_help_lines;-	}-	-	public void redraw_help_lines () {-		redraw_area (0, 0, allocation.width, allocation.height);-	}-	-	public void set_show_help_lines (bool hl) {-		show_help_lines = hl;-	}-	-	private void add_line (Line line) {-		if (line.is_vertical ()) {-			vertical_help_lines.append (line);-			line.list_item = vertical_help_lines.last ();-		} else {-			horizontal_help_lines.append (line);-			line.list_item = horizontal_help_lines.last ();-		}--		sort_help_lines ();-		-		line.queue_draw_area.connect ((x, y, w, h) => {-			this.redraw_area (x, y, w, h);-		});-	}-	-	public void sort_help_lines () {-		vertical_help_lines.sort ((a, b) => {-			return (int) (a.get_pos () - b.get_pos ());-		});-		-		horizontal_help_lines.sort ((a, b) => {-			return (int) (a.get_pos () - b.get_pos ());-		});-	}-	-	public override string get_name () {-		return name;-	}-			-	private void help_line_event (double x, double y) {-		double mx = x;-		double my = y;-		-		if (GridTool.is_visible ()) {-			GridTool.tie (ref mx, ref my);-		}-		-		foreach (Line line in get_all_help_lines ()) {-			line.event_move_to (mx, my, x, y, allocation);-		}-	}--	public override void key_release (uint keyval) {	-		Tool t;-		t = MainWindow.get_toolbox ().get_current_tool ();-		t.key_release_action (t, keyval);		-	}--	public override void key_press (uint keyval) {	-		Tool t = MainWindow.get_toolbox ().get_current_tool ();-		t.key_press_action (t, keyval);-	}-	-	/** Delete edit point from path. */-	public void delete_edit_point (EditPoint ep) {-		foreach (Path p in path_list) {-			if (p.points.length () > 0) {-				p.delete_edit_point (ep);-			} else {-				delete_path (p);-			}-		}-	}-	-	public override void motion_notify (double x, double y) {-		Tool t;-		t = MainWindow.get_toolbox ().get_current_tool ();-		-		if (move_view && KeyBindings.has_ctrl ()) {-			move_view_offset  (x, y);-			return;-		}--		t.move_action (t, (int) x, (int) y);--		if (Supplement.show_coordinates) {-			motion_x = x * ivz () - xc () + view_offset_x;-			motion_y = yc () - y * ivz () - view_offset_y;-		}-	-		help_line_event (x, y);	-	}-	-	public override void button_release (int button, double ex, double ey) {-		bool line_moving = false;-		-		if (KeyBindings.has_ctrl ()) {-			move_view = false;-			return;-		}-		-		foreach (Line line in get_all_help_lines ()) {-			if (!line.set_move (false)) {-				line_moving = true;-			}-		}--		if (!line_moving) {-			Tool t = MainWindow.get_toolbox ().get_current_tool ();-			t.release_action (t, (int) button, (int) ex, (int) ey);-		}-		-		update_view ();-	}--	private unowned List<Line> get_all_help_lines () {-		while (all_lines.length () > 0) all_lines.delete_link (all_lines.first ());-		-		all_lines.concat (vertical_help_lines.copy ());-		all_lines.concat (horizontal_help_lines.copy ());-		-		if (GridTool.is_visible ()) {-			all_lines.concat (GridTool.get_vertical_lines ().copy ());-			all_lines.concat (GridTool.get_horizontal_lines ().copy ());-		}-		-		return all_lines;-	}-		-	public void update_view () {-		MainWindow.get_glyph_canvas ().redraw ();-	}-	-	public override void double_click (uint button, double ex, double ey) {	-		Tool t = MainWindow.get_toolbox ().get_current_tool ();-		t.double_click_action (t, (int) button, (int) ex, (int) ey);-	}-	-	public override void button_press (uint button, double ex, double ey) {				-		pointer_begin_x = ex;-		pointer_begin_y = ey;-		-		foreach (Line line in get_all_help_lines ()) {-			if (line.button_press ()) {-				return;-			}-		}-				-		if (KeyBindings.has_ctrl ()) {-			move_view = true;-			move_offset_x = view_offset_x;-			move_offset_y = view_offset_y;-		} else {-			Tool t = MainWindow.get_toolbox ().get_current_tool ();-			t.press_action (t, (int) button, (int) ex, (int) ey);-		}-	}--	/** Insert new edit point for current path on the appropriate zoom-	 * level.-	 */-	public EditPoint add_new_edit_point (int x, int y) {-		insert_edit_point (x, y);-		-		return_if_fail (active_paths.length () > 0);-		-		last_added_edit_point = active_paths.last ().data.get_last_point ();-		return (!) last_added_edit_point;-	}--	public EditPoint get_last_edit_point () {-		return_val_if_fail (last_added_edit_point != null, new EditPoint ());-		return (!) last_added_edit_point;-	}--	/** Move view port centrum to this coordinate. */-	public void set_center (double x, double y) {-		x -= allocation.width / 2.0;-		y -= allocation.height / 2.0;-		-		view_offset_x += x / view_zoom;-		view_offset_y += y / view_zoom;		-	}--	public void set_zoom_from_area () {-		double x = Math.fmin (zoom_x1, zoom_x2);-		double y = Math.fmin (zoom_y1, zoom_y2);--		double w = Math.fabs (zoom_x1 - zoom_x2);-		double h = Math.fabs (zoom_y1 - zoom_y2);-		-		double view_zoom_x, view_zoom_y;-		double ivz, off;--		view_offset_x += x / view_zoom;-		view_offset_y += y / view_zoom;-			-		view_zoom_x = allocation.width * view_zoom / w;-		view_zoom_y = allocation.height * view_zoom / h;-				-		// TODO: there is a max zoom level: probably ivz > 0.1-		-		if (view_zoom_x * allocation.width < view_zoom_y * allocation.height) {-			view_zoom = view_zoom_x;-			ivz = 1 / view_zoom;--			off = (view_zoom / view_zoom_y) * allocation.height / view_zoom;	-			off = allocation.height/view_zoom - off;-			off /= 2;-			-			view_offset_y -= off;--		} else {-			view_zoom = view_zoom_y;-			ivz = 1 / view_zoom;--			off = (view_zoom / view_zoom_x) * allocation.width / view_zoom;	-			off = allocation.width / view_zoom - off;-			off /= 2;			--			view_offset_x -= off;-		}-		-		zoom_area_is_visible = false;-		store_current_view ();-	}-	-	public void show_zoom_area (int sx, int sy, int nx, int ny) {-		double x, y, w, h;-				-		set_zoom_area (sx, sy, nx, ny);-		-		zoom_area_is_visible = true;-		-		x = Math.fmin (zoom_x1, zoom_x2) - 50;-		y = Math.fmin (zoom_y1, zoom_y2) - 50;--		w = Math.fabs (zoom_x1 - zoom_x2) + 100;-		h = Math.fabs (zoom_y1 - zoom_y2) + 100;-				-		redraw_area ((int)x, (int)y, (int)w, (int)h);-	}--	public void set_zoom_area (int sx, int sy, int nx, int ny) {-		zoom_x1 = sx;-		zoom_y1 = sy;-		zoom_x2 = nx;-		zoom_y2 = ny;-	}--	public static double path_coordinate_x (double x) {-		Glyph g = MainWindow.get_current_glyph ();-		return_if_fail (g.view_zoom != 0);-		return x * g.ivz () - g.xc () + g.view_offset_x;-	}--	public static int reverse_path_coordinate_x (double x) {-		Glyph g = MainWindow.get_current_glyph ();-		return_if_fail (g.view_zoom != 0);-		return (int) ((x - g.view_offset_x + g.xc ()) * g.view_zoom);-	}--	public static double path_coordinate_y (double y) {-		Glyph g = MainWindow.get_current_glyph ();-		return g.yc () - y * g.ivz () - g.view_offset_y;-	}--	public static int reverse_path_coordinate_y (double y) {-		Glyph g = MainWindow.get_current_glyph ();-		y = ((y + g.view_offset_y - g.yc ()) * g.view_zoom);-		return (int) (-y);-	}--	public bool select_path (double x, double y) {-		Path? p = null;-		bool found = false;-		-		foreach (Path pt in path_list) {-			if (pt.is_over (x, y)) {-				p = pt;-				found = true;-			}-		}-		-		if (!KeyBindings.has_shift ()) {-			clear_active_paths ();-		}-		-		add_active_path (p);-		-		return found;-	}-	-	public bool is_over_selected_path (double x, double y) {-		foreach (Path pt in active_paths) {-			if (pt.is_over (x, y)) {-				return true;-			}-		}-		return false;		-	}-		-	public void queue_redraw_path (Path path) {-		redraw_path (path.xmin, path.ymin, path.xmax, path.ymax);-	}-	-	private void redraw_path (double xmin, double ymin, double xmax, double ymax) {-		int yc = (int)(allocation.height / 2.0);--		double yta = yc - ymin - view_offset_y;-		double ytb = yc - ymax - view_offset_y;--		double xta = -view_offset_x - xmin;-		double xtb = -view_offset_x - xmax;--		redraw_area ((int)xtb - 10, (int)yta - 10, (int)(xtb - xta) + 10, (int) (yta - ytb) + 10);-	}--	public Path get_closeset_path (double x, double y) {-		double d;-		EditPoint ep = new EditPoint ();-		-		Path min_point = new Path ();-		double min_distance = double.MAX;--		double xt = path_coordinate_x (x);-		double yt = path_coordinate_y (y);--		foreach (Path p in path_list) {-			if (p.is_over (xt, yt)) {-				return p;-			}-		}--		foreach (Path p in path_list) {-			if (p.points.length () == 0) continue;-			-			p.get_closest_point_on_path (ep, xt, yt);-			d = Math.pow (ep.x - xt, 2) + Math.pow (ep.y - yt, 2);-			-			if (d < min_distance) {-				min_distance = d;-				min_point = p;-			}--		}-		-		// a path without any editpoints-		if (path_list.length () > 0) {-			return path_list.first ().data;-		}--		if (unlikely (min_distance == double.MAX)) {-			warning (@"No path found in path_list. Length: $(path_list.length ())");-			-			if (path_list.length () > 0) {-				stderr.printf (@"p.points.length () $(path_list.first ().data.points.length ()) \n");-			}-		}-		-		return min_point;-	}-	-	private EditPoint insert_edit_point (double x, double y) {-		unowned List<Path> paths;-		double xt, yt;-		bool added;-		Path np;-		EditPoint inserted;-		-		if (active_paths.length () == 0) {-			np = new Path ();-			clear_active_paths ();-			active_paths.append (np);-			path_list.append (np);-		}-		-		paths = path_list;-		-		xt = path_coordinate_x (x);-		yt = path_coordinate_y (y);-	-		added = false;--		if (new_point_on_path != null) {-			return_if_fail (active_paths.length () > 0);--			Path p = new Path ();-			EditPoint e = (!) new_point_on_path;--			return_if_fail (e.prev != null);-			-			p.add_point (e);-			e.type = PointType.CURVE;-			-			e.right_handle.type = PointType.CURVE;-			e.left_handle.type = PointType.CURVE;-			-			paths.append (p);-			add_active_path (p);-			flipping_point_on_path = new_point_on_path;-			new_point_on_path = null;-			added = true;-			return e;-		}-		-		if (!added) {-			foreach (Path p in paths) {-				if (p.is_open ()) {-					p.add (xt, yt);-					clear_active_paths ();-					add_active_path (p);-					added = true;-					break;-				}-			}-		}--		if (!added) {-			foreach (Path p in paths) {-				if (p.is_over (xt, yt)) {-					p.add (xt, yt);-					paths.append (p);-					added = true;-					redraw_area (0, 0, allocation.width, allocation.height);-					break;-				}-			}-		}-		-		if (!added) {-			if (paths.length () > 0 && paths.last ().data.is_open ()) {-				paths.last().data.add (xt, yt);-			}-			-			foreach (var p in path_list) {-				p.close ();-			}-			-			np = new Path ();-			paths.append (np);-			np.add (xt, yt);-			-			clear_active_paths ();-			add_active_path (np);-		}-	-		assert (active_paths.length () > 0);-		assert (active_paths.last ().data.points.length () > 0);-		-		inserted = active_paths.last ().data.points.data;-		-		return inserted;-	}-	-	public void move_selected_edit_point_delta (EditPoint selected_point, double dx, double dy) {-		double px = selected_point.x + xc () - view_offset_x;-		double py = -1 * (selected_point.y - yc () + view_offset_y);-		-		px *= view_zoom;-		py *= view_zoom;-		-		move_selected_edit_point (selected_point, dx + px, dy + py);-	}-	-	public void move_selected_edit_point (EditPoint selected_point, double x, double y) {		-		double xc, yc, xt, yt;-		double ivz = 1 / view_zoom;-		EditPoint p;-		-		Supplement.get_current_font ().touch ();--		xc = (allocation.width / 2.0);-		yc = (allocation.height / 2.0);-				-		x *= ivz;-		y *= ivz;-	-		xt = x - xc + view_offset_x;-		yt = yc - y - view_offset_y;-		-		// redraw control point-		redraw_area ((int)(x - 4*view_zoom), (int)(y - 4*view_zoom), (int)(x + 3*view_zoom), (int)(y + 3*view_zoom));-		-		// update position of selected point-		selected_point.set_position (xt, yt);-		-		if (view_zoom >= 2) {-			redraw_area (0, 0, allocation.width, allocation.height);-		} else {-			redraw_last_stroke (x, y);-		}-		-		if (flipping_point_on_path != null) {-			p = (!) flipping_point_on_path;-			p.recalculate_handles (x, y);-			redraw_area (0, 0, allocation.width, allocation.height);-		}		-	}-	-	private void redraw_last_stroke (double x, double y) {-		// redraw line, if we have more than one new point on path-		double px = 0;-		double py = 0;-		int tw = 0;-		int th = 0;--		double xc = (allocation.width / 2.0);--		if (active_paths.length () == 0) {-			return;-		}-		-		foreach (Path path in active_paths) {-			EditPoint? pt = path.get_second_last_point ();-			if (pt != null) {-				EditPoint p = (!) pt;-				-				px = p.x + xc;-				py = p.y - xc;-								-				tw = (px > x) ? (int) (px - x) : (int) (x - px);-				th = (py > y) ? (int) (py - y) : (int) (y - py);-				-				if (px > x) px -= tw + 60;-				if (py > y) py -= th + 60;-				-			} else {-				px = x - 60;-				py = y - 60;-				tw = 0;-				th = 0;-			}-		}-		-		redraw_area ((int)px - 20, (int)py - 20, tw + 120, th + 120); 		-	}-	-	public Path? get_last_path () -		ensures (result != null)-	{-		return_if_fail (path_list.length () > 0);-		return path_list.last ().data;-	}-	-	public bool has_active_path () {-		return active_paths.length () > 0;-	}-	-	public bool is_open () {-		return open;-	}-	-	/** Close all editable paths and return false if no path have boon closed. -	 * Paths without area (points and lines) will be deleted.-	 */-	public bool close_path () {-		bool r = false;-		-		foreach (var p in path_list) {-			if (p.is_editable ()) {-				r = true;-				p.close ();-			}-		}-		-		clear_active_paths ();-		new_point_on_path = null;-		flipping_point_on_path = null;-		-		delete_invisible_paths ();-		-		redraw_area (0, 0, allocation.width, allocation.height);-		-		open = false;-		-		return r;-	}--	public void open_path () {	-		foreach (var p in path_list) {-			p.set_editable (true);-		}-		-		open = true;-		redraw_area (0, 0, allocation.width, allocation.height);-	}-	-	public void redraw_path_region (Path p) {-		int x, y, w, h;-			-		p.update_region_boundries ();-		-		x = reverse_path_coordinate_x (p.xmin);-		y = reverse_path_coordinate_x (p.xmin);-		w = reverse_path_coordinate_x (p.xmax) - x;-		h = reverse_path_coordinate_x (p.ymax) - y; // FIXME: redraw path-					-		redraw_area (x, y, w, h);		-	}--	/** Delete all paths without area. */-	private void delete_invisible_paths () {-		foreach (var p in path_list) {-			-			if (p.points.length () < 2) {-				path_list.remove (p);-				continue;-			}-			-			if (p.points.length () == 2) {-				if (p.points.first ().data.type == PointType.LINE && p.points.last ().data.type == PointType.LINE) {-					path_list.remove (p);-					continue;-				}-			}-			-		}-	}--	public Line get_line (string name) {		-		foreach (Line line in vertical_help_lines) {-			if (likely (line.get_label () == name)) {-				return line;-			}-		}-		-		foreach (Line line in horizontal_help_lines) {-			if (likely (line.get_label () == name)) {-				return line;-			}-		}-		-		warning (@"No line with label $name found");-		return new Line ("Err");-	}-	-	public override void zoom_in () {-		set_zoom_area (10, 10, allocation.width - 10, allocation.height - 10);-		set_zoom_from_area ();-		update_view ();-	}-	-	public override void zoom_out () {-		double w = allocation.width;-		int n = (int) (10 * ((w - 10) / allocation.width));-		set_zoom_area (-n, -n, allocation.width + n, allocation.height + n);-		set_zoom_from_area ();-		update_view ();-	}-	-	public override void zoom_max () {-		default_zoom ();-	}-	-	public override void zoom_min () {-		double ax =  1000;-		double ay =  1000;-		double bx = -1000;-		double by = -1000;-		-		int iax, iay, ibx, iby;-		-		reset_zoom ();--		foreach (var p in path_list) {-			p.update_region_boundries ();-			-			if (p.points.length () > 2) {-				if (p.xmin < ax) ax = p.xmin;-				if (p.ymin < ay) ay = p.ymin;			-				if (p.xmax > bx) bx = p.xmax;-				if (p.ymax > by) by = p.ymax;-			}-		}-	-		if (ax == 1000) return; // empty page-		-		iax = (int) ((ax + view_offset_x + allocation.width / 2.0) * view_zoom);-		iay = (int) ((-ay + view_offset_y + allocation.height / 2.0) * view_zoom);-		ibx = (int) ((bx + view_offset_x + allocation.width / 2.0) * view_zoom);-		iby = (int) ((-by + view_offset_y + allocation.height / 2.0) * view_zoom);--		show_zoom_area (iax, iay, ibx, iby); // set this later on button release-		set_zoom_from_area ();-		zoom_out (); // add some margin-		-		redraw_area (0, 0, allocation.width, allocation.height);-		-	}--	public override void store_current_view () {-		if (zoom_list_index + 1 < zoom_list.length ()) {-			unowned List<ZoomView> n = zoom_list.nth (zoom_list_index);-			while (n != zoom_list.last ()) zoom_list.delete_link (zoom_list.last ());-		}-		-		zoom_list.append (new ZoomView (view_offset_x, view_offset_y, view_zoom, allocation));-		zoom_list_index = (int) zoom_list.length () - 1;-		if (zoom_list.length () > 50) zoom_list.delete_link (zoom_list.first ());-	}-	-	public override void restore_last_view () -		requires (zoom_list.length () > 0)-	{		-		if (zoom_list_index - 1 < 0 || zoom_list.length () == 0)-			return;-		-		zoom_list_index--;-			-		ZoomView z = zoom_list.nth (zoom_list_index).data;-			-		view_offset_x = z.x;-		view_offset_y = z.y;-		view_zoom = z.zoom;-		allocation = z.allocation;-	}--	public override void next_view () -		requires (zoom_list.length () > 0)-	{-		if (zoom_list_index + 1 >= zoom_list.length ())-			return;-		-		zoom_list_index++;-		-		ZoomView z = zoom_list.nth (zoom_list_index).data;-			-		view_offset_x = z.x;-		view_offset_y = z.y;-		view_zoom = z.zoom;-		allocation = z.allocation;		-	}-	-	public override void reset_zoom () {-		view_offset_x = 0;-		view_offset_y = 0;-		-		set_zoom (1);-		-		store_current_view ();-	}-	-	/** Get x-height or top line. */-	public Line get_upper_line () -		requires (horizontal_help_lines.length () > 2)-	{-		-		if (has_top_line () || xheight_lines_visible) {-			return get_line ("top");-		}-		-		return get_line ("x-height");-	}--	/** Get base line. */-	public Line get_lower_line () -		requires (horizontal_help_lines.length () > 2)-	{-		return horizontal_help_lines.last ().prev.data;-	}-		-	/** Set default zoom. See default_zoom. */-	public void set_default_zoom () {-		int l, t, b, r;-		-		double bottom = 0;-		double top = 0;-		double left = 0;-		double right = 0;-	-		unowned List<Line>? n = horizontal_help_lines;-		unowned List<Line>? v = vertical_help_lines;-		-		if (unlikely (n == null)) {-				warning ("n == null");-				warning (@"Can not set default zoom for $name, help lines are not available.");-		}-		-		return_if_fail (v != null);-		return_if_fail (vertical_help_lines.length () != 0);-		return_if_fail (horizontal_help_lines.length () != 0);--		reset_zoom ();-		-		bottom = -get_lower_line ().get_pos ();-		top = -get_upper_line ().get_pos ();--		left = vertical_help_lines.last ().data.get_pos ();-		right = vertical_help_lines.first ().data.get_pos ();-		-		l = reverse_path_coordinate_x (left);-		t = reverse_path_coordinate_y (top);-		r = reverse_path_coordinate_x (right); -		b = reverse_path_coordinate_y (bottom);-		-		set_zoom_area (l + 10, t - 10, r - 10, b + 10);-		set_zoom_from_area ();-	}-	-	/** Set default zoom and redraw canvas. */-	public void default_zoom () {-		set_default_zoom ();-		update_view ();-	}-	-	public bool is_empty () {-		foreach (Path p in path_list) {-			if (p.points.length () > 0) {-				return false;-			}-		}-		-		return true;-	}-	-	private void set_zoom (double z)-		requires (z > 0)-	{		-		view_zoom = z;-	}--	public void set_background_visible (bool visibility) {-		background_image_visible = visibility;-	}-	-	public bool get_background_visible () {-		return background_image_visible;-	}-	-	private void plot_outline (Context cr) {-		foreach (unowned Path p in path_list) {-			p.plot (cr, allocation, view_zoom);-		}		-	}-	-	private void draw_coordinate (Context cr) {-		cr.set_source_rgba (0.5, 0.5, 0.5, 1);-		cr.set_font_size (12);-		cr.move_to (0, 10);-		cr.show_text (@"($motion_x, $motion_y)");-		cr.stroke ();		-	}-	-	public void draw_path (Context cr) {-		double left, baseline;--		// plot_outline (cr);-			-		baseline = get_line ("baseline").pos;-		left = get_line ("left").pos;-		-		if (!is_open ()) {-			Svg.draw_svg_path (cr, get_svg_data (), Glyph.xc () + left, Glyph.yc () + baseline, SCALE);-		}-		-		if (is_open ()) {-			foreach (unowned Path p in path_list) {-				p.draw_outline (cr, allocation, view_zoom);-				p.draw_edit_points (cr, allocation, view_zoom);-			}-		}--		if (!is_open ()) {-			foreach (unowned Path p in active_paths) {-				if (!p.is_editable ()) {-					p.fill_path (cr, allocation, view_zoom);-				}-			}-		}--		if (new_point_on_path != null) {-			Path.draw_edit_point_center ((!) new_point_on_path, cr);-		}-	}-	-	private void draw_zoom_area(Context cr) {-		cr.save ();-		cr.set_line_width (2.0);-		cr.set_source_rgba (0, 0, 1, 0.3);-		cr.rectangle (Math.fmin (zoom_x1, zoom_x2), Math.fmin (zoom_y1, zoom_y2), Math.fabs (zoom_x1 - zoom_x2), Math.fabs (zoom_y1 - zoom_y2));-		cr.stroke ();-		cr.restore ();-	}--	private void draw_background_color (Context cr, double opacity) {-		cr.save ();-		cr.rectangle (0, 0, allocation.width, allocation.height);-		cr.set_line_width (0);-		cr.set_source_rgba (1, 1, 1, opacity);-		cr.fill ();-		cr.stroke ();-		cr.restore ();-	}-	-	private void draw_help_lines (Context cr) {-		foreach (Line line in get_all_help_lines ()) {-			cr.save ();-			line.draw (cr, allocation);-			cr.restore ();-		}-	}-	-	public override void draw (Allocation allocation, Context cr) {-		Tool tool;-		-		this.allocation = allocation;--		ImageSurface ps = new ImageSurface (Format.ARGB32, allocation.width, allocation.height);-		Context cmp = new Context (ps);--		cr.save ();-		draw_background_color (cr, 1);-		cr.restore ();--		if (background_image != null && background_image_visible) {-			((!)background_image).draw (cr, allocation, view_offset_x, view_offset_y, view_zoom);-		}-		-		juxtapose (allocation, cr);--		if (Supplement.show_coordinates) {-			draw_coordinate (cmp);-		}--		if (show_help_lines) {-			cmp.save ();-			cmp.scale (view_zoom, view_zoom);-			cmp.translate (-view_offset_x, -view_offset_y);-			draw_help_lines (cmp);-			cmp.restore ();-		}--		if (zoom_area_is_visible) {-			cmp.save ();-			draw_zoom_area (cmp);-			cmp.restore ();-		}--		if (!is_empty ()) {-			cmp.save ();-			cmp.scale (view_zoom, view_zoom);-			cmp.translate (-view_offset_x, -view_offset_y);-			draw_path (cmp);-			cmp.restore ();-		}--		cmp.save (); -		tool = MainWindow.get_toolbox ().get_current_tool ();-		tool.draw_action (tool, cmp, this);-		cmp.restore ();-				-		cr.save ();-		cr.set_source_surface (ps, 0, 0);-		cr.paint ();-		cr.restore ();-	}	--	private void zoom_in_at_point (double x, double y) {-		int n = -10;-		zoom_at_point (x, y, n);-	}-		-	private void zoom_out_at_point (double x, double y) {-		int n = (int) (10.0 * ((allocation.width - 10.0) / allocation.width));-		zoom_at_point (x, y, n);-	}-	-	/** Zoom in @param n pixels. */-	private void zoom_at_point (double x, double y, int n) {-		double w = allocation.width;-		double h = allocation.height;-		-		double rx = Math.fabs (w / 2 - x) / (w / 2);-		double ry = Math.fabs (h / 2 - y) / (h / 2);-		-		int xd = (x < w / 2) ? (int) (n * rx) : (int) (-n * rx);-		int yd = (y < h / 2) ? (int) (n * ry) : (int) (-n * ry);-			-		show_zoom_area (-n + xd, -n + yd, allocation.width + n + xd, allocation.height + n + yd);-		set_zoom_from_area ();-	}-	-	private void move_view_offset (double x, double y) {-		view_offset_x = move_offset_x + (pointer_begin_x - x) * (1/view_zoom);-		view_offset_y = move_offset_y + (pointer_begin_y - y) * (1/view_zoom);-		redraw_area (0, 0, allocation.width, allocation.height);-	}--	public void store_undo_state () {-		Glyph g = copy ();-		undo_list.append (g);		-	}--	public Glyph copy () {-		Glyph g = new Glyph.no_lines (name, unichar_code);-		-		g.left_limit = left_limit;-		g.right_limit = right_limit;-		-		g.remove_lines ();-		-		foreach (Line line in get_all_help_lines ()) {-			g.add_line (line.copy ());-		}--		foreach (Path p in path_list) {-			g.add_path (p.copy ());-		}-		-		foreach (Path p in active_paths) {-			g.active_paths.append (p);-		}--		if (background_image != null) {-			g.background_image = ((!) background_image).copy ();-		}-		-		return g;-	}--	public void reload () {-		Glyph g;-		Font f = Supplement.get_current_font ();-		-		if (f.has_glyph (name)) {-			set_glyph_data ((!) f.get_glyph (name));-		}-	}--	public override void undo () {-		Glyph g;-		-		if (undo_list.length () == 0) {-			return;-		}-		-		g = undo_list.last ().data;	-		set_glyph_data (g);-		-		undo_list.remove_link (undo_list.last ());-	}-	-	void set_glyph_data (Glyph g) {-		while (path_list.length () > 0) {-			path_list.remove_link (path_list.last ());-		}-		-		foreach (Path p in g.path_list) {-			add_path (p);-		}--		remove_lines ();-		foreach (Line line in g.get_all_help_lines ()) {-			add_line (line.copy ());-		}--		add_help_lines ();-		-		if (g.background_image != null) {-			background_image = ((!) g.background_image).copy ();-		}-		-		clear_active_paths ();-		foreach (Path p in g.active_paths) {-			add_active_path (p);-		}-		-		open_path ();-		close_path ();-		-		redraw_area (0, 0, allocation.width, allocation.height);		-	}-	-	public ImageSurface get_thumbnail () {-		ImageSurface img;-		Context cr;-		double gx, gy;-		double x1, x2, y1, y2;-		Font font = Supplement.get_current_font ();--		remove_empty_paths ();-		boundries (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);-		} else {-			img = new ImageSurface (Format.ARGB32, (int) (x2 - x1), (int) (y2 - y1));-		}-		-		gx = left_limit - x1;-		gy = (y2 - y1) + font.base_line + y1;-		-		cr = new Context (img);-		-		Svg.draw_svg_path (cr, get_svg_data (), gx, gy, 1.0);	-		-		return img;-	}--	/** Split curve in two parts and add a new point in between. */-	public void insert_new_point_on_path (double x, double y) {-		double min, distance;-		Path? p = null;-		EditPoint? np = null;-		EditPoint lep;-		-		double xt;-		double yt;--		xt = x * ivz () - xc () + view_offset_x;-		yt = yc () - y * ivz () - view_offset_y;-		-		min = double.MAX;-		-		delete_invisible_paths ();-		-		foreach (Path pp in path_list) {-			lep = new EditPoint ();-			pp.get_closest_point_on_path (lep, xt, yt);-			distance = Math.sqrt (Math.pow (Math.fabs (xt - lep.x), 2) + Math.pow (Math.fabs (yt - lep.y), 2));-			-			if (distance < min) {-				min = distance;-				p = pp;-				np = lep;-			}-		}--		if (p == null) {-			return;-		}--		lep = new EditPoint ();-		((!)p).get_closest_point_on_path (lep, xt, yt);-		((!)p).insert_new_point_on_path (lep);-	}-	-	/** Merge selected paths. */-	public void merge_all () {-		store_undo_state ();-		delete_invisible_paths ();-		-		if (active_paths.length () < 2) {-			return;-		}-		-		foreach (Path p0 in active_paths) {-			merge_path (p0);-		}-		-		//open_path ();-		close_path ();--		foreach (Path p in path_list) {-			p.update_region_boundries ();-		}		-		-		redraw_area (0, 0, allocation.width, allocation.height);-	}-	-	public bool merge_path (Path p0) {-		Path mp;-		PathList path_list;-		-		foreach (Path p1 in active_paths) {-			if (p0 == p1) {-				p0 = p1;-				continue;-			}-			-			path_list = Path.merge (p1, p0);--			if (path_list.paths.length () == 0) {-				p0 = p1;-				continue;-			}-			-			mp = path_list.paths.first ().data;--			delete_path (p0);-			delete_path (p1);-			-			// add new path + it's counter paths-			foreach (Path p in path_list.paths) {-				p.close ();			-				add_path (p.copy ());-			}-			-			add_active_path (mp);-			-			active_paths.remove_all (p0);-			active_paths.remove_all (p1);--			p0 = mp;-			-			mp.create_list ();-			-			return true;-		}-		-		return false;-	}--	public void juxtapose (Allocation allocation, Context cr) {-		string glyph_sequence = Preferences.get ("glyph_sequence");-		unichar c;-		Font font = Supplement.get_current_font ();-		Glyph glyph = MainWindow.get_current_glyph ();-		Glyph juxtaposed;-		StringBuilder current = new StringBuilder ();-		int pos;-		string name;-		double x, kern;-		double left, baseline;-		string last_name;-				-		x = 0;-		-		current.append_unichar (glyph.unichar_code);-		pos = glyph_sequence.index_of (current.str);-		-		baseline = glyph.get_line ("baseline").pos;-		left = glyph.get_line ("left").pos;--		x = glyph.get_width ();-		last_name = glyph.name;-		for (int i = pos + 1; i < glyph_sequence.char_count (); i++) {-			c = glyph_sequence.get_char (i);-			name = font.get_name_for_character (c);			-			juxtaposed = (font.has_glyph (name)) ? (!) font.get_glyph (name) : font.get_space ();-			-			if (font.has_glyph (last_name) && font.has_glyph (name)) {-				kern = font.get_kerning (last_name, name);-			} else {-				kern = 0;-			}--			if (!juxtaposed.is_empty ()) {-				cr.save ();-				cr.scale (glyph.view_zoom, glyph.view_zoom);-				cr.translate (-glyph.view_offset_x, -glyph.view_offset_y);-				Svg.draw_svg_path (cr, juxtaposed.get_svg_data (), Glyph.xc () + left + x + kern, Glyph.yc () + baseline, Glyph.SCALE);-				cr.restore ();-			}-			-			x += juxtaposed.get_width () + font.get_kerning (glyph.name, name) + kern;-			last_name = name;-		}-		-		x = 0;-		last_name = glyph.name;-		for (int i = pos - 1; i >= 0; i--) {-			c = glyph_sequence.get_char (i);-			name = font.get_name_for_character (c);			-			juxtaposed = (font.has_glyph (name)) ? (!) font.get_glyph (name) : font.get_space ();-			-			if (font.has_glyph (last_name) && font.has_glyph (name)) {-				kern = font.get_kerning (name, last_name);-			} else {-				kern = 0;-			}-			-			x -= juxtaposed.get_width ();-			x -= kern;-			-			if (!juxtaposed.is_empty ()) {-				cr.save ();-				cr.scale (glyph.view_zoom, glyph.view_zoom);-				cr.translate (-glyph.view_offset_x, -glyph.view_offset_y);--				Svg.draw_svg_path (cr, juxtaposed.get_svg_data (), Glyph.xc () + left + x, Glyph.yc () + baseline, Glyph.SCALE);-				cr.restore ();-			}-			-			last_name = name;-		}	-	}-}--}diff --git a/src/GlyphBackgroundImage.vala b/src/GlyphBackgroundImage.valadeleted file mode 100644index ca141e9..0000000--- a/src/GlyphBackgroundImage.vala+++ /dev/null@@ -1,793 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Math;-using Gdk;--namespace Supplement {-	-public class GlyphBackgroundImage {-		-	public double img_x = 0;-	public double img_y = 0;-	-	public double img_scale_x = 1;-	public double img_scale_y = 1;-	public double img_rotation = 0;-	private int size = -1;--	public int active_handle = -1;-	public int selected_handle = -1;-	-	private ImageSurface? background_image = null;-	private ImageSurface? original_image = null;-	-	private string path;--	private double contrast = 1.0;-	private bool desaturate = false;-	private double threshold = 1.0;-	-	private bool background_image_is_processing = false;--	public signal void updated ();--	public double img_offset_x {-		get { return img_x + Glyph.xc (); }-		set { img_x = value - Glyph.xc (); }-	}--	public double img_offset_y {-		get { return Glyph.yc () - img_y; }-		set { img_y = Glyph.yc () - value; }-	}-	-	public int size_margin {-		get {-			if (unlikely (size == -1)) {-				size = (int) (Math.sqrt (Math.pow (get_img ().get_height (), 2) + Math.pow (get_img ().get_width (), 2)) + 0.5);-			}-			-			return size;-		}-	}-		-	public GlyphBackgroundImage (string fn) {-		path = fn;-	}--	public GlyphBackgroundImage copy () {-		GlyphBackgroundImage bg = new GlyphBackgroundImage (path);-			-		bg.img_x = img_x;-		bg.img_y = img_y;--		bg.img_scale_x = img_scale_x;-		bg.img_scale_y = img_scale_y;-		bg.img_rotation = img_rotation;--		bg.contrast = contrast;-		bg.desaturate = desaturate;-		bg.threshold = threshold;--		return bg;-		-	}--	public double get_margin_width () {-		return ((size_margin - get_img ().get_width ()) / 2.0);-	}--	public double get_margin_height () {-		return ((size_margin - get_img ().get_height ()) / 2.0);-	}--	public void set_img_offset (double x, double y) {-		img_offset_x = x;-		img_offset_y = y;-	}--	public void set_position (double coordinate_x, double coordinate_y) {-		img_x = coordinate_x;-		img_y = coordinate_y;-	}-			-	public ImageSurface get_img () {-		if (path.index_of (".png") == -1) {-			create_png ();-		}-		-		if (background_image == null) {-			background_image = new ImageSurface.from_png (path);-			original_image = new ImageSurface.from_png (path);-		}-		-		return (!) background_image;-	}--	public bool is_valid () {-		FileInfo file_info;-		File file = File.new_for_path (path);-	-		if (!file.query_exists ()) {-			return false;-		}-		-		try {-			file_info = file.query_info ("*", FileQueryInfoFlags.NONE);-			-			if (file_info.get_size () == 0) {-				return false;-			}-		} catch (GLib.Error e) {-			warning (e.message);-			return false;-		}-				-		return true;-	}--	public string get_png_base64 () {-		try {-			File file = File.new_for_path (path);-			FileInfo file_info = file.query_info ("*", FileQueryInfoFlags.NONE);-			uint8[] buffer = new uint8[file_info.get_size ()];-			FileInputStream file_stream;-			DataInputStream png_stream;-			-			if (!file.query_exists ()) {-				warning (@"Failed to save image $path, file does not exist.");-				return "";-			}-			-			if (is_null (buffer)) {-				warning (@"Colud not allocate a buffer of $(file_info.get_size ()) bytes to store $path.");-				return "";-			}-			-			file_stream = file.read ();-			-			png_stream = new DataInputStream (file_stream);-			png_stream.read (buffer);-			-			return Base64.encode (buffer);-		} catch (GLib.Error e) {-			warning (e.message);-		}-		-		return "";-	}--	public string get_sha1 () {-		try {-			File file = File.new_for_path (path);-			FileInfo file_info;-			uint8[] buffer;-			FileInputStream file_stream;-			DataInputStream png_stream;-			-			if (!file.query_exists ()) {-				warning (@"Can't save $path file does not exist.");-				return "";-			}-			-			file_info = file.query_info ("*", FileQueryInfoFlags.NONE);-			-			if (file_info.get_size () == 0) {-				warning (@"length of image $path is zero");-				return "";-			}-			-			buffer = new uint8[file_info.get_size ()];-			file_stream = file.read ();-			png_stream = new DataInputStream (file_stream);--			png_stream.read (buffer);-			-			return Checksum.compute_for_data (ChecksumType.SHA1, buffer);-		} catch (GLib.Error e) {-			warning (e.message);-		}-		-		return "";-	}-	-	private void create_png () {-		string fn = @"$path.png";-		-		Font font = Supplement.get_current_font ();-		File folder = font.get_backgrounds_folder ();-		File original = File.new_for_path (fn);-		File png_image = folder.get_child (@"full_$((!)original.get_basename ())");--		Pixbuf pixbuf;-		-		if (png_image.query_exists ()) {-			path = (!) png_image.get_path ();-			return;-		}-		-		try {-			pixbuf = new Pixbuf.from_file (path);-			pixbuf.save ((!) png_image.get_path (), "png");-			path = (!) png_image.get_path ();-		} catch (GLib.Error e) {-			warning (e.message);-		}-	}-	-	public void set_img_rotation_from_coordinate (double x, double y) {-		double bcx, bcy;-		double a, b, c, length;--		img_center (out bcx, out bcy);--		a = bcx - x;-		b = bcy - y;-		c = a * a + b * b;-		-		if (c == 0) {-			return;-		}-		-		length = sqrt (fabs (c));	-		-		if (c < 0) {-			length = -length;-		}-	-		img_rotation =  (y < bcy) ? acos (a / length) + PI : -acos (a / length) + PI;-	}-	-	public void set_img_scale (double xs, double ys) {-		img_scale_x = xs;-		img_scale_y = ys;-	}--	public void reset_scale (Glyph g) {-		double w, h;--		w = g.get_width ();-		h = g.get_height ();--		img_scale_x = 1;			-		img_scale_y = 1;-		-		img_offset_x = g.get_line ("left").pos;-		img_offset_y = g.get_line ("top").pos;		-	}--	public void draw (Context cr, Allocation allocation, double view_offset_x, double view_offset_y, double view_zoom) {-		double x, y;-		double iw, ih;-		int h, w;-		double scale_x, scale_y;-		double oy, ox;-				-		Surface s;-		Context c;--		Surface sg;-		Context cg;--		Surface st;-		Context ct;--		double wc, hc;--		if (unlikely (get_img ().status () != Cairo.Status.SUCCESS)) {-			stderr.printf (@"Background image is invalid. (\"$path\")\n");-			MainWindow.get_current_glyph ().set_background_visible (false);-			return;-		}-		-		// add margin-		sg = new Surface.similar (get_img (), get_img ().get_content (), size_margin, size_margin);-		cg = new Context (sg);-		-		wc = get_margin_width ();-		hc = get_margin_height ();-		-		cg.set_source_rgba (1, 1, 1, 1);-		cg.rectangle (0, 0, size_margin, size_margin);-		cg.fill ();-		-		cg.set_source_surface (get_img (), wc, hc);-		cg.paint ();--		x = Glyph.reverse_path_coordinate_x (img_offset_x);-		y = Glyph.reverse_path_coordinate_y (img_offset_y);-		-		ih = (int) get_img ().get_height ();-		iw = (int) get_img ().get_width ();-		-		w = (int) iw;-		h = (int) ih;--		oy = size_margin;-		ox = size_margin;--		// rotate image-		s = new Surface.similar (sg, sg.get_content (), (int) (ox), (int) (oy));-		c = new Context (s);-	-		c.save ();--		c.set_source_rgba (1, 1, 1, 1);-		c.rectangle (0, 0, size_margin, size_margin);-		c.fill ();-			-   		c.translate (size_margin * 0.5, size_margin * 0.5);-		c.rotate (img_rotation);-		c.translate (-size_margin * 0.5, -size_margin * 0.5);--		c.set_source_surface (cg.get_target (), 0, 0);-		c.paint ();	-		c.restore ();--		// mask-		st = new Surface.similar (s, s.get_content (), allocation.width, allocation.height);-		ct = new Context (st);-		ct.save ();--		ct.set_source_rgba (1, 1, 1, 1);-		ct.rectangle (0, 0, allocation.width, allocation.height);-		ct.fill ();--		// scale both canvas and image at the same time; rather tricky.-		scale_x = view_zoom * img_scale_x;-		scale_y = view_zoom * img_scale_y;-		-		ct.scale (scale_x, scale_y);-		ct.translate (-view_offset_x / img_scale_x, -view_offset_y / img_scale_y);-		-		ct.set_source_surface (s, img_offset_x / img_scale_x, img_offset_y / img_scale_y);--		ct.paint ();-		ct.restore ();-		-		// append it-		cr.save ();-		cr.set_source_surface (st, 0, 0);-		cr.paint ();-		cr.restore ();-	}-	-	public void handler_release (double nx, double ny) {		-		selected_handle = 0;-		handler_move (nx, ny);-	}-			-	public void handler_press (double nx, double ny) {		-		if (is_over_rotate (nx, ny)) {-			selected_handle = 2;-		} else if (is_over_resize (nx, ny)) {-			selected_handle = 1;-		} else {-			selected_handle = 0;-		}	-	}--	void img_center (out double x, out double y) {-		Glyph g = MainWindow.get_current_glyph ();-		-		x = img_offset_x - g.view_offset_x + (size_margin / 2) * img_scale_x;-		y = img_offset_y - g.view_offset_y + (size_margin / 2) * img_scale_y;--		y *= g.view_zoom;-		x *= g.view_zoom;-	}--	bool is_over_rotate (double nx, double ny) {-		double x, y, d;--		img_center (out x, out y);--		x += cos (img_rotation) * 75;-		y += sin (img_rotation) * 75;--		d = sqrt (pow (x - nx, 2) + pow (y - ny, 2));-		-		return d < 10;-	}--	bool is_over_resize (double nx, double ny) {-		Glyph g = MainWindow.get_current_glyph ();-		double x, y, size;-		bool inx, iny;--		size = 12 * g.view_zoom;--		x = img_offset_x - g.view_offset_x;-		y = img_offset_y - g.view_offset_y + (size_margin) * img_scale_y;-				-		y *= g.view_zoom;-		x *= g.view_zoom;-				-		inx = x - size <= nx <= x + size;-		iny = y - size <= ny <= y + size;-		-		return inx && iny;-	}--	public void handler_move (double nx, double ny) {-		int prev_handle = active_handle;--		if (is_over_rotate (nx, ny)) {-			active_handle = 2;-		} else if (is_over_resize (nx, ny)) {-			active_handle = 1;-		} else {-			active_handle = 0;-		}-		-		if (prev_handle != active_handle) {-			MainWindow.get_glyph_canvas ().redraw ();-		}-	}-	-	public void draw_handle (Context cr, Glyph g) {-		draw_resize_handle (cr, g);-		draw_rotate_handle (cr, g);-	}--	public void draw_resize_handle (Context cr, Glyph g) {-		double x, y;-		cr.save ();-		-		cr.set_source_rgba (1, 0, 0.3, 1);--		x = img_offset_x - g.view_offset_x;-		y = img_offset_y - g.view_offset_y + (size_margin) * img_scale_y;-				-		y *= g.view_zoom;-		x *= g.view_zoom;-				-		draw_handle_triangle (x, y, cr, g, 6);-		-		cr.restore ();-	}-		-	public void draw_rotate_handle (Context cr, Glyph g) {-		double x, y, hx, hy, x2, y2;-		-		double ivz = 1.0 / (g.view_zoom);-		-		cr.save ();-		-		cr.scale (g.view_zoom, g.view_zoom);-		-		if (selected_handle == 2) cr.set_source_rgba (1, 0, 0.3, 1);-		else if (active_handle == 2) cr.set_source_rgba (0, 0, 0.3, 1);-		else cr.set_source_rgba (0.7, 0.7, 0.8, 1);--		x = img_offset_x - g.view_offset_x + (size_margin / 2) * img_scale_x;-		y = img_offset_y - g.view_offset_y + (size_margin / 2) * img_scale_y;-				-		cr.rectangle (x, y, 5 * ivz, 5 * ivz);-		cr.fill ();--		hx = cos (img_rotation) * 75 * ivz;-		hy = sin (img_rotation) * 75 * ivz;-		-		x2 = x + hx;-		y2 = y + hy;--		cr.rectangle (x2, y2, 5 * ivz, 5 * ivz);-		cr.fill ();--		cr.set_line_width (ivz);-		cr.move_to (x + 2.5* ivz, y + 2.5* ivz);-		cr.line_to (x2 + 2.5* ivz, y2 + 2.5* ivz);-		cr.stroke ();-										-		cr.restore ();-	}--	void draw_handle_triangle (double x, double y, Context cr, Glyph g, int direction, double s = 1) -		requires (0 < direction < 8)-	{-		double ivz = 1.0 / (g.view_zoom);-		double size;-		-		cr.save ();-		cr.set_line_width (ivz);-		-		if (selected_handle == 1) cr.set_source_rgba (1, 0, 0.3, 1);-		else if (active_handle == 1) cr.set_source_rgba (0, 0, 0.3, 1);-		else cr.set_source_rgba (0.7, 0.7, 0.8, 1);	--		size = (8) * s;-		-		cr.scale (1, 1);-		cr.new_path ();-		-		// up + left-		if (direction == 1) {-			cr.move_to (x - size, y - size);-			cr.line_to (x + size, y - size);-			cr.line_to (x - size, y + size);	-		}-		-		if (direction == 6) {-			cr.move_to (x + size, y + size);	-			cr.line_to (x - size, y + size);-			cr.line_to (x - size, y - size);	-		}-		-		cr.close_path();-		cr.fill ();--		cr.restore ();-		-	}--	private static uchar ftu (double c) {-		if (c < 0) return 0;-		if (c > 255) return 255;-		return (uchar) c;-	}--	private static ImageSurface? process_background_colors (ImageSurface img, double contrast, bool desaturate, double threshold) {-		int i;-		unowned uchar[]? pix_buff_t;-		unowned uchar[] pix_buff;-		int len;-		uchar[] contrast_img;-		ImageSurface contrast_surface;-		double c, thres;-		uchar nc;--		int w = img.get_width();-		int h = img.get_height();--		if (unlikely (img.status () != Cairo.Status.SUCCESS)) {-			warning ("Err");-			return null;-		}-		-		len = 4 * w * h;-		-		pix_buff_t = img.get_data ();-		return_if_fail (pix_buff_t != null);-		pix_buff = pix_buff_t;-		-		contrast_img = new uchar[len];--		pix_buff_t = contrast_img;-		return_if_fail (pix_buff_t != null);-		-		contrast += 70;-		contrast /= 71;-		-		if (desaturate) {-			i = 0;-			-			while (i < len) {-				c = (pix_buff[i] + pix_buff[i+1] + pix_buff[i+2]) / 3.0;-				nc = ftu (Math.pow (c, contrast));--				contrast_img[i]   = nc;-				contrast_img[i+1] = nc; -				contrast_img[i+2] = nc;-				contrast_img[i+3] = 255;-				-				i += 4;-			}-		} else {-			i = 0;-			while (i < len) {-				contrast_img[i] = ftu (Math.pow (pix_buff[i], contrast) - Math.pow (100, contrast) + 100);-				contrast_img[i+1] = ftu (Math.pow (pix_buff[i+1], contrast) - Math.pow (100, contrast) + 100);-				contrast_img[i+2] = ftu (Math.pow (pix_buff[i+2], contrast) - Math.pow (100, contrast) + 100);-				contrast_img[i+3] = 255;-				-				i += 4;-			}-		}--		if (true) {-			thres = (threshold - 0.5) * 255;-			-			i = 0;-			while (i < len) {-				c = (pix_buff[i] + pix_buff[i+1] + pix_buff[i+2]) / 3.0;-				nc = (c > thres) ? 255 : 0;--				contrast_img[i]   = nc;-				contrast_img[i+1] = nc; -				contrast_img[i+2] = nc;-				contrast_img[i+3] = 255;-				-				i += 4;-			}-		}--		contrast_surface = new ImageSurface.for_data (contrast_img, img.get_format(), w, h, img.get_stride());--		if (unlikely (contrast_surface.status () != Cairo.Status.SUCCESS)) {-			warning ("Err");-			return null;-		}-		-		contrast_surface.mark_dirty	();-			-		return contrast_surface;	-	}--	async ImageSurface? do_process_background_colors () {-		SourceFunc callback = do_process_background_colors.callback;-		ImageSurface? output = null;-		double contrast = get_contrast ();-		bool desaturate = get_desaturate_background ();-		double threshold = this.threshold;--		if (original_image == null) {-			return null;-		}-		-		// Fixa: Add multi threading-		//ThreadFunc<void*> run = () => {-			output = process_background_colors ((!)original_image, contrast, desaturate, threshold);-			Idle.add ((owned) callback);-			//return null;-		//};-		-		try {-			//Thread.create<void*> (run, false);-			yield;-		} catch (ThreadError e) {-			stderr.printf ("Thread error: %s\n", e.message);-		}--		return output;-	}--	public void update_background (double contrast, bool desaturate) {-		background_image_is_processing = true;-		-		do_process_background_colors.begin ((obj, res) => {-			ImageSurface? img;--			img = do_process_background_colors.end (res);-			-			if (img != null) {-				background_image = (!) img;-				updated ();-			} else {-				stderr.printf ("Error: No background image");	-			}-			-			// update image if new value was set while background task was running -			if (get_contrast () != contrast || get_desaturate_background () != desaturate) {--				do_process_background_colors.begin ((obj, res) => {-					img = do_process_background_colors.end (res);-					if (likely (img != null)) {-						background_image = (!) img;-						updated ();-					} else {-						stderr.printf ("Error: No background image");	-					}-				});-			}--			background_image_is_processing = false;-			updated ();-			-		});-	}--	public double get_contrast () {-		double bc;-		lock (contrast) {-			bc = contrast;-		}-		return bc;-	}-		-	public bool get_desaturate_background () {-		bool bw;-		lock (desaturate) {-			bw = desaturate;-		}-		return bw;-	}-	-	public void set_desaturate_background (bool bw) {-		lock (desaturate) {-			desaturate = bw;-		}-	-		if (background_image_is_processing) {-			return;-		}-		-		background_image_is_processing = true;-		-		update_background (get_contrast (), bw);-	}-	-	public void set_threshold (double t) {-		threshold = t;-	}-	-	public Path auto_trace () {-		ImageSurface img;-		int len, w, h, i;-		unowned uchar[] pix_buff;-		Path p = new Path ();-		double x, y;--		if (background_image == null) {-			return p;-		}--		img = (!) background_image;--		w = img.get_width();-		h = img.get_height();--		if (unlikely (img.status () != Cairo.Status.SUCCESS)) {-			warning ("Err");-			return p;-		}-		-		len = 4 * w * h;--		pix_buff = img.get_data ();--		i = 0;-		while (i < len - 4) {-			if (is_edge (pix_buff[i], pix_buff[i + 4])) {-				y = i / w;-				x = i - y;-				-				p.add (x, y);-				-				if (p.points.length () > 10) {-					return p;-				}-			}-			-			i += 4;-		}-		-		return p;-	}-	-	private bool is_edge (uchar a, uchar b) {-		return (a == 255 && b == 0);-	}-	-	public void set_contrast (double contrast) {-		lock (this.contrast) {-			this.contrast = contrast;-		}-		-		if (background_image_is_processing) {-			return;-		}-		-		background_image_is_processing = true;-		-		update_background (contrast, get_desaturate_background ());-	}-}-	-}diff --git a/src/GlyphCanvas.vala b/src/GlyphCanvas.valadeleted file mode 100644index d95a2cf..0000000--- a/src/GlyphCanvas.vala+++ /dev/null@@ -1,82 +0,0 @@-/*
-    Copyright (C) 2012 Johan Mattsson
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-using Cairo;
-using Math;
-
-namespace Supplement {
-
-public class GlyphCanvas : GLib.Object {
-
-	public FontDisplay current_display;
-	public signal void signal_redraw_area (int x, int y, int w, int h);
-	public Allocation allocation;
-	
-	public GlyphCanvas () {
-	}
-	
-	public void key_release (uint e) {
-		current_display.key_release (e);
-	}
-	
-	public void key_press (uint e) {
-		current_display.key_press (e);
-	}
-	
-	public void set_current_glyph (FontDisplay fd) {
-		if (fd is Glyph) {
-			Glyph g = (Glyph) fd;
-			
-			Supplement.current_glyph = g;
-			Supplement.current_glyph.resized ();
-
-			g.allocation = allocation;
-			
-			warn_if_fail (g.allocation.width != 0 && g.allocation.height != 0);
-		}
-
-		current_display = fd;
-		
-		fd.selected_canvas ();
-		
-		fd.redraw_area.connect ((x, y, w, h) => {
-			signal_redraw_area ((int)x, (int)y, (int)w, (int)h);
-		});
-
-		redraw ();
-
-		MainWindow.native_window.update_window_size ();
-	}
-	
-	public static Glyph get_current_glyph ()  {
-		return Supplement.current_glyph;
-	}
-	
-	public FontDisplay get_current_display () {
-		return current_display;
-	}
-
-	public void redraw_area (int x, int y, int w, int h) {
-		signal_redraw_area (x, y, w, h);
-	}
-	
-	public void redraw () {
-		signal_redraw_area (0, 0, allocation.width, allocation.height);
-	}
-}
-
-}
diff --git a/src/GlyphCollection.vala b/src/GlyphCollection.valadeleted file mode 100644index c5d2d0b..0000000--- a/src/GlyphCollection.vala+++ /dev/null@@ -1,52 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Math;--namespace Supplement {--public class GlyphCollection : GLib.Object {-	VersionList versions;-	-	public GlyphCollection (Glyph? current = null) {-		versions = new VersionList (current);-	}-	-	public VersionList get_version_list () {-		return versions;-	}-	-	public Glyph get_current () {-		return versions.get_current ();-	}-	-	public void insert_glyph (Glyph g, bool selected) {-		versions.add_glyph (g, selected);		-		assert (versions.glyphs.length () > 0);-	}-	-	public uint length () {-		return versions.glyphs.length ();-	}-	-	public string get_name () {-		return get_current ().get_name ();-	}-}-	-}diff --git a/src/GlyphRange.vala b/src/GlyphRange.valadeleted file mode 100644index 5528060..0000000--- a/src/GlyphRange.vala+++ /dev/null@@ -1,262 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--public class GlyphRange {-	-	List<UniRange> ranges;-	-	public unowned List<string> unassigned = new List<string> ();-	-	uint32 len = 0;-	-	public GlyphRange () {-	}-		-	public unowned List<UniRange> get_ranges () {-		return ranges;-	}-	-	public void use_default_range () {-		/// All lower case letters in alphabetic order separated by space-		string lower_case = _("a b c d e f g h i j k l m n o p q r s t u v w x y z");-		-		/// All upper case letters in alphabetic order separated by space-		string upper_case = _("A B C D E F G H I J K L M N O P Q R S T U V W X Y Z");--		foreach (string c in lower_case.split (" ")) {-			add_single (c.get_char ());-		}--		foreach (string c in upper_case.split (" ")) {-			add_single (c.get_char ());-		}-				-		add_range ('0', '9');-		-		add_single (' '); // TODO: add all spaces here.-		-		add_single ('.');-		add_single ('?');-		-		add_single (',');-		-		add_single ('’');--		add_range ('“', '”');--		add_single ('&');-		-		add_range (':', ';');-		-		add_single ('/'); -		-		add_range ('!', '/');-		-		add_single ('-');-		add_range ('‐', '—');-		add_range ('<', '@');-		add_range ('(', ')');-	}-	-	public void use_full_unicode_range () {-		add_range ('\0', (unichar) 0xFFF8);-	}-	-	// Todo: complete localized alphabetical sort åäö is the right order for example.-	public void sort () {-		ranges.sort ((a, b) => {-			bool r = a.start > b.start;-			-			return_if_fail (a.start != b.start);-			-			return (r) ? 1 : -1;-		});-	}-	-	public void add_single (unichar c) {-		add_range (c, c);-	}-	-	public uint32 get_length () {-		unichar l = len;-		l += unassigned.length ();-		return l;-	}-	-	public void add_range (unichar start, unichar stop) {-		unichar b, s;-		if (unique (start, stop)) {-			append_range (start, stop);-		} else {-			-			// make sure this range does not overlap existing ranges-			b = start;-			s = b;-			if (!unique (b, b)) {			-				while (b < stop) {-					if (!unique (b, b)) {-						b++;-					} else {-						if (s != b) {-							add_range (b, stop);-						}-						-						b++;-						s = b;-					}-				}-			} else {-				while (b < stop) {-					if (unique (b, b)) {-						b++;-					} else {-						if (s != b) {-							add_range (start, b - 1);-						}-						-						b++;-						s = b;-					}-				}				-			}-		}-	}-	-	private void append_range (unichar start, unichar stop) {-		UniRange r;-		-		StringBuilder s = new StringBuilder ();-		StringBuilder e = new StringBuilder ();-		s.append_unichar (start);-		e.append_unichar (stop);-		-		r = insert_range (start, stop); // insert a unique range-		merge_range (r); // join connecting ranges-	}-	-	private void merge_range (UniRange r) {-		foreach (UniRange u in ranges) {-			if (u == r) {-				continue;-			}			-			-			if (u.start == r.stop + 1) {-				u.start = r.start;-				ranges.remove_all (r);-				merge_range (u);-			}-			-			if (u.stop == r.start - 1) {-				u.stop = r.stop;-				ranges.remove_all (r);-				merge_range (u);-			}-		}-	}--	public string get_char (uint32 index) {-		int64 ti;-		string chr;-		UniRange r;-		StringBuilder sb;-		unichar c;-		-		if (index > len + unassigned.length ()) { -			return "\0".dup();-		}-		-		if (index >= len) {-			-			if (index - len >= unassigned.length ()) {-				return "\0".dup();-			} -			-			chr = ((!) unassigned.nth (index - len)).data;-			return chr;-		}--		r = ranges.first ().data;-		ti = index;--		foreach (UniRange u in ranges) {-			ti -= u.length ();-			-			if (ti < 0) {-				r = u;-				break;-			}-		}-				-		sb = new StringBuilder ();-		c = r.get_char ((unichar) (ti + r.length ()));-		sb.append_unichar (c);--		return sb.str;-	}-	-	public uint32 length () {-		return len;-	}--	private bool unique (unichar start, unichar stop) {-		foreach (UniRange u in ranges) {-			-			if (inside (start, u.start, u.stop)) return false;-			if (inside (stop, u.start, u.stop)) return false;-			if (inside (u.start, start, stop)) return false;-			if (inside (u.stop, start, stop)) return false;-			-		}--		return true;-	}--	private bool inside (unichar start, unichar u_start, unichar u_stop) {-		return (u_start <= start <= u_stop);-	}-	-	private UniRange insert_range (unichar start, unichar stop) {-		if (unlikely (start > stop)) {-			warning ("start > stop");-			stop = start;-		}-		-		UniRange ur = new UniRange (start, stop);-		len += ur.length ();-		ranges.append (ur);-		-		return ur;-	}--	public void print_all () {-		StringBuilder s;-		-		stdout.printf ("Ranges:\n");-		foreach (UniRange u in ranges) {-			s = new StringBuilder ();-			s.append_unichar (u.start);-			s.append (" - ");-			s.append_unichar (u.stop);-			s.append ("\n");-			stdout.printf (s.str);-		}-	}-}--}diff --git a/src/GlyphTable.vala b/src/GlyphTable.valadeleted file mode 100644index ffe69a7..0000000--- a/src/GlyphTable.vala+++ /dev/null@@ -1,412 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--/** A sorted table of glyphs with search index. */-class GlyphTable : GLib.Object {-	List<Item> data = new List<Item> ();-	-	List<RowItem> row1 = new List<RowItem> ();-	List<RowItem> row2 = new List<RowItem> ();-	List<RowItem> row3 = new List<RowItem> ();-	-	public GlyphTable () {-	}--	public void remove_all () {-		while (data.length () > 0) {-			data.remove_link (data.first ());-		}-	}--	public void @for_each (Func<GlyphCollection> func) {-		data.foreach ((v) => {-			func (v.glyph_collection);-		});-	}--	unowned List<Item>? find (string n) {-		return find_index (n);-	}--	unowned List<Item>? find_next (string n) {-		return find_next_index (n);-	}-		-	public void remove (string name) {-		unowned List<Item>? d = find (name);-		-		if (d == null) {-			warning (@"did not find $name");-			return;-		}-		-		data.remove_link ((!) d);-		build_index ();-	}--	public GlyphCollection? nth (uint i) {-		unowned List<Item>? d = data.nth (i);-		-		if (d == null) {-			warning (@"no glyph for index $i");-			return null;-		}-		-		return ((!) d).data.glyph_collection;-	}--	public uint length () {-		return data.length ();-	}--	public new GlyphCollection? @get (string name) {-		unowned List<Item>? d = find_index (name);-		-		if (d == null) {-			return null;-		}-		-		return ((!) d).data.glyph_collection;-	}--	public bool insert (GlyphCollection g) {-		Item item;-		unowned List<Item>? next;-		-		if (g.get_name () == "") {-			warning (@"No proper name for glyph collection. g.get_name (): \"$(g.get_name ())\"");-			return false;-		}--		if (find (g.get_name ()) != null) {-			warning (@"Table does already contain character \"$(g.get_name ())\".");-			return false;-		}-				-		next = find_next (g.get_name ());-		-		item = new Item (g.get_name (), g);-		-		if (next == null) {-			data.append (item);-		} else {-			data.insert_before ((!) next, item); // _sorted-		}-		-		build_index ();-		-		if (find (g.get_name ()) == null) {-			warning (@"Can not find glyph \"$(g.get_name ())\".");-			print_all ();-			return false;-		}-		-		return true;-	}--	public void print_all () {-		int indice = 0;-		foreach (Item i in data) {-			print (@"$(i.name) indice: $(indice)\n");-			indice++;-		}-		-		print ("Index:\n");-		print ("\n");-		print ("row3\n");-		foreach (RowItem i in row3) {-			print (@"$(i.item.data.name)\n");-		}-		-		print ("\n");-		print ("row2\n");-		foreach (RowItem i in row2) {-			print (@"$(i.item.data.name)\n");-		}-		-		print ("\n");-		print ("row1\n");-		foreach (RowItem i in row1) {-			print (@"$(i.item.data.name)\n");-		}		-	}--	static int compare (string an, Item b) {-		string? bt = b.glyph_collection.get_name ();-		string bn = (!) bt;-		-		uint32 ac = 0;-		uint32 bc = 0;-				-		int i1 = 0;-		int i2 = 0;-		-		return_if_fail (bt != null);-		-		int ca = an.char_count ();-		int cb = bn.char_count ();-		-		if (ca != cb) {-			return ca < cb ? 1 : -1;-		}-		-		while (true) {-			an.get_next_char (ref i1, out ac);-			bn.get_next_char (ref i2, out bc);-			-			if (ac == bc && ac != 0) {-				continue;-			}-			-			if (ac == bc) return 0;-			-			return (ac < bc) ? 1 : -1;-		}-	}-	-	public void build_index () {-		uint len;-		RowItem r;-		-		while (row1.length () > 0) {-			row1.remove_link (row1.first ());-		}--		while (row2.length () > 0) {-			row2.remove_link (row2.first ());-		}--		while (row3.length () > 0) {-			row3.remove_link (row3.first ());-		}--		if (data.length () == 0) {-			return;-		}-				-		// row 1-		len = data.length () / 5;-		if (len == 0) len++;-		for (int i = 0; i < len; i++) {-			r = new RowItem ();-			r.item = data.nth (i * 5);-			r.level = 1;-			row1.append (r);-		}-		r = new RowItem ();-		r.item = data.last ();-		r.level = 1;-		r.last = true;-		row1.append (r);	-		-		// row 2-		len = row1.length () / 5;-		if (len == 0) len++;-		for (int i = 0; i < len; i++) {-			r = new RowItem ();-			r.next_row = row1.nth (i * 5);-			r.item = r.next_row.data.item; -			r.level = 2;-			row2.append (r);-		}-		r = new RowItem ();-		r.item = data.last ();-		r.level = 2;-		r.next_row = row1.last ();		-		r.last = true;-		row2.append (r);-		-		// row 3-		len = row2.length () / 5;-		if (len == 0) len++;-		for (int i = 0; i < len; i++) {-			r = new RowItem ();-			r.next_row = row2.nth (i * 5);-			r.item = r.next_row.data.item; -			r.level = 3;-			row3.append (r);-		}-		r = new RowItem ();-		r.item = data.last ();-		r.level = 3;-		r.next_row = row2.last ();-		r.last = true;	-		row3.append (r);-	}--	unowned List<Item>? find_next_index (string n) {-		unowned List<Item>? next = find_next_via_index (n);-		return next;-	}--	unowned List<Item>? find_index (string n) {-		unowned List<Item>? next = find_next_via_index (n);-		-		if (next == null) {-			return null;-		}-		-		if (compare (n, ((!)next).data) == 0) {-			return next;-		}-	-		if (((!)next) != data.first () && compare (n, ((!)next).prev.data) == 0) {-			return ((!)next).prev; // fetch previous since we are searching for the next one-		}-				-		return null; -	}-	-	unowned List<Item>? find_next_via_index (string n) {-		unowned List<Item> i = data.first ();-		unowned List<RowItem> r;-		int cmp;-		-		if (data.length () == 0) {-			return null;-		}--		r = row3.first ();-		int ti = 0;-		while (true) {-			cmp = compare (n, r.data.item.data);-			-			ti++;-			-			if (cmp == 1) {-				break;-			}-			-			if (cmp == 0) {-				return r.data.item;-			}-			-			if (r != row3.last ()) {-				r = r.next;-			} else {-				return null;-			}-		} -		r = r.data.next_row;--		while (true) {-			cmp = compare (n, r.data.item.data);-			if (cmp == -1) {-				break;-			}--			if (cmp == 0) {-				return r.data.item;-			}-						-			if (r == row2.first ()) {;-				return r.data.item;-			} else r = r.prev;-		}-		r = r.data.next_row;-		-		while (true) {-			cmp = compare (n, r.data.item.data);-			if (cmp == 1) {-				break;-			}-			-			if (cmp == 0) {-				return r.data.item;-			}-								-			if (r == row1.last ()) return null;-			else r = r.next;-		}-		i = r.data.item;-		-		-		while (true) {-			cmp = compare (n, i.data);--			if (cmp == -1) {-				// null?-				return i.next;-			}--			if (cmp == 0) {-				return i;-			}-						-			if (i == data.first ()) {-				return i;-			} else { -				i = i.prev;-			}-		}			-	}--	public bool validate_index () {-		int cmp;-		Item prev;-		int index;-		-		if (data.length () == 0) {-			return true;-		}-		-		prev = data.first ().data;-		index = 0;-		foreach (Item i in data) {-			cmp = compare (prev.name, i);-		-			if (cmp == 1) {-				continue;-			}-						-			if (cmp == 0 && index != 0) {-				warning (@"Value is not unique: $(i.name).");-				return false;-			}--			if (cmp == -1) {-				warning (@"Value is not sorted: $cmp == cmp $(prev.name) $(i.name).");-				return false;-			}-			-			index++;-		}-	-		return true;-	}--	class Item : GLib.Object {-		public string name;-		public GlyphCollection glyph_collection;-		-		public Item (string n, GlyphCollection gc) {-			glyph_collection = gc;-			name = n;-		}-	}--	class RowItem : GLib.Object {-		public unowned List<RowItem> next_row;-		public unowned List<Item> item;-		public int level = 0;-		public bool last = false;-	}-}--}diff --git a/src/GridTool.vala b/src/GridTool.valadeleted file mode 100644index e2dc97c..0000000--- a/src/GridTool.vala+++ /dev/null@@ -1,244 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Math;--namespace Supplement {--class GridTool : Tool {-	-	static List <Line> horizontal;-	static List <Line> vertical;-	-	static bool visible = false;-	-	public static double size_x;-	public static double size_y;-	-	public static List <SpinButton> sizes;-	-	public GridTool (string n) {-		base (n, _("Show grid"), 'g', NONE);-		-		horizontal = new List <Line> ();-		vertical = new List <Line> ();-		-		sizes = new List <SpinButton> ();-		-		size_x = 2;-		size_y = 2;-	-		updata_lines ();-			-		select_action.connect((self) => {-			visible = !visible;-			updata_lines ();-			MainWindow.get_glyph_canvas ().redraw ();-			-			self.set_selected (visible);-			-			Toolbox tb = MainWindow.get_toolbox ();-			Tool t = tb.get_tool ("help_lines");-			-			if (visible && !t.is_selected ()) {-				MainWindow.get_toolbox ().select_tool (t);-			}-			-		});-		-		press_action.connect((self, b, x, y) => {-		});--		release_action.connect((self, b, x, y) => {-		});-		-		move_action.connect ((self, x, y)	 => {-		});-		-		draw_action.connect ((self, cairo_context, glyph) => {-		});-			-	}-	-	public static void set_grid_width (double w) {-		size_x = w;-		size_y = w;-		-		updata_lines ();-	}-	-	private static void updata_lines () {-		Glyph g = MainWindow.get_current_glyph ();-		double step = size_y * Glyph.SCALE;-		double i;-		int max_lines = 400;-		int n;-		Line t, l, u;--		Line baseline = g.get_line ("baseline");-		Line bottom_margin = g.get_line ("bottom margin");-		Line top_margin = g.get_line ("top margin");-		-		Line left = g.get_line ("left");-		Line right = g.get_line ("right");--		while (vertical.length () > 0) vertical.delete_link (vertical.first ());-		while (horizontal.length () > 0) horizontal.delete_link (horizontal.first ());-	-		n = 0;-		for (i = left.get_pos () - 7 * step; i <= right.get_pos () + 7 * step; i += step) {	-			l = new Line ("grid", i, Line.VERTICAL);-			l.set_moveable (false);-			l.set_color (0.2, 0.6, 0.2, 0.2);-			horizontal.append (l);-			-			if (++n >= max_lines) {-				break;-			}-		}-		-		n = 0;-		for (i = baseline.get_pos () - step; i >= top_margin.get_pos () - 7 * step; i -= step) {-			t = new Line ("grid", i, Line.HOROIZONTAL);-			t.set_moveable (false);-			t.set_color (0.2, 0.6, 0.2, 0.2);-			vertical.append (t);	--			if (++n >= max_lines) {-				break;-			}--		}-		-		for (i = baseline.get_pos (); i <= bottom_margin.get_pos () + 7 * step; i += step) {-			u = new Line ("grid", i, Line.HOROIZONTAL);-			u.set_moveable (false);-			u.set_color (0.2, 0.6, 0.2, 0.2);-			vertical.append (u);	--			if (++n >= max_lines) {-				break;-			}--		}	-	}-		-	/** Sets x and y the point closest on grid. */-	public static void tie (ref double x, ref double y) {-		tie_point (ref x, ref y, false);-	}--	public static void tie_coordinate (ref double x, ref double y) {-		tie_point (ref x, ref y, true);-	}-		-	private static void tie_point (ref double x, ref double y, bool coordinate)-		requires (horizontal.length () > 0 && vertical.length () > 0)-	{-		double d, m;-		-		Glyph g = MainWindow.get_current_glyph ();-		-		Line xmin = horizontal.first ().data;-		Line ymin = vertical.first ().data;--		Line xpos;-		Line ypos;-		-		if (!coordinate) {-			xpos = new Line ("", 0, Line.VERTICAL);-			ypos = new Line ("", 0, Line.HOROIZONTAL);--			xpos.set_move (true);-			ypos.set_move (true);-			-			xpos.move_line_to (x, y, g.allocation);-			ypos.move_line_to (x, y, g.allocation);-		} else {-			xpos = new Line ("", x, Line.VERTICAL);-			ypos = new Line ("", y, Line.HOROIZONTAL);-		}--		m = double.MAX;--		foreach (Line line in vertical) {-			d = Math.fabs (line.get_pos () - ypos.get_pos ());-			-			if (d <= m) {-				m = d;-				ymin = line;-			}-			-		}--		m = double.MAX;-		foreach (Line line in horizontal) {-			d = Math.fabs (line.get_pos () - xpos.get_pos ());-			-			if (d <= m) {-				m = d;-				xmin = line;-			}-			-		}-		-		if (!coordinate) {-			x = Glyph.reverse_path_coordinate_x (xmin.get_pos ());-			y = Glyph.reverse_path_coordinate_y (-ymin.get_pos ());-		} else {-			x = xmin.get_pos ();-			y = ymin.get_pos ();-		}-	}--	public static void tie_to_prev (EditPoint ep, double tx, double ty) {-		EditPoint pp;-		double a, b;-		-		return_if_fail (ep.prev != null);-		-		pp = ep.get_prev ().data;-		-		ep.to_coordinate (ref tx, ref ty);-		-		a = fabs (tx - pp.x);-		b = fabs (ty - pp.y);-		-		if (a > b) {-			ep.y = pp.y;-		} else {-			ep.x = pp.x;-		}-	}	--	public static bool is_visible () {-		return visible;-	}-	-	public static unowned List<Line> get_horizontal_lines () {-		return horizontal;-	}-	-	public static unowned List<Line> get_vertical_lines () {-		return vertical;-	}--}--}-diff --git a/src/GtkWindow.vala b/src/GtkWindow.valadeleted file mode 100644index 4d44788..0000000--- a/src/GtkWindow.vala+++ /dev/null@@ -1,808 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Gtk;-using Gdk;-using Supplement;-using WebKit;--namespace Supplement {--public class GtkWindow : Gtk.Window, NativeWindow {--	HBox list_box;-	HBox canvas_box;-	-	WebView html_canvas;-	ScrolledWindow html_box;--	VBox tab_box;--	static DrawingArea margin_bottom;-	static DrawingArea margin_right;-	-	GlyphCanvasArea glyph_canvas_area;-	-	static List<uint> key_pressed = new List<uint> ();-	-	public GtkWindow (string title) {-		// set_title (title);-	}-	-	public void init () {-		margin_bottom = new DrawingArea ();-		margin_right = new DrawingArea ();-	-		margin_bottom.set_size_request (0, 0);-		margin_right.set_size_request (0, 0);-		-		delete_event.connect (quit);-		-		set_size_and_position ();-		-		html_canvas = new WebView ();-		WebKit.set_cache_model (CacheModel.DOCUMENT_VIEWER);-		html_canvas.get_settings ().enable_default_context_menu = false;-				-		html_canvas.title_changed.connect ((p, s) => {-			webkit_callback (s);-		});-		-		html_box = new ScrolledWindow (null, null);-		html_box.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);-		html_box.add (html_canvas);-		html_canvas.set_editable (true);-		-		MainWindow.get_tab_bar ().signal_tab_selected.connect ((f, tab) => {-			bool n;-			File layout_dir;-			File layout_uri;-			string uri = "";-			FontDisplay fd = tab.get_display ();-			-			MainWindow.glyph_canvas.set_current_glyph (fd);-			n = fd.is_html_canvas ();-			-			if (n) {-				layout_dir = FontDisplay.find_layout_dir ();-				uri = fd.get_uri ();-				-				if (uri == "") {-					layout_uri = layout_dir.get_child (fd.get_html_file ());-					uri = fd.path_to_uri ((!) layout_uri.get_path ());-				}-		-				if (fd.get_html () == "") {-					-					if (fd.get_name () == "Preview") {-						// hack: force webkit to ignore cache in preview					-						html_box.set_visible (false);-						glyph_canvas_area.set_visible (true);-												-						try {-							Preview preview = (Preview) fd;-							DataInputStream dis = new DataInputStream (preview.get_html_file ().read ());-							string? line;-							StringBuilder sb = new StringBuilder ();-							uint rid = Random.next_int ();-							Font font = Supplement.get_current_font ();-							-							File preview_directory = Supplement.get_preview_directory ();-							-							File f_ttf = font.get_folder ().get_child (@"$(font.get_name ()).ttf");-							File f_eot = font.get_folder ().get_child (@"$(font.get_name ()).eot");-							File f_svg = font.get_folder ().get_child (@"$(font.get_name ()).svg");--							if (f_ttf.query_exists ()) {-								f_ttf.delete ();-							}-								-							if (f_eot.query_exists ()) {-								f_eot.delete ();-							}-							-							if (f_svg.query_exists ()) {-								f_svg.delete ();-							}-							-							ExportTool.export_ttf_font ();							-							ExportTool.export_svg_font ();-							-							File r_ttf = preview_directory.get_child (@"$(font.get_name ())$rid.ttf");-							File r_svg = preview_directory.get_child (@"$(font.get_name ())$rid.svg");-							-							if (Supplement.win32) {-								f_ttf.copy (r_ttf, FileCopyFlags.NONE);-							}-							-							f_svg.copy (r_svg, FileCopyFlags.NONE);--							while ((line = dis.read_line (null)) != null) {-								line = ((!) line).replace (@"$(font.get_name ()).ttf", @"$(FontDisplay.path_to_uri ((!) f_ttf.get_path ()))?$rid");-								line = ((!) line).replace (@"$(font.get_name ()).eot", @"$(FontDisplay.path_to_uri ((!) f_eot.get_path ()))?$rid");-								line = ((!) line).replace (@"$(font.get_name ()).svg", @"$(FontDisplay.path_to_uri ((!) f_svg.get_path ()))?$rid");-								sb.append ((!) line);-							}-					-							html_canvas.load_html_string (sb.str, uri);							-						} catch (Error e) {-							warning (e.message);-							warning ("Failed to load html into canvas.");-						}-						-					} else {-						// normal way to load a uri for all other pages-						html_canvas.load_uri (uri);-						html_canvas.reload_bypass_cache ();			-					}-						-				} else {-					html_canvas.load_html_string (fd.get_html (), uri);-				}-				-				html_box.set_visible (n);-				glyph_canvas_area.set_visible (!n);-			} else {-				html_box.set_visible (false);-				glyph_canvas_area.set_visible (true);-			}-		});--		// Hide this canvas when window is realized and flip canvas -		// visibility in tab selection signal.-		html_canvas.expose_event.connect ((t, e) => {-			glyph_canvas_area.set_visible (false);-			return false;-		});-				-		MainWindow.tabs.add_unique_tab (MainWindow.menu_tab, 60, true);-		-		MainWindow.tabs.select_tab_name ("Menu");--		glyph_canvas_area = new GlyphCanvasArea (MainWindow.glyph_canvas);--		canvas_box = new HBox (false, 0);-		canvas_box.pack_start (glyph_canvas_area, true, true, 0);-		canvas_box.pack_start (html_box, true, true, 0);-		-		tab_box = new VBox (false, 0);-		tab_box.pack_start (new TabbarCanvas (MainWindow.tabs), false, false, 0);	-		-		tab_box.pack_start (canvas_box, true, true, 0);--		tab_box.pack_start (new TooltipCanvas (MainWindow.tool_tip), false, false, 0);-		tab_box.pack_start (margin_bottom, false, false, 0);-		-		list_box = new HBox (false, 0);-		list_box.pack_start (tab_box, true, true, 0);-		list_box.pack_start (new ToolboxCanvas (MainWindow.tools), false, false, 0);-		list_box.pack_start (margin_right, false, false, 0);--		VBox vbox = new VBox (false, 0);-		-		vbox.pack_start(create_menu (), false, false, 0);-		vbox.pack_start(list_box, true, true, 0);--		add (vbox);-		-		try {-			set_icon_from_file ((!) Icons.find_icon ("window_icon.png").get_path ());-		} catch (GLib.Error e) {-			warning (e.message);-		}--		key_press_event.connect ((t, event) => {-			MainWindow window = MainWindow.get_singleton ();-			FontDisplay fd = window.glyph_canvas.current_display;-			-			if (fd is Glyph) {-				window.tools.key_press (event.keyval);-			}-			-			window.glyph_canvas.key_press (event.keyval);-			return false;-		});-		-		key_release_event.connect ((t, event) => {-			MainWindow window = MainWindow.get_singleton ();-			if (window.glyph_canvas is Glyph) {-				window.glyph_canvas.key_release (event.keyval);-			}-			return false;-		});-		-		show_all ();-	}--	MenuBar create_menu () {-		MenuBar menubar = new MenuBar ();-		Gtk.Menu file_menu = new Gtk.Menu ();-		Gtk.Menu edit_menu = new Gtk.Menu ();-		Gtk.Menu tab_menu = new Gtk.Menu ();-		Gtk.Menu tool_menu = new Gtk.Menu ();--		AccelGroup accel_group = new Gtk.AccelGroup();-		add_accel_group (accel_group);-		-		// File-		Gtk.MenuItem new_item = new Gtk.MenuItem.with_mnemonic (_("_New"));-		file_menu.append (new_item);-		new_item.activate.connect (() => { MenuTab.new_file (); });-		new_item.add_accelerator ("activate", accel_group, 'N', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem load_item = new Gtk.MenuItem.with_mnemonic (_("_Open"));-		file_menu.append (load_item);-		load_item.activate.connect (() => { MenuTab.load (); });-		load_item.add_accelerator ("activate", accel_group, 'O', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem save_item = new Gtk.MenuItem.with_mnemonic (_("_Save"));-		file_menu.append (save_item);-		save_item.activate.connect (() => { MenuTab.save (); });-		save_item.add_accelerator ("activate", accel_group, 'S', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem save_as_item = new Gtk.MenuItem.with_mnemonic (_("Save _as"));-		file_menu.append (save_as_item);-		save_as_item.activate.connect (() => { MenuTab.save_as (); });-				-		Gtk.MenuItem export_item = new Gtk.MenuItem.with_mnemonic (_("_Export"));-		file_menu.append (export_item);-		export_item.activate.connect (() => { ExportTool.export_all (); });-		export_item.add_accelerator ("activate", accel_group, 'E', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem preview_item = new Gtk.MenuItem.with_mnemonic(_("_Preview"));-		file_menu.append (preview_item);-		preview_item.activate.connect (() => { MenuTab.preview (); });-		preview_item.add_accelerator ("activate", accel_group, 'P', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem kerning_item = new Gtk.MenuItem.with_mnemonic (_("_Kerning"));-		file_menu.append (kerning_item);-		kerning_item.activate.connect (() => { MenuTab.show_kerning_context (); });-		kerning_item.add_accelerator ("activate", accel_group, 'K', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem quit_item = new Gtk.MenuItem.with_mnemonic (_("_Quit"));-		file_menu.append (quit_item);-		quit_item.activate.connect (() => { quit(); });--		// Edit-		Gtk.MenuItem undo_item = new Gtk.MenuItem.with_mnemonic (_("_Undo"));-		edit_menu.append (undo_item);-		undo_item.activate.connect (() => { MainWindow.get_current_display ().undo (); });	-		undo_item.add_accelerator ("activate", accel_group, 'Z', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);-		-		Gtk.MenuItem copy_item = new Gtk.MenuItem.with_mnemonic (_("_Copy"));-		edit_menu.append (copy_item);-		copy_item.activate.connect (() => { ClipTool.copy (); });		-		copy_item.add_accelerator ("activate", accel_group, 'C', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem paste_item = new Gtk.MenuItem.with_mnemonic (_("_Paste"));-		edit_menu.append (paste_item);-		paste_item.activate.connect (() => { ClipTool.paste (); });	-		paste_item.add_accelerator ("activate", accel_group, 'V', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);-		-		// Tab-		Gtk.MenuItem next_tab_item = new Gtk.MenuItem.with_mnemonic (_("_Next tab"));-		tab_menu.append (next_tab_item);-		next_tab_item.activate.connect (() => { -			TabBar tb = MainWindow.get_tab_bar ();-			int n = tb.get_selected () + 1;-			-			if (!(0 <= n < tb.get_length ())) {-				return;-			}-			-			tb.select_tab (n);-		});	--		Gtk.MenuItem prevoius_tab_item = new Gtk.MenuItem.with_mnemonic (_("_Previous tab"));-		tab_menu.append (prevoius_tab_item);-		prevoius_tab_item.activate.connect (() => { -			TabBar tb = MainWindow.get_tab_bar ();-			int n = tb.get_selected () - 1;--			if (!(0 <= n < tb.get_length ())) {-				return;-			}-			-			tb.select_tab (n);-		});					--		Gtk.MenuItem close_tab_item = new Gtk.MenuItem.with_mnemonic (_("_Close tab"));-		tab_menu.append (close_tab_item);-		close_tab_item.activate.connect (() => { -			TabBar tb = MainWindow.get_tab_bar ();-			int n = tb.get_selected ();--			if (!(0 <= n < tb.get_length ())) {-				return;-			}-			-			tb.close_tab (n);-		});	-		close_tab_item.add_accelerator ("activate", accel_group, 'W', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		// Tool-		Gtk.MenuItem pen_item = new Gtk.MenuItem.with_mnemonic (_("_Create path"));-		tool_menu.append (pen_item);-		pen_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("pen_tool");-		});-		pen_item.add_accelerator ("activate", accel_group, ',', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem zoom_item = new Gtk.MenuItem.with_mnemonic (_("_Zoom"));-		tool_menu.append (zoom_item);-		zoom_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("zoom_tool");-		});--		Gtk.MenuItem counter_item = new Gtk.MenuItem.with_mnemonic (_("_Create counter path"));-		tool_menu.append (counter_item);-		counter_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("cut");-		});-		counter_item.add_accelerator ("activate", accel_group, 'U', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem move_item = new Gtk.MenuItem.with_mnemonic (_("_Move"));-		tool_menu.append (move_item);-		move_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("move");-		});-		move_item.add_accelerator ("activate", accel_group, 'M', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem full_unicode_item = new Gtk.MenuItem.with_mnemonic (_("Show _full unicode characters set"));-		tool_menu.append (full_unicode_item);-		full_unicode_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("utf_8");-		});-		full_unicode_item.add_accelerator ("activate", accel_group, 'F', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem default_charset_item = new Gtk.MenuItem.with_mnemonic (_("Show de_fault characters set"));-		tool_menu.append (default_charset_item);-		default_charset_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("custom_character_set");-		});-		default_charset_item.add_accelerator ("activate", accel_group, 'R', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem avalilable_characters_item = new Gtk.MenuItem.with_mnemonic (_("Show characters in font"));-		tool_menu.append (avalilable_characters_item);-		avalilable_characters_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("available_characters");-		});-		avalilable_characters_item.add_accelerator ("activate", accel_group, 'D', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem add_grid_item = new Gtk.MenuItem.with_mnemonic (_("Add new _grid item"));-		tool_menu.append (add_grid_item);-		add_grid_item.activate.connect (() => { -			MainWindow.get_toolbox ().add_new_grid ();-		});--		Gtk.MenuItem remove_grid_item = new Gtk.MenuItem.with_mnemonic (_("Remove gr_id item"));-		tool_menu.append (remove_grid_item);-		remove_grid_item.activate.connect (() => { -			MainWindow.get_toolbox ().remove_current_grid ();-		});-		-		Gtk.MenuItem zoom_in_item = new Gtk.MenuItem.with_mnemonic (_("_Zoom in"));-		tool_menu.append (zoom_in_item);-		zoom_in_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("zoom_in");-		});-		zoom_in_item.add_accelerator ("activate", accel_group, '+', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem zoom_out_item = new Gtk.MenuItem.with_mnemonic (_("Zoom _out"));-		tool_menu.append (zoom_out_item);-		zoom_out_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("zoom_out");-		});-		zoom_out_item.add_accelerator ("activate", accel_group, '-', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		Gtk.MenuItem zoom_1_1_item = new Gtk.MenuItem.with_mnemonic (_("Zoom to _scale 1:1"));-		tool_menu.append (zoom_1_1_item);-		zoom_1_1_item.activate.connect (() => { -			Toolbox.select_tool_by_name ("zoom_1_1");-		});-		zoom_1_1_item.add_accelerator ("activate", accel_group, '0', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);--		// Add menus-		Gtk.MenuItem file_launcher = new Gtk.MenuItem.with_mnemonic (_("_File"));-		file_launcher.set_submenu (file_menu);--		Gtk.MenuItem edit_launcher = new Gtk.MenuItem.with_mnemonic (_("_Edit"));-		edit_launcher.set_submenu (edit_menu);--		Gtk.MenuItem tab_launcher = new Gtk.MenuItem.with_mnemonic (_("_Tab"));-		tab_launcher.set_submenu (tab_menu);--		Gtk.MenuItem tool_launcher = new Gtk.MenuItem.with_mnemonic (_("T_ool"));-		tool_launcher.set_submenu (tool_menu);-						-		menubar.append (file_launcher);-		menubar.append (edit_launcher);-		menubar.append (tab_launcher);-		menubar.append (tool_launcher);-		-		return menubar;	-	}--	internal void toggle_expanded_margin_bottom () {-		int w, h;-		margin_bottom.get_size_request (out w, out h);-		-		if (h == 1) h = 2; -		else h = 1;-		-		margin_bottom.set_size_request (w, h);-	}-	-	internal void toggle_expanded_margin_right () {	-		int w, h;-		margin_right.get_size_request (out w, out h);--		if (w == 1) w = 2; -		else w = 1;--		margin_right.set_size_request (w, h);-	}--	public static void hide_cursor () {-		/*-		Pixmap pixmap = new Pixmap (null, 1, 1, 1);-		Color color = { 0, 0, 0, 0 };-		Cursor cursor = new Cursor.from_pixmap (pixmap, pixmap, color, color, 0, 0);-		*/-		-		// Fixa: But why?-		// (Supplement.exe:1300): Gdk-CRITICAL **: gdk_window_set_cursor: assertion `GDK_IS_WINDOW (window)' failed		-		// singleton.frame.set_cursor (cursor);-	}--	public void update_window_size () {-		int w, h;-		get_size (out w, out h);-		-		Preferences.set ("window_width", @"$w");-		Preferences.set ("window_height", @"$h");-	}-		-	private void set_size_and_position () {-		int w = Preferences.get_window_width ();-		int h = Preferences.get_window_height ();-		-		set_default_size (w, h);-		// move (10, 240);-	}-	-	public bool quit () {-		bool added;-		SaveDialog s = new SaveDialog ();-		-		if (Supplement.get_current_font ().is_modified ()) {-			added = MainWindow.get_tab_bar ().add_unique_tab (s, 50);-		} else {-			added = false;-		}-		-		if (!added) {-			Supplement.get_current_font ().save_backup ();-			Gtk.main_quit ();-		}-		-		s.finished.connect (() => {-			Supplement.get_current_font ().delete_backup ();-			Gtk.main_quit ();-		});-		-		return true;-	}-	-	public string? file_chooser (string title) {-		string? fn = null;-		FileChooserDialog file_chooser = new FileChooserDialog (title, this, FileChooserAction.SAVE, Stock.CANCEL, ResponseType.CANCEL, Stock.SAVE, ResponseType.ACCEPT);-		Font font = Supplement.get_current_font ();-		-		try {-			file_chooser.set_current_folder_file (font.get_folder ());-		} catch (GLib.Error e) {-			stderr.printf (e.message);-		}-		-		if (file_chooser.run () == ResponseType.ACCEPT) {	-			MainWindow.get_glyph_canvas ().redraw ();-			fn = file_chooser.get_filename ();-		}--		file_chooser.destroy ();-		-		return fn;-	}	-}--class TabbarCanvas : DrawingArea {-	TabBar tabbar;-	-	public TabbarCanvas (TabBar tb) {		-		tabbar = tb;--		set_extension_events (ExtensionMode.CURSOR | EventMask.POINTER_MOTION_MASK);-		add_events (EventMask.BUTTON_PRESS_MASK | EventMask.POINTER_MOTION_MASK | EventMask.LEAVE_NOTIFY_MASK);-	  -		motion_notify_event.connect ((t, e)=> {-			Gtk.Allocation alloc;-			tabbar.motion (e.x, e.y);-			get_allocation (out alloc);-			queue_draw_area (0, 0, alloc.width, alloc.height);-			return true;-		});	-				-		button_press_event.connect ((t, e)=> {-			Gtk.Allocation alloc;-			get_allocation (out alloc);-			tabbar.select_tab_click (e.x, e.y, alloc.width, alloc.height);-			queue_draw_area (0, 0, alloc.width, alloc.height);-			return true;-		});--		expose_event.connect ((t, e)=> {-			Context cr = cairo_create (get_window ());--			Gtk.Allocation alloc;-			get_allocation (out alloc);--			tabbar.draw (cr, alloc.width, alloc.height);-			return true;-		});-	-		tabbar.signal_tab_selected.connect ((t) => {-			Gtk.Allocation alloc;-			get_allocation (out alloc);-			queue_draw_area (0, 0, alloc.width, alloc.height);	-		});-		-		set_size_request (20, 25);-	}-	-}--class ToolboxCanvas : DrawingArea {-	Toolbox tb;-	-	public ToolboxCanvas (Toolbox toolbox) {-		tb = toolbox;-		-		tb.redraw.connect ((x, y, w, h) => {-			queue_draw_area (x, y, w, h);-		});-		-		button_press_event.connect ((se, e)=> {-			tb.press (e.button, e.x, e.y);-			return true;-		});	-				-		button_release_event.connect ((se, e)=> {-			tb.release (e.button, e.x, e.y);-			return true;-		});--		motion_notify_event.connect ((sen, e)=> {-			tb.move (e.x, e.y);-			return true;-		});-		-		expose_event.connect ((t, e)=> {-			Gtk.Allocation allocation;-			get_allocation (out allocation);-			-			Context cw = cairo_create(get_window());-			tb.allocation_width = allocation.width;-			tb.allocation_height = allocation.height;-			tb.draw (allocation.width, allocation.height, cw);-			-			return true;-		});-		-		add_events (EventMask.BUTTON_PRESS_MASK | EventMask.BUTTON_RELEASE_MASK | EventMask.POINTER_MOTION_MASK | EventMask.LEAVE_NOTIFY_MASK);--		set_size_request (160, 100);--		leave_notify_event.connect ((t, e)=> {-			tb.reset_active_tool ();-			return true;-		});-		-	}-}--public class GlyphCanvasArea : DrawingArea  {-	GlyphCanvas glyph_canvas;-	Gtk.Allocation alloc;--	/* State on linux */-	public static const int L_SHIFT = 17;-	public static const int L_CTRL = 20;-	public static const int L_SHIFT_CTRL = 21;--	/* State on windows */-	public static const int W_SHIFT = 1;-	public static const int W_CTRL = 4;-	public static const int W_SHIFT_CTRL = 5;-		-	public GlyphCanvasArea (GlyphCanvas gc) {-		int event_flags;-		-		glyph_canvas = gc;-		-		event_flags = EventMask.BUTTON_PRESS_MASK;-		event_flags |= EventMask.BUTTON_RELEASE_MASK;-		event_flags |= EventMask.POINTER_MOTION_MASK;-		event_flags |= EventMask.LEAVE_NOTIFY_MASK;-		event_flags |= EventMask.SCROLL_MASK;-		-		add_events (event_flags);--		glyph_canvas.signal_redraw_area.connect ((x, y, w, h) => {-			queue_draw_area ((int)x, (int)y, (int)w, (int)h);-		});--		expose_event.connect ((t, e)=> {		-			Gtk.Allocation allocation;-			get_allocation (out allocation);-			-			glyph_canvas.allocation.width = allocation.width;-			glyph_canvas.allocation.height = allocation.height;-			glyph_canvas.allocation.y = allocation.x;-			glyph_canvas.allocation.y = allocation.y;-				-			if (unlikely (allocation != alloc && alloc.width != 0)) {-				// Set size of glyph widget to an even number and notify -				// set new allocation for glyph-				bool ug = false;-				-				if (allocation.height % 2 != 0) {-					MainWindow.native_window.toggle_expanded_margin_bottom ();-					ug = true;-				}-				-				if (allocation.width % 2 != 0) {-					MainWindow.native_window.toggle_expanded_margin_right ();-					ug = true;-				}					-				-				if (ug) {-					//redraw_area (1, 1, 2, 2);-				} else if (unlikely (allocation.width % 2 != 0 || allocation.height % 2 != 0)) {-					warning (@"\nGlyph canvas is not divisible by two.\nWidth: $(allocation.width)\nHeight: $(allocation.height)");-				}-				-				Supplement.current_glyph.resized ();-			}-			-			alloc = allocation;-			-			Context cw = cairo_create (get_window());-			-			Surface s = new Surface.similar (cw.get_target (), Cairo.Content.COLOR_ALPHA, allocation.width, allocation.height);-			Context c = new Context (s); --			glyph_canvas.current_display.draw (glyph_canvas.allocation, c);--			cw.save ();-			cw.set_source_surface (c.get_target (), 0, 0);-			cw.paint ();-			cw.restore ();-			-			return true;-		});--		button_press_event.connect ((t, e)=> {-			set_modifier ((int) e.state);-			-			if (e.type == EventType.BUTTON_PRESS) {-				glyph_canvas.current_display.button_press (e.button, e.x, e.y);	-			} else if (e.type == EventType.2BUTTON_PRESS) {-				glyph_canvas.current_display.double_click (e.button, e.x, e.y);-			}-				-			return true;-		});-		-		button_release_event.connect ((t, e)=> {-			set_modifier ((int) e.state);-			glyph_canvas.current_display.button_release ((int) e.button, e.x, e.y);-			return true;-		});-		-		motion_notify_event.connect ((t, e)=> {-			glyph_canvas.current_display.motion_notify (e.x, e.y);		-			return true;-		});-		-		scroll_event.connect ((t, e)=> {-			set_modifier ((int) e.state);-			-			if (e.direction == Gdk.ScrollDirection.UP) {-				glyph_canvas.current_display.scroll_wheel_up (e.x, e.y);-			} else if (e.direction == Gdk.ScrollDirection.DOWN) {-				glyph_canvas.current_display.scroll_wheel_down (e.x, e.y);-			}-			-			glyph_canvas.current_display.button_release (2, e.x, e.y);-			return true;-		});		-	}--	static void set_modifier (int k) {-		switch (k) {-			case L_SHIFT:-				KeyBindings.set_modifier (SHIFT);-				break;-			case L_CTRL:-				KeyBindings.set_modifier (CTRL);-				break;-			case L_SHIFT_CTRL:-				KeyBindings.set_modifier (SHIFT | CTRL);-				break;-			case W_SHIFT:-				KeyBindings.set_modifier (SHIFT);-				break;-			case W_CTRL:-				KeyBindings.set_modifier (CTRL);-				break;-			case W_SHIFT_CTRL:-				KeyBindings.set_modifier (SHIFT | CTRL);-				break;--			default:-				KeyBindings.set_modifier (NONE);-				break;-		}		-	}-}--public class TooltipCanvas : DrawingArea {-	TooltipArea tooltip_area;-	-	public TooltipCanvas (TooltipArea ta) {-		tooltip_area = ta;--		expose_event.connect ((t, e)=> {-				Allocation allocation = {0, 0, 0, 0};-				Gtk.Allocation alloc;-				Context cr = cairo_create (get_window ());-				-				get_allocation (out alloc);--				allocation.width = alloc.width;-				allocation.height = alloc.height;-				allocation.y = alloc.x;-				allocation.y = alloc.y;-				-				tooltip_area.draw (cr, allocation);--				return true;-		});-		-		tooltip_area.redraw.connect (() => {-			Gtk.Allocation alloc;-			get_allocation (out alloc);-			queue_draw_area (0, 0, alloc.width, alloc.height);-		});-		-		set_size_request (10, 20);-	}-}--}diff --git a/src/IOThread.vala b/src/IOThread.valadeleted file mode 100644index 9869591..0000000--- a/src/IOThread.vala+++ /dev/null@@ -1,32 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--class IOThread {--	public static void add (ThreadFunc<weak void*> func) {-		try {-			unowned Thread<void*> thread = Thread.create<void*> (func, false);-		} catch (Error e) {-			warning (@"e.message");-		}-	}-	-}--}diff --git a/src/Icons.vala b/src/Icons.valadeleted file mode 100644index 3dc19f3..0000000--- a/src/Icons.vala+++ /dev/null@@ -1,50 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--using Cairo;	--public class Icons {-	-	public static ImageSurface? get_icon (string? name) {-		ImageSurface? img = null;-		File f;-		-		if (name == null) {-			warning ("Can't find a file for name \"null\".");-			return null;-		}--		f = find_icon ((!) name);-		-		if (!f.query_exists ()) {-			warning (@"Can't load icon: $((!)f.get_path ())");-			return null;-		}-		-		img = new ImageSurface.from_png ((!)f.get_path ());-		-		return img;-	}--	public static File find_icon (string name) {-		return FontDisplay.find_file ("icons", name);-	}-}-	-}diff --git a/src/Intersection.vala b/src/Intersection.valadeleted file mode 100644index c3e3533..0000000--- a/src/Intersection.vala+++ /dev/null@@ -1,36 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Math;--namespace Supplement {--class Intersection {-	public double x;-	public double y;-	public double distance;-	-	public EditPoint editpoint_a;-	public EditPoint editpoint_b;-	-	public Intersection (double x, double y, double distance) {-		this.x = x;-		this.y = y;-	}-}--}diff --git a/src/IntersectionList.vala b/src/IntersectionList.valadeleted file mode 100644index aad5713..0000000--- a/src/IntersectionList.vala+++ /dev/null@@ -1,236 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Math;--namespace Supplement {--class IntersectionList {-	public List<Intersection> points = new List<Intersection> ();-	-	public void add (Intersection i) {-		double d;-		Intersection l;-		bool close = false;-			-		if (points.length () == 0) {-			points.append (i);-			return;-		}-		-		if (has_point (i)) {-			return;-		}-	-		for (unowned List<Intersection> pl = points.first (); ; pl = pl.next) {-			l = pl.data;-			d = Math.fabs (Math.sqrt (Math.pow (i.x - l.x, 2) + Math.pow (i.y - l.y, 2)));--			if (l.distance >= i.distance) {-				close = true;-				-				if (d < 0.3) {-					points.remove_link (points.last ());-					points.append (i);-					return;-				}-			}-			-			if (pl == points.last ()) {-				break;-			}-		}-		-		if (!close) {-			points.append (i);-		}-	}-	-	public void clear () {-		while (points.length () > 0) {-			points.remove_link (points.first ());-		}-	}-	-	public void remove_point (EditPoint e) {-		foreach (Intersection n in points) {-			if (n.editpoint_a == e || n.editpoint_b == e) {-				points.remove_all (n);-			}-		}	-	}-	-	public bool has_edit_point (EditPoint e) {-		foreach (Intersection n in points) {-			if (n.editpoint_a == e || n.editpoint_b == e) {-				return true;-			}-		}-		-		return false;-	}-	-	bool has_point (Intersection i) {-		return has_point_at (i.x, i.y);-	}--	bool has_point_at (double x, double y) {-		foreach (Intersection n in points) {-			if (x == n.x && y == n.y) {-				return true;-			}-		}-		-		return false;-	}-		-	public int get_point_index (EditPoint e) {-		int i = 0;-		foreach (Intersection n in points) {-			if (e.x == n.x && e.y == n.y) {-				return i;-			}-			i++;-		}-		return -1;-	}-	-	public Intersection? get_next_intersection (EditPoint e) {-		bool found = false;-		foreach (Intersection n in points) {-			if (found) {-				return n;-			}-			-			if (n.editpoint_a == e || n.editpoint_b == e) {-				found = true;-			}-		}-		return null;-	}-	-	public Intersection? get_intersection (EditPoint e) {-		Intersection? p = null;-		foreach (Intersection n in points) {-			if (n.editpoint_a == e || n.editpoint_b == e) {-				assert (p == null);-				p = n;-			}-		}-		return p;-	}-	-	public void append (IntersectionList i) {-		foreach (Intersection inter in i.points) {-			points.append (inter);-		}-	}--	public static IntersectionList create_intersection_list (Path p1, Path p2) {-		IntersectionList il = new IntersectionList ();-		-		unowned List<EditPoint> a_start, a_stop;-		unowned List<EditPoint> b_start, b_stop;-		-		if (p1 == p2) {-			return il;-		}-		-		// find crossing paths-		a_start = p1.points.first ();-		for (int i = 0; i < p1.points.length (); i++) {-			-			if (a_start.data.next == null) {-				a_stop = p1.points.first ();-			} else {-				a_stop = a_start.data.get_next ();-			}-			-			b_start = p2.points.first ();-			for (int j = 0; j < p2.points.length (); j++) {-				-				if (b_start.data.next == null) {-					b_stop = p2.points.first ();-				} else {-					b_stop = b_start.data.get_next ();-				}--				il.append (find_intersections (a_start.data, a_stop.data, b_start.data, b_stop.data));--				b_start = b_stop;-			}-			-			a_start = a_stop;-		}-		-		return il;-	}--	static IntersectionList find_intersections (EditPoint a0, EditPoint a1, EditPoint b0, EditPoint b1) {-		IntersectionList il = new IntersectionList ();-		-		double mind = double.MAX; ;-		-		Path.all_of (a0, a1, (ax, ay, at) => {-			if (at == 0 || at == 1) {-				return true;-			}-			-			Path.all_of (b0, b1, (bx, by, bt) => {-				double d = Math.fabs (Math.sqrt (Math.pow (ax - bx, 2) + Math.pow (ay - by, 2)));-				-				if (d < 0.2) {-					il.add (new Intersection (ax, ay, d));-				}-				-				if (d < mind) mind = d;-				return true;-			});-			-			return true;-		});--		return il;-	}-	-	/** return true if ranges does overlap */-	bool has_intersection (double a0, double a1, double b0, double b1) {-		if (a0 <= b0 <= a1 || a1 <= b0 <= a0) {-			if (a0 <= b1 <= a1 || a1 <= b1 <= a0) {-				return true;-			}-			-			if (b0 <= a1 <= b1 || b1 <= a1 <= b0) {-				return true;-			}-		}-		-		if (b0 <= a0 <= b1 || b1 <= a0 <= b0) {-			if (a0 <= a0 <= a1 || a1 <= a0 <= a0) {-				return true;-			}-			-			if (b0 <= a0 <= b1 || b1 <= a0 <= b0) {-				return true;-			}-		}-		-		return false;-	}-}--}diff --git a/src/Kerning.vala b/src/Kerning.valadeleted file mode 100644index 537e57f..0000000--- a/src/Kerning.vala+++ /dev/null@@ -1,33 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Xml;--namespace Supplement {--public class Kerning : GLib.Object {-	public string glyph_right;-	public double val;-	-	public Kerning (string right, double v) {-		glyph_right = right;-		val = v;-	}-}--}diff --git a/src/KeyBindings.vala b/src/KeyBindings.valadeleted file mode 100644index 14ce53b..0000000--- a/src/KeyBindings.vala+++ /dev/null@@ -1,366 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Gdk;--namespace Supplement {--// FIXA a lot of these things have been replaced and can safely be removed--public enum Key {-	NONE = 0,-	UP = 65362,-	RIGHT = 65363,-	DOWN = 65364,-	LEFT = 65361,-	PG_UP = 65365,-	PG_DOWN = 65366,-	ENTER = 65293,-	BACK_SPACE = 65288,-	SHIFT_LEFT = 65505,-	SHIFT_RIGHT = 65506,-	CTRL_LEFT = 65507,-	CTRL_RIGHT = 65508,-	CAPS_LOCK = 65509,-	ALT_LEFT = 65513,-	ALT_RIGHT = 65514,-	ALT_GR = 65027,-	LOGO_LEFT = 65515,-	LOGO_RIGHT = 65516,-	CONTEXT_MENU = 65383,-	TAB = 65289,-	DEL = 65535-}--bool is_modifier_key (uint i) {-	return Key.UP == i ||-		Key.RIGHT == i ||-		Key.DOWN == i ||-		Key.LEFT == i ||-		Key.PG_UP == i ||-		Key.PG_DOWN == i ||-		Key.ENTER == i ||-		Key.BACK_SPACE == i ||-		Key.SHIFT_LEFT == i ||-		Key.SHIFT_RIGHT == i ||-		Key.CTRL_LEFT == i ||-		Key.CTRL_RIGHT == i ||-		Key.ALT_LEFT == i ||-		Key.ALT_RIGHT == i ||-		Key.ALT_GR == i || -		Key.LOGO_LEFT == i || -		Key.LOGO_RIGHT == i || -		Key.TAB == i || -		Key.CAPS_LOCK == i || -		Key.LOGO_RIGHT == i;-}--/** Modifier flags */-public static const uint NONE  = 0;-public static const uint CTRL  = 1 << 0;-public static const uint ALT   = 1 << 2;-public static const uint SHIFT = 1 << 3;--/** A list of all valid key bindings. */-public class BindingList {-	static ShortCut[]? nc = null;-	-	public static ShortCut[] get_default_bindings () {	-		-		if (nc == null) {-		-			// add all bindings to this list-			ShortCut[] default_bindings = { };--			nc = default_bindings;-		}-		-		return nc;-	}-}---/** Function to be executed for each global key binding. */-public abstract class ShortCut : GLib.Object {-	uint modifier = 0;-	uint key = 0;-	-	public abstract void run ();-	-	public void set_modifier (uint m) {-		modifier = m;-	}--	public void set_key (uint k) {-		key = k;-	}-	-	public uint get_modifier () {-		return modifier;-	}-	-	public uint get_key () {-		return key;-	}-	-	public abstract uint get_default_modifier ();-	public abstract uint get_default_key ();-	-	public abstract unowned string get_description ();-	-	public static EqualFunc<ShortCut> equal = (a, b) => {-		if (a.get_key () != b.get_key ()) return false;-		if (a.get_modifier () != b.get_modifier ()) return false;-		if (a.get_description () != b.get_description ()) return false;-		return true;-	};-	-}--public class KeyBindings {-	-	bool modifier_ctrl = false;-	bool modifier_alt = false;-	bool modifier_shift = false;-		-	public static uint modifier = 0;-	-	bool require_modifier = false;-	-	/** First uint is modifer flag, second uint is keyval */-	HashTable<uint, HashTable<uint, ShortCut>> short_cuts = new HashTable<uint, HashTable<uint, ShortCut>> (null, null);-	-	public static KeyBindings singleton;-	-	public KeyBindings () {-		-		HashFunc<uint> hash = (v) => {-			return v;-		};-	-		HashTable<uint, ShortCut> alt_ctrl_shift = new HashTable<uint, ShortCut> (hash, ShortCut.equal);-		HashTable<uint, ShortCut> alt_shift      = new HashTable<uint, ShortCut> (hash, ShortCut.equal);-		HashTable<uint, ShortCut> ctrl_shift     = new HashTable<uint, ShortCut> (hash, ShortCut.equal);-		HashTable<uint, ShortCut> alt            = new HashTable<uint, ShortCut> (hash, ShortCut.equal);-		HashTable<uint, ShortCut> ctrl           = new HashTable<uint, ShortCut> (hash, ShortCut.equal);-		-		// Possible modifiers-		short_cuts.insert (ALT|CTRL|SHIFT, alt_ctrl_shift);-		short_cuts.insert (ALT|SHIFT,      alt_shift);-		short_cuts.insert (CTRL|SHIFT,     ctrl_shift);-		short_cuts.insert (ALT,            alt);-		short_cuts.insert (CTRL,           ctrl);-		-		// Add default bindings-		foreach (var b in BindingList.get_default_bindings ()) {-			add_binding (b);-		}-		-		save ();-		load ();-		-		singleton = this;-	}-	-	public void reset () {-		modifier = NONE;-		modifier_ctrl = false;-		modifier_alt = false;-		modifier_shift = false;-	}-	-	public void set_require_modifier (bool m) {-		require_modifier = m;-	}-	-	void load () {-		File home = File.new_for_path (Environment.get_home_dir ());-		File settings = home.get_child (".birdfont");-		File bindings = settings.get_child ("keybindings");--		if (!bindings.query_exists ()) {-			return;-		}--		FileStream? bindings_file = FileStream.open ((!) bindings.get_path (), "r");-		-		if (bindings_file == null) {-			stderr.printf ("Failed to load keybindings from file %s.\n", (!) bindings.get_path ());-			return;-		}-		-		return_if_fail (bindings_file != null);-		-		unowned FileStream b = (!) bindings_file;-		-		string? l;-		l = b.read_line ();-		while ((l = b.read_line ())!= null) {-			string line;-			-			line = (!) l;-			-			if (line.get_char (0) == '#') {-				continue;-			}-			-			int i = 0;-			int s = 0;-			-			i = line.index_of_char(' ', s);-			string mod = line.substring (s, i - s);-			-			s = i + 1;-			i = line.index_of_char(' ', s);-			string key = line.substring (s, i - s);-			-			s = i + 1;-			i = line.index_of_char('\n', s);-			string description = line.substring (s, i - s);--			update_short_cut (mod, key, description);-		}-		-	}-	-	void save () {-		try {-			File settings = Supplement.get_settings_directory ();-			File bindings = settings.get_child ("keybindings");-			-			if (bindings.query_exists ()) {-				bindings.delete ();-			}--			DataOutputStream os = new DataOutputStream(bindings.create(FileCreateFlags.REPLACE_DESTINATION));-			uint8[] data;-			long written = 0;-			-			StringBuilder sb = new StringBuilder ();-			-			sb.append_printf ("# Supplement keybindings\n");-			sb.append_printf ("# Version: 1.0\n");-			-			short_cuts.foreach ( (k, v) => {-				v.foreach ( (k, s) => {-					sb.append_printf ("%u %u %s\n", s.get_modifier (), s.get_key (), s.get_description ());-				});-			});-			-			data = sb.str.data;-			-			while (written < data.length) { -				written += os.write (data[written:data.length]);-			}-			-		} catch (Error e) {-			stderr.printf ("Can not save key bindings. (%s)", e.message);	-		}-		-	}-	-	void update_short_cut (string mod, string key, string description) {-		ShortCut? sc = null;-		ShortCut binding;-		short_cuts.foreach ( (k, ht) => {-			ht.foreach ((ki, s) => {-				if (s.get_description () == description) {-					sc = s;-				}-				-				// ht.remove (ki); // FIXME! why does it remove all items?-				-			});-		});-		-		if (unlikely (sc == null)) {-			stderr.printf ("Can not set bindings for \"%s-%s-%s-\"\n", mod, key, description);-			return;-		}-		-		binding = (!) sc;--		binding.set_modifier ((uint) uint64.parse (mod));-		binding.set_key ((uint) uint64.parse (key));-		-		add_binding (binding);-	}-	-	void add_binding (ShortCut binding) {-		uint m = binding.get_modifier ();-		-		if (m == 0) {-			m = binding.get_default_modifier ();-			binding.set_modifier (m);-		}--		if (binding.get_key () == 0) {-			binding.set_key (binding.get_default_key ());-		}-		-		HashTable<uint, ShortCut>? e = short_cuts.lookup (m);-		-		if (e == null) {-			stderr.printf ("Invalid modifier in key binding: expecting ctrl, alt or shift. Flag: (%u) \n", modifier);-			return;-		}-		-		var events = (!) e;-		events.insert (binding.get_key (), binding);-	}-	-	bool get_short_cut (uint modifier, uint key, out ShortCut? sc) {-		sc = null;-		-		foreach (var v in BindingList.get_default_bindings ()) {-			if (v.get_default_modifier () == modifier && v.get_default_key () == key) {-				sc = v;-				return true;-			}-		}-		-		return false;-	}--	public static void set_modifier (uint mod) {-		modifier = mod;--		singleton.modifier_ctrl = ((modifier & CTRL) > 0);-		singleton.modifier_alt = ((modifier & ALT) > 0);-		singleton.modifier_shift = ((modifier & SHIFT) > 0);-	}--	public static bool has_alt () {-		return singleton.modifier_alt;-	}-	-	public static bool has_shift () {-		return singleton.modifier_shift;-	}-		-	public static bool has_ctrl () {-		return singleton.modifier_ctrl;-	}-	-	public void key_release (uint keyval) {-	}-	-	public void key_press (uint keyval) {-	}-}--}diff --git a/src/Line.vala b/src/Line.valadeleted file mode 100644index d5adc9f..0000000--- a/src/Line.vala+++ /dev/null@@ -1,342 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--/** Help line */-public class Line : GLib.Object {-	public static const bool VERTICAL = true;-	public static const bool HOROIZONTAL = false;-	-	string label;-	bool vertical;-	-	public double pos;-	-	bool active = false;-	bool move = false;-	-	public signal void queue_draw_area (int x, int y, int w, int h);-	public signal void position_updated (double pos);-	-	public unowned List<Line>? list_item = null;-	-	double r;-	double g;-	double b;-	double a;-	-	bool visible = true;-	bool moveable = true;-	-	public Line (string label = "No label set", double position = 10, bool vertical = false) {-		this.label = label;-		this.vertical = vertical;-		this.pos = position;-		-		r = 0.7;-		g = 0.7;-		b = 0.8;-		a = 1;-	}-	-	public Line copy () {-		Line l = new Line (label, pos, vertical);-		-		l.r = r;-		l.g = g;-		l.b = b;-		l.a = a;-		-		l.visible = visible;-		-		return l;-	}-	-	public void set_visible (bool v) {-		visible = v;-	}-	--	public void set_moveable (bool m) {-		moveable = m;-	}-	-	public void set_color (double r, double g, double b, double a) {-		this.r = r;-		this.g = g;-		this.b = b;-		this.a = a;-	}-	-	// Valabug, it should be: public “void set_list_item (unowned List<Line> ll) {”-	// Keeping list_item public for now-	public void set_list_item (void* ll) {-		// list_item = ll;-	}-	-	public bool is_moving () {-		return move;-	}-	-	public bool set_move (bool moving) {-		bool r = move;-		move = moving;-		return (r == moving);-	}-	-	public bool button_press () {-		Glyph g;-		if (get_active ()) {-			move = true;-			-			g = MainWindow.get_current_glyph ();-			g.store_undo_state ();-			-			return true;-		}-		-		return false;-	}--	public void redraw_line () {-		double p;-		Glyph g = MainWindow.get_current_glyph ();-		-		if (vertical) {-			p = g.reverse_path_coordinate_x (pos);-			queue_draw_area ((int)p - 10, 0, 200, g.allocation.height);-		} else {-			p = g.reverse_path_coordinate_y (pos);-			queue_draw_area (0, (int)p - 10, g.allocation.width, 300);-		}-	}	-	-	public void move_line_to (double x, double y, Allocation allocation) {-		set_move (true);-		event_move_to (x, y, x, y, allocation);-	}-	-	public void event_move_to (double x, double y, double ax, double ay, Allocation allocation) {-		double p, c;-		bool a = false;-		Glyph g = MainWindow.get_current_glyph ();-		double ivz = 1/g.view_zoom;-		double margin = 10;--		if (!moveable) return;--		if (move) {-			double np = pos;-		-			redraw_line (); // clear old position-			-			if (is_vertical ()) {-				pos = Glyph.path_coordinate_x (x);-				redraw_line (); // draw at new position-			} else {-				pos = -Glyph.path_coordinate_y (y);-				redraw_line ();-			}--			if (Math.fabs (np - pos) > 10) {-				queue_draw_area (0, 0, g.allocation.width, g.allocation.height);-			}--			position_updated (pos); // sinal update--			Supplement.get_current_font ().touch ();-			-			swap_lines ();-		}--		if (is_vertical ()) {-			if (ay > g.allocation.height - 10) {-				-				p = get_coordinate ();--				c = ax * ivz + g.view_offset_x;-				a = (p - margin * ivz <= c <= p + margin * ivz);-			}-					-			if (a != get_active ()) {-				redraw_line ();-			}--			set_active (a);-			-		} else {-			if (ax > g.allocation.width - 10) {-				p = get_coordinate ();-				c = ay * ivz + g.view_offset_y;-				a = (p - margin * ivz <= c <= p + margin * ivz);-			}-			-			if (a != get_active ()) {-				redraw_line ();-			}-			-			set_active (a);-		}-	}-	-	private void swap_lines () {-		double ivz = 1 / MainWindow.get_current_glyph ().view_zoom;-		-		if (!moveable) return;-		-		// switch to correct line if this line passes the next one in the sorted list-		if (list_item != null) {-			unowned List<Line> ll = (!) list_item;-			-			if (ll != ll.first ()) {-				if (ll.prev.data.pos > pos) {-					ll.prev.data.set_move (true);-					ll.prev.data.set_visible (true);-					set_move (false);-					pos = ll.prev.data.pos;-					ll.prev.data.pos += 1 * ivz;-				}-			}-			-			if (ll != ll.last ()) {-				if (ll.next.data.pos < pos) {-					ll.next.data.set_move (true);-					ll.next.data.set_visible (true);-					pos = ll.next.data.pos;-					ll.next.data.pos -= 1 * ivz;-					set_move (false);-				}-			}-			-		}-		-	}-	-	public bool get_active () {-		return active;-	}-	-	protected void set_active (bool active) {-		this.active = active;-	}-	-	public string get_label () {-		return label;-	}-	-	public bool is_vertical () {-		return vertical;-	}--	public double get_coordinate () {-		Glyph g = MainWindow.get_current_glyph ();-		double t = (is_vertical ()) ? (g.allocation.width / 2.0) : (g.allocation.height / 2.0);-		return pos + t;-	}-	-	public double get_pos () {-		return pos;-	}-	-	private double get_handle_size () {-		Glyph g = MainWindow.get_current_glyph ();-		double ivz = 1/g.view_zoom;-		return (get_active ()) ? (10 * ivz) : (5 * ivz);-	}-	-	public void draw (Context cr, Allocation allocation) {-		Glyph g = MainWindow.get_current_glyph ();-		-		double p, h, w;-		double ivz = 1/g.view_zoom;--		double size = get_handle_size ();-		-		if (!visible) return;-		-		cr.save ();-		cr.set_line_width (ivz);-		-		if (active) cr.set_source_rgba (0, 0, 0.3, 1);-		else cr.set_source_rgba (r, this.g, b, a);-		-		// Line-		if (is_vertical ()) {-			p = get_coordinate ();-			-			h = g.allocation.height * ivz + g.view_offset_y;-			-			cr.move_to (p, g.view_offset_y);-			cr.line_to (p, h);-			cr.stroke ();--			cr.scale (1, 1);--			if (moveable) {-				cr.new_path ();-				cr.move_to (p - size, h);	-				cr.line_to (p, h - size);	-				cr.line_to (p + size, h);-				cr.close_path();-				cr.fill ();-			}-			-		} else {-			p = get_coordinate ();-			-			w = g.allocation.width * ivz  + g.view_offset_x;-			-			cr.move_to (g.view_offset_x, p);-			cr.line_to (w, p);	-			cr.stroke ();-			-			if (moveable) {-				cr.new_path ();-				cr.move_to (w, p - size);	-				cr.line_to (w - size, p);	-				cr.line_to (w, p + size);-				cr.close_path();-				cr.fill ();-			}-		}-		-		// Label-		if (get_active ()) {				 -			if (is_vertical ()) {-				h = g.allocation.height * ivz + g.view_offset_y;-				cr.move_to (p + 8*ivz, h - 30*ivz);-			} else {-				w = g.allocation.width * ivz + g.view_offset_x;-				cr.move_to (w - 70*ivz, p + 15*ivz);-			}--			cr.set_font_size (12 * ivz);-			cr.select_font_face ("Cantarell", FontSlant.NORMAL, FontWeight.BOLD);-	-			cr.show_text (get_label ());-			cr.stroke ();-		}-		-		cr.restore ();-	}--}--}diff --git a/src/Main.vala b/src/Main.valadeleted file mode 100644index 09cbc58..0000000--- a/src/Main.vala+++ /dev/null@@ -1,53 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Birdfont;-using Supplement;--const string GETTEXT_PACKAGE = "birdfont"; --void parse_gtk_rc () {-	File f = FontDisplay.find_file ("layout", "birdfont.rc");-	Gtk.rc_parse ((!) f.get_path ());-}--public static int main (string[] arg) {-	GtkWindow native_window;-	MainWindow window;-	string file;-	Supplement.Supplement supplement = new Supplement.Supplement ();-	supplement.init (arg);-	-	Gtk.init (ref arg);-	parse_gtk_rc ();--	native_window = new GtkWindow ("birdfont");	-	window = new MainWindow ();-	-	window.set_native (native_window);-	native_window.init ();--	file = supplement.args.get_file ();-	if (file != "") {-		window.menu_tab.load_font (file);-	}-	-	Gtk.main ();--	return 0;-}-diff --git a/src/MainWindow.vala b/src/MainWindow.valadeleted file mode 100644index c00409c..0000000--- a/src/MainWindow.vala+++ /dev/null@@ -1,158 +0,0 @@-/*
-    Copyright (C) 2012 Johan Mattsson
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-namespace Supplement {
-
-public class MainWindow {
-	
-	public static Toolbox tools;
-	public static GlyphCanvas glyph_canvas;
-	public static MainWindow singleton;
-	public static KeyBindings key_bindings;
-	public static MenuTab menu_tab;
-	public static TooltipArea tool_tip;
-	public static OverView over_view;	
-	public static TabBar tabs;
-	public static NativeWindow native_window;
-
-	public MainWindow () {
-		singleton = this;
-
-		key_bindings = new KeyBindings ();
-		glyph_canvas = new GlyphCanvas ();
-		tools = new Toolbox (glyph_canvas);
-		tabs = new TabBar ();
-		menu_tab = new MenuTab ();
-		tool_tip = new TooltipArea ();
-		over_view = new OverView();
-	}
-
-	public void set_native (NativeWindow nw) {
-		native_window = nw;
-	}
-
-	internal static NativeWindow get_native () {
-		return native_window;
-	}
-	
-	internal static MainWindow get_current_window () {
-		return singleton;
-	}
-	 
-	public static FontDisplay get_current_display () {
-		return get_glyph_canvas ().get_current_display ();
-	}
-	
-	public static GlyphCanvas get_glyph_canvas () {
-		return glyph_canvas;
-	}
-	
-	internal static Glyph get_current_glyph () {
-		if (unlikely (is_null (Supplement.current_glyph))) {
-			warning ("No default glyph have been set yet.\n");
-			return new Glyph ("no_glyph_created");
-		}
-		
-		return Supplement.current_glyph;
-	}
-	
-	public static Toolbox get_toolbox () {
-		return get_singleton ().tools;
-	}
-	
-	internal static Tool get_tool (string n) {
-		return get_singleton ().tools.get_tool (n);
-	}
-	
-	public static TabBar get_tab_bar () {
-		return get_singleton ().tabs;
-	}
-
-	internal static Tab get_current_tab () {
-		return get_singleton ().tabs.get_selected_tab ();
-	}
-
-	internal static TooltipArea get_tool_tip () {
-		return tool_tip;
-	}
-
-	internal static bool select_tab (Tab t) {
-		return get_singleton ().tabs.selected_open_tab (t);
-	}
-
-	internal static OverView get_overview () {
-		OverView over_view;
-		
-		foreach (var t in tabs.tabs) {
-			if (t.get_display () is OverView) {
-				return (OverView) t.get_display ();
-			}
-		}
-		
-		over_view = new OverView();
-		tabs.add_unique_tab (over_view, 100, false);
-				
-		return over_view;
-	}
-	
-	internal static MenuTab get_content_display () {
-		if (unlikely ((MenuTab?) get_singleton ().menu_tab == null)) {
-			warning ("MenuTab not instantiated.");
-		}
-		
-		return get_singleton ().menu_tab;
-	}
-	
-	public static MainWindow get_singleton () {
-		return singleton;
-	}
-	
-	public static string? file_chooser (string title) {
-		return get_singleton ().native_window.file_chooser (title);
-	}
-	
-	public void set_title (string title) {
-		// FIXA:
-		// native_window.set_title (title);
-	}
-	
-	/** Reaload all paths and help lines from disk. */
-	internal static void clear_glyph_cache () {
-		Glyph g;
-		foreach (Tab t in get_tab_bar ().tabs) {
-			if (t.get_display () is Glyph) {
-				g = (Glyph) t.get_display ();
-				g.add_help_lines ();
-			}
-		}
-		
-		get_glyph_canvas ().redraw ();
-	}
-		
-	internal static void close_all_tabs () {
-		uint i = 0;
-		uint len = get_tab_bar ().get_length ();
-		
-		while (i < len) {
-			if (!get_tab_bar ().close_tab ((int) i)) {
-				i++;
-			}
-		}
-	}
-}
-
-}
diff --git a/src/MenuAction.vala b/src/MenuAction.valadeleted file mode 100644index d44e7c4..0000000--- a/src/MenuAction.vala+++ /dev/null@@ -1,62 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--public class MenuAction : GLib.Object {-	public string label;-	public DropMenu.Selected action;-	public DropMenu? parent = null;-	public int index = -1;-	bool selected = false;-	-	public MenuAction (string label) {-		this.label = label;-	}-	-	public void set_selected (bool s) {-		selected = s;-	}-	-	public virtual void draw (double x, double y, Context cr) {-		if (selected) {-			cr.save ();-			cr.set_line_join (LineJoin.ROUND);-			cr.set_line_width (12);-			cr.set_source_rgba (102/255.0, 120/255.0, 149/255.0, 1);-			cr.rectangle (x - 2, y - 9, 88, 8);-			cr.fill_preserve ();-			cr.stroke ();-			cr.restore ();			-		}-		-		cr.save ();-		cr.set_source_rgba (1, 1, 1, 1);-		-		cr.set_font_size (12);-		cr.select_font_face ("Cantarell", FontSlant.NORMAL, FontWeight.NORMAL);-		-		cr.move_to (x, y);--		cr.show_text (label);-		cr.restore ();-	}-}--}diff --git a/src/MenuTab.vala b/src/MenuTab.valadeleted file mode 100644index 9371772..0000000--- a/src/MenuTab.vala+++ /dev/null@@ -1,339 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using WebKit;--namespace Supplement {--public class MenuTab : FontDisplay {	-	-	List<Font> recent_fonts = new List<Font> ();--	public MenuTab () {-		// html callbacks:-		add_html_callback ("export_name", (val) => {-			Font f = Supplement.get_current_font ();-			f.set_name (val);-			f.touch ();-		});--		add_html_callback ("export", (val) => {-			ExportTool.export_all ();-		});--		add_html_callback ("preview", (val) => {-			preview ();-		});		--		add_html_callback ("load", (val) => {-			load_font (val);-		});	--		add_html_callback ("glyph_sequence", (val) => {-			Preferences.set ("glyph_sequence", val);-		});--		add_html_callback ("help", (val) => {-			MainWindow.get_tool_tip ().show_text (val);-		});-	}-		-	public override string get_name () {-		return "Menu";-	}-	-	public override bool is_html_canvas () {-		return true;-	}--	public override string get_html () {-		Font f = Supplement.get_current_font ();-		StringBuilder c = new StringBuilder ();-		string fn;-		-		propagate_recent_files ();-		-		c.append (-"""-<html>-<head>-	<script type="text/javascript" src="supplement.js"></script>-	<style type="text/css">@import url("style.css");</style>-	<script type="text/javascript">-		document.onkeyup = update_text_fields; -	</script>-</head>-<body>-	<div class="inner_format_box">-		<div class="content">-			<div class="heading"><h2>""" + _("Preferences") + """</h2></div>-			<form>-""");-				-c.append ("""-				<h3>""" + _("Glyph sequence") + """</h3>-				<input class="text" type="text" id="glyph_sequence" value=""" + "\"" + Preferences.get ("glyph_sequence") + "\"" + """ onchange="update_text_fields ();"/><br />--				<h3>""" + _("Name") + """</h3>-				<input class="text" type="text" id="fontname" value=""" + "\"" + f.get_name () + "\"" + """ onchange="update_text_fields ();"/><br />-				-				<input class="button" type="button" value=""" + "\"" + _("Export") + "\"" + """ id="export_button" onclick="call ('export:fonts');" onmouseover="call ('help:(Ctrl+e) """ + _("Export SVG, TTF & EOT fonts") + """');"/>-				<input class="button" type="button" value=""" + "\"" + _("Preview") + "\"" + """ id="preview_button" onclick="call ('preview:fonts');" onmouseover="call ('help:(Ctrl+p) """ + _("Export SVG font and view the result") + """');"/><br />-""");-	-c.append ("""--			</form> --		</div>-	</div>-	-	<img src="birdfont_logo.png" alt="" style="float:right;margin: 50px 0 0 0;">-	-	<br class="clearBoth" />-	-	<div class="recent_list">-""");--if (recent_fonts.length () > 0) {-	c.append ("""<div class="heading"><h2>""" + _("Recent files") + """</h2></div>""");-	c.append ("\n");-}--foreach (Font font in recent_fonts) {-	fn = (!) font.font_file;-	fn = fn.substring (fn.replace ("\\", "/").last_index_of ("/") + 1);	-	-	c.append ("""<div class="recent_font" """ + "onclick=\"call ('load:" + ((!) font.font_file).replace ("\\", "\\\\") + "');\">");--	c.append ("<div class=\"one_line\">");-	c.append (fn);-	c.append ("</div>");--	c.append ("<img src=\"");-	c.append (path_to_uri ((!) Supplement.get_thumbnail_directory ().get_path ()));-	c.append ("/");-	c.append (fn);-	c.append (@".png?$(Random.next_int ())\" alt=\"\">");-	-	c.append ("<br /><br />");-	c.append ("</div>\n");-}--c.append ("</div>\n");--c.append ("""-</body>-</html>-""");--		return c.str;-	}-	-	void translations () {-		// xgettext needs these lines in order to extract strings properly-		// TODO: update xgettext to handle vala strings well.-		_("Preferences");-		_("Export SVG, TTF & EOT fonts");-		_("Name");-		_("Glyph sequence");-		_("Export SVG font and view the result");-		_("Export SVG font and view the result");-	}--	public void propagate_recent_files () {-		Font font;--		while (recent_fonts.length () != 0) {-			recent_fonts.delete_link (recent_fonts.first ());-		}-		-		foreach (var f in Preferences.get_recent_files ()) {-			if (f == "") continue;-			-			File file = File.new_for_path (f);--			font = new Font ();-			-			font.set_font_file (f);-			-			if (file.query_exists ()) { -				recent_fonts.append (font);-			}-		}-		-		recent_fonts.reverse ();-	}-	-	public void load_font (string fn) {-		Font font = Supplement.get_current_font ();-			-		SaveDialog save = new SaveDialog ();-		save.finished.connect (() => {-			Font f;-			bool loaded;-			-			f = Supplement.get_current_font ();-			f.delete_backup ();-			-			MainWindow.clear_glyph_cache ();-			MainWindow.close_all_tabs ();-			-			loaded = f.load (fn);-			-			if (!unlikely (loaded)) {-				warning (@"Failed to load fond $fn");-				return;-			}-				-			MainWindow.get_singleton ().set_title (f.get_name ());-			-			MainWindow.get_toolbox ().remove_all_grid_buttons ();-			foreach (string v in f.grid_width) {-				MainWindow.get_toolbox ().parse_grid (v);-			}-			-			MainWindow.get_toolbox ().background_scale.set_value (f.background_scale);-			-			select_overview ();-		});-		-		MainWindow.get_tab_bar ().add_unique_tab (save, 80);-		-		if (!font.is_modified ()) {-			save.finished ();-		}-	}-	-	private static void select_overview () {-		Toolbox tb = MainWindow.get_toolbox ();-		-		if (Supplement.get_current_font ().is_empty ()) {-			tb.select_tool_by_name ("custom_character_set");-		} else {-			tb.select_tool_by_name ("available_characters");	-		}-	}--	public static bool save_as ()  {-		string? fn = null;-		string f;-		bool saved = false;-		Font font = Supplement.get_current_font ();-		-		fn = MainWindow.file_chooser (_("Save"));-		-		if (fn != null) {-			f = (!) fn;-			-			if (!f.has_suffix (".ffi")) {-				f += ".ffi";-			}-			-			font.font_file = f;-			save ();-			saved = true;-		}--		return saved;-	}--	public static bool save () {-		Font f = Supplement.get_current_font ();-		string fn;-		bool saved = false;-		-		fn = (!) f.font_file;-		-		if (f.font_file != null && fn.has_suffix (".ffi")) {-			f.background_scale = MainWindow.get_toolbox ().background_scale.get_display_value ();-			-			while (f.grid_width.length () > 0) {-				f.grid_width.remove_link (f.grid_width.first ());-			}-			-			foreach (SpinButton s in GridTool.sizes) {-				f.grid_width.append (s.get_display_value ());-			}-			-			f.save (fn);-			saved = true;-		} else {-			saved = save_as ();-		}-		-		return saved;-	}-	-	public static void new_file () {-		Font font = Supplement.get_current_font ();-		SaveDialog save = new SaveDialog ();-		save.finished.connect (() => {-			Font f = Supplement.get_current_font ();-			f.delete_backup ();-			-			Supplement.new_font ();-			MainWindow.close_all_tabs ();-			-			MainWindow.get_toolbox ().remove_all_grid_buttons ();-			MainWindow.get_toolbox ().add_new_grid ();-			MainWindow.get_toolbox ().add_new_grid ();-			-			select_overview ();-		});-		-		MainWindow.get_tab_bar ().add_unique_tab (save, 80);-		-		if (!font.is_modified ()) {-			save.finished ();-		}-	}-	-	public static void load () {-		SaveDialog save = new SaveDialog ();-		save.finished.connect (() => {-			load_new_font ();-		});-		MainWindow.get_tab_bar ().add_unique_tab (save, 80);-	}--	private static void load_new_font () {-		string? fn = MainWindow.file_chooser ("load");-		Font f = Supplement.get_current_font ();-		-		if (fn != null) {-			f.delete_backup ();-			-			MainWindow.clear_glyph_cache ();-			MainWindow.close_all_tabs ();-			f.load ((!)fn);-			-			MainWindow.get_singleton ().set_title (f.get_name ());-			select_overview ();		-		}-	}-	-	public static void show_kerning_context () {-		MainWindow.get_tab_bar ().add_unique_tab (new ContextDisplay (), 85, false);-	}-	-	public static void preview () {-		TabBar t = MainWindow.get_tab_bar ();-		t.add_unique_tab (new Preview (), 80);		-	}-}-}diff --git a/src/MergeTool.vala b/src/MergeTool.valadeleted file mode 100644index 030d9bb..0000000--- a/src/MergeTool.vala+++ /dev/null@@ -1,271 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--class MergeTool : Tool {--	public MergeTool (string n) {-		base (n, _("Merge paths"));-	-		select_action.connect((self) => {-			Glyph g = MainWindow.get_current_glyph ();-			g.merge_all ();-		});-	}--	public override bool test () {-		test_merge_second_triangle ();-		test_merge_simple_path_box_and_triangle ();-		test_merge_simple_path_box ();-		test_merge_odd_paths ();-		return true;-	}-	-	private void test_merge_simple_path_box () {-		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");--		this.yield ();-		MainWindow.get_tab_bar ().select_overview ();--		this.yield ();-		MainWindow.get_overview ().open_current_glyph ();-		-		pen_tool.test_select_action ();--		int x_offset = 10;-		int y_offset = 350;-		-		// draw it-		pen_tool.test_click_action (1, 10 + x_offset, 10 + y_offset);-		pen_tool.test_click_action (1, 20 + x_offset, 10 + y_offset);-		pen_tool.test_click_action (1, 20 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset); -		pen_tool.test_click_action (3, 0, 0);--		pen_tool.test_press_action (1, 1, 1);-		pen_tool.test_move_action (15 + x_offset, 15 + y_offset);-		pen_tool.test_release_action (1, 15 + x_offset, 15 + y_offset);-				-		pen_tool.test_click_action (1, 25 + x_offset, 15 + y_offset);-		pen_tool.test_click_action (1, 25 + x_offset, 25 + y_offset);-		pen_tool.test_click_action (1, 15 + x_offset, 25 + y_offset); -		pen_tool.test_click_action (3, 0, 0);-		-		// merge it-		this.test_select_action ();-		-		// test result-		Path merged_outline = new Path ();-		add_point_on_path (merged_outline, 10 + x_offset, 10 + y_offset);-		add_point_on_path (merged_outline, 20 + x_offset, 10 + y_offset);-		add_point_on_path (merged_outline, 20 + x_offset, 15 + y_offset);-		add_point_on_path (merged_outline, 25 + x_offset, 15 + y_offset);-		add_point_on_path (merged_outline, 25 + x_offset, 25 + y_offset);-		add_point_on_path (merged_outline, 15 + x_offset, 25 + y_offset);-		add_point_on_path (merged_outline, 15 + x_offset, 20 + y_offset);-		add_point_on_path (merged_outline, 10 + x_offset, 20 + y_offset);-		merged_outline.close ();-		-		// select path-		pen_tool.test_click_action (3, 12 + x_offset, 12 + y_offset);-		-		Glyph g = MainWindow.get_current_glyph ();-		Path? l = g.get_active_path ();-		-		if (l == null) {-			critical ("No path found in merge test, it did not merge correctly.");-			return;-		}-		-		Path last = (!) l;-		bool merged_path_looks_good = false;-		 merged_path_looks_good = last.test_is_outline (merged_outline);-		-		if (!merged_path_looks_good) critical ("Failed to merge path correctly.");-	}--	private void test_merge_simple_path_box_and_triangle () {-		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");--		this.yield ();-		MainWindow.get_tab_bar ().select_overview ();--		this.yield ();-		MainWindow.get_overview ().open_current_glyph ();-		-		pen_tool.test_select_action ();--		int x_offset = 40;-		int y_offset = 350;-		-		// draw it-		pen_tool.test_click_action (1, 0 + x_offset, -50 + y_offset);-		pen_tool.test_click_action (1, 25 + x_offset, -50 + y_offset);-		pen_tool.test_click_action (1, 25 + x_offset, 0 + y_offset);-		pen_tool.test_click_action (1, 0 + x_offset, 0 + y_offset); -		pen_tool.test_click_action (3, 0, 0);-		-		pen_tool.test_click_action (1, 0 + x_offset, 0 + y_offset);-		pen_tool.test_click_action (1, 50 + x_offset, -50 + y_offset);-		pen_tool.test_click_action (1, 50 + x_offset, 0 + y_offset); -		pen_tool.test_click_action (3, 0, 0);--		// merge it-		this.test_select_action ();-		-		// test result-		Path merged_outline = new Path ();-		add_point_on_path (merged_outline, 0 + x_offset, -50 + y_offset);-		add_point_on_path (merged_outline, 25 + x_offset, -50 + y_offset);-		add_point_on_path (merged_outline, -50 + x_offset, -50 + y_offset);-		add_point_on_path (merged_outline, -50 + x_offset, 0 + y_offset);-		add_point_on_path (merged_outline, 0 + x_offset, 0 + y_offset);-		merged_outline.close ();--		// select path-		pen_tool.test_click_action (3, 21 + x_offset, -11 + y_offset);-		-		Glyph g = MainWindow.get_current_glyph ();-		Path? l = g.get_active_path ();-		-		if (l == null) {-			critical ("No path found in test_merge_simple_path_box_and_triangle, it did not merge correctly.");-			return;-		}-		-		Path last = (!) l;-		bool merged_path_looks_good = false;-		 merged_path_looks_good = last.test_is_outline (merged_outline);-		-		if (!merged_path_looks_good) critical ("Failed to merge path correctly.");-	}--	private void test_merge_second_triangle () {-		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");--		this.yield ();-		MainWindow.get_tab_bar ().select_overview ();--		this.yield ();-		MainWindow.get_overview ().open_current_glyph ();-		-		pen_tool.test_select_action ();--		int x_offset = 100;-		int y_offset = 350;-		-		// draw it-		pen_tool.test_click_action (1, 25 + x_offset, 0 + y_offset);-		pen_tool.test_click_action (1, 25 + x_offset, -50 + y_offset);-		pen_tool.test_click_action (1, 0 + x_offset, -50 + y_offset);-		pen_tool.test_click_action (1, 0 + x_offset, 0 + y_offset); --		pen_tool.test_click_action (3, 0, 0);-		-		pen_tool.test_click_action (1, 40 + x_offset, -50 + y_offset);-		pen_tool.test_click_action (1, 10 + x_offset, -20 + y_offset);-		pen_tool.test_click_action (1, 25 + x_offset, -20 + y_offset); -		pen_tool.test_click_action (3, 0, 0);-		-		// merge it-		this.test_select_action ();-		-		// test result-		Path merged_outline = new Path ();-		add_point_on_path (merged_outline, 0 + x_offset, -50 + y_offset);-		add_point_on_path (merged_outline, 25 + x_offset, -50 + y_offset);-		add_point_on_path (merged_outline, -50 + x_offset, -50 + y_offset);-		add_point_on_path (merged_outline, -50 + x_offset, 0 + y_offset);-		add_point_on_path (merged_outline, 0 + x_offset, 0 + y_offset);-		merged_outline.close ();--		// select path-		pen_tool.test_click_action (3, 21 + x_offset, -11 + y_offset);-		-		Glyph g = MainWindow.get_current_glyph ();-		Path? l = g.get_active_path ();-		-		if (l == null) {-			critical ("No path found in triangle_right, it did not merge correctly.");-			return;-		}-		-		Path last = (!) l;-		bool merged_path_looks_good = false;-		 merged_path_looks_good = last.test_is_outline (merged_outline);-		-		if (!merged_path_looks_good) critical ("Failed to merge path correctly.");-	}-	-	private void add_point_on_path (Path p, int x, int y) {-		Glyph g = MainWindow.get_current_glyph ();-		p.add (g.path_coordinate_x (x), g.path_coordinate_y (y));-	}-	-	-	private void test_merge_odd_paths () {-		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");--		this.yield ();-		MainWindow.get_tab_bar ().select_overview ();--		this.yield ();-		MainWindow.get_overview ().open_current_glyph ();-		-		pen_tool.test_select_action ();--		// paint-		int x_offset = 100;-		int y_offset = 100;-						-		// rectangle-		pen_tool.test_click_action (1, 100 + x_offset, 100 + y_offset);-		pen_tool.test_click_action (1, 170 + x_offset, 100 + y_offset);-		pen_tool.test_click_action (1, 170 + x_offset, 120 + y_offset);-		pen_tool.test_click_action (1, 100 + x_offset, 120 + y_offset); -		pen_tool.test_click_action (3, 0, 0); // close-		-		// triangle-		pen_tool.test_click_action (1, 100 + x_offset, 110 + y_offset);-		pen_tool.test_click_action (1, 180 + x_offset, 130 + y_offset);-		pen_tool.test_click_action (1, -10 + x_offset, 140 + y_offset);-		pen_tool.test_click_action (3, 0, 0);--		// several triangles-		pen_tool.test_click_action (1, 198, 379); -		pen_tool.test_click_action (1, 274, 328); -		pen_tool.test_click_action (1, 203, 286); -		pen_tool.test_click_action (3, 230, 333);--		pen_tool.test_click_action (1, 233, 429); -		pen_tool.test_click_action (1, 293, 382); -		pen_tool.test_click_action (1, 222, 322); -		pen_tool.test_click_action (3, 225, 406);--		pen_tool.test_click_action (1, 164, 316); -		pen_tool.test_click_action (1, 262, 289); -		pen_tool.test_click_action (1, 203, 260); -		pen_tool.test_click_action (3, 203, 260); --		this.yield ();-		-		test_select_action ();-	}	-}--}diff --git a/src/MoveTool.vala b/src/MoveTool.valadeleted file mode 100644index 6b23ba6..0000000--- a/src/MoveTool.vala+++ /dev/null@@ -1,187 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Math;-using Cairo;--namespace Supplement {--class MoveTool : Tool {--	bool move_path = false;-	double last_x = 0;-	double last_y = 0;--	bool resize_path = false;-	Path? resized_path = null;-	double last_resize_y;-	-	ImageSurface? resize_handle;-	-	public MoveTool (string n) {-		base (n, _("Move paths"), 'm', CTRL);-		-		resize_handle = Icons.get_icon ("resize_handle.png");-	-		select_action.connect((self) => {-		});--		deselect_action.connect((self) => {-			Glyph glyph = MainWindow.get_current_glyph ();-			glyph.clear_active_paths ();-		});-				-		press_action.connect((self, b, x, y) => {-			Glyph glyph = MainWindow.get_current_glyph ();-						-			glyph.store_undo_state ();-			-			foreach (Path p in glyph.active_paths) {-				if (is_over_resize_handle (p, x, y)) {-					resize_path = true;-					resized_path = p;-					last_resize_y = y;-					return;-				}-			}-			-			if (resized_path != null) {-				if (is_over_resize_handle ((!) resized_path, x, y)) {-					resize_path = true;-					last_resize_y = y;-					return;					-				}-			}-			-			if (!glyph.is_over_selected_path (x, y)) {-				if (!glyph.select_path (x, y)) {-					glyph.clear_active_paths ();-				}-			}-			-			move_path = true;-				-			last_x = x;-			last_y = y;-		});--		release_action.connect((self, b, x, y) => {-			Glyph glyph = MainWindow.get_current_glyph ();-			-			move_path = false;-			resize_path = false;-				-			if (GridTool.is_visible ()) {-				foreach (Path p in glyph.active_paths) {-					tie_path_to_grid (p, x, y);-				}-			}-		});-		-		move_action.connect ((self, x, y)	 => {-			Glyph glyph = MainWindow.get_current_glyph ();-			double dx = last_x - x;-			double dy = last_y - y; -			double p = PenTool.precision;-			Path rp;-			double h, ratio;-			-			if (move_path) {-				foreach (Path path in glyph.active_paths) {-					path.move (glyph.ivz () * -dx * p, glyph.ivz () * dy * p);-				}-			}-			-			if (resize_path) {-				return_if_fail (!is_null (resized_path));-				rp = (!) resized_path;-				h = rp.xmax - rp.xmin;-				-				ratio = 1;-				ratio -= 0.7 * p * (Glyph.path_coordinate_y (last_resize_y) - Glyph.path_coordinate_y (y)) / h;-				-				rp.resize (ratio);-				last_resize_y = y;-			}--			last_x = x;-			last_y = y;--			MainWindow.get_glyph_canvas ().redraw ();-		});-		-		key_press_action.connect ((self, keyval) => {-			Glyph g = MainWindow.get_current_glyph ();-			-			if (keyval == Key.DEL) {-				foreach (Path p in g.active_paths) {-					g.path_list.remove (p);-					g.update_view ();-				}-			}-		});-		-		draw_action.connect ((self, cr, glyph) => {-			Glyph g = MainWindow.get_current_glyph ();-			ImageSurface img = (!) resize_handle;-		-			foreach (Path p in g.active_paths) {-				cr.set_source_surface (img, Glyph.reverse_path_coordinate_x (p.xmax) - 10, Glyph.reverse_path_coordinate_y (p.ymax) - 10);-				cr.paint ();	-			}-			-			if (resized_path != null) {-				cr.set_source_surface (img, Glyph.reverse_path_coordinate_x (((!) resized_path).xmax) - 10, Glyph.reverse_path_coordinate_y (((!)resized_path).ymax) - 10);-				cr.paint ();-			}-		});-	}--	bool is_over_resize_handle (Path p, double x, double y) {-		double handle_x = Math.fabs (Glyph.reverse_path_coordinate_x (p.xmax)); -		double handle_y = Math.fabs (Glyph.reverse_path_coordinate_y (p.ymax));-		return fabs (handle_x - x + 10) < 20 && fabs (handle_y - y + 10) < 20;-	}--	void tie_path_to_grid (Path p, double x, double y) {-		double sx, sy, qx, qy;	-		-		// tie to grid-		sx = p.xmax;-		sy = p.ymax;-		qx = p.xmin;-		qy = p.ymin;-		-		GridTool.tie_coordinate (ref sx, ref sy);-		GridTool.tie_coordinate (ref qx, ref qy);-		-		if (Math.fabs (qy - p.ymin) < Math.fabs (sy - p.ymax)) {-			p.move (0, qy - p.ymin);-		} else {-			p.move (0, sy - p.ymax);-		}--		if (Math.fabs (qx - p.xmin) < Math.fabs (sx - p.xmax)) {-			p.move (qx - p.xmin, 0);-		} else {-			p.move (sx - p.xmax, 0);-		}		-	}--}--}diff --git a/src/NativeWindow.vala b/src/NativeWindow.valadeleted file mode 100644index 3adf50b..0000000--- a/src/NativeWindow.vala+++ /dev/null@@ -1,35 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--public interface NativeWindow : GLib.Object {-	public abstract string? file_chooser (string title);-	public abstract string? set_title (string title);--	public abstract void toggle_expanded_margin_bottom ();-	public abstract void toggle_expanded_margin_right ();-	-	public abstract void update_window_size ();--	protected void webkit_callback (string s) {-		FontDisplay fd = MainWindow.get_current_display ();-		fd.process_property (s);-	}-}--}diff --git a/src/OpenFontFormatReader.vala b/src/OpenFontFormatReader.valadeleted file mode 100644index d7815ed..0000000--- a/src/OpenFontFormatReader.vala+++ /dev/null@@ -1,136 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--[SimpleType]-[CCode (has_type_id = false)] // Vala bug-internal struct Fixed : uint32 {--	public void @set (uint16 upper, uint16 lower) {-		uint32* p = &this;-		*p = (upper << 16) + lower;-	}--	public bool @equals (uint16 upper, uint16 lower) {-		uint32 t = (upper << 16) + lower;-		return (t == this);		-	}--	public string get_string () {-		uint a = this >> 16;-		return @"$(a).$(this - (a << 16))";-	}-}--[SimpleType]-[CCode (has_type_id = false)] // same as above-internal struct F2Dot14 : uint32 {-}--errordomain BadFormat {-	PARSE-}--class OpenFontFormatReader : Object {-	-	public DirectoryTable directory_table;-	-	OtfInputStream dis;-	File file;-	-	public OpenFontFormatReader () {-		directory_table = new DirectoryTable ();-	}-	-	public void close () {-		dis.close ();-	}-	-	public Glyph? read_glyph (string name) {-		try {-			return directory_table.glyf_table.read_glyph (name);-		} catch (GLib.Error e) {-			warning (e.message);-		}-		-		return null;-	}-	-	public void parse_index (string file_name) throws Error {-		file = File.new_for_path (file_name);-		if (!file.query_exists ()) {-			throw new FileError.EXIST(@"OpenFontFormatReader: file does not exist. $((!) file.get_path ())");-		}-			-		dis = new OtfInputStream (file.read ());-		-		parse_index_tables ();-	}-	-	void parse_index_tables () throws Error {-		OffsetTable offset_table;-		FontData fd = new FontData ();-		-		FileInfo file_info = file.query_info ("*", FileQueryInfoFlags.NONE);-		uint32 file_size = (uint32) file_info.get_size ();--        try {-			fd.write_table (dis, 0, file_size);-		} catch (GLib.Error e) {-			warning (@"Failed to read font data. $(e.message)");-		}--		offset_table = new OffsetTable (directory_table);-		offset_table.parse (fd);-		-		directory_table = new DirectoryTable ();-		directory_table.set_offset_table (offset_table);-		directory_table.parse (fd, file, this);-	}--	public void set_limits () {-		Font f = OpenFontFormatWriter.font;-		-		if (is_null (f)) {-			f = Supplement.get_current_font ();-		}-		-		f.top_position = -get_ascender ();-		f.top_limit = f.top_position - 5;-		f.xheight_position = f.top_position - 5;-		f.bottom_position = -get_descender ();-		f.bottom_limit = f.bottom_position + 5;	-	}--	public double get_ascender () {-		return directory_table.hhea_table.get_ascender ();-	}-	-	public double get_descender () {-		return directory_table.hhea_table.get_descender ();-	}--	public unowned List<string> get_all_names () {-		return directory_table.post_table.get_all_names ();-	}-	-	public uint32 get_head_checksum () {-		return directory_table.head_table.get_adjusted_checksum ();-	}-}-}diff --git a/src/OpenFontFormatWriter.vala b/src/OpenFontFormatWriter.valadeleted file mode 100644index 71e2350..0000000--- a/src/OpenFontFormatWriter.vala+++ /dev/null@@ -1,4875 +0,0 @@-/*
-    Copyright (C) 2012 Johan Mattsson
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-using Math;
-
-namespace Supplement {
-
-const double UNITS = 10;
-
-class OpenFontFormatWriter : Object  {
-
-	DataOutputStream os;
-	DirectoryTable directory_table;
-	
-	public static Font font;
-	
-	public OpenFontFormatWriter () {
-		directory_table = new DirectoryTable ();
-	}
-	
-	public static Font get_current_font () {
-		return font;
-	}
-	
-	public void open (File file) throws Error {
-		assert (!is_null (file));
-		
-		if (file.query_exists ()) {
-			warning ("File exists in export.");
-			throw new FileError.EXIST("OpenFontFormatWriter: file exists.");
-		}
-		
-		os = new DataOutputStream(file.create (FileCreateFlags.REPLACE_DESTINATION));
-	}
-	
-	public void write_ttf_font (Font font) throws Error {
-		long dl;
-		uint8* data;
-		unowned List<Table> tables;
-		FontData fd;
-		uint l;
-		
-		this.font = font;
-				
-		directory_table.process ();	
-		tables = directory_table.get_tables ();
-
-		dl = directory_table.get_font_file_size ();
-		
-		if (dl == 0) {
-			warning ("font is of zero size.");
-			return;
-		}
-		
-		foreach (Table t in tables) {
-			fd = t.get_font_data ();
-			data = fd.table_data;
-			l = fd.length_with_padding ();
-			
-			for (int j = 0; j < l; j++) {
-				os.put_byte (data[j]);
-			}
-		}
-	}
-	
-	public void close () throws Error {
-		os.close ();
-	}
-}
-
-/** Reader for otf data types. */
-class OtfInputStream : Object  {
-	
-	public FileInputStream fin;
-	public DataInputStream din;
-	
-	public OtfInputStream (FileInputStream i) throws Error {
-		din = new DataInputStream (i);
-		fin = i;
-	}
-	
-	public void seek (int64 pos) throws Error
-		requires (fin.can_seek ()) {
-		int64 p = fin.tell ();		
-		fin.seek (pos - p, SeekType.CUR);
-	}
-
-	public uint8 read_byte () throws Error {
-		return din.read_byte ();
-	}
-	
-	public void close () {
-		try {
-			fin.close ();
-			din.close ();
-		} catch (GLib.IOError e) {
-			warning (e.message);
-		}
-	}
-}
-
-class FontData : Object {
-
-	// Read pointer
-	uint rp = 0;
-
-	// Write pointer
-	uint wp = 0;
-	
-	// length without padding
-	uint32 len = 0;
-	uint32 padding = 0;
-	
-	uint32 capacity;
-	
-	public uint8* table_data = null;
-	
-	public FontData (uint32 size = 1024) {
-		capacity = size;
-		table_data = malloc (size);
-	}
-	
-	~FontData () {
-		if (table_data != null) {
-			delete table_data;
-			table_data = null;
-		}
-	}
-	
-	public void write_at (uint pos, uint8 new_data) throws GLib.Error
-		requires (pos <= capacity) 
-	{		
-		if (unlikely (pos >= len)) {
-			warning ("end of table reached");
-			assert (false);
-		}
-		
-		table_data[pos]= new_data;
-	}
-	
-	public void write_table_data (FontData fd, uint32 offset, uint32 length) {
-		fd.seek (offset);
-		for (uint32 i = 0; i < length; i++) {
-			add (table_data [rp++]);
-		}
-		fd.seek (0);
-	}
-	
-	public void write_table (OtfInputStream dis, uint32 offset, uint32 length) throws Error {
-		uint32 l = length + (length % 4);  // padding after end of table
-		uint8 b;
-		
-		if (length >= l) {
-			expand (l);
-		}
-		
-		if (table_data == null) {
-			warning ("Failed to allocate memory for ttf data.");
-			return;
-		}
-		
-		seek (0);
-		dis.seek (offset);
-		
-		wp = 0;
-		while (wp < l) {
-			b = dis.read_byte ();
-			add (b);
-		}
-		
-		rp = 0;
-	}
-	
-	public uint length_with_padding () {
-		return len;
-	}	
-	
-	public uint length () {
-		return len - padding;
-	}
-	
-	public void pad () {
-		while (len % 4 != 0) {
-			add (0);
-			padding++;
-		}
-	}
-	
-	/** Add additional checksum data to this checksum. */
-	public void continous_check_sum (ref uint32 current_check_sum) {
-		uint trp = rp;
-		uint l;
-		
-		if (length_with_padding () % 4 != 0) {
-			warning ("Table is not padded to correct size.");
-		}
-				
-		seek (0);
-
-		l = (length () % 4 > 0) ? length () / 4 + 1 : length () / 4; 
-
-		for (uint32 i = 0; i < l; i++) {
-			current_check_sum += read_uint32 ();
-		}
-		
-		rp = trp;
-		
-		return;
-	}
-
-	public uint32 check_sum () {
-		uint32 sum = 0;
-		continous_check_sum (ref sum);
-		return sum;
-	}
-	
-	public void seek_end () {
-		seek (len);
-	}
-		
-	public void seek (uint i) {
-		rp = i;
-		wp = i;
-	}
-	
-	public uint8 read () {
-		if (unlikely (rp >= len)) {
-			warning ("end of table reached");
-			return 0;
-		}
-		
-		return table_data [rp++];
-	}
-	
-	public Fixed read_fixed () {
-		return (Fixed) read_uint32 ();
-	}
-
-	public uint32 read_ulong () {
-		return read_uint32 ();
-	}
-
-	public uint16 read_ushort () {
-		uint16 f;
-		f = read () << 8;
-		f += read ();
-		return f;
-	}
-	
-	public int16 read_int16 () {
-		int16 f;
-		f = read () << 8;
-		f += read ();
-		return f;		
-	}
-	
-	public int16 read_short () throws Error {
-		return read_int16 ();
-	}
-	
-	public uint32 read_uint32 () {
-		uint32 f;
-		f = read () << 8 * 3;
-		f += read () << 8 * 2;
-		f += read () << 8 * 1;
-		f += read () << 8 * 0;
-		return f;
-	}
-
-	public uint64 read_uint64 () {
-		uint64 f;
-		f = read () << 8 * 7;
-		f += read () << 8 * 6;
-		f += read () << 8 * 5;
-		f += read () << 8 * 4;
-		f += read () << 8 * 3;
-		f += read () << 8 * 2;
-		f += read () << 8 * 1;
-		f += read () << 8 * 0;
-		return f;
-	}
-
-	public F2Dot14 read_f2dot14 () throws Error {
-		F2Dot14 f = (F2Dot14) read_int16 ();
-		return f;
-	}
-
-	public uint64 read_udate () throws Error {
-		return read_uint64 ();
-	}
-
-	public uint8 read_byte () throws Error {
-		return read ();
-	}
-
-	public char read_char () throws Error {
-		return (char) read_byte ();
-	}
-	
-	public void add_udate (int64 d) throws Error {
-		add_64 (d);
-	}
-	
-	public void add_fixed (Fixed f) throws Error {
-		add_u32 (f);
-	}
-
-	public void add_fword (int16 word) {
-		add_16 (word);
-	}
-
-	public void add_f2dot14 (F2Dot14 f) throws Error {
-		add_u32 (f);
-	}
-
-	public void add_short (int16 d) throws Error {
-		add_16 (d);
-	}
-	
-	public void add_ushort (uint16 d) throws Error {
-		add_u16 (d);
-	}
-		
-	public void add_ulong (uint32 d) throws Error {
-		add_u32 (d);
-	}
-		
-	public void add_byte (uint8 b) throws Error {
-		add (b);
-	}
-	
-	private void expand (uint extra_bytes = 1024) {
-		capacity += extra_bytes;
-		table_data = (uint8*) try_realloc (table_data, capacity);
-		
-		if (table_data == null) {
-			warning ("Out of memory.");
-			capacity = 0;
-		}		
-	}
-	
-	public void add (uint8 d) {
-		if (unlikely (len == capacity)) {
-			expand ();
-		}
-		
-		table_data[wp] = d;
-
-		if (wp == len) {
-			len++;
-		}
-				
-		wp++;
-	}
-
-	public void add_littleendian_u16 (uint32 i) {
-		add ((int8) (i & 0x00FF));
-		add ((int8) ((i & 0xFF00) >> 8));
-	}
-		
-	public void add_u16 (uint16 d) {
-		uint16 n = d >> 8;
-		add ((uint8)n);
-		add ((uint8)(d - (n << 8)));
-	}
-
-	public void add_16 (int16 i) {
-		uint8 s = (uint8) (i >> 8);
-		
-		add ((uint8) s);
-		add ((uint8) (i - (s << 8)));
-	}
-
-	public void add_littleendian_u32 (uint32 i) {
-		add ((int8) (i & 0x000000FF));
-		add ((int8) ((i & 0x0000FF00) >> 8));
-		add ((int8) ((i & 0x00FF0000) >> 16));
-		add ((int8) ((i & 0xFF000000) >> 24));
-	}
-		
-	public void add_u32 (uint32 i) {
-		uint32 s = (uint16) (i >> 16);
-		
-		add_u16 ((uint16) s);
-		add_u16 ((uint16) (i - (s << 16)));
-	}
-
-	public void add_32 (int32 i) {
-		int32 s = (int16) (i >> 16);
-		
-		add_16 ((int16) s);
-		add_16 ((int16) (i - (s << 16)));
-	}
-	
-	public void add_u64(uint64 i) {
-		uint64 s = (uint32) (i >> 32);
-		
-		add_u32 ((uint32) s);
-		add_u32 ((uint32)(i - (s << 32)));		
-	}
-
-	public void add_64(int64 i) {
-		int64 s = (int32) (i >> 32);
-		
-		add_u32 ((int32) s);
-		add_u32 ((int32)(i - (s << 32)));		
-	}
-	
-	public void add_str_littleendian_utf16 (string s) {
-		add_str_utf16 (s, true);
-	}
-	
-	public void add_str_utf16 (string s, bool little_endian = false) {
-		int index = 0;
-		unichar c;
-		uint8 c0;
-		uint8 c1;
-		int l = 0;
-		
-		while (s.get_next_char (ref index, out c)) {
-			c0 = (uint8) (c >> 8);
-			c1 = (uint8) (c - (c0 << 8));
-			
-			if (little_endian) {
-				add (c1);
-				add (c0);
-			} else {
-				add (c0);
-				add (c1);
-			}
-			
-			l += 2;
-		}
-				
-		assert (l == 2 * s.char_count ());
-	}
-	
-	public void add_str (string s) {
-		uint8[] data = s.data;
-		for (int n = 0; n < data.length; n++) { 
-			add (data[n]);
-		}		
-	}
-	
-	public void add_tag (string s) 
-		requires (s.length == 4 && s.data.length == 4) {
-		add_str (s);
-	}
-}
-
-class Coordinate {
-	/** TTF coordinate flags. */
-
-	public static const uint8 NONE           = 0;
-	public static const uint8 ON_PATH        = 1 << 0;
-	public static const uint8 X_SHORT_VECTOR = 1 << 1;
-	public static const uint8 Y_SHORT_VECTOR = 1 << 2;
-	public static const uint8 REPEAT         = 1 << 3;
-
-	// same flag or short vector sign flag
-	public static const uint8 X_IS_SAME               = 1 << 4; 
-	public static const uint8 Y_IS_SAME               = 1 << 5;
-	public static const uint8 X_SHORT_VECTOR_POSITIVE = 1 << 4;
-	public static const uint8 Y_SHORT_VECTOR_POSITIVE = 1 << 5;
-
-}
-
-class Table : Object {
-
-	public string id = "NO_ID";
-
-	public uint32 checksum = 0;
-	public uint32 offset = 0;
-	public uint32 length = 0;
-
-	protected FontData? font_data = null;
-
-	public virtual string get_id () {
-		return id;
-	}
-	
-	public FontData get_font_data () {
-		if (font_data == null) {
-			warning (@"No font data for $(id).");
-			font_data = new FontData ();
-		}
-		
-		return (!) font_data;
-	}
-
-	public bool has_data () {
-		return length > 0;
-	}
-
-	public virtual void parse (FontData dis) throws GLib.Error {
-		warning (@"Parse is not implemented for $(id).");
-	}
-
-	/** Validate table checksum. */
-	public bool validate (FontData dis) {
-		bool valid;
-		
-		if (length == 0) {
-			stderr.printf (@"Table $id is of zero length.\n");			
-			valid = false;
-		} else {
-			valid = Table.validate_table (dis, checksum, offset, length, id);
-		}
-		
-		if (!valid) {
-			stderr.printf (@"Table $id is invalid.\n");
-		}
-		
-		return valid;
-	}
-
-	public static bool validate_table (FontData dis, uint32 checksum, uint32 offset, uint32 length, string name) {
-		uint32 ch = calculate_checksum (dis, offset, length, name);
-		bool c;
-		
-		c = (ch == checksum);
-	
-		if (!c) {
-			stderr.printf(@"Checksum does not match data for $(name).\n");
-			stderr.printf(@"name: $name, checksum: $checksum, offset: $offset, length: $length\n");
-			stderr.printf(@"calculated checksum $(ch)\n");
-		}
-		
-		return c;	
-	}
-	
-	public static uint32 calculate_checksum (FontData dis, uint32 offset, uint32 length, string name) {
-		uint32 checksum = 0;
-		uint32 l;
-			 
-		dis.seek (offset);
-
-		l = (length % 4 > 0) ? length / 4 + 1 : length / 4; 
-
-		for (uint32 i = 0; i < l; i++) {
-			checksum += dis.read_ulong ();
-		}
-		
-		return checksum;
-	}
-	
-	public static uint16 max_pow_2_less_than_i (uint16 ind) {
-		uint16 last = 0;
-		uint16 i = 1;
-		
-		while ((i <<= 1) < ind) {
-			last = i;
-		}
-		
-		return last;
-	}
-
-	public static uint16 max_log_2_less_than_i (uint16 ind) {
-		return (uint16) (Math.log (ind) / Math.log (2));
-	}
-}
-
-class LocaTable : Table {
-	
-	uint32* glyph_offsets = null;
-	public uint32 size = 0;
-	
-	public LocaTable () {
-		id = "loca";
-	}	
-	
-	~LocaTable () {
-		if (glyph_offsets != null) delete glyph_offsets;
-	}
-	
-	public uint32 get_offset (uint32 i) {
-		return_if_fail (glyph_offsets != null);
-		
-		if (size == 0) {
-			warning ("No glyphs in loca table");
-		}
-		
-		if (!(0 <= i < size + 1)) {
-			warning (@"No offset for glyph $i. Requires (0 <= $i < $(size + 1)");
-		}
-		
-		return glyph_offsets [i];
-	}
-	
-	/** Returns true if glyph at index i is empty and have no body to parse. */
-	public bool is_empty (uint32 i) {
-		return_if_fail (glyph_offsets != null);
-
-		if (size == 0) {
-			warning ("No glyphs in loca table");
-		}
-				
-		if (!(0 <= i < size + 1)) {
-			warning (@"No offset for glyph $i. Requires (0 <= $i < $(size + 1)");
-		}
-		
-		return glyph_offsets[i] == glyph_offsets[i + 1];
-	}
-	
-	public new void parse (FontData dis, HeadTable head_table, MaxpTable maxp_table) throws GLib.Error {
-		size = maxp_table.num_glyphs;
-		glyph_offsets = new uint32[size + 1];
-		
-		dis.seek (offset);
-		
-		printd (@"size: $size\n");
-		printd (@"length: $length\n");
-		printd (@"length/4-1: $(length / 4 - 1)\n");
-		printd (@"length/2-1: $(length / 2 - 1)\n");
-		printd (@"head_table.loca_offset_size: $(head_table.loca_offset_size)\n");
-		
-		switch (head_table.loca_offset_size) {
-			case 0:
-				for (long i = 0; i < size + 1; i++) {
-					glyph_offsets[i] = 2 * dis.read_ushort ();	
-					
-					if (0 < i < size && glyph_offsets[i - 1] > glyph_offsets[i]) {
-						warning (@"Invalid loca table, it must be sorted. ($(glyph_offsets[i - 1]) > $(glyph_offsets[i]))");
-					}
-				}
-				break;
-				
-			case 1:
-				for (long i = 0; i < size + 1; i++) {
-					glyph_offsets[i] = 	dis.read_ulong ();
-									
-					if (0 < i < size && glyph_offsets[i - 1] > glyph_offsets[i]) {
-						warning (@"Invalid loca table, it must be sorted. ($(glyph_offsets[i - 1]) > $(glyph_offsets[i]))");
-					}		
-				}
-
-				break;
-			
-			default:
-				warning ("unknown size for offset in loca table");
-				break;
-		}
-	}
-
-	public void print_offsets () {
-		for (int i = 0; i < size; i++) {
-			print (@"get_offset ($i): $(get_offset (i))\n");
-		}
-	}
-	
-	public void process (GlyfTable glyf_table, HeadTable head_table) {
-		FontData fd = new FontData ();
-		uint32 last = 0;
-		uint32 prev = 0;
-		int i = 0;
-		
-		foreach (uint32 o in glyf_table.location_offsets) {
-			if (i != 0 && (o - prev) % 4 != 0) {
-				warning (@"glyph length is not a multiple of four in gid $i");
-			}
-			
-			if (o % 4 != 0) {
-				warning ("glyph is not on a four byte boundry");
-				assert_not_reached ();
-			}
-			
-			prev = o;
-			i++;
-		}
-	
-		if (head_table.loca_offset_size == 0) {
-			foreach (uint32 o in glyf_table.location_offsets) {
-				fd.add_u16 ((uint16) (o / 2));
-				
-				if (o < last) {
-					warning (@"Loca table must be sorted. ($o < $last)");
-				}
-				
-				last = o;
-			}
-			
-		} else if (head_table.loca_offset_size == 1) {
-			foreach (uint32 o in glyf_table.location_offsets) {
-				fd.add_u32 (o);
-
-				if (o < last) {
-					warning (@"Loca table must be sorted. ($o < $last)");
-				}
-				
-				last = o;
-			}
-			
-		} else {
-			warn_if_reached ();
-		}
-
-		if (!(glyf_table.location_offsets.length () == glyf_table.glyphs.length () + 1)) {
-			warning (@"(glyf_table.location_offsets.length () == glyf_table.glyphs.length () + 1) ($(glyf_table.location_offsets.length ()) == $(glyf_table.glyphs.length () + 1))");
-		}
-
-		fd.pad ();		
-		font_data = fd;		
-	}
-}
-
-class GlyfTable : Table {
-	
-	// Flags for composite glyph
-	static const uint16 BOTH_ARE_WORDS = 1 << 0;
-	static const uint16 BOTH_ARE_XY_VALUES = 1 << 1;
-	static const uint16 ROUND_TO_GRID = 1 << 2;
-	static const uint16 SCALE = 1 << 3;
-	static const uint16 RESERVED = 1 << 4;
-	static const uint16 MORE_COMPONENTS = 1 << 5;
-	static const uint16 SCALE_X_Y = 1 << 6;
-	static const uint16 SCALE_WITH_ROTATTION = 1 << 7;
-	static const uint16 INSTRUCTIONS = 1 << 8;
-
-	public int16 xmin = int16.MAX;
-	public int16 ymin = int16.MAX;
-	public int16 xmax = int16.MIN;
-	public int16 ymax = int16.MIN;
-
-	public FontData dis;
-	public HeadTable head_table;
-	public HmtxTable hmtx_table;
-	public LocaTable loca_table;
-	public CmapTable cmap_table; // hack, hack, hack. cmap and post is null when inistialized and set in parse method
-	public PostTable post_table;
-	public KernTable kern_table;
-	
-	public List<uint32> location_offsets; 
-
-	// list of glyphs sorted in the order we expect to find them in a
-	// ttf font. notdef is the firs glyph followed by null and nonmarkingreturn.
-	// after that will all assigned glyphs appear in sorted order, all 
-	// remaining unassigned glyphs live in the last part of the file.	
-	public List<Glyph> glyphs;
-	
-	uint16 max_points = 0;
-	uint16 max_contours = 0;
-	
-	double total_width = 0;
-	int non_zero_glyphs = 0;
-	
-	public GlyfTable (LocaTable l) {
-		id = "glyf";
-		loca_table = l;
-		location_offsets = new List<uint32> ();
-		glyphs = new List<Glyph> ();
-	}	
-
-	public int get_gid (string name) {
-		int i = 0;
-		foreach (Glyph g in glyphs) {
-			if (g.name == name) {
-				return i;
-			}
-		
-			i++;
-		}
-		
-		return -1;
-	}
-
-	public int16 get_average_width () {
-		return (int16) (total_width / non_zero_glyphs);
-	}
-
-	public uint16 get_max_contours () {
-		return max_contours;
-	}
-
-	public uint16 get_max_points () {
-		return max_points;
-	}
-
-	public uint16 get_first_char () {
-		return 32; // space
-	}
-	
-	public uint16 get_last_char () {
-		unowned List<Glyph> gl = glyphs.last ();
-		Glyph g = (!) gl.data;
-		
-		while (gl != glyphs.first ()) {
-			g = (!) gl.data;
-			
-			if (!g.is_unassigned ()) {
-				break;
-			}
-			
-			gl = gl.prev;
-		}
-		
-		return (uint16)g.unichar_code; 
-	}
-	
-	public Glyph? read_glyph (string name) throws GLib.Error {
-		Glyph? glyph;
-		Glyph g;
-		int i;
-		KernList kl;
-		string right;
-		double units_per_em;
-		double kern_val;
-		
-		i = post_table.get_gid (name);
-		
-		if (i == -1) {
-			return null;
-		}
-		
-		glyph = parse_index (i, dis, loca_table, hmtx_table, head_table, post_table);
-		
-		if (glyph != null) {
-			printd (@"kern: ");
-			units_per_em = head_table.get_units_per_em ();
-			kl = kern_table.get_all_pairs (i);
-			foreach (Kern k in kl.kernings) {
-				g = (!) glyph;
-				right = post_table.get_name (k.right);
-				kern_val = k.kerning * 1000.0 / units_per_em;
-				g.add_kerning (right, kern_val);
-			}
-		}
-		
-		return glyph;
-	}
-	
-	public new void parse (FontData dis, CmapTable cmap_table, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table, KernTable kern_table) throws GLib.Error {
-		this.cmap_table = cmap_table;
-		this.post_table = post_table;
-		this.loca_table = loca;
-		this.hmtx_table = hmtx_table;
-		this.head_table = head_table;
-		this.kern_table = kern_table;
-		this.dis = dis;
-	}
-	
-	Glyph parse_index (int index, FontData dis, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table) throws GLib.Error {
-		Glyph glyph = new Glyph ("");
-		double xmin, xmax;
-		double units_per_em = head_table.get_units_per_em ();
-		unichar character = 0;
-		string name;	
-		uint32 end;
-		
-		character = cmap_table.get_char (index);
-		name = post_table.get_name (index);
-		
-		if (name == "") {
-			StringBuilder name_c = new StringBuilder ();
-			name_c.append_unichar (character);
-			name = name_c.str;
-		}
-
-		printd (@"name: $(name)\n");
-
-		if (!loca.is_empty (index)) {	
-			glyph = parse_next_glyf (dis, character, index, out xmin, out xmax, units_per_em);
-			
-			glyph.left_limit = xmin - hmtx_table.get_lsb (index);
-			glyph.left_limit = 0;
-			glyph.right_limit = glyph.left_limit + hmtx_table.get_advance (index);
-		} else {
-			// add empty glyph
-			glyph = new Glyph (name, character);
-			glyph.left_limit = -hmtx_table.get_lsb (index);
-			glyph.right_limit = hmtx_table.get_advance (index) - hmtx_table.get_lsb (index);				
-		}
-		
-		glyph.name = name;
-
-		if (character == 0) {
-			glyph.set_unassigned (true);
-		}
-		
-		if (character == 0 && name != "") {
-			stderr.printf (@"Got null character\n");
-			stderr.printf (@"gid: $index\n");
-			stderr.printf (@"char: $((uint) character)\n");
-			stderr.printf (@"name: $(name)\n");
-		}
-		
-		return glyph;
-	}
-	
-	Glyph parse_next_composite_glyf (FontData dis, unichar character, int pgid) throws Error {
-		uint16 component_flags = 0;
-		uint16 glyph_index;
-		int16 arg1 = 0;
-		int16 arg2 = 0;
-		uint16 arg1and2;
-		F2Dot14 scale;
-		
-		F2Dot14 scalex;
-		F2Dot14 scaley;
-		
-		F2Dot14 scale01;
-		F2Dot14 scale10;
-
-		Glyph glyph, linked_glyph;
-		List<int> x = new List<int> ();
-		List<int> y = new List<int> ();
-		List<int> gid = new List<int> ();
-		
-		double xmin, xmax;
-		double units_per_em = head_table.get_units_per_em ();
-		
-		int glid;
-		
-		StringBuilder name = new StringBuilder ();
-		name.append_unichar (character);
-		
-		glyph = new Glyph (name.str, character);
-		
-		do {
-			component_flags = dis.read_ushort ();
-			glyph_index = dis.read_ushort ();
-			
-			if ((component_flags & BOTH_ARE_WORDS) > 0) {
-				arg1 = dis.read_short ();
-				arg2 = dis.read_short ();			
-			} else if ((component_flags & BOTH_ARE_XY_VALUES) > 0) {
-				arg1 = dis.read_byte ();
-				arg2 = dis.read_byte ();
-			}
-			
-			gid.append (glyph_index);
-			x.append (arg1);
-			y.append (arg2);
-
-			// if ((component_flags & RESERVED) > 0)
-			
-			if ((component_flags & SCALE) > 0) {
-				scale = dis.read_f2dot14 ();
-			} else if ((component_flags & SCALE_X_Y) > 0) {
-				scalex = dis.read_f2dot14 ();
-				scaley = dis.read_f2dot14 ();
-			} else if ((component_flags & SCALE_WITH_ROTATTION) > 0) {
-				scalex = dis.read_f2dot14 ();
-				scale01 = dis.read_f2dot14 ();
-				scale10 = dis.read_f2dot14 ();
-				scaley = dis.read_f2dot14 ();
-			}
-			
-		} while ((component_flags & MORE_COMPONENTS) > 0);
-	
-		
-		for (int i = 0; i < gid.length (); i++) {
-			// compensate xmax ymax with coordinate
-			glid = gid.nth (i).data;
-
-			if (glid == pgid) {
-				warning ("Cannot link a glyph to it self.");
-				continue;
-			}
-
-			linked_glyph = parse_next_glyf (dis, character, glid, out xmin, out xmax, units_per_em);
-		}
-
-		
-		/*
-		if ((component_flags & INSTRUCTIONS) > 0) {
-			num_instructions = dis.read_ushort ();
-			
-			for (int i = 0; i < num_instructions; i++) {
-				dis.read_byte ();
-			}
-		}
-		*/
-		
-		return glyph;
-	}
-	
-	Glyph parse_next_glyf (FontData dis, unichar character, int gid, out double xmin, out double xmax, double units_per_em) throws Error {
-
-		uint16* end_points = null;
-		uint8* instructions = null;
-		uint8* flags = null;
-		int16* xcoordinates = null;
-		int16* ycoordinates = null;
-		
-		int npoints = 0;
-		
-		int16 ncontours;
-		int16 ixmin; // set boundries
-		int16 iymin;
-		int16 ixmax;
-		int16 iymax;
-		uint16 ninstructions;
-
-		int16 rxmin = int16.MAX; // real xmin
-		int16 rymin = int16.MAX;;
-		int16 rxmax = int16.MIN;
-		int16 rymax = int16.MIN;
-				
-		int nflags;
-		
-		Error? error = null;
-		
-		uint start, end, len;
-		
-		StringBuilder name = new StringBuilder ();
-		name.append_unichar (character);
-
-		start = loca_table.get_offset (gid);
-		end = loca_table.get_offset (gid + 1);
-		len = start - end;
-
-		dis.seek (offset + start);
-		
-		ncontours = dis.read_short ();
-		
-		return_val_if_fail (start < end, new Glyph (""));
-
-		if (ncontours == 0) {
-			warning (@"Got zero contours in glyph $(name.str).");
-
-			// should skip body
-		}
-				
-		if (ncontours == -1) {
-			return parse_next_composite_glyf (dis, character, gid);
-		}
-
-		return_val_if_fail (ncontours < len, new Glyph (""));
-						
-		if (ncontours < -1) {
-			warning (@"Got $ncontours contours in glyf table.");
-			error = new BadFormat.PARSE ("Invalid glyf");
-			throw error;
-		}
-		
-		ixmin = dis.read_short ();
-		iymin = dis.read_short ();
-		ixmax = dis.read_short ();
-		iymax = dis.read_short ();
-
-		end_points = new uint16[ncontours + 1];
-		for (int i = 0; i < ncontours; i++) {
-			end_points[i] = dis.read_ushort (); // FIXA: mind shot vector is negative
-			
-			if (i > 0 && end_points[i] < end_points[i -1]) {
-				warning (@"Next endpoint has bad value in $(name.str). (end_points[i] > end_points[i -1])  ($(end_points[i]) > $(end_points[i -1])) i: $i ncontours: $ncontours");
-			}
-		}
-		
-		if (ncontours > 0) {
-			npoints = end_points[ncontours - 1] + 1;
-		} else {
-			npoints = 0;
-		}
-		
-		return_val_if_fail (npoints < len, new Glyph.no_lines (""));
-		
-		ninstructions = dis.read_ushort ();
-		
-		return_val_if_fail (ninstructions < len, new Glyph.no_lines (""));
-		
-		instructions = new uint8[ninstructions + 1];
-		uint8 repeat;
-		for (int i = 0; i < ninstructions; i++) {
-			instructions[i] = dis.read_byte ();
-		}
-
-		nflags = 0;
-		flags = new uint8[npoints + 1];
-		for (int i = 0; i < npoints; i++) {
-			flags[i] = dis.read_byte ();
-			
-			if ((flags[i] & Coordinate.REPEAT) > 0) {
-				repeat = dis.read_byte ();
-				
-				if (i + repeat >= npoints) {
-					error = new BadFormat.PARSE ("Too many flags in glyf in glyph $(name.str). (i >= ninstructions).");
-					break;
-				}
-				
-				for (int j = 0; j < repeat; j++) {
-					flags[j + i + 1] = flags[i];
-				}
-				
-				nflags += repeat;
-				i += repeat;
-			}
-			
-			nflags++;
-		}
-		
-		if (nflags != npoints) {
-			warning (@"(nflags != npoints) ($nflags != $npoints) in $(name.str)");
-			error = new BadFormat.PARSE (@"Wrong number of flags in glyph $(name.str). (nflags != npoints) ($nflags != $npoints)");
-		}
-		
-		warn_if_fail (nflags == npoints);
-
-		printd (@"npoints: $npoints\n");
-		printd (@"ncontours: $ncontours\n");
-		printd (@"ninstructions: $ninstructions\n");
-		printd (@"nflags: $nflags\n");
-				
-		int16 last = 0;
-		xcoordinates = new int16[npoints + 1];
-		for (int i = 0; i < npoints; i++) {
-			if ((flags[i] & Coordinate.X_SHORT_VECTOR) > 0) {	
-				if ((flags[i] & Coordinate.X_SHORT_VECTOR_POSITIVE) > 0) {
-					xcoordinates[i] = last + dis.read_byte ();
-				} else {
-					xcoordinates[i] = last - dis.read_byte ();
-				}
-			} else {
-				if ((flags[i] & Coordinate.X_IS_SAME) > 0) {
-					xcoordinates[i] = last;
-				} else {
-					xcoordinates[i] = last + dis.read_short ();
-				}
-			}
-			
-			last = xcoordinates[i];
-			
-			if (last > rxmax) rxmax = last;
-			if (last < rxmin) rxmin = last;
-			
-			if (!(ixmin <= last <= ixmax))	{
-				stderr.printf (@"x is out of bounds in glyph $(name.str). ($ixmin <= $last <= $ixmax) char $((uint)character)\n");
-			}
-			
-			if (!(head_table.xmin <= last <= head_table.xmax))	{
-				stderr.printf (@"x is outside of of font bounding box in glyph $(name.str). ($(head_table.xmin) <= $last <= $(head_table.xmax)) char $((uint)character)\n");
-			}			
-		}
-		
-		last = 0;
-		ycoordinates = new int16[npoints + 1];
-		for (int i = 0; i < npoints; i++) {
-			if ((flags[i] & Coordinate.Y_SHORT_VECTOR) > 0) {	
-				if ((flags[i] & Coordinate.Y_SHORT_VECTOR_POSITIVE) > 0) {
-					ycoordinates[i] = last + dis.read_byte ();
-				} else {
-					ycoordinates[i] = last - dis.read_byte ();
-				}
-			} else {
-				if ((flags[i] & Coordinate.Y_IS_SAME) > 0) {
-					ycoordinates[i] = last;
-				} else {
-					ycoordinates[i] = last + dis.read_short ();
-				}
-			}
-			
-			last = ycoordinates[i];
-
-			if (last > rymax) rymax = last;
-			if (last < rymin) rymin = last;
-			
-			if (!(iymin <= last <= iymax))	{
-				stderr.printf (@"y is out of bounds in glyph $(name.str). ($iymin <= $last <= $iymax) char $((uint)character)\n");
-			}
-			
-			if (!(head_table.ymin <= last <= head_table.ymax))	{
-				stderr.printf (@"y is outside of of font bounding box in glyph $(name.str). ($(head_table.ymin) <= $last <= $(head_table.ymax)) char $((uint)character)\n");
-			}
-		}
-		
-		if (rymin != iymin || rxmin != ixmin || rxmax != ixmax || rymax != iymax) {
-			warning (@"Warning real boundry for glyph does not match boundry set in glyph header for glyph $(name.str).");
-			stderr.printf (@"ymin: $rymin header: $iymin\n");
-			stderr.printf (@"xmin: $rxmin header: $ixmin\n");
-			stderr.printf (@"ymax: $rymax header: $iymax\n");
-			stderr.printf (@"xmax: $rxmax header: $ixmax\n");
-		} 
-		
-		int j = 0;
-		int first_point;
-		int last_point = 0;
-		Glyph glyph;
-		double x, y;
-
-		glyph = new Glyph (name.str, character);
-		
-		xmin = ixmin * 1000.0 / units_per_em;
-		xmax = ixmax * 1000.0 / units_per_em;
-		
-		for (int i = 0; i < ncontours; i++) {
-			x = 0;
-			y = 0;
-			
-			Path path = new Path ();
-			EditPoint edit_point = new EditPoint ();
-			bool prev_is_curve = false;
-			
-			first_point = j;
-			last_point = end_points[i];
-			for (; j <= end_points[i]; j++) {
-
-				if (j >= npoints) {
-					warning (@"j >= npoints in glyph $(name.str). (j: $j, end_points[i]: $(end_points[i]), npoints: $npoints)");
-					break;
-				}
-								
-				x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width
-				y = ycoordinates[j] * 1000.0 / units_per_em;
-				
-				if ((flags[j] & Coordinate.ON_PATH) > 0) {
-					// Point
-					edit_point = new EditPoint ();
-					edit_point.set_position (x, y);
-					path.add_point (edit_point);
-					
-					if (prev_is_curve) {
-						edit_point.get_left_handle ().set_point_type (PointType.NONE);
-						edit_point.get_left_handle ().length = 0;
-					} else {
-						edit_point.recalculate_linear_handles ();
-					}
-									
-					prev_is_curve = false;
-				} else {
-									
-					if (prev_is_curve) {
-						x = x - (x - edit_point.right_handle.x ()) / 2;
-						y = y - (y - edit_point.right_handle.y ()) / 2;
-
-						edit_point = new EditPoint ();
-						edit_point.set_position (x, y);
-						path.add_point (edit_point);
-					}
-
-					x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width
-					y = ycoordinates[j] * 1000.0 / units_per_em;
-
-					edit_point.get_left_handle ().set_point_type (PointType.NONE);
-					edit_point.get_left_handle ().length = 0;
-						
-					edit_point.type = PointType.CURVE;
-					edit_point.get_right_handle ().set_point_type (PointType.CURVE);
-					edit_point.get_right_handle ().move_to_coordinate (x, y);
-					
-					prev_is_curve = true;
-				} 
-			}
-			
-			// last to first point
-			if (prev_is_curve) {
-				x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width
-				y = ycoordinates[first_point] * 1000.0 / units_per_em;
-				
-				x = x - (x - edit_point.right_handle.x ()) / 2;
-				y = y - (y - edit_point.right_handle.y ()) / 2;
-				
-				edit_point = new EditPoint ();
-				edit_point.set_position (x, y);
-				path.add_point (edit_point);
-				
-				x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width
-				y = ycoordinates[first_point] * 1000.0 / units_per_em;
-
-				edit_point.get_left_handle ().set_point_type (PointType.CURVE);
-				edit_point.get_left_handle ().length = 0;
-					
-				edit_point.type = PointType.CURVE;
-				edit_point.get_right_handle ().set_point_type (PointType.CURVE);
-				edit_point.get_right_handle ().move_to_coordinate (x, y);
-			}
-			
-			// curve last to first
-			x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width
-			y = ycoordinates[first_point] * 1000.0 / units_per_em;
-			edit_point.type = PointType.CURVE;
-			edit_point.get_right_handle ().set_point_type (PointType.CURVE);
-			edit_point.get_right_handle ().move_to_coordinate (x, y);
-			
-			path.close ();
-			
-			glyph.add_path (path);
-		}
-		
-		// glyphs with no bounding boxes
-		if (ixmax <= ixmin) {
-			warning (@"Bounding box is bad. (xmax == xmin) ($xmax == $xmin)");
-			
-			if (glyph.path_list.length () > 0) {
-				
-				Path ps = ((!) glyph.path_list.first ()).data;
-				
-				ps.update_region_boundries ();
-				xmin = ps.xmin;
-				xmax = ps.xmax;
-
-				foreach (Path p in glyph.path_list) {
-					p.update_region_boundries ();
-					
-					if (p.xmin < xmin) {
-						xmin = p.xmin;
-					}
-					
-					if (p.xmax > xmax) {
-						xmax = p.xmax;
-					}
-				}
-				
-			}
-		}
-						
-		if (end_points != null) delete end_points;
-		if (instructions != null) delete instructions;
-		if (flags != null) delete flags;
-		if (xcoordinates != null) delete xcoordinates;
-		if (ycoordinates != null) delete ycoordinates;
-		
-		if (error != null) {
-			warning ("failed to parse glyph");
-			throw (!) error;
-		}
-		
-		return glyph;
-	}
-
-	public void process_glyph (Glyph g, FontData fd) throws GLib.Error {
-		int16 txmin, tymin, txmax, tymax;
-
-		int16 end_point;
-		int16 last_end_point;
-		int16 npoints;
-		int16 ncontours;
-		int16 nflags;
-		
-		double x, y;
-
-		Font font = OpenFontFormatWriter.get_current_font ();
-		int glyph_offset;
-		
-		uint len; 
-		uint coordinate_length;
-		
-		fd.seek_end (); // append glyph
-		
-		glyph_offset = (int) fd.length ();
-		
-		printd (@"glyph_offset: $(glyph_offset)\n");
-		
-		g.remove_empty_paths ();
-		if (g.path_list.length () == 0) {
-			// location_offsets == location_offset + 1 to tell parser that this glyf does not have a body
-			return;
-		}
-		
-		non_zero_glyphs++;
-		
-		ncontours = (int16) g.path_list.length ();
-		fd.add_short (ncontours);
-		
-		txmin = int16.MAX;
-		tymin = int16.MAX;
-		txmax = int16.MIN;
-		tymax = int16.MIN;
-		
-		// bounding box will be set again after coordinate arrays have been created
-		fd.add_16 (10);
-		fd.add_16 (20);
-		fd.add_16 (30);
-		fd.add_16 (40);
-		
-		// end points
-		end_point = 0;
-		last_end_point = 0;
-		foreach (Path p in g.path_list) {
-			p = p.get_quadratic_points ();
-			foreach (EditPoint e in p.points) {
-				end_point++;
-				
-				if (e.get_right_handle ().type == PointType.CURVE) {
-					end_point++;
-				}
-			}
-			fd.add_u16 (end_point - 1);
-			
-			if (end_point - 1 < last_end_point) {
-				warning (@"Next endpoint has bad value. (end_point - 1 < last_end_point)  ($(end_point - 1) < $last_end_point");
-			}
-			
-			last_end_point = end_point - 1;
-		}
-		
-		fd.add_u16 (0); // instruction length 
-		
-		uint glyph_header = 12 + ncontours * 2;
-		
-		printd (@"\next glyf: $(g.name)\n");
-		printd (@"glyf header length: $(glyph_header)\n");
-		
-		// instructions should go here 
-		
-		npoints = (ncontours > 0) ? end_point : 0; // +1?
-		
-		if (npoints > max_points) {
-			max_points = npoints;
-		}
-		
-		if (ncontours > max_contours) {
-			max_contours = ncontours;
-		}
-		
-		// flags
-		nflags = 0;
-		List<uint8> flags = new List<uint8> ();
-		foreach (Path p in g.path_list) {
-			p = p.get_quadratic_points ();
-			foreach (EditPoint e in p.points) {
-				fd.add_byte (Coordinate.ON_PATH);
-				flags.append (Coordinate.ON_PATH);
-				nflags++;
-				
-				if (e.get_right_handle ().type == PointType.CURVE) {
-					fd.add_byte (Coordinate.NONE);
-					flags.append (Coordinate.NONE);
-					nflags++;
-				}
-			}
-		}
-		
-		if (nflags != npoints) {
-			warning (@"(nflags != npoints)  ($nflags != $npoints) in glyph $(g.name). ncontours: $ncontours");
-		}
-		assert (nflags == npoints);
-
-		printd (@"flags: $(nflags)\n");
-		
-		// x coordinates
-		List<int16> coordinate_x = new List<int16> ();
-		List<int16> coordinate_y = new List<int16> ();
-		double prev = 0;
-		
-		foreach (Path p in g.path_list) {
-			p = p.get_quadratic_points ();
-
-			foreach (EditPoint e in p.points) {
-				x = rint (e.x * UNITS - prev - g.left_limit * UNITS);
-
-				fd.add_16 ((int16) x);
-				coordinate_x.append ((int16) x);
-				-				prev = rint (e.x * UNITS - g.left_limit * UNITS);
-				-				if (e.get_right_handle ().type == PointType.CURVE) {
-					x = rint (e.get_right_handle ().x () * UNITS - prev - g.left_limit * UNITS);
-
-					fd.add_16 ((int16) x);
-					coordinate_x.append ((int16) x);
-					
-					prev = rint (e.get_right_handle ().x () * UNITS - g.left_limit * UNITS);
-				}
-			}
-		}
-
-		// y coordinates
-		prev = 0;
-		foreach (Path p in g.path_list) {
-			p = p.get_quadratic_points ();
-			foreach (EditPoint e in p.points) {
-				y = rint (e.y * UNITS - prev + font.base_line  * UNITS);
-				fd.add_16 ((int16) y);
-				-				coordinate_y.append ((int16) y);-				
-				prev = rint (e.y * UNITS + font.base_line * UNITS);
-
-				if (e.get_right_handle ().type == PointType.CURVE) {
-					y = rint (e.get_right_handle ().y () * UNITS - prev + font.base_line * UNITS);
-					
-					fd.add_16 ((int16) y);
-					coordinate_y.append ((int16) y);
-					
-					prev = rint (e.get_right_handle ().y () * UNITS + font.base_line  * UNITS);
-				}
-			}
-		}
-		
-		len = fd.length ();
-		printd (@"fd.length (): $(fd.length ())\n");
-		coordinate_length = fd.length () - nflags - glyph_header;
-		printd (@"coordinate_length: $(coordinate_length)\n");
-		assert (fd.length () > nflags + glyph_header);
-				
-		// bounding box	
-		int16 last = 0;
-			
-		int i = 0;
-		foreach (int16 c in coordinate_x) {
-			c += last;
-			
-			// Only on curve points are good for calculating bounding box
-			if ((flags.nth (i).data & Coordinate.ON_PATH) > 0) { 
-				if (c < txmin) txmin = c;
-				if (c > txmax) txmax = c;
-			}
-				
-			last = c;
-			i++;
-		}
-
-		last = 0;
-		i = 0;
-		foreach (int16 c in coordinate_y) {
-			c += last;
-			
-			if ((flags.nth (i).data & Coordinate.ON_PATH) > 0) {
-				if (c < tymin) tymin = c;
-				if (c > tymax) tymax = c;			
-			}
-			
-			last = c;
-			i++;
-		}
-		
-		fd.seek_end ();
-		
-		printd (@"glyph_offset: $(glyph_offset)\n");
-		printd (@"len: $(len)\n");
-		
-		fd.seek (glyph_offset + 2); // go to box boundries for this glyf
-		// assert (fd.read_short () == int16.MAX);
-
-		// add bounding box
-		fd.add_16 (txmin);
-		fd.add_16 (tymin);
-		fd.add_16 (txmax);
-		fd.add_16 (tymax);
-		fd.seek_end ();
-	
-		assert (len == fd.length ());
-		
-		fd.seek (glyph_offset + 2);
-		assert (fd.read_int16 () == txmin);
-		assert (fd.read_int16 () == tymin);
-		assert (fd.read_int16 () == txmax);
-		assert (fd.read_int16 () == tymax);
-		fd.seek_end ();
-
-		printd (@"\n");
-		printd (@"txmin: $txmin\n");
-		printd (@"tymin: $tymin\n");
-		printd (@"txmax: $txmax\n");
-		printd (@"tymax: $tymax\n");
-
-		// save this for head table
-		if (txmin < this.xmin) this.xmin = txmin;
-		if (tymin < this.ymin) this.ymin = tymin;
-		if (txmax > this.xmax) this.xmax = txmax;
-		if (tymax > this.ymax) this.ymax = tymax;
-				
-		// part of average width calculation for OS/2 table
-		total_width += xmax - xmin;
-		
-		printd (@"length before padding: $(fd.length ())\n");
-		
-		// all glyphs needs padding for loca table to be correct
-		while (fd.length () % 4 != 0) {
-			fd.add (0);
-		}
-		printd (@"length after padding: $(fd.length ())\n");
-	}
-
-	// necessary in order to have glyphs sorted according to ttf specification
-	public void create_glyph_table () {
-		Glyph? gl;
-		Glyph g;
-		Font font = OpenFontFormatWriter.get_current_font ();
-		uint32 indice;
-		List<Glyph> unassigned_glyphs;
-		
-		// add notdef. character at index zero + other special chars first
-		glyphs.append (font.get_not_def_character ());
-		glyphs.append (font.get_null_character ());
-		glyphs.append (font.get_nonmarking_return ());
-		glyphs.append (font.get_space ());
-		
-		unassigned_glyphs = new List<Glyph> ();
-		
-		// add glyphs, first all assigned then the unassigned ones
-		for (indice = 0; (gl = font.get_glyph_indice (indice)) != null; indice++) {		
-			g = (!) gl;
-			
-			if (g.unichar_code <= 27) { // skip control characters
-				continue;
-			}
-
-			if (g.unichar_code == 32) { // skip space
-				continue;
-			}
-						
-			if (g.name == ".notdef") {
-				continue;
-			}
-			
-			g.remove_empty_paths ();
-
-			if (!g.is_unassigned ()) {
-				glyphs.append (g);
-			} else {
-				unassigned_glyphs.append (g);
-			}
-		}
-		
-		foreach (Glyph ug in unassigned_glyphs) {
-			// glyphs.append (ug);
-		}
-		
-	}
-
-	public void process () throws GLib.Error {
-		FontData fd = new FontData ();
-		
-		create_glyph_table ();
-		uint last_len = 0;
-		foreach (Glyph g in glyphs) {
-			// set values for loca table
-			assert (fd.length () % 4 == 0);
-			location_offsets.append (fd.length ());
-			process_glyph (g, fd);
-
-			printd (@"glyf length: $(fd.length () - last_len)\n");
-			printd (@"loca fd.length (): $(fd.length ())\n");
-
-			last_len = fd.length ();
-		}
-
-		location_offsets.append (fd.length ()); // last entry in loca table is special
-		
-		// every glyph is padded, no padding to be done here
-		assert (fd.length () % 4 == 0);
-
-		font_data = fd;	
-	}
-}
-
-class CmapSubtable : Table {
-
-	// Override these methods in subtables for each format.
-	
-	/** Obtain length of subtable in bytes. */
-	public virtual uint get_length () {
-		warning ("Invalid CmapSubtable");
-		return 0;
-	}
-	
-	/** Get char code for a glyph id. */
-	public virtual unichar get_char (uint32 i) {
-		warning ("Invalid CmapSubtable");
-		return 0;
-	}
-	
-	public void print_cmap () {
-		StringBuilder s;
-		unichar c;
-		for (uint32 i = 0; i < get_length (); i++) {
-			s = new StringBuilder ();
-			c = get_char (i);
-			s.append_unichar (c);
-			printd (@"Char: $(s.str)  val ($((uint32)c))\tindice: $(i)\n");
-		}
-	}
-}
-
-/** Format 4 cmap subtable */
-class CmapSubtableWindowsUnicode : CmapSubtable {
-	uint16 format = 0;
-	HashTable <uint64?, unichar> table = new HashTable <uint64?, unichar> (int64_hash, int_equal);
-	
-	public CmapSubtableWindowsUnicode () {
-	}
-	
-	~CmapSubtableWindowsUnicode () {
-
-	}
-
-	public override uint get_length () {
-		return table.size ();
-	}
-	
-	public override unichar get_char (uint32 indice) {
-		int64? c = table.lookup (indice);
-		
-		if (c == 0 && indice == 0) {
-			return 0;
-		}
-		
-		if (c == 0) {
-			while (table.lookup (--indice) == 0) {
-				if (indice == 0) {
-					return 0;
-				}
-			} 
-			
-			warning (@"There is no char for glyph number $indice in cmap table. table.size: $(table.size ()))");
-			return 0;
-		}
-		
-		return (unichar) c;
-	}
-	
-	public override void parse (FontData dis) throws GLib.Error {
-		dis.seek (offset);
-		
-		format = dis.read_ushort ();
-		
-		switch (format) {
-			case 4:
-				parse_format4 (dis);
-				break;
-			
-			default:
-				stderr.printf (@"CmapSubtable is in format $format, it is not supportet (yet).\n");
-				break;
-		}
-	}
-		
-	public void parse_format4 (FontData dis) throws GLib.Error {
-		uint16 lang;
-		uint16 seg_count_x2;
-		uint16 seg_count;
-		uint16 search_range;
-		uint16 entry_selector;
-		uint16 range_shift;
-
-		uint16* end_char = null;
-		uint16* start_char = null;
-		int16* id_delta = null;
-		uint16* id_range_offset = null;
-		uint16* glyph_id_array = null;
-	
-		uint32 gid_len;
-		
-		length = dis.read_ushort ();
-		lang = dis.read_ushort ();
-		seg_count_x2 = dis.read_ushort ();
-		search_range = dis.read_ushort ();
-		entry_selector = dis.read_ushort ();
-		range_shift = dis.read_ushort ();
-		
-		return_if_fail (seg_count_x2 % 2 == 0);
-
-		seg_count = seg_count_x2 / 2;
-
-		end_char = new uint16[seg_count];
-		for (int i = 0; i < seg_count; i++) {
-			end_char[i] = dis.read_ushort ();
-		}
-		
-		if (end_char[seg_count - 1] != 0xFFFF) {
-			warning ("end_char is $(end_char[seg_count - 1]), expecting 0xFFFF.");
-		}
-		
-		dis.read_ushort (); // Reserved
-		
-		start_char = new uint16[seg_count];
-		for (int i = 0; i < seg_count; i++) {
-			start_char[i] = dis.read_ushort ();
-		}
-
-		id_delta = new int16[seg_count];
-		for (int i = 0; i < seg_count; i++) {
-			id_delta[i] = dis.read_short ();
-		}
-
-		id_range_offset = new uint16[seg_count];
-		for (int i = 0; i < seg_count; i++) {
-			id_range_offset[i] = dis.read_ushort ();
-		}
-
-		if (length == 0) {
-			warning ("cmap subtable version 4 has length 0.");
-			return;
-		}
-
-		gid_len = (length - 16 - 8 * seg_count) / 2;
-		glyph_id_array = new uint16[gid_len];
-		for (int i = 0; i < gid_len; i++) {
-			glyph_id_array[i] = dis.read_ushort ();
-		}
-		
-		// map all values in a hashtable
-		int indice = 0;
-		unichar character = 0;
-		uint32 id;
-		for (uint16 i = 0; i < seg_count && start_char[i] != 0xFFFF; i++) {
-			
-			// print_range (start_char[i], end_char[i], id_delta[i], id_range_offset[i]);
-			
-			uint16 j = 0;
-			do {
-				character = start_char[i] + j;
-				indice = start_char[i] + id_delta[i] + j;
-				
-				if (id_range_offset[i] == 0) {
-					table.insert (indice, character);
-				} else {
-					// the awkward indexing trick:
-					id = id_range_offset[i] / 2 + j + i - seg_count;
-					
-					if (!(0 <= id < gid_len)) {
-						warning (@"(0 <= id < gid_len) (0 <= $id < $gid_len)");
-						break;
-					}
-					
-					indice = glyph_id_array [id] + id_delta[i];
-										
-					StringBuilder s = new StringBuilder ();
-					s.append_unichar (character);
-										
-					table.insert (indice, character);
-				}
-				
-				j++;
-			} while (character != end_char[i]);
-	
-		}
-		
-		if (end_char != null) delete end_char;
-		if (start_char != null) delete start_char;
-		if (id_delta != null) delete id_delta;
-		if (id_range_offset != null) delete id_range_offset;
-		if (glyph_id_array != null) delete glyph_id_array;
-		
-		// it has a character for every glyph indice
-		// assert (validate_subtable ());
-	}
-	
-	void print_range (unichar start_char, unichar end_char, uint16 delta_offset, uint16 range_offset) {
-		StringBuilder s = new StringBuilder ();
-		StringBuilder e = new StringBuilder ();
-		
-		s.append_unichar (start_char);
-		e.append_unichar (end_char);
-		
-		print (@"New range $(s.str) - $(e.str) delta: $delta_offset, range: $range_offset\n");
-	}
-	
-	public void process (FontData fd, GlyfTable glyf_table) throws GLib.Error {
-		GlyphRange glyph_range = new GlyphRange ();
-		unowned List<UniRange> ranges;
-
-		uint16 seg_count_2;
-		uint16 seg_count;
-		uint16 search_range;
-		uint16 entry_selector;
-		uint16 range_shift;				
-		
-		uint16 gid_length = 0;
-		
-		uint32 indice;
-		uint32 first_assigned = 1;
-		
-		foreach (Glyph g in glyf_table.glyphs) {
-			if (!g.is_unassigned ()) {
-				glyph_range.add_single (g.unichar_code);
-			}
-		}
-		
-		// glyph_range.print_all ();
-		
-		ranges = glyph_range.get_ranges ();
-		seg_count = (uint16) ranges.length () + 1;
-		seg_count_2 =  seg_count * 2;
-		search_range = 2 * largest_pow2 (seg_count);
-		entry_selector = largest_pow2_exponent (seg_count);
-		range_shift = seg_count_2 - search_range;
-		
-		// format
-		fd.add_ushort (4);
-		
-		// length of subtable
-		fd.add_ushort (16 + 8 * seg_count + gid_length);
-		
-		// language
-		fd.add_ushort (0);
-		
-		fd.add_ushort (seg_count_2);
-		fd.add_ushort (search_range);
-		fd.add_ushort (entry_selector);
-		fd.add_ushort (range_shift);
-		
-		// end codes
-		indice = first_assigned;
-		foreach (UniRange u in ranges) {
-			if (u.stop >= 0xFFFF) {
-				warning ("Not implemented yet.");
-			}
-			
-			fd.add_ushort ((uint16) u.stop);
-			indice += u.length ();
-		}
-		fd.add_ushort (0xFFFF);
-		
-		fd.add_ushort (0); // Reserved
-		
-		// start codes
-		indice = first_assigned; // since first glyph are notdef, null and nonmarkingreturn
-		foreach (UniRange u in ranges) {
-			if (u.start >= 0xFFFF) {
-				warning ("Not implemented yet.");
-			}
-			
-			fd.add_ushort ((uint16) u.start);
-			indice += u.length ();
-		}
-		fd.add_ushort (0xFFFF);
-
-		// delta
-		indice = first_assigned;
-		foreach (UniRange u in ranges) {
-			
-			if ((u.start - indice) > 0xFFFF && u.start > indice) {
-				warning ("Need range offset.");
-			}
-			
-			fd.add_ushort ((uint16) (indice - u.start));
-			indice += u.length ();
-		}
-		fd.add_ushort (1);
-		
-		// range offset
-		foreach (UniRange u in ranges) {
-			if (u.stop <= 0xFFFF) {
-				fd.add_ushort (0);
-			} else {
-				warning ("Not implemented yet.");
-			}
-		}
-		fd.add_ushort (0);
-		
-		// Fixa: implement rest of type 4 (mind gid_length in length field)
-	}
-	
-	public bool validate_subtable () {
-		uint32 length = get_length ();
-		unichar c;
-		unichar prev;
-		uint32 i = 0;
-		uint32 err = 0;
-		StringBuilder s;
-		
-		c = get_char (i);
-		if (c != 0) {
-			s = new StringBuilder ();
-			s.append_unichar (c);
-			warning ("nodef. is mapped to $(s.str)");
-		}
-		
-		i++;
-		
-		while (i < length) {
-			if (c == 0) {
-				s = new StringBuilder ();
-				s.append_unichar (c);
-				warning (@"No entry in cmap for index $i. Last avalable char is $(s.str) Got $(s.str), ($((uint32)c))");
-				err++;
-				return false;
-			} else {
-				prev = c;
-			}
-			
-			c = get_char (i);
-			i++;
-		}
-		
-		if (err > 0) {
-			stderr.printf (@"$err glyphs without mapping to a charactercode were found in this font.\n");
-		}
-		
-		return true;
-	}
-}
-
-class CmapTable : Table { 
-	
-	GlyfTable glyf_table;	
-	List<CmapSubtable> subtables;
-
-	public CmapTable(GlyfTable gt) {
-		glyf_table = gt;
-		subtables = new List<CmapSubtable> ();
-		id = "cmap";
-	}
-	
-	public uint32 get_length () {
-		return get_prefered_table ().get_length ();
-	}
-	
-	public unichar get_char (uint32 i) {
-		return get_prefered_table ().get_char (i) ;
-	}
-	
-	CmapSubtable get_prefered_table () {
-		if (subtables.length () == 0) {
-			warning ("No cmap table has been parsed.");
-			return new CmapSubtable ();
-		}
-		
-		return subtables.first ().data;
-	}
-	
-	public override string get_id () {
-		return "cmap";
-	}
-	
-	public override void parse (FontData dis) throws GLib.Error {
-		uint16 version;
-		uint16 nsubtables;
-		
-		uint16 platform;
-		uint16 encoding;
-		uint32 sub_offset;
-		
-		CmapSubtable subtable;
-
-		return_if_fail (offset > 0 && length > 0);
-		
-		dis.seek (offset);
-		
-		version = dis.read_ushort ();
-		nsubtables = dis.read_ushort ();
-
-		printd (@"cmap version: $version\n");
-		printd (@"cmap subtables: $nsubtables\n");
-				
-		if (version != 0) {
-			warning (@"Bad version for cmap table: $version expecting 0. Number of subtables: $nsubtables");
-			return;
-		}
-		
-		for (uint i = 0; i < nsubtables; i++) {
-			platform = dis.read_ushort ();
-			encoding = dis.read_ushort ();
-			sub_offset = dis.read_ulong ();	
-		
-			if (platform == 3 && encoding == 1) {
-				printd (@"Parsing Unicode BMP (UCS-2) Platform: $platform Encoding: $encoding\n");
-				subtable = new CmapSubtableWindowsUnicode ();
-				subtable.offset = offset + sub_offset;
-				subtables.append (subtable);
-			} else {
-				stderr.printf (@"Unknown cmap format. Platform: $platform Encoding: $encoding.\n");
-			}
-			
-			if (encoding == 3) {
-				stderr.printf ("Font contains a cmap table with the obsolete encoding 3.\n");
-			}
-		}
-		
-		if (subtables.length () == 0) {
-			warning ("No suitable cmap subtable found.");
-		}
-		
-		foreach (CmapSubtable t in subtables) {
-			t.parse (dis);
-			t.print_cmap ();
-		}
-
-	}
-	
-	/** Character to glyph mapping */
-	public void process (GlyfTable glyf_table) throws GLib.Error {
-		FontData fd = new FontData ();
-		CmapSubtableWindowsUnicode cmap = new CmapSubtableWindowsUnicode ();
-		uint16 n_encoding_tables;
-		uint32 subtable_offset = 0;
-			
-		n_encoding_tables = 1;
-		
-		fd.add_u16 (0); // table version
-		fd.add_u16 (n_encoding_tables);
-		
-		fd.add_u16 (3); // platform 
-		fd.add_u16 (1); // encoding (Format Unicode UCS-4)
-
-		subtable_offset = fd.length () + 4;
-		printd (@"subtable_offset: $(subtable_offset)\n");
-		
-		fd.add_ulong (subtable_offset);
-		cmap.process (fd, glyf_table);
-
-		// padding
-		fd.pad ();
-
-		this.font_data = fd;
-	}
-}
-
-class HeadTable : Table {
-
-	public int16 xmin = int16.MIN;
-	public int16 ymin = int16.MIN;
-	public int16 xmax = int16.MAX;
-	public int16 ymax = int16.MAX;
-	
-	uint32 adjusted_checksum = 0;
-
-	uint16 mac_style;
-	uint16 lowest_PPEM;
-	int16 font_direction_hint;
-		
-	public int16 loca_offset_size = 0; // 0 for int16 1 for int32
-	int16 glyph_data_format;
-
-	Fixed version;
-	Fixed font_revision;
-	
-	uint32 magic_number;
-	
-	uint16 flags;
-	
-	uint64 created;
-	uint64 modified;
-		
-	uint16 units_per_em = 2048;
-	
-	const uint8 BASELINE_AT_ZERO = 1 << 0;
-	const uint8 LSB_AT_ZERO = 1 << 1;
-	
-	GlyfTable glyf_table;
-	
-	public HeadTable (GlyfTable gt) {
-		glyf_table = gt;
-		id = "head";
-	}
-	
-	public uint32 get_adjusted_checksum () {
-		return adjusted_checksum;
-	}
-	
-	public double get_units_per_em () {
-		return units_per_em * 10; // Fixa: we can refactor this number
-	}
-	
-	public override void parse (FontData dis) throws GLib.Error {
-		return_if_fail (offset > 0 && length > 0);
-
-		dis.seek (offset);
-		
-		font_data = new FontData ();		
-	
-		version = dis.read_fixed ();
-
-		if (!version.equals (1, 0)) {
-			warning (@"Expecting head version 1.0 got $(version.get_string ())\n");
-		}
-		
-		font_revision = dis.read_fixed ();
-		adjusted_checksum = dis.read_ulong ();
-		magic_number = dis.read_ulong ();
-		
-		if (magic_number != 0x5F0F3CF5) {
-			warning (@"Magic number is invalid. Got $(magic_number).");
-			return;
-		}
-		
-		flags = dis.read_ushort ();
-		
-		if ((flags & BASELINE_AT_ZERO) > 0) {
-			printd ("Flag BASELINE_AT_ZERO has been set.\n");
-		}
-
-		if ((flags & LSB_AT_ZERO) > 0) {
-			printd ("Flags LSB_AT_ZERO has been set.\n");
-		}
-		
-		units_per_em = dis.read_ushort ();
-		
-		created = dis.read_udate ();
-		modified = dis.read_udate ();
-		
-		xmin = dis.read_short ();
-		ymin = dis.read_short ();
-		
-		xmax = dis.read_short ();
-		ymax = dis.read_short ();
-
-		printd (@"font boundries:\n");
-		printd (@"xmin: $xmin\n");
-		printd (@"ymin: $ymin\n");
-		printd (@"xmax: $xmax\n");
-		printd (@"ymax: $ymax\n");
-				
-		mac_style = dis.read_ushort ();
-		lowest_PPEM = dis.read_ushort ();
-		font_direction_hint = dis.read_short ();
-		
-		loca_offset_size = dis.read_short ();
-		glyph_data_format = dis.read_short ();
-		
-		if (glyph_data_format != 0) {
-			warning (@"Unknown glyph data format. Expecting 0 got $glyph_data_format.");
-		}
-		
-		// print_values ();
-		// Some deprecated values follow here ...
-	}
-	
-	void print_values () {
-		print (@"Version: $(version.get_string ())\n");
-		print (@"flags: $flags\n");
-		print (@"font_revision: $(font_revision.get_string ())\n");
-		print (@"flags: $flags\n");
-		print (@"Units per em: $units_per_em\n");
-		print (@"lowest_PPEM: $lowest_PPEM\n");
-		print (@"font_direction_hint: $font_direction_hint\n");
-		print (@"loca_offset_size: $loca_offset_size\n");
-		print (@"glyph_data_format: $glyph_data_format\n");
-	}
-	
-	public uint32 get_font_checksum () {
-		return adjusted_checksum;
-	}
-	
-	public void set_check_sum_adjustment (uint32 csa) {
-		this.adjusted_checksum = csa;
-	}
-	
-	public uint32 get_checksum_position () {
-		return 8;
-	}
-	
-	public void process () throws GLib.Error {
-		FontData font_data = new FontData ();
-		Fixed version = 1 << 16;
-		Fixed font_revision = 1 << 16;
-
-		font_data.add_fixed (version);
-		font_data.add_fixed (font_revision);
-		
-		// Zero on the first run and updated by directory tables checksum calculation
-		// for the entire font.
-		font_data.add_u32 (adjusted_checksum);
-		
-		font_data.add_u32 (0x5F0F3CF5); // magic number
-		
-		//font_data.add_u16 (BASELINE_AT_ZERO | LSB_AT_ZERO);
-		font_data.add_u16 (0); // flags
-		
-		font_data.add_u16 (1000); // units per em (should be a power of two for ttf fonts)
-		
-		font_data.add_64 (0); // creation time since 1904-01-01
-		font_data.add_64 (0); // modified time since 1904-01-01
-
-		xmin = glyf_table.xmin;
-		ymin = glyf_table.ymin;
-		xmax = glyf_table.xmax;
-		ymax = glyf_table.ymax;
-
-		printd (@"font boundries:\n");
-		printd (@"xmin: $xmin\n");
-		printd (@"ymin: $ymin\n");
-		printd (@"xmax: $xmax\n");
-		printd (@"ymax: $ymax\n");
-
-		font_data.add_short (xmin);
-		font_data.add_short (ymin);
-		font_data.add_short (xmax);
-		font_data.add_short (ymax);
-	
-		font_data.add_u16 (0); // mac style
-		font_data.add_u16 (2); // smallest recommended size in pixels, ppem
-		font_data.add_16 (2); // deprecated direction hint
-		font_data.add_16 (loca_offset_size);  // long offset
-		font_data.add_16 (0);  // Use current glyph data format
-		
-		font_data.pad ();
-		
-		this.font_data = font_data;
-	}
-}
-
-class HheaTable : Table {
-
-	Fixed version;
-	int16 ascender;
-	int16 descender;
-	int16 linegap;
-	uint16 max_advance;
-	int16 min_lsb;
-	int16 min_rsb;
-	int16 xmax_extent;
-	int16 carret_slope;
-	int16 carret_slope_run;
-	int16 carret_offset;
-	
-	int16 metric_format;
-	public int16 num_horizontal_metrics;
-		
-	GlyfTable glyf_table;
-	HeadTable head_table;
-	HmtxTable hmtx_table;
-	
-	public HheaTable (GlyfTable g, HeadTable h, HmtxTable hm) {
-		glyf_table = g;
-		head_table = h;
-		hmtx_table = hm;
-		id = "hhea";
-	}
-	
-	public double get_ascender () {
-		return ascender * 1000 / head_table.get_units_per_em ();
-	}
-
-	public double get_descender () {
-		return descender * 1000 / head_table.get_units_per_em ();
-	}
-	
-	public override void parse (FontData dis) throws GLib.Error {
-		dis.seek (offset);
-		
-		version = dis.read_fixed ();
-		
-		if (!version.equals (1, 0)) {
-			warning (@"wrong version in hhea table $(version.get_string ())");
-		}
-		
-		ascender = dis.read_short ();
-		descender = dis.read_short ();
-		linegap = dis.read_short ();
-		max_advance = dis.read_ushort ();
-		min_lsb = dis.read_short ();
-		min_rsb = dis.read_short ();
-		xmax_extent = dis.read_short ();
-		carret_slope = dis.read_short ();
-		carret_slope_run = dis.read_short ();
-		carret_offset = dis.read_short ();
-		
-		// reserved x 4
-		dis.read_short ();
-		dis.read_short ();
-		dis.read_short ();
-		dis.read_short ();
-		
-		metric_format = dis.read_short ();
-		num_horizontal_metrics = dis.read_short ();
-	}
-	
-	public void process () throws GLib.Error {
-		Font font = OpenFontFormatWriter.get_current_font ();
-		FontData fd = new FontData ();
-		Fixed version = 1 << 16;
-		
-		fd.add_fixed (version); // table version
-		
-		fd.add_16 ((int16) (-1 * (font.top_position - font.base_line) * UNITS)); // Ascender
-		fd.add_16 ((int16) (-1 * font.bottom_position * UNITS)); // Descender
-		fd.add_16 (0); // LineGap
-				
-		fd.add_u16 (hmtx_table.max_advance); // maximum advance width value in 'hmtx' table.
-		
-		fd.add_16 (hmtx_table.min_lsb); // min left side bearing
-		fd.add_16 (hmtx_table.min_rsb); // min right side bearing
-		fd.add_16 (hmtx_table.max_extent); // x max extent Max(lsb + (xMax - xMin))
-		
-		fd.add_16 (1); // caretSlopeRise
-		fd.add_16 (0); // caretSlopeRun
-		fd.add_16 (0); // caretOffset
-		
-		// reserved
-		fd.add_16 (0);
-		fd.add_16 (0);
-		fd.add_16 (0);
-		fd.add_16 (0);
-		
-		fd.add_16 (0); // metricDataFormat 0 for current format.
-		
-		fd.add_u16 ((uint16) glyf_table.glyphs.length()); // numberOfHMetrics Number of hMetric entries in 'hmtx' table
-
-		// padding
-		fd.pad ();
-		this.font_data = fd;
-	}
-}
-
-class HmtxTable : Table {
-	
-	uint32 nmetrics;
-	uint32 nmonospaced;
-		
-	uint16* advance_width = null;
-	uint16* left_side_bearing = null;
-	uint16* left_side_bearing_monospaced = null;
-	
-	public int16 max_advance = 0;
-	public int16 max_extent = 0;
-	public int16 min_lsb = 0; 
-	public int16 min_rsb = 0;
-			
-	HeadTable head_table;
-	GlyfTable glyf_table;
-	
-	public HmtxTable (HeadTable h, GlyfTable gt) {
-		head_table = h;
-		glyf_table = gt;
-		id = "hmtx";
-	}
-	
-	~HmtxTable () {
-		if (advance_width != null) delete advance_width;
-		if (left_side_bearing != null) delete left_side_bearing; 
-	}
-
-	public double get_advance (uint32 i) {
-		if (i >= nmetrics) {
-			warning (@"i >= nmetrics $i >= $nmetrics");
-			return 0;
-		}
-		
-		return_val_if_fail (advance_width != null, 0.0);
-		
-		return advance_width[i] * 1000 / head_table.get_units_per_em ();
-	}
-		
-	/** Get left side bearing relative to xmin. */
-	public double get_lsb (uint32 i) {
-		return_val_if_fail (i < nmetrics, 0.0);
-		return_val_if_fail (left_side_bearing != null, 0.0);
-		
-		return left_side_bearing[i] * 1000 / head_table.get_units_per_em ();
-	}
-
-	/** Get left side bearing relative to xmin for monospaces fonts. */
-	public double get_lsb_mono (uint32 i) 
-		requires (i < nmonospaced && left_side_bearing_monospaced != null) {
-		return left_side_bearing_monospaced[i] * 1000 / head_table.get_units_per_em ();
-	}
-	
-	public void print_all () {
-		return_if_fail (advance_width != null);
-		return_if_fail (left_side_bearing != null);
-		
-		for (int i = 0; i < nmetrics; i++) {
-			print (@"get_lsb ($i): $(get_lsb(i))\n");
-			print (@"get_advance ($i): $(get_advance(i))\n");
-		}
-
-		if (left_side_bearing_monospaced != null) {
-			for (int i = 0; i < nmonospaced; i++) {
-				print (@"get_lsb_mono ($i): $(get_lsb_mono(i))\n");
-			}
-		}
-
-	}
-			
-	public new void parse (FontData dis, HheaTable hhea_table, LocaTable loca_table) throws GLib.Error {
-		nmetrics = hhea_table.num_horizontal_metrics;
-		nmonospaced = loca_table.size - nmetrics;
-		
-		dis.seek (offset);
-		
-		if (nmetrics > loca_table.size) {
-			warning (@"(nmetrics > loca_table.size) ($nmetrics > $(loca_table.size))");
-			return;
-		}
-		
-		printd (@"nmetrics: $nmetrics\n");
-		printd (@"loca_table.size: $(loca_table.size)\n");
-		
-		advance_width = new uint16[nmetrics];
-		left_side_bearing = new uint16[nmetrics];
-		left_side_bearing_monospaced = new uint16[nmonospaced];
-		
-		for (int i = 0; i < nmetrics; i++) {
-			advance_width[i] = dis.read_ushort ();
-			left_side_bearing[i] = dis.read_short ();
-		}
-		
-		for (int i = 0; i < nmonospaced; i++) {
-			left_side_bearing_monospaced[i] = dis.read_short ();
-		}
-	}
-	
-	public void process () {
-		FontData fd = new FontData ();
-
-		int16 advance;
-		int16 extent;
-		int16 rsb;
-		int16 lsb;
-		
-		double xmin;
-		double ymin;
-		double xmax;
-		double ymax;
-		
-		// advance and lsb
-		foreach (Glyph g in glyf_table.glyphs) {
-			g.boundries (out xmin, out ymin, out xmax, out ymax);
-			lsb = (int16) (-1 * (g.left_limit - xmin) * UNITS);
-			advance = (int16) ((g.right_limit - g.left_limit) * UNITS);
-			extent = (int16) (lsb + (xmax - xmin) * UNITS);
-			rsb = (int16) (advance - extent);
-						
-			fd.add_u16 (advance);
-			fd.add_16 (lsb);
-	
-			if (advance > max_advance) {
-				max_advance = advance;
-			}
-			
-			if (extent > max_extent) {
-				max_extent = extent;
-			}
-			
-			if (rsb < min_rsb) {
-				min_rsb = rsb;
-			}
-
-			if (lsb < min_lsb) {
-				min_lsb = lsb;
-			}
-		}
-		
-		// monospaced lsb ...
-		
-		font_data = fd;
-		
-		warn_if_fail (max_advance != 0);
-	}
-}
-
-
-class MaxpTable : Table {
-	
-	GlyfTable glyf_table;
-	
-	public uint16 num_glyphs = 0;
-	
-	public MaxpTable (GlyfTable g) {
-		glyf_table = g;
-		id = "maxp";
-	}
-	
-	public override void parse (FontData dis) 
-		requires (offset > 0 && length > 0) {
-		Fixed format;
-		
-		dis.seek (offset);
-		
-		format = dis.read_fixed ();
-		printd (@"Maxp version: $(format.get_string ())\n");
-		
-		num_glyphs = dis.read_ushort ();
-		
-		if (format == 0x00005000) {
-			return;
-		}
-		
-		// Format 1.0 continues here
-	}
-	
-	public void process () {
-		FontData fd = new FontData();
-
-		// Version 0.5 for fonts with cff data and 1.0 for ttf
-		fd.add_u32 (0x00010000);
-		
-		if (glyf_table.glyphs.length () == 0) {
-			warning ("Zero glyphs in maxp table.");
-		}
-		
-		fd.add_u16 ((uint16) glyf_table.glyphs.length ()); // numGlyphs in the font
-
-		fd.add_u16 (glyf_table.get_max_points ()); // max points
-		fd.add_u16 (glyf_table.get_max_contours ()); // max contours
-		fd.add_u16 (0); // max composite points
-		fd.add_u16 (0); // max composite contours
-		fd.add_u16 (1); // max zones
-		fd.add_u16 (0); // twilight points
-		fd.add_u16 (0); // max storage
-		fd.add_u16 (0); // max function defs
-		fd.add_u16 (0); // max instruction defs
-		fd.add_u16 (0); // max stack elements
-		fd.add_u16 (0); // max size of instructions
-		fd.add_u16 (0); // max component elements
-		fd.add_u16 (0); // component depth
-		
-		fd.pad ();
-		
-		this.font_data = fd;
-	}
-}
-
-class OffsetTable : Table {
-	DirectoryTable directory_table;
-		
-	public uint16 num_tables = 0;
-	uint16 search_range = 0;
-	uint16 entry_selector = 0;
-	uint16 range_shift = 0;
-	
-	public OffsetTable (DirectoryTable t) {
-		id = "Offset table";
-		directory_table = t;
-	}
-		
-	public override void parse (FontData dis) throws Error {
-		Fixed version;
-		
-		dis.seek (offset);
-		
-		version = dis.read_fixed ();
-		num_tables = dis.read_ushort ();
-		search_range = dis.read_ushort ();
-		entry_selector = dis.read_ushort ();
-		range_shift = dis.read_ushort ();
-		
-		printd (@"Font file version $(version.get_string ())\n");
-		printd (@"Number of tables $num_tables\n");		
-	}
-	
-	public void process () throws GLib.Error {
-		FontData fd = new FontData ();
-		Fixed version = 0x00010000; // sfnt version 1.0 for TTF CFF else use OTTO
-
-		
-		num_tables = (uint16) directory_table.get_tables ().length () - 2; // number of tables, skip DirectoryTable and OffsetTable
-		
-		search_range = max_pow_2_less_than_i (num_tables) * 16;
-		entry_selector = max_log_2_less_than_i (num_tables);
-		range_shift = 16 * num_tables - search_range;
-
-		fd.add_fixed (version); 
-		fd.add_u16 (num_tables);
-		fd.add_u16 (search_range);
-		fd.add_u16 (entry_selector);
-		fd.add_u16 (range_shift);
-		
-		// skip padding for offset table 
-		
-		this.font_data = fd;
-	}
-}
-
-class NameTable : Table {
-
-	public static const uint16 COPYRIGHT_NOTICE = 0;
-	public static const uint16 FONT_NAME = 1;
-	public static const uint16 SUBFAMILY_NAME = 2;
-	public static const uint16 UNIQUE_IDENTIFIER = 3;
-	public static const uint16 FULL_FONT_NAME = 4; // name + subfamily
-	public static const uint16 VERSION = 5;
-	public static const uint16 DESCRIPTION = 10;
-	public static const uint16 PREFERED_FAMILY = 16;
-	public static const uint16 PREFERED_SUB_FAMILY = 17;
-	
-	List<uint16> identifiers;
-	List<string> text;
-			
-	public NameTable () {
-		id = "name";
-		text = new List<string> ();
-		identifiers = new List<uint16> ();
-	}
-	
-	public string get_name (uint16 identifier) {
-		int i = 0;
-		
-		foreach (uint16 n in identifiers) {
-			if (n == identifier) {
-				return text.nth (i).data;
-			}
-			i++;
-		}
-		
-		return "";
-	}
-
-	public void print_all () {
-		stdout.printf (@"$(text.length ()) name table texts:\n");
-		foreach (string s in text) {
-			stdout.printf (s);
-			stdout.printf ("\n");
-		}
-	}
-
-	public override void parse (FontData dis) throws Error {
-		uint16 format;
-
-		dis.seek (offset);
-		
-		format = dis.read_ushort ();
-		
-		switch (format) {
-			case 0:
-				parse_format0 (dis);
-				break;
-				
-			case 1:
-				warning ("name table format 1 is not implemented yet");
-				break;
-			
-			default:
-				warning (@"unknown format $format in name table");
-				break;
-		}
-	}
-	
-	public void parse_format0 (FontData dis) throws Error {
-		uint16 count;
-		uint16 storage_offset;
-		
-		List<uint16> strlen = new List<uint16> ();
-		List<uint16> off = new List<uint16> ();
-		List<uint16> name_id = new List<uint16> ();
-		List<uint16> encoding_id = new List<uint16> ();
-		List<uint16> platform = new List<uint16> ();
-		List<uint16> lang = new List<uint16> ();
-				
-		count = dis.read_ushort ();
-		storage_offset = dis.read_ushort ();
-		
-		for (int i = 0; i < count; i++) {
-			platform.append (dis.read_ushort ());
-			encoding_id.append (dis.read_ushort ());
-			lang.append (dis.read_ushort ());
-			name_id.append (dis.read_ushort ());
-			strlen.append (dis.read_ushort ());
-			off.append (dis.read_ushort ());
-			
-			identifiers.append (name_id.last ().data);	
-		}
-
-		int plat;
-		StringBuilder str;
-		for (int i = 0; i < count; i++) {
-			plat = platform.nth (i).data;
-			dis.seek (offset + storage_offset + off.nth (i).data);
-			str = new StringBuilder ();
-			
-			switch (plat) {
-				case 1:
-					for (int j = 0; j < strlen.nth (i).data; j++) {
-						char c = dis.read_char ();
-						str.append_c (c);
-					}
-					break;
-					
-				case 3:
-					for (int j = 0; j < strlen.nth (i).data; j += 2) {
-						unichar c;
-						char c0 = dis.read_char ();
-						char c1 = dis.read_char ();
-												
-						c = c0 << 8;
-						c += c1;
-						
-						str.append_unichar (c);
-					}
-					break;
-				
-				default:
-					break;
-			} 
-			
-			text.append (str.str);
-			printd (@"Name id: $(name_id.nth (i).data) platform:  $(platform.nth (i).data) enc: $(encoding_id.nth (i).data) lang: $(lang.nth (i).data) len: $(strlen.nth (i).data) str: \"$(str.str)\"\n");		
-		}
-	}
-	
-	public void process () throws GLib.Error {
-		FontData fd = new FontData ();
-		Font font = OpenFontFormatWriter.get_current_font ();
-		uint16 len = 0;
-		string t;
-		uint16 p;
-		uint16 l;
-		uint16 num_records;
-
-		List<uint16> type = new List<uint16> ();
-		List<string> text = new List<string> ();
-		
-		text.append ("Copyright");
-		type.append (COPYRIGHT_NOTICE);
-		
-		text.append (font.get_name ());
-		type.append (FONT_NAME);
-
-		text.append ("Regular");
-		type.append (SUBFAMILY_NAME);
-
-		text.append (font.get_name ()); // TODO: validate
-		type.append (UNIQUE_IDENTIFIER);
-
-		text.append (font.get_name ());
-		type.append (FULL_FONT_NAME);
-		
-		// This does for some reason cause an internal error in ms fontvalidatior utility.
-		// Head table can't parse integer from string.
-		text.append ("Version 1.0");
-		type.append (VERSION);		
-		
-		text.append ("");
-		type.append (DESCRIPTION);
-				
-		text.append (font.get_name ());
-		type.append (PREFERED_FAMILY);
-		
-		text.append ("Regular");
-		type.append (PREFERED_SUB_FAMILY);
-			
-		num_records = (uint16) text.length ();
-		
-		fd.add_ushort (0); // format 1
-		fd.add_ushort (2 * num_records); // nplatforms * nrecords 
-		fd.add_ushort (6 + 12 * 2 * num_records); // string storage offset
-
-		for (int i = 0; i < num_records; i++) {
-			t = (!) text.nth (i).data;
-			p = (!) type.nth (i).data;
-			l = (uint16) t.length;
-			
-			fd.add_ushort (1); // platform
-			fd.add_ushort (0); // encoding id
-			fd.add_ushort (0); // language
-			fd.add_ushort (p); // name id 
-			fd.add_ushort (l); // strlen
-			fd.add_ushort (len); // offset from begining of string storage
-			len += l;				
-		}	
-
-		for (int i = 0; i < num_records; i++) {
-			t = (!) text.nth (i).data;
-			p = (!) type.nth (i).data;
-			l = (uint16) (2 * t.char_count ());
-
-			fd.add_ushort (3); // platform
-			fd.add_ushort (1); 	// encoding id
-			fd.add_ushort (0x0409); // language
-			fd.add_ushort (p); // name id 
-			fd.add_ushort (l); // strlen
-			fd.add_ushort (len); // offset from begining of string storage
-			len += l;
-		}
-
-		// platform 1
-		foreach (string s in text) {
-			fd.add_str (s); 
-		}
-		
-		// platform 3
-		foreach (string s in text) {
-			fd.add_str_utf16 (s); 
-		}
-
-		fd.pad ();
-		
-		this.font_data = fd;
-	}
-}
-
-class Os2Table : Table {
-	
-	public Os2Table () {
-		id = "OS/2";
-	}
-	
-	public override void parse (FontData dis) throws Error {
-		
-	}
-	
-	public void process (GlyfTable glyf_table) {
-		FontData fd = new FontData ();
-		Font font = OpenFontFormatWriter.get_current_font ();
-		
-		int16 ascender;
-		int16 descender;
-		
-		fd.add_u16 (0x0002); // USHORT Version 0x0000, 0x0001, 0x0002, 0x0003, 0x0004
-
-		fd.add_16 (glyf_table.get_average_width ()); // SHORT xAvgCharWidth
-
-		fd.add_u16 (400); // USHORT usWeightClass (400 is normal)
-		fd.add_u16 (5); // USHORT usWidthClass (5 is normal)
-		fd.add_u16 (0); // USHORT fsType
-
-		fd.add_16 (40); // SHORT ySubscriptXSize
-		fd.add_16 (40); // SHORT ySubscriptYSize
-		fd.add_16 (40); // SHORT ySubscriptXOffset
-		fd.add_16 (40); // SHORT ySubscriptYOffset
-		fd.add_16 (40); // SHORT ySuperscriptXSize
-		fd.add_16 (40); // SHORT ySuperscriptYSize
-		fd.add_16 (40); // SHORT ySuperscriptXOffset
-		fd.add_16 (40); // SHORT ySuperscriptYOffset
-		fd.add_16 (40); // SHORT yStrikeoutSize
-		fd.add_16 (200); // SHORT yStrikeoutPosition
-		fd.add_16 (40); // SHORT sFamilyClass
-
-		// PANOSE
-		fd.add (0); 
-		fd.add (0); 
-		fd.add (0); 
-		fd.add (0); 
-		fd.add (0); 
-		fd.add (0); 
-		fd.add (0); 
-		fd.add (0); 
-		fd.add (0); 
-		fd.add (0); 
-
-		// FIXA:
-		fd.add_u32 (0); // ulUnicodeRange1 Bits 0-31
-		fd.add_u32 (0); // ULONG ulUnicodeRange2 Bits 32-63
-		fd.add_u32 (0); // ULONG ulUnicodeRange3 Bits 64-95
-		fd.add_u32 (0); // ULONG ulUnicodeRange4 Bits 96-127
-
-		fd.add_tag ("----"); // VendID
-
-		fd.add_u16 (0); // USHORT fsSelection
-		
-		fd.add_u16 (glyf_table.get_first_char ()); // USHORT usFirstCharIndex
-		fd.add_u16 (glyf_table.get_last_char ()); // USHORT usLastCharIndex
-
-		ascender = (int16) (glyf_table.xmax + font.base_line * UNITS);
-		descender = (int16) (-glyf_table.xmin  + font.base_line * UNITS);
-		
-		fd.add_16 (ascender); // SHORT sTypoAscender
-		fd.add_16 (descender); // SHORT sTypoDescender
-		fd.add_16 (3); // SHORT sTypoLineGap
-
-		fd.add_u16 (ascender); // USHORT usWinAscent
-		fd.add_u16 (descender); // USHORT usWinDescent
-
-		// FIXA:
-		fd.add_u32 (0); // ULONG ulCodePageRange1 Bits 0-31
-		fd.add_u32 (0); // ULONG ulCodePageRange2 Bits 32-63
-
-		fd.add_16 (ascender); // SHORT sxHeight version 0x0002 and later
-		fd.add_16 (ascender); // SHORT sCapHeight version 0x0002 and later
-
-		fd.add_16 (0); // USHORT usDefaultChar version 0x0002 and later
-		fd.add_16 (0x0020); // USHORT usBreakChar version 0x0002 and later, also known as space
-		
-		// FIXA: calculate these values
-		fd.add_16 (1); // USHORT usMaxContext version 0x0002 and later
-
-		// padding
-		fd.pad ();
-	
-		this.font_data = fd;
-	}
-
-}
-
-class PostTable : Table {
-	
-	GlyfTable glyf_table;
-	
-	List<uint16> index = new List<uint16> ();
-	List<string> names = new List<string> ();
-
-	List<string> available_names = new List<string> ();
-	
-	public PostTable (GlyfTable g) {
-		id = "post";
-		glyf_table = g;
-	}
-
-	public unowned List<string> get_all_names () {
-		return available_names;
-	}
-		
-	public int get_gid (string name) { // fixa do fast lookup
-		int i = 0;
-		int j = 0;
-		foreach (string n in names) {
-			if (n == name) {				
-				j = 0;
-				foreach (uint16 k in index) {
-					if (k == i) {
-						return j;
-					}
-					j++;
-				}
-								
-				return i;
-			}
-			i++;
-		}
-		
-		// warn_if_reached ();
-		return -1;
-	}
-
-	public string get_name (int gid) {
-		int k;
-		
-		if (!(0 <= gid < index.length ())) {
-			warning ("gid is out of range.");
-			return "";
-		}
-				
-		k = (!) index.nth (gid).data;
-		
-		if (gid != 0 && k == 0) {
-			warning (@"Glyph $gid is assigned to name .notdef, only gid 0 can be .notdef character.");
-			return "";
-		}
-		
-		if (!(0 <= k < names.length ())) {
-			warning ("k is out of range.");
-			return "";
-		}
-				
-		return (!) names.nth (k).data;
-	}
-	
-	public override void parse (FontData dis) throws Error {
-		dis.seek (offset);
-		
-		Fixed format = dis.read_fixed ();
-		Fixed italic = dis.read_fixed ();
-		
-		int16 underlie_pos = dis.read_short ();
-		int16 underlie_thickness = dis.read_short ();
-		uint32 is_fixed_pitch  = dis.read_ulong ();
-		
-		uint32 mem_min42  = dis.read_ulong ();
-		uint32 mem_max42  = dis.read_ulong ();
-		uint32 mem_min1  = dis.read_ulong ();
-		uint32 mem_max1  = dis.read_ulong ();
-		
-		uint16 nnames  = dis.read_ushort ();
-		
-		if (format != 0x00020000) {
-			warning ("Only post tables of version 2 will parset got $(format.get_string ())");
-			return;
-		}
-		
-		printd (@"format: $(format.get_string ())\n");
-		printd (@"italic: $(italic.get_string ())\n");
-		printd (@"underlie_pos: $(underlie_pos)\n");
-		printd (@"underlie_thickness: $(underlie_thickness)\n");
-		printd (@"is_fixed_pitch: $(is_fixed_pitch)\n");
-		printd (@"mem_min42: $(mem_min42)\n");
-		printd (@"mem_max42: $(mem_max42)\n");
-		printd (@"mem_min1: $(mem_min1)\n");
-		printd (@"mem_max1: $(mem_max1)\n");
-		printd (@"\n");
-		
-		printd (@"Num names: $(nnames)\n");
-		
-		uint16 k;
-		int non_standard_names = 0;
-		for (uint16 i = 0; i < nnames; i++) {
-			k = dis.read_ushort ();
-			index.append (k);
-			
-			if (k >= 258) {
-				non_standard_names++;
-			}
-		}
-		
-		add_standard_names ();
-		
-		// read non standard names
-		for (int i = 0; i < non_standard_names; i++) {
-			uint8 len = dis.read_byte ();
-			StringBuilder name = new StringBuilder ();
-			
-			for (int j = 0; j < len; j++) {
-				name.append_c (dis.read_char ());
-			}
-
-			names.append (name.str);
-		}
-		
-		populate_available ();
-	}
-
-	void populate_available () {
-		for (int i = 0; i < index.length (); i++) {
-			available_names.append (get_name (i));
-		}
-	}
-		
-	public void print_all () {
-		print (@"PostScript glyph mapping:\n");
-		for (int i = 0; i < index.length (); i++) {
-			print (@"gid $i -> $(get_name (i))\n");
-		}
-	}
-	
-	public void print_all_names () {
-		print (@"Post table names:\n");
-		foreach (var n in names) {
-			print (@"$n\n");
-		}
-	}
-	
-	// the Macintosh standard order
-	private void add_standard_names () {
-		names.append (".notdef");
-		names.append (".null");
-		names.append ("nonmarkingreturn");
-		names.append ("space");
-		names.append ("exclam");
-		names.append ("quotedbl");
-		names.append ("numbersign");
-		names.append ("dollar");
-		names.append ("percent");
-		names.append ("ampersand");
-		names.append ("quotesingle");
-		names.append ("parenleft");
-		names.append ("parenright");
-		names.append ("asterisk");
-		names.append ("plus");
-		names.append ("comma");
-		names.append ("hyphen");
-		names.append ("period");
-		names.append ("slash");
-		names.append ("zero");
-		names.append ("one");
-		names.append ("two");
-		names.append ("three");
-		names.append ("four");
-		names.append ("five");
-		names.append ("six");
-		names.append ("seven");
-		names.append ("eight");
-		names.append ("nine");
-		names.append ("colon");
-		names.append ("semicolon");
-		names.append ("less");
-		names.append ("equal");
-		names.append ("greater");
-		names.append ("question");
-		names.append ("at");
-		names.append ("A");
-		names.append ("B");
-		names.append ("C");
-		names.append ("D");
-		names.append ("E");
-		names.append ("F");
-		names.append ("G");
-		names.append ("H");
-		names.append ("I");
-		names.append ("J");
-		names.append ("K");
-		names.append ("L");
-		names.append ("M");
-		names.append ("N");
-		names.append ("O");
-		names.append ("P");
-		names.append ("Q");
-		names.append ("R");
-		names.append ("S");
-		names.append ("T");
-		names.append ("U");
-		names.append ("V");
-		names.append ("W");
-		names.append ("X");
-		names.append ("Y");
-		names.append ("Z");
-		names.append ("bracketleft");
-		names.append ("backslash");
-		names.append ("bracketright");
-		names.append ("asciicircum");
-		names.append ("underscore");
-		names.append ("grave");
-		names.append ("a");
-		names.append ("b");
-		names.append ("c");
-		names.append ("d");
-		names.append ("e");
-		names.append ("f");
-		names.append ("g");
-		names.append ("h");
-		names.append ("i");
-		names.append ("j");
-		names.append ("k");
-		names.append ("l");
-		names.append ("m");
-		names.append ("n");
-		names.append ("o");
-		names.append ("p");
-		names.append ("q");
-		names.append ("r");
-		names.append ("s");
-		names.append ("t");
-		names.append ("u");
-		names.append ("v");
-		names.append ("w");
-		names.append ("x");
-		names.append ("y");
-		names.append ("z");
-		names.append ("braceleft");
-		names.append ("bar");
-		names.append ("braceright");
-		names.append ("asciitilde");
-		names.append ("Adieresis");
-		names.append ("Aring");
-		names.append ("Ccedilla");
-		names.append ("Eacute");
-		names.append ("Ntilde");
-		names.append ("Odieresis");
-		names.append ("Udieresis");
-		names.append ("aacute");
-		names.append ("agrave");
-		names.append ("acircumflex");
-		names.append ("adieresis");
-		names.append ("atilde");
-		names.append ("aring");
-		names.append ("ccedilla");
-		names.append ("eacute");
-		names.append ("egrave");
-		names.append ("ecircumflex");
-		names.append ("edieresis");
-		names.append ("iacute");
-		names.append ("igrave");
-		names.append ("icircumflex");
-		names.append ("idieresis");
-		names.append ("ntilde");
-		names.append ("oacute");
-		names.append ("ograve");
-		names.append ("ocircumflex");
-		names.append ("odieresis");
-		names.append ("otilde");
-		names.append ("uacute");
-		names.append ("ugrave");
-		names.append ("ucircumflex");
-		names.append ("udieresis");
-		names.append ("dagger");
-		names.append ("degree");
-		names.append ("cent");
-		names.append ("sterling");
-		names.append ("section");
-		names.append ("bullet");
-		names.append ("paragraph");
-		names.append ("germandbls");
-		names.append ("registered");
-		names.append ("copyright");
-		names.append ("trademark");
-		names.append ("acute");
-		names.append ("dieresis");
-		names.append ("notequal");
-		names.append ("AE");
-		names.append ("Oslash");
-		names.append ("infinity");
-		names.append ("plusminus");
-		names.append ("lessequal");
-		names.append ("greaterequal");
-		names.append ("yen");
-		names.append ("mu");
-		names.append ("partialdiff");
-		names.append ("summation");
-		names.append ("product");
-		names.append ("pi");
-		names.append ("integral");
-		names.append ("ordfeminine");
-		names.append ("ordmasculine");
-		names.append ("Omega");
-		names.append ("ae");
-		names.append ("oslash");
-		names.append ("questiondown");
-		names.append ("exclamdown");
-		names.append ("logicalnot");
-		names.append ("radical");
-		names.append ("florin");
-		names.append ("approxequal");
-		names.append ("Delta");
-		names.append ("guillemotleft");
-		names.append ("guillemotright");
-		names.append ("ellipsis");
-		names.append ("nonbreakingspace");
-		names.append ("Agrave");
-		names.append ("Atilde");
-		names.append ("Otilde");
-		names.append ("OE");
-		names.append ("oe");
-		names.append ("endash");
-		names.append ("emdash");
-		names.append ("quotedblleft");
-		names.append ("quotedblright");
-		names.append ("quoteleft");
-		names.append ("quoteright");
-		names.append ("divide");
-		names.append ("lozenge");
-		names.append ("ydieresis");
-		names.append ("Ydieresis");
-		names.append ("fraction");
-		names.append ("currency");
-		names.append ("guilsinglleft");
-		names.append ("guilsinglright");
-		names.append ("fi");
-		names.append ("fl");
-		names.append ("daggerdbl");
-		names.append ("periodcentered");
-		names.append ("quotesinglbase");
-		names.append ("quotedblbase");
-		names.append ("perthousand");
-		names.append ("Acircumflex");
-		names.append ("Ecircumflex");
-		names.append ("Aacute");
-		names.append ("Edieresis");
-		names.append ("Egrave");
-		names.append ("Iacute");
-		names.append ("Icircumflex");
-		names.append ("Idieresis");
-		names.append ("Igrave");
-		names.append ("Oacute");
-		names.append ("Ocircumflex");
-		names.append ("apple");
-		names.append ("Ograve");
-		names.append ("Uacute");
-		names.append ("Ucircumflex");
-		names.append ("Ugrave");
-		names.append ("dotlessi");
-		names.append ("circumflex");
-		names.append ("tilde");
-		names.append ("macron");
-		names.append ("breve");
-		names.append ("dotaccent");
-		names.append ("ring");
-		names.append ("cedilla");
-		names.append ("hungarumlaut");
-		names.append ("ogonek");
-		names.append ("caron");
-		names.append ("Lslash");
-		names.append ("lslash");
-		names.append ("Scaron");
-		names.append ("scaron");
-		names.append ("Zcaron");
-		names.append ("zcaron");
-		names.append ("brokenbar");
-		names.append ("Eth");
-		names.append ("eth");
-		names.append ("Yacute");
-		names.append ("yacute");
-		names.append ("Thorn");
-		names.append ("thorn");
-		names.append ("minus");
-		names.append ("multiply");
-		names.append ("onesuperior");
-		names.append ("twosuperior");
-		names.append ("threesuperior");
-		names.append ("onehalf");
-		names.append ("onequarter");
-		names.append ("threequarters");
-		names.append ("franc");
-		names.append ("Gbreve");
-		names.append ("gbreve");
-		names.append ("Idotaccent");
-		names.append ("Scedilla");
-		names.append ("scedilla");
-		names.append ("Cacute");
-		names.append ("cacute");
-		names.append ("Ccaron");
-		names.append ("ccaron");
-		names.append ("dcroat");
-	}
-	
-	// mapping with char code to standard order
-	int get_standard_index (unichar c) {
-		switch (c) {
-			// entry 0 is the .notdef
-			
-			case '\0':
-				return 1;
-
-			case '\r':
-				return 2;
-
-			case ' ': // space
-				return 3;
-
-			case '!':
-				return 4;
-
-			case '"':
-				return 5;
-
-			case '#':
-				return 6;
-
-			case '$':
-				return 7;
-
-			case '%':
-				return 8;
-
-			case '&':
-				return 9;
-
-			case '\'':
-				return 10;
-
-			case '(':
-				return 11;
-
-			case ')':
-				return 12;
-
-			case '*':
-				return 13;
-
-			case '+':
-				return 14;
-
-			case ',':
-				return 15;
-
-			case '-':
-				return 16;
-
-			case '.':
-				return 17;
-
-			case '/':
-				return 18;
-
-			case '0':
-				return 19;
-
-			case '1':
-				return 20;
-
-			case '2':
-				return 21;
-
-			case '3':
-				return 22;
-
-			case '4':
-				return 23;
-
-			case '5':
-				return 24;
-
-			case '6':
-				return 25;
-
-			case '7':
-				return 26;
-
-			case '8':
-				return 27;
-
-			case '9':
-				return 28;
-
-			case ':':
-				return 29;
-
-			case ';':
-				return 30;
-
-			case '<':
-				return 31;
-
-			case '=':
-				return 32;
-
-			case '>':
-				return 33;
-
-			case '?':
-				return 34;
-
-			case '@':
-				return 35;
-
-			case 'A':
-				return 36;
-
-			case 'B':
-				return 37;
-
-			case 'C':
-				return 38;
-
-			case 'D':
-				return 39;
-
-			case 'E':
-				return 40;
-
-			case 'F':
-				return 41;
-
-			case 'G':
-				return 42;
-
-			case 'H':
-				return 43;
-
-			case 'I':
-				return 44;
-
-			case 'J':
-				return 45;
-
-			case 'K':
-				return 46;
-
-			case 'L':
-				return 47;
-
-			case 'M':
-				return 48;
-
-			case 'N':
-				return 49;
-
-			case 'O':
-				return 50;
-
-			case 'P':
-				return 51;
-
-			case 'Q':
-				return 52;
-
-			case 'R':
-				return 53;
-
-			case 'S':
-				return 54;
-
-			case 'T':
-				return 55;
-
-			case 'U':
-				return 56;
-
-			case 'V':
-				return 57;
-
-			case 'W':
-				return 58;
-
-			case 'X':
-				return 59;
-
-			case 'Y':
-				return 60;
-
-			case 'Z':
-				return 61;
-
-			case '[':
-				return 62;
-
-			case '\\':
-				return 63;
-
-			case ']':
-				return 64;
-
-			case '^':
-				return 65;
-
-			case '_':
-				return 66;
-
-			case '`':
-				return 67;
-
-			case 'a':
-				return 68;
-
-			case 'b':
-				return 69;
-
-			case 'c':
-				return 70;
-
-			case 'd':
-				return 71;
-
-			case 'e':
-				return 72;
-
-			case 'f':
-				return 73;
-
-			case 'g':
-				return 74;
-
-			case 'h':
-				return 75;
-
-			case 'i':
-				return 76;
-
-			case 'j':
-				return 77;
-
-			case 'k':
-				return 78;
-
-			case 'l':
-				return 79;
-
-			case 'm':
-				return 80;
-
-			case 'n':
-				return 81;
-
-			case 'o':
-				return 82;
-
-			case 'p':
-				return 83;
-
-			case 'q':
-				return 84;
-
-			case 'r':
-				return 85;
-
-			case 's':
-				return 86;
-
-			case 't':
-				return 87;
-
-			case 'u':
-				return 88;
-
-			case 'v':
-				return 89;
-
-			case 'w':
-				return 90;
-
-			case 'x':
-				return 91;
-
-			case 'y':
-				return 92;
-
-			case 'z':
-				return 93;
-
-			case '{':
-				return 94;
-
-			case '|':
-				return 95;
-
-			case '}':
-				return 96;
-
-			case '~':
-				return 97;
-
-			case 'Ä':
-				return 98;
-
-			case 'Å':
-				return 99;
-
-			case 'Ç':
-				return 100;
-
-			case 'É':
-				return 101;
-
-			case 'Ñ':
-				return 102;
-
-			case 'Ö':
-				return 103;
-
-			case 'Ü':
-				return 104;
-
-			case 'á':
-				return 105;
-
-			case 'à':
-				return 106;
-
-			case 'â':
-				return 107;
-
-			case 'ä':
-				return 108;
-
-			case 'ã':
-				return 109;
-
-			case 'å':
-				return 110;
-
-			case 'ç':
-				return 111;
-
-			case 'é':
-				return 112;
-
-			case 'è':
-				return 113;
-
-			case 'ê':
-				return 114;
-
-			case 'ë':
-				return 115;
-
-			case 'í':
-				return 116;
-
-			case 'ì':
-				return 117;
-
-			case 'î':
-				return 118;
-
-			case 'ï':
-				return 119;
-
-			case 'ñ':
-				return 120;
-
-			case 'ó':
-				return 121;
-
-			case 'ò':
-				return 122;
-
-			case 'ô':
-				return 123;
-
-			case 'ö':
-				return 124;
-
-			case 'õ':
-				return 125;
-
-			case 'ú':
-				return 126;
-
-			case 'ù':
-				return 127;
-
-			case 'û':
-				return 128;
-
-			case 'ü':
-				return 129;
-
-			case '†':
-				return 130;
-
-			case '°':
-				return 131;
-
-			case '¢':
-				return 132;
-
-			case '£':
-				return 133;
-
-			case '§':
-				return 134;
-
-			case '•':
-				return 135;
-
-			case '¶':
-				return 136;
-
-			case 'ß':
-				return 137;
-
-			case '®':
-				return 138;
-
-			case '©':
-				return 139;
-
-			case '™':
-				return 140;
-
-			case '´':
-				return 141;
-
-			case '¨':
-				return 142;
-
-			case '≠':
-				return 143;
-
-			case 'Æ':
-				return 144;
-
-			case 'Ø':
-				return 145;
-
-			case '∞':
-				return 146;
-
-			case '±':
-				return 147;
-
-			case '≤':
-				return 148;
-
-			case '≥':
-				return 149;
-
-			case '¥':
-				return 150;
-
-			case 'µ':
-				return 151;
-
-			case '∂':
-				return 152;
-
-			case '∑':
-				return 153;
-
-			case '∏':
-				return 154;
-
-			case 'π':
-				return 155;
-
-			case '∫':
-				return 156;
-
-			case 'ª':
-				return 157;
-
-			case 'º':
-				return 158;
-
-			case 'Ω':
-				return 159;
-
-			case 'æ':
-				return 160;
-
-			case 'ø':
-				return 161;
-
-			case '¿':
-				return 162;
-
-			case '¡':
-				return 163;
-
-			case '¬':
-				return 164;
-
-			case '√':
-				return 165;
-
-			case 'ƒ':
-				return 166;
-
-			case '≈':
-				return 167;
-
-			case '∆':
-				return 168;
-
-			case '«':
-				return 169;
-
-			case '»':
-				return 170;
-
-			case '…':
-				return 171;
-
-			case ' ': // non breaking space
-				return 172;
-							
-			case 'À':
-				return 173;
-
-			case 'Ã':
-				return 174;
-
-			case 'Õ':
-				return 175;
-
-			case 'Œ':
-				return 176;
-
-			case 'œ':
-				return 177;
-
-			case '–':
-				return 178;
-
-			case '—':
-				return 179;
-
-			case '“':
-				return 180;
-
-			case '”':
-				return 181;
-
-			case '‘':
-				return 182;
-
-			case '’':
-				return 183;
-
-			case '÷':
-				return 184;
-
-			case '◊':
-				return 185;
-
-			case 'ÿ':
-				return 186;
-
-			case 'Ÿ':
-				return 187;
-
-			case '⁄':
-				return 188;
-
-			case '¤':
-				return 189;
-
-			case '‹':
-				return 190;
-
-			case '›':
-				return 191;
-
-			case 'fi':
-				return 192;
-
-			case 'fl':
-				return 193;
-
-			case '‡':
-				return 194;
-
-			case '·':
-				return 195;
-
-			case '‚':
-				return 196;
-
-			case '„':
-				return 197;
-
-			case '‰':
-				return 198;
-
-			case 'Â':
-				return 199;
-
-			case 'Ê':
-				return 200;
-
-			case 'Á':
-				return 201;
-
-			case 'Ë':
-				return 202;
-
-			case 'È':
-				return 203;
-
-			case 'Í':
-				return 204;
-
-			case 'Î':
-				return 205;
-
-			case 'Ï':
-				return 206;
-
-			case 'Ì':
-				return 207;
-
-			case 'Ó':
-				return 208;
-
-			case 'Ô':
-				return 209;
-				
-			// Machintosh apple goes here
-			// return 210;
-
-			case 'Ò':
-				return 211;
-
-			case 'Ú':
-				return 212;
-
-			case 'Û':
-				return 213;
-
-			case 'Ù':
-				return 214;
-
-			case 'ı':
-				return 215;
-
-			case 'ˆ':
-				return 216;
-
-			case '˜':
-				return 217;
-
-			case '¯':
-				return 218;
-
-			case '˘':
-				return 219;
-
-			case '˙':
-				return 220;
-
-			case '˚':
-				return 221;
-
-			case '¸':
-				return 222;
-
-			case '˝':
-				return 223;
-
-			case '˛':
-				return 224;
-
-			case 'ˇ':
-				return 225;
-
-			case 'Ł':
-				return 226;
-
-			case 'ł':
-				return 227;
-
-			case 'Š':
-				return 228;
-
-			case 'š':
-				return 229;
-
-			case 'Ž':
-				return 230;
-
-			case 'ž':
-				return 231;
-
-			case '¦':
-				return 232;
-
-			case 'Ð':
-				return 233;
-
-			case 'ð':
-				return 234;
-
-			case 'Ý':
-				return 235;
-
-			case 'ý':
-				return 236;
-
-			case 'Þ':
-				return 237;
-
-			case 'þ':
-				return 238;
-
-			case '−':
-				return 239;
-
-			case '×':
-				return 240;
-
-			case '¹':
-				return 241;
-				
-			case '²':
-				return 242;
-
-			case '³':
-				return 243;
-
-			case '½':
-				return 244;
-
-			case '¼':
-				return 245;
-
-			case '¾':
-				return 246;
-
-			case '₣':
-				return 247;
-
-			case 'Ğ':
-				return 248;
-
-			case 'ğ':
-				return 249;
-
-			case 'İ':
-				return 250;
-
-			case 'Ş':
-				return 251;
-
-			case 'ş':
-				return 252;
-
-			case 'Ć':
-				return 253;
-
-			case 'ć':
-				return 254;
-
-			case 'Č':
-				return 255;
-
-			case 'č':
-				return 256;
-
-			case 'đ':
-				return 257;
-		}
-		
-		return 0;
-	}
-	
-	public void process () throws Error {
-		FontData fd = new FontData ();
-		string n;
-		
-		fd.add_fixed (0x00020000); // Version
-		fd.add_fixed (0x00000000); // italicAngle
-		
-		fd.add_short (-2); // underlinePosition
-		fd.add_short (1); // underlineThickness
-
-		fd.add_ulong (0); // non zero for monospaced font
-		
-		// mem boundries may be omitted
-		fd.add_ulong (0); // min mem for type 42
-		fd.add_ulong (0); // max mem for type 42
-		
-		fd.add_ulong (0); // min mem for Type1
-		fd.add_ulong (0); // max mem for Type1
-
-		fd.add_ushort ((uint16) glyf_table.glyphs.length ());
-
-		// this part of the spec is so weird
-		
-		fd.add_ushort ((uint16) 0); // first index is .notdef
-		index.append (0);
-		
-		assert (names.length () == 0);
-		add_standard_names ();
-
-		int index;
-		Glyph g;
-		for (int i = 1; i < glyf_table.glyphs.length (); i++) {
-			g = (!) glyf_table.glyphs.nth (i).data;
-			index = get_standard_index (g.unichar_code);
-			
-			if (index != 0) {
-				fd.add_ushort ((uint16) index);  // use standard name
-			} else {
-				printd ("Adding non standard postscript name $(g.get_name ())\n");
-				
-				index = (int) names.length (); // use font specific name
-				fd.add_ushort ((uint16) index);
-				names.append (g.get_name ());
-			}
-			
-			this.index.append ((uint16) index);
-		}
-
-		for (int i = 258; i < names.length (); i++) {
-			n = (!) names.nth (i).data;
-			
-			if (n.length > 0xFF) {
-				warning ("too long name for glyph $n");
-			}
-						
-			fd.add ((uint8) n.length); // length of string
-			fd.add_str (n);
-		}		
-
-		fd.pad ();
-		
-		this.font_data = fd;
-	}
-
-}
-
-class GaspTable : Table {
-	
-	public GaspTable () {
-		id = "gasp";
-	}
-	
-	public override void parse (FontData dis) throws Error {
-	}
-	
-	public void process () throws GLib.Error {
-		FontData fd = new FontData ();
-
-		fd.add_ushort (0);
-		fd.add_ushort (0);
-
-		fd.pad ();
-	
-		this.font_data = fd;
-	}
-
-}
-
-class GdefTable : Table {
-	
-	public GdefTable () {
-		id = "GDEF";
-	}
-	
-	public override void parse (FontData dis) throws Error {
-	}
-	
-	public void process () throws GLib.Error {
-		FontData fd = new FontData ();
-
-		fd.add_ulong (0x00010002);
-		fd.add_ushort (0); // class def
-		fd.add_ushort (0); // attach list
-		fd.add_ushort (0); // ligature carret
-		fd.add_ushort (0); // mark attach
-		fd.add_ushort (0); // mark glyf
-		fd.add_ushort (0); // mark glyf set def
-		
-		fd.pad ();
-	
-		this.font_data = fd;
-	}
-
-}
-
-class CvtTable : Table {
-	
-	public CvtTable () {
-		id = "cvt ";
-	}
-	
-	public override void parse (FontData dis) throws Error {
-	}
-	
-	public void process () throws GLib.Error {
-		FontData fd = new FontData ();
-		
-		fd.add_ushort (0);
-		fd.pad ();
-	
-		this.font_data = fd;
-	}
-
-}
-
-class Kern : GLib.Object {
-	public uint16 left;
-	public uint16 right;
-	public int16 kerning;
-	
-	public Kern (uint16 l, uint16 r, int16 k) {
-		left = l;
-		right = r;
-		kerning = k;
-	}
-}
-
-class KernList : GLib.Object {
-	public List<Kern> kernings;
-	
-	public KernList () {
-		kernings = new List<Kern> ();
-	}
-}
-
-class KernTable : Table {
-	
-	public static const uint16 HORIZONTAL = 1;
-	public static const uint16 MINIMUM = 1 << 1;
-	public static const uint16 CROSS_STREAM = 1 << 2;
-	public static const uint16 OVERRIDE = 1 << 3;
-	public static const uint16 FORMAT = 1 << 8;
-	
-	GlyfTable glyf_table;
-	
-	List<Kern> kernings = new List<Kern> ();
-	
-	public int kerning_pairs = 0;
-	
-	public KernTable (GlyfTable gt) {
-		glyf_table = gt;
-		id = "kern";
-	}
-	
-	public KernList get_all_pairs (int gid) {
-		KernList kl = new KernList ();
-		
-		foreach (Kern k in kernings) {
-			if (k.left == gid) {
-				kl.kernings.append (k);
-			}
-		}
-		
-		return kl;
-	}
-	
-	public override void parse (FontData dis) throws GLib.Error {
-		uint16 version;
-		uint16 sub_tables;
-		
-		uint16 subtable_version;
-		uint16 subtable_length;
-		uint16 subtable_flags;
-
-		uint16 search_range;
-		uint16 entry_selector;
-		uint16 range_shift;
-		
-		uint16 n_pairs;
-			
-		dis.seek (offset);
-		
-		version = dis.read_ushort ();
-		warn_if_fail (version == 0);
-		sub_tables = dis.read_ushort ();
-		
-		for (uint16 i = 0; i < sub_tables; i++) {
-			subtable_version = dis.read_ushort ();			
-			subtable_length = dis.read_ushort ();			
-			subtable_flags = dis.read_ushort ();
-
-			n_pairs = dis.read_ushort ();
-			search_range = dis.read_ushort ();
-			entry_selector = dis.read_ushort ();
-			range_shift = dis.read_ushort ();
-						
-			// TODO: check more flags
-			if ((subtable_flags & HORIZONTAL) > 0 && (subtable_flags & CROSS_STREAM) == 0 && (subtable_flags & MINIMUM) == 0) {
-				parse_pairs (dis, n_pairs);
-			}
-		}
-	}
-	
-	public void parse_pairs (FontData dis, uint16 n_pairs) throws Error {
-		uint16 left;
-		uint16 right;
-		int16 kerning;
-		
-		for (int i = 0; i < n_pairs; i++) {
-			left = dis.read_ushort ();
-			right = dis.read_ushort ();
-			kerning = dis.read_short ();
-						
-			kernings.append (new Kern (left, right, kerning));
-		}		
-	}
-	
-	public void process () throws GLib.Error {
-		FontData fd = new FontData ();
-		uint16 n_pairs = 0;
-		
-		uint16 gid_left;
-		int gid_right;
-		
-		uint16 range_shift = 0;
-		uint16 entry_selector = 0;
-		uint16 search_range = 0;
-		
-		fd.add_ushort (0); // version 
-		fd.add_ushort (1); // n subtables
-
-		fd.add_ushort (0); // subtable version 
-
-		foreach (Glyph g in glyf_table.glyphs) {
-			foreach (Kerning k in g.kerning) {
-				n_pairs++;
-			}
-		}
-		
-		this.kerning_pairs = n_pairs;
-		
-		fd.add_ushort (6 * n_pairs + 14); // subtable length
-		fd.add_ushort (HORIZONTAL); // subtable flags
-
-		fd.add_ushort (n_pairs);
-		
-		search_range = 6 * largest_pow2 (n_pairs);
-		entry_selector = largest_pow2_exponent (n_pairs);
-		range_shift = 6 * n_pairs - search_range;
-		
-		fd.add_ushort (search_range);
-		fd.add_ushort (entry_selector);
-		fd.add_ushort (range_shift);
-
-		gid_left = 0;
-		foreach (Glyph g in glyf_table.glyphs) {
-			
-			foreach (Kerning k in g.kerning) {
-				gid_right = glyf_table.get_gid (k.glyph_right);
-				
-				if (gid_right == -1) {
-					warning ("right glyph not found in kerning table");
-				}
-				
-				fd.add_ushort (gid_left);
-				fd.add_ushort ((uint16)gid_right);
-				fd.add_short ((int16)k.val);
-			}
-			
-			gid_left++;
-		}
-
-		fd.pad ();
-		this.font_data = fd;
-	}
-
-}
-
-/** Table with list of tables sorted by table tag. */
-class DirectoryTable : Table {
-	
-	public CmapTable cmap_table;
-	public CvtTable  cvt_table;
-	public GaspTable gasp_table;
-	public GdefTable gdef_table;
-	public GlyfTable glyf_table;
-	public HeadTable head_table;
-	public HheaTable hhea_table;
-	public HmtxTable hmtx_table;
-	public KernTable kern_table;
-	public MaxpTable maxp_table;
-	public NameTable name_table;
-	public Os2Table  os_2_table;
-	public PostTable post_table;
-	public LocaTable loca_table;
-	
-	public OffsetTable offset_table;
-	
-	List<Table> tables;
-	
-	public DirectoryTable () {
-		offset_table = new OffsetTable (this);
-		
-		loca_table = new LocaTable ();
-		gasp_table = new GaspTable ();
-		gdef_table = new GdefTable ();
-		glyf_table = new GlyfTable (loca_table);
-		cmap_table = new CmapTable (glyf_table);
-		cvt_table  = new CvtTable ();
-		head_table = new HeadTable (glyf_table);
-		hmtx_table = new HmtxTable (head_table, glyf_table);
-		hhea_table = new HheaTable (glyf_table, head_table, hmtx_table);
-		kern_table = new KernTable (glyf_table);
-		maxp_table = new MaxpTable (glyf_table);
-		name_table = new NameTable ();
-		os_2_table = new Os2Table (); 
-		post_table = new PostTable (glyf_table);
-		
-		id = "Directory table";
-	}
-
-	public void process () throws GLib.Error {
-		// generate font data
-		glyf_table.process ();
-		gasp_table.process ();
-		gdef_table.process ();
-		cmap_table.process (glyf_table);
-		cvt_table.process ();
-		hmtx_table.process ();
-		hhea_table.process ();
-		maxp_table.process ();
-		name_table.process ();
-		os_2_table.process (glyf_table);
-		head_table.process ();
-		loca_table.process (glyf_table, head_table);
-		post_table.process ();
-		kern_table.process ();
-		
-		offset_table.process ();
-		process_directory (); // this table
-	}
-
-	public unowned List<Table> get_tables () {
-		if (tables.length () == 0) {
-			tables.append (offset_table);
-			tables.append (this);
-			
-			// tables.append (gdef_table); // invalid table
-			tables.append (os_2_table);
-			tables.append (cmap_table);
-			// tables.append (cvt_table);
-			// tables.append (gasp_table);
-			tables.append (glyf_table);
-			tables.append (head_table);
-			tables.append (hhea_table);
-			tables.append (hmtx_table);
-			
-			if (kern_table.kerning_pairs > 0) {
-				tables.append (kern_table);
-			}
-			
-			tables.append (loca_table);
-			tables.append (maxp_table);
-			tables.append (name_table);
-			tables.append (post_table);
-		}
-
-		return tables;
-	}
-
-	public void set_offset_table (OffsetTable ot) {
-		offset_table = ot;
-	}
-	
-	public new void parse (FontData dis, File file, OpenFontFormatReader reader_callback) throws Error {
-		StringBuilder tag = new StringBuilder ();
-		uint32 checksum;
-		uint32 offset;
-		uint32 length;
-		
-		return_if_fail (offset_table.num_tables > 0);
-		
-		for (uint i = 0; i < offset_table.num_tables; i++) {
-			tag.erase ();
-			
-			tag.append_unichar ((unichar) dis.read_byte ());
-			tag.append_unichar ((unichar) dis.read_byte ());
-			tag.append_unichar ((unichar) dis.read_byte ());
-			tag.append_unichar ((unichar) dis.read_byte ());
-			
-			checksum = dis.read_ulong ();
-			offset = dis.read_ulong ();
-			length = dis.read_ulong ();
-			
-			printd (@"$(tag.str) \toffset: $offset \tlength: $length \tchecksum: $checksum.\n");
-			
-			if (tag.str == "cvt") {
-				cvt_table.id = tag.str;
-				cvt_table.checksum = checksum;
-				cvt_table.offset = offset;
-				cvt_table.length = length;
-			} else if (tag.str == "hmtx") {
-				hmtx_table.id = tag.str;
-				hmtx_table.checksum = checksum;
-				hmtx_table.offset = offset;
-				hmtx_table.length = length;
-			} else if (tag.str == "hhea") {
-				hhea_table.id = tag.str;
-				hhea_table.checksum = checksum;
-				hhea_table.offset = offset;
-				hhea_table.length = length;	
-			} else if (tag.str == "loca") {
-				loca_table.id = tag.str;
-				loca_table.checksum = checksum;
-				loca_table.offset = offset;
-				loca_table.length = length;	
-			} else if (tag.str == "cmap") {
-				cmap_table.id = tag.str;
-				cmap_table.checksum = checksum;
-				cmap_table.offset = offset;
-				cmap_table.length = length;
-			} else if (tag.str == "maxp") {
-				maxp_table.id = tag.str;
-				maxp_table.checksum = checksum;
-				maxp_table.offset = offset;
-				maxp_table.length = length;
-			} else if (tag.str == "gasp") {
-				gasp_table.id = tag.str;
-				gasp_table.checksum = checksum;
-				gasp_table.offset = offset;
-				gasp_table.length = length;
-			} else if (tag.str == "gdef") {
-				gdef_table.id = tag.str;
-				gdef_table.checksum = checksum;
-				gdef_table.offset = offset;
-				gdef_table.length = length;
-			} else if (tag.str == "glyf") {
-				glyf_table.id = tag.str;
-				glyf_table.checksum = checksum;
-				glyf_table.offset = offset;
-				glyf_table.length = length;
-			} else if (tag.str == "head") {
-				head_table.id = tag.str;
-				head_table.checksum = checksum;
-				head_table.offset = offset;
-				head_table.length = length;
-			} else if (tag.str == "name") {
-				name_table.id = tag.str;
-				name_table.checksum = checksum;
-				name_table.offset = offset;
-				name_table.length = length;
-			} else if (tag.str == "OS/2") {
-				os_2_table.id = tag.str;
-				os_2_table.checksum = checksum;
-				os_2_table.offset = offset;
-				os_2_table.length = length;
-			} else if (tag.str == "post") {
-				post_table.id = tag.str;
-				post_table.checksum = checksum;
-				post_table.offset = offset;
-				post_table.length = length;
-			} else if (tag.str == "kern") {
-				kern_table.id = tag.str;
-				kern_table.checksum = checksum;
-				kern_table.offset = offset;
-				kern_table.length = length;
-			}
-		}
-
-		head_table.parse (dis);
-		
-		if (!validate_tables (dis, file)) {
-			warning ("Missing required table or bad checksum.");
-		}
-		
-		hhea_table.parse (dis);
-		reader_callback.set_limits ();
-		
-		name_table.parse (dis);
-		post_table.parse (dis);
-		os_2_table.parse (dis);
-		maxp_table.parse (dis);
-		loca_table.parse (dis, head_table, maxp_table);
-		hmtx_table.parse (dis, hhea_table, loca_table);
-		cmap_table.parse (dis);
-		
-		if (kern_table.has_data ()) {
-			kern_table.parse (dis);
-		}
-		
-		glyf_table.parse (dis, cmap_table, loca_table, hmtx_table, head_table, post_table, kern_table);
-		
-		if (kern_table.has_data ()) {
-			gasp_table.parse (dis);
-		}
-		
-		if (kern_table.has_data ()) {
-			cvt_table.parse (dis);
-		}
-	}
-	
-	public bool validate_tables (FontData dis, File file) {
-		bool valid = true;
-		
-		try {
-			dis.seek (0);
-			
-			if (!validate_checksum_for_entire_font (dis, file)) {
-				warning ("file has invalid checksum");
-			} else {
-				printd ("Font file has valid checksum.\n");
-			}
-
-			// Skip validation of head table for now it should be realy simple but seems to
-			// be broken in some funny way.
-
-			if (!glyf_table.validate (dis)) {
-				warning ("glyf_table has invalid checksum");
-				valid = false;
-			}
-			
-			if (!maxp_table.validate (dis)) {
-				warning ("maxp_table has is invalid checksum");
-				valid = false;
-			}
-			
-			if (!loca_table.validate (dis)) {
-				warning ("loca_table has invalid checksum");
-				valid = false;
-			}
-			
-			if (!cmap_table.validate (dis)) {
-				warning ("cmap_table has invalid checksum");
-				valid = false;
-			}
-			
-			if (!hhea_table.validate (dis)) {
-				warning ("hhea_table has invalid checksum");
-				valid = false;
-			}
-			
-			if (!hmtx_table.validate (dis)) {
-				warning ("hmtx_table has invalid checksum");
-				valid = false;
-			}
-
-			if (!name_table.validate (dis)) {
-				warning ("name_table has invalid checksum");
-				valid = false;
-			}
-
-			if (!os_2_table.validate (dis)) {
-				warning ("os_2_table has invalid checksum");
-				valid = false;
-			}
-
-			if (!post_table.validate (dis)) {
-				warning ("post_table has invalid checksum");
-				valid = false;
-			}
-			
-			if (kern_table.has_data () && !kern_table.validate (dis)) {
-				warning ("kern_table has invalid checksum");
-				valid = false;
-			}
-			
-		} catch (GLib.Error e) {
-			warning (e.message);
-			valid = false;
-		}
-		
-		return valid;
-	}
-	
-	bool validate_checksum_for_entire_font (FontData dis, File f) throws GLib.Error {
-		uint p = head_table.offset + head_table.get_checksum_position ();
-		uint32 checksum_font, checksum_head;
-
-		checksum_head = head_table.get_font_checksum ();
-		
-		dis.seek (0);
-		
-		// zero out checksum entry in head table before validating it
-		dis.write_at (p + 0, 0);
-		dis.write_at (p + 1, 0);
-		dis.write_at (p + 2, 0);
-		dis.write_at (p + 3, 0);
-		
-		checksum_font = (uint32) (0xB1B0AFBA - dis.check_sum ());
-
-		if (checksum_font != checksum_head) {
-			warning (@"Fontfile checksum in head table does not match calculated checksum. checksum_font: $checksum_font checksum_head: $checksum_head");
-			return false;
-		}
-		
-		return true;
-	}
-	
-	public long get_font_file_size () {
-		long length = 0;
-		
-		foreach (Table t in tables) {
-			length += t.get_font_data ().length_with_padding ();
-		}
-		
-		return length;
-	}
-	
-	public void process_directory () throws GLib.Error {
-		create_directory (); // create directory without offsets to calculate length of offset table and checksum for entre file
-		create_directory (); // generate a valid directory
-	}
-
-	// Check sum adjustment for the entire font
-	public uint32 get_font_file_checksum () {
-		uint32 check_sum = 0;
-		foreach (Table t in tables) {
-			t.get_font_data ().continous_check_sum (ref check_sum);
-		}
-		return check_sum;
-	}
-
-	public void create_directory () throws GLib.Error {
-		FontData fd;
-	
-		uint32 table_offset = 0;
-		uint32 table_length = 0;
-		
-		uint32 check_sum = 0;
-		
-		fd = new FontData ();
-
-		return_val_if_fail (offset_table.num_tables > 0, fd);
-		
-		table_offset += offset_table.get_font_data ().length_with_padding ();
-		
-		if (this.font_data != null) {
-			table_offset += this.get_font_data ().length_with_padding ();
-		}
-
-		head_table.set_check_sum_adjustment (0); // Set this to zero, calculate checksums and update the value
-		head_table.process ();
-		
-		// write the directory 
-		foreach (Table t in tables) {
-						
-			if (t is DirectoryTable || t is OffsetTable) {
-				continue;
-			}
-			
-			printd (@"c $(t.id)  offset: $(table_offset)  len with pad  $(t.get_font_data ().length_with_padding ())\n");
-
-			table_length = t.get_font_data ().length (); // without padding
-			
-			fd.add_tag (t.get_id ()); // name of table
-			fd.add_u32 (t.get_font_data ().check_sum ());
-			fd.add_u32 (table_offset);
-			fd.add_u32 (table_length);
-			
-			table_offset += t.get_font_data ().length_with_padding ();
-		}
-
-		// padding
-		fd.pad ();
-						
-		this.font_data = fd;
-
-		check_sum = get_font_file_checksum ();
-		head_table.set_check_sum_adjustment ((uint32)(0xB1B0AFBA - check_sum));
-		head_table.process (); // update the value		
-	}
-	
-}
-
-/** Largest power of two less than max. */
-internal static uint16 largest_pow2 (uint16 max) {
-	uint16 x = 1;
-	uint16 l = 0;
-	
-	while (x <= max) {
-		l = x;
-		x = x << 1;
-	}
-	
-	return l;
-}
-
-internal static uint16 largest_pow2_exponent (uint16 max) {
-	uint16 exp = 0;
-	uint16 l = 0;
-	uint16 x = 0;
-	
-	while (x <= max) {
-		l = exp;
-		exp++;
-		x = 1 << exp;
-	}	
-	
-	return l;
-}
-
-void printd (string s) {
-	// print (s);
-}
-
-}
diff --git a/src/OverView.vala b/src/OverView.valadeleted file mode 100644index e8915c2..0000000--- a/src/OverView.vala+++ /dev/null@@ -1,1069 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--/** A display with all glyphs present in this font. */-public class OverView : FontDisplay {-	Allocation allocation;-	-	int rows = 0;-	int items_per_row = 0;-	uint32 first_character = 0;-	int64 first_visible = 0;-	uint32 selected = 0;-	-	double view_offset_y = 0;-	double view_offset_x = 0;-	-	int nail_height = 0;-	int nail_width = 0;--	double nail_zoom_height = 0;-	double nail_zoom_width = 0;-	-	public signal void open_glyph_signal (string c);--	GlyphRange glyph_range;--	List<GlyphCollection> visible_characters = new List<GlyphCollection> ();--	List<unichar> zoom_list = new List<unichar> ();-	int zoom_list_index = 0;--	List<GlyphCollection> deleted_glyphs = new List<GlyphCollection> ();--	Scrollbar scrollbar;-	-	bool all_avail = true;-	-	public OverView (GlyphRange? range = null) {-		GlyphRange gr;-		-		if (range == null) {-			gr = new GlyphRange ();-			set_glyph_range (gr);-		}-		-		reset_zoom ();--		this.open_glyph_signal.connect ((t, s) => {-			TabBar tabs = MainWindow.get_tab_bar ();-			Toolbox tools = MainWindow.get_toolbox ();-			bool selected = tabs.select_char (s);-			unichar new_char = s.get_char (0);-			Font f = Supplement.get_current_font ();-							-			if (!selected) {-				GlyphCollection? fg = f.get_glyph_collection (s);-				Glyph g = (fg == null) ? new Glyph (s, new_char) : ((!) fg).get_current ();-				ZoomTool z = (ZoomTool) tools.get_tool ("zoom_tool");-				-				stdout.printf (@"Open '%s' %u (%s)\n", s, new_char, Font.to_hex (new_char));-				-				z.store_current_view ();-				f.add_glyph (g);-				tabs.add_tab (g);-				-				MainWindow.get_glyph_canvas ().set_current_glyph (g);-				-				g.close_path ();-				g.default_zoom ();-				z.store_current_view ();-			}-		});-		-		scrollbar = new Scrollbar ();-		scrollbar.signal_scroll.connect ((delta, delta_last, absolute) => {-			int64 adjustement = (int64) (-delta_last * get_height ());-			int64 pixels = (int64) (-5.0 * delta_last * nail_height);-			-			if (absolute <= 0.002) {-				first_character = 0;-				first_visible = 0;-				selected = 0;-				scroll_top ();-			} else if (absolute >= 1) {-				scroll_bottom ();-			} else {-				scroll_to (absolute);-			}		-			-			redraw_area (0, 0, allocation.width, allocation.height);-		});-		-		update_scrollbar ();-	}-	-	void scroll_bottom () {-		Font f;-		-		if (all_avail) {-			f = Supplement.get_current_font ();-			scroll_to_position (f.length () - items_per_row * (rows - 1));-		} else {-			scroll_to_position (glyph_range.length () - items_per_row * (rows - 1));-		}-	}-	-	public double get_height () {-		double l;-		Font f;-		-		if (rows == 0) {-			return 0;-		}-				-		if (all_avail) {-			f = Supplement.get_current_font ();-			l = f.length ();-		} else {-			l = glyph_range.length ();-		}-				-		return 2.0 * nail_height * (l / rows);-	}--	public bool selected_char_is_visible () {-		return first_visible <= selected <= first_visible + items_per_row * rows;-	}--	public override void scroll_wheel_up (double x, double y) {-		key_up ();-		close_menus ();-		redraw_area (0, 0, allocation.width, allocation.height);-	}-	-	public override void scroll_wheel_down (double x, double y) {-		key_down ();-		close_menus ();-		redraw_area (0, 0, allocation.width, allocation.height);-	}-	-	public override void selected_canvas () {-		redraw_area (0, 0, allocation.width, allocation.height);-		KeyBindings.singleton.set_require_modifier (true);-	}-	-	public override void zoom_min () {-		int minw = 26 * 2;-		int minh = 46 * 2;-		-		if (zoom_value_in_range (minw, minh)) {-			nail_width = minw;-			nail_height = minh;-				-			nail_zoom_height = minw; -			nail_zoom_width = minh;		-		-			adjust_scroll ();-			redraw_area (0, 0, allocation.width, allocation.height);-		} else {-			warn_if_reached ();-		}	-	}-	-	public override void reset_zoom () {-		nail_height = 210;-		nail_width = 150;-		-		nail_zoom_height = nail_height; -		nail_zoom_width = nail_width;-		-		adjust_scroll ();-		redraw_area (0, 0, allocation.width, allocation.height);-	}--	public override void zoom_max () {-		if (zoom_value_in_range (allocation.width, allocation.height)) {-			nail_width = allocation.width;-			nail_height = allocation.height;-				-			nail_zoom_height = allocation.height; -			nail_zoom_width = allocation.width;		-		-			adjust_scroll ();-			redraw_area (0, 0, allocation.width, allocation.height);-		} else {-			warn_if_reached ();-		}-	}-	-	public override void zoom_in () {-		double w = (1.1 * nail_width);-		double h = (1.1 * nail_height);-		int nw = (int) w;-		int nh = (int) h;-		-		if (zoom_value_in_range (nw, nh)) {-			nail_width = nw;-			nail_height = nh;-			-			nail_zoom_height = h; -			nail_zoom_width = w;-			-			adjust_scroll ();-			redraw_area (0, 0, allocation.width, allocation.height);-		}-	}-	-	public override void zoom_out () {-		double w = (nail_width / 1.1);-		double h = (nail_height / 1.1);-		int nw = (int) w;-		int nh = (int) h;-		-		if (zoom_value_in_range (nw, nh)) {-			nail_width = nw;-			nail_height = nh;--			nail_zoom_height = h; -			nail_zoom_width = w;-			-			adjust_scroll ();		-			redraw_area (0, 0, allocation.width, allocation.height);-		}-	}-	-	private bool zoom_value_in_range (int w, int h) {-		return (25 < w <= allocation.width && 45 < h <= allocation.height);-	}--	public override void store_current_view () {-		if (zoom_list_index + 1 < zoom_list.length ()) {-			unowned List<unichar> n = zoom_list.nth (zoom_list_index);-			while (n != zoom_list.last ()) zoom_list.delete_link (zoom_list.last ());-		}-		-		zoom_list.append (selected);-		zoom_list_index = (int) zoom_list.length () - 1;-		-		if (zoom_list.length () > 50) zoom_list.delete_link (zoom_list.first ());-	}-	-	public override void restore_last_view () -		requires (zoom_list.length () > 0)-	{		-		if (zoom_list_index - 1 < 0 || zoom_list.length () == 0)-			return;-		-		zoom_list_index--;-	}--	public override void next_view () -		requires (zoom_list.length () > 0)-	{-		if (zoom_list_index + 1 >= zoom_list.length ())-			return;-		-		zoom_list_index++;-	}--	-	public override string get_name () {-		return "Overview";-	}-	-	public void display_all_available_glyphs () {-		all_avail = true;-		-		first_character = 0;-		first_visible = 0;-		selected = 0;-	}-	-	private unowned List<GlyphCollection> get_visible_glyphs () {-		return visible_characters;-	}-	-	public void draw_caption (int row, double width, Context cr, double y, uint64 index_begin) {-		string character_string;-		double left_margin, x, caption_y; // for glyph caption-		uint64 index = index_begin;-		int i = row * items_per_row;-		Font f = Supplement.get_current_font ();-		-		cr.save ();-		cr.set_line_width (1);-		-		x = 0;-		-		cr.set_font_size (14);--		left_margin = 0;-			-		for (int j = 0; j < items_per_row; j++) {-			-			if (all_avail) {-				if (! (0 <= index < f.length ())) {-					break;-				}-				-				character_string = ((!) f.get_glyph_indice ((uint32) index)).get_name ();-			} else {-				if (!(0 <= index < glyph_range.get_length ())) {-					break;-				}-				-				character_string = glyph_range.get_char ((uint32) index);-			}-			-			cr.save ();-			draw_thumbnail (cr, character_string, x, y);-			cr.restore ();--			cr.save ();-						-			if (index == selected) {-				cr.set_source_rgba (142/255.0, 158/255.0, 190/255.0, 1);-			} else {-				cr.set_source_rgba (164/255.0, 185/255.0, 215/255.0, 1);-			}-			-			cr.set_line_join (LineJoin.ROUND);-			cr.set_line_width(12);-			-			cr.rectangle (x, y + nail_height - 20, nail_width - 30, 10);			-			cr.stroke ();-			cr.restore ();-			-			draw_menu (cr, character_string, x, y);-			-			caption_y = y + 10 + nail_height - 20;-			-			cr.move_to (x, caption_y);-			cr.show_text (character_string);-			-			x += nail_width;-			index++;-			i++;-		}-		-		cr.stroke();-	}-		-	private void draw_menu (Context cr, string name, double x, double y) {-		GlyphCollection? gl = Supplement.get_current_font ().get_glyph_collection (name);-		GlyphCollection g;-		DropMenu menu;-		-		if (gl == null) {-			return;-		}-		-		g = (!) gl;-		-		menu = g.get_version_list ();-		menu.set_position (x + nail_width - 40, y + nail_height - 21);-		menu.draw_icon (cr);-		menu.draw_menu (cr);-		-		visible_characters.append (g);-	}-		-	private bool draw_thumbnail (Context cr, string name, double x, double y) {-		Glyph? gl = Supplement.get_current_font ().get_glyph (name);-		Glyph g;-		-		double gx, gy;-		double x1, x2, y1, y2;-		double scale = nail_zoom_width / 150.0;-		double w, h;--		w = nail_width  - 19;-		h = nail_height - 22;-		-		scale = nail_zoom_height / (210.0 - 15.0);-		-		if (gl == null) {-			return false;-		}-		-		g = (!) gl;-		g.boundries (out x1, out y1, out x2, out y2);--		gx = 0;-		gy = h / scale - 40;-		-		Surface s = new Surface.similar (cr.get_target (), Content.COLOR_ALPHA, (int) w, (int) h);-		Context c = new Context (s);-/*-		c.set_source_rgb (200/255.0, 200/255.0, 229/255.0);-		c.rectangle (0, 0, w, h);	-		c.fill ();-*/-		c.scale (scale, scale);				-		Svg.draw_svg_path (c, g.get_svg_data (), gx, gy, 1.0);-		-		cr.save ();-		cr.set_source_surface (s, x - 6, y);-		cr.paint ();-		cr.restore ();--		return true;-	}--	// 150 * 190 px-	public override void draw (Allocation allocation, Context cr) {-		double width;-		double y;-		uint64 t;-		int i;-		int n_items;-		-		while (visible_characters.length () > 9) {-			visible_characters.remove_link (visible_characters.first ());-		}--		this.allocation = allocation;-		-		cr.save ();-		cr.set_source_rgba (1, 1, 1, 1);-		cr.rectangle (0, 0, allocation.width, allocation.height + nail_height);-		cr.fill ();-		cr.restore ();-		-		if (Supplement.experimental) {-			scrollbar.draw (cr, allocation);-		}-		-		n_items = ((allocation.width - 40) / nail_width);-		rows = (allocation.height / nail_height);--		if (items_per_row != n_items) { 	-			if (items_per_row == 0) {-				items_per_row = n_items;-				first_visible = first_character;-			-				key_down ();-				key_up ();-			}	else {-				items_per_row = n_items;-				first_visible = first_character;-			}-		}--		width = nail_width * items_per_row;-		view_offset_x = (allocation.width - width) / 2.0;-		-		cr.translate(view_offset_x, view_offset_y);-	-		y = 0;-		t = first_visible;-		i = 0;-		for (int j = 0; j < rows + 1; j++) {-			draw_caption (i, width, cr, y, t);--			t += items_per_row;-			y += nail_height;-			i++;-		}-		-		draw_menus (allocation, cr);-	}--	private void draw_menus (Allocation allocation, Context cr) {-	}--	private void close_menus () {-	}-	-	public void scroll_rows (int row_adjustment) {-		for (int i = 0; i < row_adjustment; i++) {-			scroll (-nail_height);-		}-		-		for (int i = 0; i > row_adjustment; i--) {-			scroll (nail_height);-		}-	}-	-	public void scroll_adjustment (double pixel_adjustment) {-		double l;-		Font f;-				-		if (all_avail) {-			f = Supplement.get_current_font ();-			l = f.length ();-		} else {-			l = glyph_range.length ();-		}-		-		if (first_visible <= 0) {-			return;-		}--		if (first_visible + rows * items_per_row >= l) {-			return;-		}-		-		scroll ((int64) pixel_adjustment);-	}-	-	void default_position () {-		scroll_top ();-		scroll_rows (1);-	}-	-	void scroll_to_position (int64 r) {-		int64 l;-		Font f;-		-		if (all_avail) {-			f = Supplement.get_current_font ();-			l = f.length ();-		} else {-			l = glyph_range.length ();-		}-		-		// bottom-		if (r > l - items_per_row * rows) {-			return;-		}-		-		if (r < 0) {-			scroll_top ();-			return;-		}-		-		default_position ();--		first_visible = r;-		selected = (uint32) r;-		-		adjust_scroll ();-	}-	-	public void scroll_to (double percent) {-		int64 r;-		double l;-		Font f;-		-		if (all_avail) {-			f = Supplement.get_current_font ();-			l = f.length ();-		} else {-			l = glyph_range.length ();-		}-		-		l /= rows;-		r = (int64) (percent * l * items_per_row);-		scroll_to_position (r);-	}-		-	private void scroll (int64 pixel_adjustment) {--		if (first_visible < 0 && pixel_adjustment < 0) {-			scroll_top ();-			return;-		}-				-		view_offset_y += pixel_adjustment;-		-		if (view_offset_y >= 0) {-			while (view_offset_y > nail_height) {			-				view_offset_y -= nail_height;-				first_visible -= items_per_row;-			}--			first_visible -= items_per_row;-			view_offset_y -= nail_height;-		} else if (view_offset_y < -nail_height) {-			view_offset_y = 0;-			first_visible += items_per_row;-		}-		-	}-	-	public void scroll_top () {-		first_visible = first_character;-		view_offset_y = 0;-	}-	-	private int selected_offset_y (unichar item) {-		int64 di;-		int64 a, b;-		if (unlikely (items_per_row == 0)) return 0;-		-		a = (int64) item;-		b = (int64) first_visible;-		di = (a - b);-		-		return (int) (view_offset_y + ((di / items_per_row) * nail_height));-	}--	/** Make selected item fully visible if it partly off screen. */-	private void adjust_scroll ()  {-		int off =  selected_offset_y (selected);--		if (off < 0)  {-			close_menus ();-			scroll (-1 * off);-		} else if (off > allocation.height - nail_height) {-			scroll ((allocation.height - nail_height) - off);-		}-	}--	public void key_down () {-		double l;-		Font f;-				-		if (all_avail) {-			f = Supplement.get_current_font ();-			l = f.length ();-		} else {-			l = glyph_range.length ();-		}-		-		update_scrollbar ();-		-		if (l == 0) return;-	-		if (at_bottom ()) {-			int len = (int) l;-			int s = (int) selected;-			-			if (len - items_per_row >= s) {-				selected += items_per_row; -			} else if (s < len) {-				selected++;-			}-			-			if (selected >= len) {-				selected = len - 1;-			}-			-			adjust_scroll ();-			-			return;-		}-		-		selected += items_per_row;-		-		if (selected > rows * items_per_row + first_visible) {-			scroll (-nail_height);-		}-		-		adjust_scroll ();-	}--	public void key_up () {-		update_scrollbar ();--		if (selected < first_character) {		-			warn_if_reached ();-		}-		-		if (selected < first_character + items_per_row) {-			scroll_top ();-			return;-		}-		-		if (selected - items_per_row >= first_character) {-			selected -= items_per_row;-			if (selected < first_visible) {-				scroll (-nail_height);-			}-		} -		-		if (first_visible < first_character) {-			selected = first_character;-		}-		-		adjust_scroll ();--		if (first_visible < first_character) {-			scroll_top ();-		}-	}	-	-	public void open_current_glyph () {-		key_press (Key.ENTER);-	}--	public void select_next_glyph () {-		key_press (Key.RIGHT);-	}--	public void key_right () {		-		uint len;-		Font f;-		-		if (all_avail) {-			f = Supplement.get_current_font ();-			len = f.length ();-		} else {-			len = glyph_range.length ();-		}-		-		if (len == 0) return;-		-		selected++;-		-		if (selected >= len) {-			selected = len - 1;-		}--		if (selected_offset_y (selected) > allocation.height - nail_height) {-			selected -= items_per_row;-			key_down ();-		}-	}-	-	public void key_left () {-		if (selected > first_character) {-			selected--;					-			if (selected_offset_y (selected) < 0) {-				selected -= items_per_row;-				key_up ();-			}-		}-	}-	-	public string get_selected_char () {-		Font f;-		Glyph? g;-		-		if (all_avail) {-			f = Supplement.get_current_font ();-			g = f.get_glyph_indice (selected);-			return_val_if_fail (g != null, "".dup ());-			return ((!) g).get_name ();-		}-		-		return glyph_range.get_char (selected);-	}-	-	public override void key_press (uint keyval) {-		redraw_area (0, 0, allocation.width, allocation.height);-		close_menus ();--		if (KeyBindings.modifier == CTRL) {-			return;-		}--		switch (keyval) {-			case Key.ENTER:-				open_glyph_signal (get_selected_char ());-				return;-			-			case Key.UP:-				key_up ();-				return;-				-			case Key.RIGHT:-				key_right ();-				return;-				-			case Key.LEFT:-				key_left ();-				return;-				-			case Key.DOWN:-				key_down ();-				return;-				-			case Key.PG_UP:-				for (int i = 0; i++ < rows; i++) {-					key_up ();-				}-				return;-				-			case Key.PG_DOWN:-				for (int i = 0; i++ < rows; i++) {-					key_down ();-				}-				return;-				-			case Key.DEL:-				delete_selected_glyph ();-				return;-		}--		scroll_to_char (keyval);-	}--	public void delete_selected_glyph () {-		delete_glyph (glyph_range.get_char (selected));-	}-		-	public void delete_glyph (string g) {-		GlyphCollection? gc;-		Font f = Supplement.get_current_font ();-		-		gc = f.get_glyph_collection (g);--		if (gc != null) {-			deleted_glyphs.append ((!) gc);-		}--		f.delete_glyph (g);-		MainWindow.get_tab_bar ().close_by_name (g);-		set_glyph_range (glyph_range);-		f.touch ();-	}-	-	public override void undo () {-		Font f = Supplement.get_current_font ();-		-		if (deleted_glyphs.length () == 0) {-			return;-		}-			-		f.add_glyph_collection (deleted_glyphs.last ().data);-		deleted_glyphs.remove_link (deleted_glyphs.last ());-	}-	-	public void scroll_to_char (unichar c) {-		GlyphRange gr = glyph_range;-		GlyphRange full = new GlyphRange ();-		-		if (is_modifier_key (c)) {-			return;-		}-		-		full.use_full_unicode_range ();-		-		set_glyph_range (full);-		selected = c;-		set_glyph_range (gr);-	}-		-	public override void motion_notify (double x, double y) {-		if (x > allocation.x - 10) {-			scrollbar.motion_notify ((int) x, (int) y);-		}-	}-	-	public override void button_release (int button, double ex, double ey) {-		if (ex > allocation.x - 10) {-			scrollbar.button_release (button, ex, ey);-		}-	}-	-	private void selection_click (uint button, double ex, double ey) {-		double x = view_offset_x;-		double y = view_offset_y;--		unichar current = selected;--		// click in margin-		int n_items = (allocation.width - 40 / nail_width);-		double m = (allocation.width - 40 - (n_items * nail_width)) / 2.0;-		-		bool menu_action = false;-		-		Font f;-		uint len;--		redraw_area (0, 0, allocation.width, allocation.height);--		if (all_avail) {-			f = Supplement.get_current_font ();-			len = f.length ();-		} else {-			len = glyph_range.length ();-		}-		-		foreach (GlyphCollection g in get_visible_glyphs ()) {-			bool a;--			a = g.get_version_list ().menu_item_action (ex - view_offset_x, ey - view_offset_y); // select one item on the menu-			-			if (a) {-				return;-			}-						-			a = g.get_version_list ().menu_icon_action (ex - view_offset_x, ey - view_offset_y); // open menu-			-			if (a) {-				menu_action = true;-			}-		}-		-		if (ex < m) {-			ex = m + 1;-		}-		-		if (ex > allocation.width - m) {-			ex = allocation.width - m - 1;-		}-		-		if (ey < 5) {-			ey = 5;-		}-		-		if (ey > allocation.height - 5) {-			ey = allocation.height - 5;-		}-			-		// empty set-		if (len == 0) return;--		// scroll to the selected glyph-		selected = (uint32) first_visible;--		while (y < ey < allocation.height) {-			selected += items_per_row;-			y += nail_height;-		}-		selected -= items_per_row;--		while (x < ex < allocation.width) {-			selected++;-			x += nail_width;-		}-		-		selected--;-	-		if (at_bottom () && selected >= len) {-			selected = len - 1;-		}-		-		adjust_scroll ();-		-		if (current == selected && !menu_action) {-			open_glyph_signal (get_selected_char ());-		}-		-		adjust_scroll ();-		adjust_scroll ();-	}-	-	public override void button_press (uint button, double x, double y) {-		if (x > allocation.width - 10) {-			if (Supplement.experimental) {-				scrollbar.button_press (button, x, y);-			}-		} else {-			selection_click (button, x, y);-		}-	}--	/** Returns true if overview shows the last character. */-	private bool at_bottom () {-		Font f;-		double t = rows * items_per_row + items_per_row + first_visible;-		-		if (all_avail) {-			f = Supplement.get_current_font ();-			return t >= f.length ();-		}-		-		return t >= glyph_range.length ();-	}--	public void set_glyph_range (GlyphRange range) {-		GlyphRange? current = glyph_range;-		string c = "";-		bool done = false;-		uint32 i;-		uint32 len = 0;-		-		if (current != null) {-			c = glyph_range.get_char (selected);-		}-		-		all_avail = false;-		-		glyph_range = range;--		// Todo: optimized search when full range is selected.-		-		// scroll to the selected character-		if (c == "") {-			c = "a";-		}-		-		if (items_per_row == 0) {-			return;-		}-		-		scroll_top ();--		// Skip scroll if this is a unassigned character-		if (c.char_count () > 1) {-			return;-		}--		while (true) {-			for (i = (uint32) first_visible; i < first_visible + items_per_row * (rows + 1); i++) {-				if (range.get_char (i) == c) {-					selected = i;-					done = true;-					break;-				}-			}-			-			if (done) break;-			if (at_bottom ()) break;--			scroll_rows (rows);-		}--		if (unlikely (!done)) {-			MainWindow.get_toolbox ().select_tool_by_name ("custom_character_set");-			warning (@"No glyph \"$c\"\n");-			return;-		}-		-		adjust_scroll ();-		-		if (at_bottom ()) {-			while (c != glyph_range.get_char (selected) && glyph_range.get_length () >= items_per_row) {-				key_right ();	-				if (selected >= glyph_range.get_length () - items_per_row) {-					break;-				}-			}--			len = glyph_range.get_length ();-			for (i = selected; i < len; i++) {-				if (c == glyph_range.get_char (i)) {-					break;-				}-			}--			if (i == len) {-				selected = first_character;-				scroll_top ();-			}-		}--		update_scrollbar ();-		redraw_area (0, 0, allocation.width, allocation.height);-	}--	public void update_scrollbar () {-		double len;-		Font f;-		-		if (all_avail) {-			f = Supplement.get_current_font ();-			len = f.length ();-		} else {-			len = glyph_range.get_length ();-		}-		-		scrollbar.set_handle_size (items_per_row * rows / len);-		scrollbar.set_handle_position (first_visible / len);-	}-}--}diff --git a/src/Path.vala b/src/Path.valadeleted file mode 100644index 6c27d01..0000000--- a/src/Path.vala+++ /dev/null@@ -1,1879 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Math;--namespace Supplement {--public enum Direction {-	CLOCKWISE,-	COUNTER_CLOCKWISE-}--public class Path {-	-	public List<EditPoint> points = new List<EditPoint> ();--	EditPoint? last_point = null;-	EditPoint? second_last_point = null;--	/** Region boundries */-	public double xmax = double.MIN;-	public double xmin = double.MAX;-	public double ymax = double.MIN;-	public double ymin = double.MAX;--	bool edit = true;-	bool open = true;-	bool direction_is_set = false;--	bool no_derived_direction = false;-	bool clockwise_direction = true;--	public delegate bool RasterIterator (double x, double y, double step); // iterate over each pixel at a given zoom level-	-	bool selected = false;-	-	private static ImageSurface? edit_point_image = null;-	private static ImageSurface? active_edit_point_image = null;-	-	private static ImageSurface? edit_point_handle_image = null;-	private static ImageSurface? active_edit_point_handle_image = null;--	private static ImageSurface? selected_edit_point_image = null;-	private static ImageSurface? active_selected_edit_point_image = null;-	-	Path quadratic_path; // quadratic points for ttf export-	-	public Path () {-		if (edit_point_image == null) {-			edit_point_image = Icons.get_icon ("edit_point.png");-			active_edit_point_image = Icons.get_icon ("active_edit_point.png");-			edit_point_handle_image = Icons.get_icon ("edit_point_handle.png");-			active_edit_point_handle_image = Icons.get_icon ("active_edit_point_handle.png");-			selected_edit_point_image  = Icons.get_icon ("selected_edit_point.png");-			active_selected_edit_point_image = Icons.get_icon ("active_selected_edit_point.png");-		}-	}--	public bool has_direction () {-		return direction_is_set;-	}--	public bool empty () {-		return points.length () == 0;-	}--	public void draw_outline (Context cr, Allocation allocation, double view_zoom) {-		unowned List<EditPoint> ep = points;-		-		unowned EditPoint? n = null;-		unowned EditPoint en;-		unowned EditPoint em;-		-		if (ep.length () < 2) {-			return;-		}-		-		cr.new_path ();-		-		// draw lines-		foreach (EditPoint e in ep) {-			if (n != null) {-				en = (!) n;-				draw_next (e, en, cr);-			}-			-			n = e;-		}--		// close path-		if (!is_open () && n != null) {-			en = (!) n;-			em = ep.first ().data;-			-			draw_next (em, en, cr);-		}--		cr.stroke ();-	}-	-	public void draw_edit_points (Context cr, Allocation allocation, double view_zoom) {-		unowned List<EditPoint> ep = points;-		-		if (is_editable ()) {-			// control points for curvature-			foreach (EditPoint e in ep) {-				if (e.selected || e.selected_handle > 0)-					draw_edit_point_handles (e, cr);-			}-						-			// control points-			foreach (EditPoint e in ep) {-				draw_edit_point (e, cr);-			}-		}-	}--	public void fill_path (Context cr, Allocation allocation, double view_zoom) {-		unowned List<EditPoint> ep = points;-		-		unowned EditPoint? n = null;-		unowned EditPoint en;-		unowned EditPoint em;-		-		cr.new_path ();-					-		// draw lines-		foreach (EditPoint e in ep) {-			if (n != null) {-				en = (!) n;-				draw_next (e, en, cr);-			}-			-			n = e;-		}-		-		// close path-		if (!is_open () && ep.length () >= 2 && n != null) {-			en = (!) n;-			em = ep.first ().data;-			draw_next (em, en, cr);-		}--		// fill path-		cr.close_path ();-			-		if (is_clockwise ()) {-			cr.set_source_rgba (80/255.0, 95/255.0, 137/255.0, 0.5);-		} else {-			cr.set_source_rgba (144/255.0, 145/255.0, 236/255.0, 0.5);-		}-		-		cr.fill ();-	}--	private void draw_next (EditPoint e, EditPoint en, Context cr) {-		draw_curve (e, en, cr);-	}-	-	private static void draw_curve (EditPoint e, EditPoint en, Context cr, double alpha = 1) {-		Glyph g = MainWindow.get_current_glyph ();-		double xa, ya, xb, yb, xc, yc, xd, yd;-		-		get_bezier_points (e, en, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);--		cr.set_source_rgba (0, 0, 0, alpha);-		cr.set_line_width (1 / g.view_zoom);-		-		cr.line_to (xa, ya); // this point makes sense only if it is the first or last position, the other points are meaning less don't export them--		cr.curve_to (xb, yb, xc, yc, xd, yd);-	}-	-	/** Curve relative to window center. */-	public static void get_bezier_points (EditPoint e, EditPoint en, out double xa, out double ya, out double xb, out double yb, out double xc, out double yc, out double xd, out double yd) {-		Glyph g = MainWindow.get_current_glyph ();-		-		double center_x, center_y;-		-		center_x = g.allocation.width / 2.0;-		center_y = g.allocation.height / 2.0;-				-		xa = center_x + en.x;-		ya = center_y - en.y;--		xb = center_x + en.get_right_handle ().x ();-		yb = center_y - en.get_right_handle ().y ();-		-		xc = center_x + e.get_left_handle ().x ();-		yc = center_y - e.get_left_handle ().y ();-		-		xd = center_x + e.x;-		yd = center_y - e.y;		-	}--	/** Curve absolute glyph data. */-	public static void get_abs_bezier_points (EditPoint e, EditPoint en, out double xa, out double ya, out double xb, out double yb, out double xc, out double yc, out double xd, out double yd) {-		xa =  + en.x;-		ya =  - en.y;--		xb =  + en.get_right_handle ().x ();-		yb =  - en.get_right_handle ().y ();-		-		xc =  + e.get_left_handle ().x ();-		yc =  - e.get_left_handle ().y ();-		-		xd =  + e.x;-		yd =  - e.y;		-	}-		-	/** Line points relative to center. */-	public static void get_line_points (EditPoint e, EditPoint en, out double xa, out double ya, out double xb, out double yb) {-		double xc = Glyph.xc ();-		double yc = Glyph.yc ();-		-		xa = xc + en.x;-		ya = yc - en.y;-		-		xb = xc + e.x;-		yb = yc - e.y;-	}-		-	public void draw_line (EditPoint e, EditPoint en, Context cr, double alpha = 1) { -		Glyph g = MainWindow.get_current_glyph ();-		double ax, ay, bx, by;--		get_line_points (e, en, out ax, out ay, out bx, out by);-	-		cr.set_source_rgba (0/255.0, 0/255.0, 0/255.0, 1);-		cr.set_line_width (1.7 * (1/g.view_zoom));--		cr.line_to (ax, ay);-		cr.line_to (bx, by);-		-		cr.stroke ();-	}-	-	public void draw_edit_point  (EditPoint e, Context cr) {-		draw_edit_point_center (e, cr);-	}-	-	public void draw_edit_point_handles (EditPoint e, Context cr)-		requires (active_edit_point_handle_image != null && edit_point_handle_image != null)-	{-		ImageSurface img_right, img_left;--		EditPoint handle_right = e.get_right_handle ().get_point ();-		EditPoint handle_left = e.get_left_handle ().get_point ();--		cr.stroke ();-		-		img_right = (e.get_right_handle ().active) ? (!) active_edit_point_handle_image : (!) edit_point_handle_image;-		img_left = (e.get_left_handle ().active) ? (!) active_edit_point_handle_image : (!) edit_point_handle_image;-		-		if (!(is_open () && e == points.last ().data)) {-			draw_line (handle_right, e, cr, 0.15);-			draw_img_center (cr, img_right, e.get_right_handle ().x (), e.get_right_handle ().y ());-		}-		-		if (!(is_open () && e == points.first ().data)) {-			draw_line (handle_left, e, cr, 0.15);-			draw_img_center (cr, img_left, e.get_left_handle ().x (), e.get_left_handle ().y ());-		}-	}--	public static void draw_edit_point_center (EditPoint e, Context cr) -		requires (active_edit_point_image != null && edit_point_image != null)-	{	-		ImageSurface img;-		-		if (e.is_selected ()) {-			img = (e.active) ? (!) active_selected_edit_point_image : (!) selected_edit_point_image;-		} else {-			img = (e.active) ? (!) active_edit_point_image : (!) edit_point_image;-		}-		-		draw_img_center (cr, img, e.x, e.y);-	}-	-	public static void draw_img_center (Context cr, ImageSurface img, double x, double y) {-		Glyph g = MainWindow.get_current_glyph ();-		-		double ivz = 1 / g.view_zoom;--		double xc = g.allocation.width / 2.0;-		double yc = g.allocation.height / 2.0;--		cr.save ();--		cr.scale (ivz, ivz);-		-		x = xc + x - (img.get_width () / 2.0) * ivz; -		y = yc - y - (img.get_height () / 2.0) * ivz;-		-		cr.set_source_surface (img, x * g.view_zoom, y * g.view_zoom);-		cr.paint ();-		-		cr.restore ();		-	}-	-	/** Returns true if there is an edit point at (p.x, p.y). */-	private bool has_edit_point (EditPoint p) {-		foreach (var t in points) {-			if (p.x == t.x && p.y == t.y) {-				return true;-			}-		}-		return false;-	}--	private EditPoint? get_edit_point_at (double x, double y) {-		foreach (var p in points) {-			if (p.x == x && p.y == y) return p;-		}-				-		return null;-	}-		-	private void remove_all_edit_points () {-		while (points.length () > 0) {-			points.remove_link (points.first ());-		}-	}-	-	/** Replace edit points in this path with @param new_path.points. */-	public void replace_path (Path new_path) {-		while (points.length () > 0) {-			points.remove_link (points.first ());-		}-		-		foreach (var np in new_path.points) {-			add (np.x, np.y);-		}-		-		close ();-	}-	-	/** Returns true if this path can be merged with @param union */ -	private bool is_valid_union (Path union) {-		if (union == this) {-			warning ("Path can't union with it self.");-			return false;-		}-		-		if (union.points.length () <= 2 || points.length () <= 2) {-			return false;-		}--		if (!has_overlapping_boundry (union)) {-			return false;-		}--		return true;-	}-	-	/** Set direction for this path to clockwise for outline and -	 * counter clockwise for inline paths.-	 */-	public bool force_direction (Direction direction) {-		bool c = (direction == Direction.CLOCKWISE);-		direction_is_set = true;-		-		if (c != is_clockwise ()) {-			this.reverse ();-		}-		-		if (unlikely (is_clockwise () != c)) {-			warning ("Failed to set direction for path in force_direction.");-			return true;-		}-				-		return false;-	}--	/** Switch direction from clockwise path to counter clockwise path or vise versa. */-	public void reverse () {-		bool direction = is_clockwise ();-		-		if (no_derived_direction) {-			clockwise_direction = !clockwise_direction;-		}-		-		reverse_points ();-		-		if (unlikely (direction == is_clockwise ())) {-			stderr.printf ("Error: Direction did not change after reversing path.\n");-			stderr.printf (@"Length: $(points.length ())\n");-			stderr.printf (@"No particular direction can be derived: $no_derived_direction \n");-			warning ("Path.reverse () failed.\n");-		}-	}--	private void reverse_points () {-		points.reverse ();-		-		EditPointHandle t;-		foreach (var e in points) {-			t = e.right_handle;-			e.right_handle = e.left_handle;-			e.left_handle = t;-		}-		-		create_list ();-	}--	public void print_all_points () {-		int i = 0;-		foreach (var p in points) {-			++i;-			string t = (p.type == PointType.END) ? " endpoint" : "";-			stdout.printf (@"Point $i at ($(p.x), $(p.y)) $t \n");-		}-	}-	-	private bool validate_list () {-		unowned List<EditPoint> ep = points.first ();-		-		if (points.length () < 3) {-			warning ("points.length () < 3");-		}-		-		if (!is_open ()) {-			for (int i = 0; i < points.length (); i++) {-				if (ep.data.next == null) {-					return false;-				}--				if (ep.data.prev == null) {-					return false;-				}-								-				ep = (!) ep.data.next;-			}-		}--		if (is_open ()) {-			for (int i = 1; i < points.length () - 1; i++) {-				if (ep.data.next == null) {-					return false;-				}--				if (ep.data.prev == null) {-					return false;-				}-								-				ep = (!) ep.data.next;-			}-			-			if (ep.data.next != null) {-				return false;-			}-		}-		-		return true;-	}-	-	private double clockwise_sum () {-		return_if_fail (points.length () >= 3);-		-		double sum = 0;-		EditPoint prev = points.last ().data;-		foreach (EditPoint e in points) {-			sum += (e.x - prev.x) * (e.y + prev.y);-			prev = e;-		}-		-		return sum;-	}-	-	public bool is_clockwise () {-		double s;-		-		if (unlikely (points.length () <= 2)) {-			no_derived_direction = true;-			return clockwise_direction;-		}-		-		s = clockwise_sum ();-		-		if (s == 0) {-			no_derived_direction = true;-			return clockwise_direction;	-		}-		-		return s > 0;-	}-	-	public bool is_editable () {-		return edit;-	}--	/** Show control points on outline path. */-	public void set_editable (bool e) {-		edit = e;-	}-		-	public bool is_open () {-		return open;-	}-	-	private bool direction_x (EditPoint start, EditPoint stop) {-		return (start.x < stop.x);-	}--	private bool direction_y (EditPoint start, EditPoint stop) {-		return (start.y < stop.y);-	}-	-	public void thinner (double ratio) -		requires (points.length () > 2)-	{		-		update_region_boundries ();-		-		double w = Math.fabs(xmax - xmin);-		double h = Math.fabs(ymax - ymin);-		double cx = w / 2 + xmin; -		double cy = h / 2 + ymin;-		-		ratio = 1 - ratio;--		unowned List<EditPoint> i = points.first ();-		unowned List<EditPoint> prev = points.last ();-		-		bool dirx = true;-		bool ldirx = direction_x (prev.data, i.data);-		bool ndirx;--		bool diry = true;-		bool ldiry = direction_y (prev.data, i.data);-		bool ndiry;-		-		while (true) {-			ndirx = direction_x (prev.data, i.data);-			-			if (ndirx != ldirx) {-				dirx = !dirx;-			}-			-			ldirx = ndirx;--			ndiry = direction_y (prev.data, i.data);-			-			if (ndiry != ldiry) {-				diry = !diry;-			}-			-			ldiry = ndiry;-			-			EditPoint p = i.data;-			-			if (dirx) {-				if (p.x < cx)-					p.x += (cx - p.x) * ratio;-				else-					p.x -= (p.x - cx) * ratio;-			} else {-				if (p.x < cx)-					p.x += (cx - p.x) * ratio * 2;-				else-					p.x -= (p.x - cx) * ratio * 2;-			}--			// err borde vara -y-			if (p.y < cy && diry || p.y > cy && ! diry)-				p.y += (cy - p.y) * ratio;-			else-				p.y -= (p.y - cy) * ratio;	-			-			if (i == points.last ()) {-				break;-			}-			-			prev = i;-			i = i.next;-		}-	}-	-	public void resize (double ratio) {-		double h = ymax - ymin;-		double px, py, nx, ny;--		px = xmin;-		py = ymin;-		-		if (ratio < 1 && h < 2) {-			return;-		}-		-		foreach (var p in points) {-			p.x *= ratio;-			p.y *= ratio;-			p.right_handle.length *= ratio;-			p.left_handle.length *= ratio;-		}-		-		xmin *= ratio;	-		xmax *= ratio;-		ymin *= ratio;-		ymax *= ratio;--		nx = xmin;-		ny = ymin;-		-		move (px - nx, py - ny);-	}-	-	public Path copy () {-		Path new_path = new Path ();-		-		foreach (var ep in points) {-			new_path.add_point (ep.copy ());-		}-		-		new_path.edit = edit;-		new_path.open = open;-	-		new_path.create_list ();-		-		return new_path;-	}	-	-	public bool is_over (double x, double y) {-		Glyph g = MainWindow.get_current_glyph ();-		-		x = x * Glyph.ivz () + g.view_offset_x - Glyph.xc ();-		y = y * Glyph.ivz () + g.view_offset_y - Glyph.yc ();--		y *= -1;-		-		return is_over_coordinate (x, y);-	}-	-	public bool is_over_coordinate (double x, double y) {-		return is_over_coordinate_var (x, y, 0.1);-	}-	-	public static double distance (double ax, double bx, double ay, double by) {-		return Math.fabs (Math.sqrt (Math.pow (ax - bx, 2) + Math.pow (ay - by, 2)));-	}-	-	/** Estimate length of path. */-	private double get_length () {-		double len = 0;-		-		return_if_fail (points.length () > 2);-		-		EditPoint prev = points.last ().data;-		foreach (EditPoint p in points) {-			len += get_length_from (prev, p);-			prev = p;-		}-		-		return len;-	}-	-	private static double get_length_from (EditPoint a, EditPoint b) {-		double x, y;-		-		x = Math.fabs (a.x - a.get_right_handle ().x ());-		x += Math.fabs (a.get_right_handle ().x () - b.get_left_handle ().x ());-		x += Math.fabs (b.get_left_handle ().x () - b.x);--		y = Math.fabs (a.y - a.get_right_handle ().y ());-		y += Math.fabs (a.get_right_handle ().y () - b.get_left_handle ().y ());-		y += Math.fabs (b.get_left_handle ().y () - b.y);-		-		return Math.fabs (Math.sqrt (x * x + y * y));-	} -	-	/** Variable precision */-	public bool is_over_coordinate_var (double x, double y, double tolerance) {-		List<EditPoint> ycoordinates = new List<EditPoint> ();-		double last = 0;-		bool on_edge = false;-		double last_x = 0;--		if (points.length () < 3) {-			return false;-		}-		-		if (!is_over_boundry_precision (x, y, tolerance)) {-			return false;-		}--		foreach (EditPoint e in points) {-			if (distance (e.x, x, e.y, y) < tolerance) {-				return true;-			}-		}--		all_of_path ((cx, cy, ct) => {-			double distance = Math.fabs (Math.sqrt (Math.pow (cx - x, 2) + Math.pow (cy - y, 2)));-			-			if (distance < tolerance) {-				on_edge = true;-				return false;-			}-			-			if (Math.fabs (cx - x) < tolerance && Math.fabs (last - cy) > 2 * tolerance) {-				ycoordinates.append (new EditPoint (cx, cy));-				last = cy;-			}-			-			last_x = cx;-			return true;-		});--		if (on_edge) {-			return true;-		}--		if (ycoordinates.length () == 0) {-			warning ("No ycoordinates is empty");-			return true;-		}--		ycoordinates.sort ((a, b) => {-			return (a.y < b.y) ? 1 : -1;-		});-		-		for (int i = 0; i < ycoordinates.length () - 1; i++) {-			if (Math.fabs (ycoordinates.nth (i).data.y - ycoordinates.nth (i + 1).data.y) < 4 * tolerance) {-				ycoordinates.remove_link (ycoordinates.nth (i));-			}-		}-		-		if (unlikely (ycoordinates.length () % 2 != 0)) {-			warning (@"not an even number of coordinates ($(ycoordinates.length ()))");-			stderr.printf (@"(ymin <= y <= ymax) && (xmin <= x <= xmax);\n");-			stderr.printf (@"($ymin <= $y <= $ymax) && ($xmin <= $x <= $xmax);\n");-		-			stderr.printf (@"tolerance: $(tolerance)\n");-			-			stderr.printf ("ycoordinates:\n");-			foreach (EditPoint e in ycoordinates) {-				stderr.printf (@"$(e.y)\n");-			}--			ycoordinates.append (ycoordinates.last ().data.copy ());-			-			return true;-		}-		-		for (unowned List<EditPoint> e = ycoordinates.first (); true; e = e.next) {-			return_val_if_fail (!is_null (e), true);-			-			if (y <= e.data.y + tolerance) {-				return_val_if_fail (!is_null (e.next), true);-				-				e = e.next;--				if (y >= e.data.y - tolerance) {-					return true;-				}-			}-			-			if (e == ycoordinates.last ()) {-				break;-			}-		}--		return false;-	}-	-	public bool is_over_boundry_precision (double x, double y, double p) {-		if (unlikely (ymin == double.MAX || ymin == 10000)) {-			warning (@"no bounding box. length ($(points.length ()))");-			update_region_boundries ();-		}-		-		return (ymin - p <= y <= ymax + p) && (xmin - p <= x <= xmax + p);-	}-	-	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.");-		}--		return (ymin <= y <= ymax) && (xmin <= x <= xmax);-	}--	public bool has_overlapping_boundry (Path p) {-		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 void add_point (EditPoint p) {-		add_point_after (p, points.last ());-	}--	/** Insert a new point after @param previous_point and return a reference -	 * to the new item in list.-	 */-	public unowned List<EditPoint> add_after (double x, double y, List<EditPoint>? previous_point) {-		EditPoint p = new EditPoint (x, y, PointType.LINE);	-		return add_point_after (p, previous_point);-	}-	-	public unowned List<EditPoint> add_point_after (EditPoint p, List<EditPoint>? previous_point) {-		unowned List<EditPoint> np;-		int prev_index;--		if (points.length () > 0 && previous_point != null && ((!)previous_point).data.type == PointType.END) {-			points.delete_link ((!) previous_point);-		}--		if (unlikely (previous_point == null && points.length () != 0)) warning ("previous_point == null");--		if (points.length () == 0) {-			points.append (p);-			np = points.last ();-			p.prev = points.last ();-			p.next = points.last ();-		} else {-			p.prev = (!) previous_point;-			p.next = ((!) previous_point).next;--			points.insert_before (((!) previous_point).next, p);-			-			prev_index = points.position ((!) previous_point);-			np = points.nth (prev_index + 1);-		}-		-		second_last_point = last_point;-		last_point = p;-		-		PenTool.set_default_handle_positions ();-		-		return np;-	}--	public void close () {-		open = false;-		edit = false;--		create_list ();-		-		if (points.length () > 2) {-			points.first ().data.recalculate_linear_handles ();-			points.last ().data.recalculate_linear_handles ();-		}-	}-	-	public void reopen () {-		open = true;-		edit = true;-	}-	-	public EditPoint? get_last_point () {-		return last_point;-	}--	public EditPoint? get_second_last_point () {-		return second_last_point;-	}-	-	/** Move path. */-	public void move (double delta_x, double delta_y) {-		foreach (var ep in points) {-			ep.x += delta_x;-			ep.y += delta_y; // kanske -y-		}-		-		update_region_boundries ();-	}--	public void update_region_boundries () {-		if (points.length () == 0) {-			xmax = 0;-			xmin = 0;-			ymax = 0;-			ymin = 0;			-		}--		// inside and outside in vala lambda functions reveals a tricky problem-		// (look at c code). that's the reason for the !new_val expression--		xmax = -10000;-		xmin = 10000;-		ymax = -10000;-		ymin = 10000;--		double txmax = -10000;-		double txmin = 10000;-		double tymax = -10000;-		double tymin = 10000;--		bool new_val = false;-			-		all_of_path ((cx, cy) => {	-			if (!new_val) {-				txmax = cx;-				txmin = cx;-				tymax = cy;-				tymin = cy;-				new_val = true;-			}-			-			if (cx < txmin) {-				txmin = cx;-			}--			if (cx > txmax) {-				txmax = cx;-			}-			-			if (cy < tymin) {-				tymin = cy;-			}--			if (cy > tymax) {-				tymax = cy;-			}--			return true;-		});-				-		xmax = txmax;-		xmin = txmin;-		ymax = tymax;-		ymin = tymin;--		if (unlikely (!new_val)) {-			// only one point-		} else if (unlikely (!got_region_boundries ())) {-			warning (@"No new region boundries.\nPoints.length: $(points.length ())");-			print_boundries ();-		}-	}-		-	/** Test if @param path is a valid outline for this object. */	-	public bool test_is_outline (Path path) {-		assert (false);-		return this.test_is_outline_of_path (path) && path.test_is_outline_of_path (this);-	}-	-	private bool test_is_outline_of_path (Path outline)-		requires (outline.points.length () >= 2 || points.length () >= 2)-	{	-		// rather slow use it for testing, only-		unowned List<EditPoint> i = outline.points.first ();-		unowned List<EditPoint> prev = i.last ();--		double tolerance = 1;-		bool g = false;-		-		EditPoint ep = new EditPoint (0, 0);-		double min = double.MAX;-		-		while (true) {-			min = 10000;-			 -			all_of (prev.data, i.data, (cx, cy) => {-					get_closest_point_on_path (ep, cx, cy);--					double n = pow (ep.x - cx, 2) + pow (cy - ep.y, 2);-					-					if (n < min) min = n;-					-					if (n < tolerance) {-						g = true;-						return false;-					}--					return true;-				});-			-			if (!g) {-				critical (@"this path does not seem to be the outline. (min $min)");-			}-			-			g = false;-			-			if (i == i.last ()) {-				break;-			}-				-			i = i.next;-		}-		-		return true;-	}--	/** Convert quadratic bezier points to cubic representation of the glyph-	 * suitable for ttf-export.-	 */ -	public Path get_quadratic_points () {-		EditPoint middle = new EditPoint ();-		unowned List<EditPoint> e;-		EditPointHandle eh;-		-		quadratic_path = copy ();-		quadratic_path.close ();-				-		if (quadratic_path.points.length () < 2) {-			return quadratic_path;-		}--		// split all curves in as many regions as we need-		split_cubic_in_parts (quadratic_path);	--		// estimate quatdratic form-		middle.prev = quadratic_path.points.last ();-		middle.next = quadratic_path.points.first ();--		convert_to_quadratic (quadratic_path, middle);-		-		e = (!) quadratic_path.points.first ();-		while (e != quadratic_path.points.last ().prev) {-			middle.prev = e;-			middle.next = e.next;-							-			convert_to_quadratic (quadratic_path, middle);	-			-			e = (!) e.next;-		}-		-		eh = ((!)quadratic_path.points.last ().prev).data.get_left_handle ();-		eh.set_point_type (PointType.NONE);-		eh.length = 0;--		eh = ((!)quadratic_path.points.last ().prev).data.get_right_handle ();-		eh.set_point_type (PointType.CURVE);-		eh.length *= 1.6;--		return quadratic_path;-	}--	void convert_to_quadratic (Path cubic_path, EditPoint middle) {-		double curve_x, curve_y;	-		EditPointHandle eh;-		-		EditPoint start = middle.get_prev ().data;-		EditPoint stop = middle.get_next ().data;--		estimate_quadratic (start, stop, out curve_x, out curve_y);-		-		eh = start.get_left_handle ();-		eh.set_point_type (PointType.NONE);-		eh.length = 0;-		-		eh = start.get_right_handle ();-		eh.set_point_type (PointType.CURVE);-		eh.move_to_coordinate (curve_x, curve_y);-	}--	public void split_cubic_in_parts (Path cubic_path) {-		int j = 0;-		while (split_all_cubic_in_half (cubic_path)) {-			j++;-			-			if (j > 5) {-				warning ("too many iterations in split path");-				break;-			}-		}	-	}--	public bool split_all_cubic_in_half (Path cubic_path) {-		EditPoint middle = new EditPoint ();-		unowned List<EditPoint> e;-		bool need_split = false;-		uint len = cubic_path.points.length ();-	-		if (len < 2) {-			return false;-		}--		middle.prev = cubic_path.points.last ();-		middle.next = cubic_path.points.first ();-		-		if (split_cubic_in_half (cubic_path, middle)) {-			need_split = true;-		}-			-		e = (!) cubic_path.points.first ();-		for (uint i = 0; i < len - 1; i++) {-			middle.prev = e;-			middle.next = e.next;-			-			middle.get_prev ().data.recalculate_linear_handles ();-			middle.get_next ().data.recalculate_linear_handles ();-			-			if (split_cubic_in_half (cubic_path, middle)) {-				e = (!) e.next.next;-				need_split = true;-			} else {-				e = (!) e.next;-			}-		}-				-		return need_split;	-	}--	public void estimate_quadratic (EditPoint start, EditPoint stop, out double curve_x, out double curve_y) {-		curve_x = -0.25 * start.x + 0.75 *  stop.get_left_handle ().x () + 0.75 * start.get_right_handle ().x () - 0.25 * stop.x;-		curve_y = -0.25 * start.y + 0.75 *  stop.get_left_handle ().y () + 0.75 * start.get_right_handle ().y () - 0.25 * stop.y;		-	}--	bool split_cubic_in_half (Path cubic_path, EditPoint middle) {-		double curve_x, curve_y, cx, cy, qx, qy, nx, ny, distance;	-		-		EditPoint start = middle.get_prev ().data;-		EditPoint stop = middle.get_next ().data;-		-		estimate_quadratic (start, stop, out curve_x, out curve_y);-	-		qx = quadratic_bezier_path (0.75, start.x, curve_x, stop.x);-		qy = quadratic_bezier_path (0.75, start.y, curve_y, stop.y);--		cx = bezier_path (0.75, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x);-		cy = bezier_path (0.75, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y);--		distance = Math.sqrt (Math.pow (cx - qx, 2) + Math.pow (cy - qy, 2));--		nx = bezier_path (0.5, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x);-		ny = bezier_path (0.5, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y);--		if (Math.fabs (distance) > 0.01) {-			EditPoint new_edit_point = new EditPoint (nx, ny, PointType.CURVE);--			new_edit_point.next = middle.get_next ();-			new_edit_point.prev = middle.get_prev ();-			cubic_path.insert_new_point_on_path (new_edit_point);-			cubic_path.create_list ();--			return true;	-		}-		-		return false;-	}--	double distance_to_path (EditPoint start, EditPoint stop, double x, double y) {-		double min = double.MAX;-		-		all_of (start, stop, (cx, cy, t) => {-			double n = pow (x - cx, 2) + pow (y - cy, 2);-			-			if (n < min) {-				min = n;-			}-			-			return true;-		});-		-		return min;-	}--	public void insert_new_point_on_path (EditPoint? epp) {-		EditPoint start, stop;-		double x0, x1, y0, y1;-		double px, py;-		-		double position, t, d, min;-		double steps = 500;-		-		EditPoint ep;--		if (epp == null) {-			return;-		}--		ep = (!) epp;--		start = ep.get_prev ().data;-		stop = ep.get_next ().data;--		add_point_after (ep, ep.get_prev ());--		min = double.MAX;--		position = 0.5;--		for (int i = 0; i < steps; i++) {-			t = i / steps;-			-			px = bezier_path (t, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x);-			py = bezier_path (t, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y);-			-			d = Math.sqrt (Math.pow (ep.x - px, 2) + Math.pow (ep.y - py, 2));-			-			if (d < min) {-				min = d;-				position = t;-			}-		}-			-		bezier_vector (position, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out x0, out x1);-		bezier_vector (position, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out y0, out y1);--		ep.get_left_handle ().set_point_type (PointType.CURVE);-		ep.get_left_handle ().move_to_coordinate (x0, y0);-		ep.get_left_handle ().parent = ep;-		-		ep.get_right_handle ().set_point_type (PointType.CURVE);-		ep.get_right_handle ().move_to_coordinate (x1, y1);-		ep.get_right_handle ().parent = ep;--		stop.get_left_handle ().length *= 1 - position;-		start.get_right_handle ().length *= position;-	}-			-	/** Get a point on the this path closest to x and y coordinates. */-	public void get_closest_point_on_path (EditPoint edit_point, double x, double y) {-		return_if_fail (points.length () != 0);-		-		double min = double.MAX;-		double n = 0;-		bool g = false;-		-		double ox = 0;-		double oy = 0;-		-		double handle_x0, handle_x1;-		double handle_y0, handle_y1;-		-		unowned List<EditPoint> i = points.first ();-		unowned List<EditPoint> prev = i.next;--		bool done = false;-		bool exit = false;-		-		unowned List<EditPoint>? previous_point = null;-		unowned List<EditPoint>? next_point = null;--		EditPoint previous;-		EditPoint next;-		double step = 0;--		if (points.length () == 1) {-			edit_point.x = i.data.x;-			edit_point.y = i.data.y;-			-			edit_point.prev = i;-			edit_point.next = i;-			-			exit = true;-			-			return;-		}-		-		if (points.length () != 1) {-			edit_point.x = i.data.x;-			edit_point.y = i.data.y;-		}-		-		while (!exit) {-			-			if (i == points.last ()) {-				done = true;-			}-			-			if (!done) {-				i = i.next;-				prev = i.prev;-			}	else if (done && !is_open ()) {-				i = points.first ();-				prev = points.last ();-				exit = true;-			} else {-				break;-			}-			-			all_of (prev.data, i.data, (cx, cy, t) => {-				n = pow (x - cx, 2) + pow (y - cy, 2);-				-				if (n < min) {-					min = n;-					-					ox = cx;-					oy = cy;-				-					previous_point = prev;-					next_point = i;-					-					step = t;-					-					g = true;-				}-				-				return true;-			});-		}--		if (previous_point == null) {-			warning (@"previous_point == null, points.length (): $(points.length ())");-			return;-		}-		-		if (next_point == null) {-			warning ("next_point != null");-			return;-		}--		previous = ((!) previous_point).data;-		next = ((!) next_point).data;--		bezier_vector (step, previous.x, previous.get_right_handle ().x (), next.get_left_handle ().x (), next.x, out handle_x0, out handle_x1);-		bezier_vector (step, previous.y, previous.get_right_handle ().y (), next.get_left_handle ().y (), next.y, out handle_y0, out handle_y1);--		// position -		edit_point.set_position (ox, oy);-		edit_point.prev = previous_point;-		edit_point.next = next_point;--		// curve (angle)-		edit_point.get_right_handle ().move_to_coordinate (handle_x0, handle_y0);-		edit_point.get_left_handle ().move_to_coordinate (handle_x1, handle_y1);-		// FIXA: maybe just edit_point.set_tie_handle (true);--		if (unlikely (!g)) {-			warning (@"Error: Got no coordinates for point on path. Num points $(points.length ())\n");-		}-	}--	public static void all_of (EditPoint start, EditPoint stop, RasterIterator iter, int steps = -1) {-		if (steps == -1) {-			steps = (int) (10 * get_length_from (start, stop));-		}-		-		all_of_curve (start.x, start.y, start.get_right_handle ().x (), start.get_right_handle ().y (), stop.get_left_handle ().x (), stop.get_left_handle ().y (), stop.x, stop.y, iter, steps);-	}--	private static void all_of_curve (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, RasterIterator iter, double steps = 400) {-		double px = x1;-		double py = y1;-		-		double t;-		-		for (int i = 0; i < steps; i++) {-			t = i / steps;-			-			px = bezier_path (t, x0, x1, x2, x3);-			py = bezier_path (t, y0, y1, y2, y3);-			-			if (!iter (px, py, t)) {-				return;-			}-			-		}	-	}-	-	void point_on_bezier_path (double t, EditPoint start, EditPoint stop, out double x, out double y) {		-		double x0 = start.x;-		double y0 = start.y; -		double x1 = start.get_right_handle ().x ();-		double y1 = start.get_right_handle ().y ();-		double x2 = stop.get_left_handle ().x ();-		double y2 = stop.get_left_handle ().y (); -		double x3 = stop.x;-		double y3 = stop.y;--		x = bezier_path (t, x0, x1, x2, x3);-		y = bezier_path (t, y0, y1, y2, y3);-	}--	private void all_of_path (RasterIterator iter) {-		unowned List<EditPoint> i, next;-		-		if (points.length () < 2) {-			return;-		}--		i = points.first ();-		next = i.next;--		while (i != points.last ()) {-			all_of (i.data, next.data, iter);-			i = i.next;-			next = i.next;-		}-		-		if (!is_open ()) {-			all_of (points.last ().data, points.first ().data, iter);-		}-	}--	public static double bezier_path (double step, double p0, double p1, double p2, double p3) {-		double q0, q1, q2;-		double r0, r1;--		q0 = step * (p1 - p0) + p0;-		q1 = step * (p2 - p1) + p1;-		q2 = step * (p3 - p2) + p2;--		r0 = step * (q1 - q0) + q0;-		r1 = step * (q2 - q1) + q1;--		return step * (r1 - r0) + r0;-	}--	public static void bezier_vector (double step, double p0, double p1, double p2, double p3, out double a0, out double a1) {-		double q0, q1, q2;--		q0 = step * (p1 - p0) + p0;-		q1 = step * (p2 - p1) + p1;-		q2 = step * (p3 - p2) + p2;--		a0 = step * (q1 - q0) + q0;-		a1 = step * (q2 - q1) + q1;-	}--	public static double quadratic_bezier_path (double step, double p0, double p1, double p2) {-		double q0 = step * (p1 - p0) + p0;-		double q1 = step * (p2 - p1) + p1;-		-		return step * (q1 - q0) + q0;-	}-			-	public void plot (Context cr, Allocation allocation, double view_zoom) {-			double px = 0, py = 0;-			double xc = allocation.width / 2.0;-			double yc = allocation.height / 2.0;--			cr.save ();-			-			all_of_path ((x, y) => {-				//cr.set_source_rgba (0.3, 0.3, 0.3, 1);-				cr.move_to (px + xc, -py + yc);-				cr.line_to (x + xc, -y + yc);-				-				px = x;-				py = y;-				-				return true;-			});--			cr.stroke ();-			cr.restore ();-	}-	-	public void print_boundries () {-		stderr.printf (@"xmax $xmax \n");-		stderr.printf (@"xmin $xmin \n");-		stderr.printf (@"ymax $ymax \n");-		stderr.printf (@"ymin $ymin \n");		-	}-	-	public bool got_region_boundries () {-		return !(xmax == -10000 || xmin ==  10000 || ymax == -10000 || ymin ==  10000);-	}-	-	public void create_list () {-		unowned List<EditPoint> ep = points.first ();-		unowned List<EditPoint> prev = ep;-		-		if (points.length () == 0) {-			return;-		}-		-		if (points.length () == 1) {-			ep.data.next = null;-			ep.data.prev = null;-			return;-		}-		-		ep.data.next = ep.next;-		ep.data.prev = points.last ();-		prev = ep;-		-		assert (ep.data.next != null);-		assert (ep.data.prev != null);-		-		while (ep != ep.last ()) {-			ep.data.next = ep.next;-			ep.data.prev = prev;-			prev = ep;-			-			assert (ep.data.next != null);-			assert (ep.data.prev != null);-		-			ep = ep.next;-		}-		-		if (is_open ()) {-			ep.data.next = points.first ();-		} else {-			ep.data.next = points.first ();-			assert (ep.data.next != null);-		}-		-		ep.data.prev = prev;-		assert (ep.data.prev != null);-	}--	public void delete_edit_point (EditPoint ep) -		requires (points.length () > 0)-	{-			unowned List<EditPoint>? pl = null;-			EditPoint p;-			-			if (ep.prev != null) {-				set_new_start (ep);-			}-			-			p = points.first ().data;-			-			for (uint i = 0; i < points.length (); i++) {-				p = points.nth (i).data;-				-				if (p == ep) {-					p.prev = null;-					p.next = null;-					-					pl = points.nth (i);-					break;-				}-			}--			if (pl != null) {-				points.delete_link ((!) pl);-				reopen ();-				MainWindow.get_current_glyph ().add_active_path (this);-				create_list ();-			}-	}-		-	public void set_new_start (EditPoint ep) {-		List<EditPoint> list = new List<EditPoint> ();-		uint len = points.length ();-		unowned List<EditPoint> iter = points.first ();-		unowned List<EditPoint>? ni = null;-		bool found = false;-		-		foreach (EditPoint it in points) {-			if (it == ep) {-				found = true;-				break;-			}-			-			iter = iter.next;-			ni = (!) iter;-		}-		-		if (unlikely (!found)) {-			warning ("Could not find edit point.");-		}-		-		if (ni == null) {-			return;			-		}-		-		iter = (!) ni;-		-		for (uint i = 0; i < len; i++) {-			list.append (iter.data);-			-			if (iter == iter.last ())-				iter = iter.first ();-			else-				iter = iter.next;-		-		}-		-		while (points.length () > 0) {-			points.remove_link (points.first ());-		}-		-		foreach (EditPoint p in list) {-			points.append (p);-		}-		-	}-	-	public static PathList merge (Path p0, Path p1) {-		bool done;-		PathList path_list;-		-		done = try_merge (p0.copy(), p1.copy(), out path_list);-		-		if (!done) {-			warning ("failed to merge paths");-		}-		-		return path_list;-	}-	-	private static bool try_merge (Path p0, Path p1, out PathList path_list) {-		EditPoint e;-		IntersectionList il;-		int i;-		Path np;-		-		unowned List<Path> pi;-		-		il = IntersectionList.create_intersection_list (p0, p1);-		-		path_list = new PathList ();-		-		if (p0 == p1) {-			return false;-		}-		-		// add editpoints on intersections -		p0.update_region_boundries ();-		p1.update_region_boundries ();-		foreach (Intersection inter in il.points) {-			e = new EditPoint ();-			p0.get_closest_point_on_path (e, inter.x, inter.y);-			inter.editpoint_a = e;-			-			e = inter.editpoint_a;-			if (!p0.has_edit_point (e)) {-				p0.insert_new_point_on_path (e);-			}--			e = new EditPoint ();-			p1.get_closest_point_on_path (e, inter.x, inter.y);-			inter.editpoint_b = e;-			-			inter.editpoint_b.x = inter.editpoint_a.x;-			inter.editpoint_b.y = inter.editpoint_a.y;-			-			e = inter.editpoint_b;-			if (!p1.has_edit_point (e)) {-				p1.insert_new_point_on_path (e);-			}-		}-			-		if (il.points.length () < 2) {-			return false;-		}-		-		//path_list.paths.append (p0);-		//path_list.paths.append (p1);-		//return false;-		-		// get all parts-		foreach (Intersection inter in il.points) {-			p0.set_new_start (inter.editpoint_a);-			get_merge_part (il, p0, p1, out np);-			path_list.paths.append (np);-		}-		-		foreach (Path pp in path_list.paths) {-			pp.update_region_boundries ();-		}--		// remove duplicate paths-		for (i = 0; i < path_list.paths.length (); i++) {-			pi = path_list.paths.nth (i);--			if (is_duplicated (path_list, pi.data)) {-				path_list.paths.remove_link (pi);-				--i;			-			}-		}-		-		// remove paths contained in other paths-		for (i = 0; i < path_list.paths.length (); i++) {-			pi = path_list.paths.nth (i);-			-			if (pi.data.is_clockwise () && is_clasped (path_list, pi.data)) {-				path_list.paths.remove_link (pi);-				i--;-			}-		}-	-		return true;-	}-	-	private static bool is_duplicated (PathList pl, Path p) {-		bool duplicate = false;-		foreach (Path pd in pl.paths) {-			if (pd == p) {-				continue;-			}-			-			if (is_duplicated_path (p, pd)) {-				duplicate = true;-			}-		}-		-		return duplicate;-	}-	-	private static bool is_duplicated_path (Path p0, Path p1) {-		bool eq;-		-		assert (p1 != p0);	-		-		foreach (EditPoint ep in p0.points) {-			eq = false;-			-			foreach (EditPoint e in p1.points) {-				eq = (Math.fabs (ep.x - e.x) < 0.04 && Math.fabs (ep.y - e.y) < 0.04);-				-				if (eq) {-					break;-				}-			}-			-			if (eq) {-				continue;-			} else {-				return false;-			}-		}-		-		return true;-	}-	-	private static bool is_clasped (PathList pl, Path p) {-		foreach (Path o in pl.paths) {-			if (o == p) {-				continue;-			}-			-			if (is_clasped_path (o, p)) {-				return true;-			}-		}-		-		return false;-	}--	private static bool is_clasped_path (Path outside, Path inside) {-		bool i = true;-		foreach (EditPoint e in inside.points) {-			if (!outside.is_over_coordinate_var (e.x, e.y, 0.5)) { // high tolerance since point may be off curve in both paths-				i = false;-				break;-			}-		}-		return i;-	}-	-	private static bool get_merge_part (IntersectionList il, Path p0, Path p1, out Path new_path) {-		EditPoint ex;-		EditPoint ix;-		EditPoint en;-		uint offset_i = 0;-		uint offset_j;-		uint len_i;-		int i, j;-		uint len_j;-		Path np = new Path ();-		Intersection s = new Intersection (0, 0, 1);-		-		ex = p0.points.last ().data;-		ix = p0.points.last ().data;-		len_i = p0.points.length ();-		-		for (i = 0; i < p0.points.length (); i++) {-			ex = p0.points.nth ((i + offset_i) % len_i).data;--			if (ex == p0.points.first ().data && i != 0) {	-				s = (!) il.get_intersection (ex);	-				break;-			}--			// add new point for path a-			if (np.has_edit_point (ex)) {-				// SPLIT-				warning ("Merged path need split");-				np.close ();-				new_path = np;-				-				return false;-			} else {-				en = ex.copy ();-				np.add_point (en);-				en.recalculate_linear_handles ();-			}-			-			// swap paths-			if (il.has_edit_point (ex)) {-				s = (!) il.get_intersection (ex);-			-				en.type = PointType.CURVE;-				en.right_handle.type = PointType.CURVE;-				en.right_handle.angle  = s.editpoint_b.right_handle.angle;-				en.right_handle.length = s.editpoint_b.right_handle.length;-							-				// read until we find ex-				for (j = 0; j < p1.points.length (); j++) {-					ix = p1.points.nth (j).data;-					-					if (ix == s.editpoint_b) {-						break;-					}-				}-				-				offset_j = j + 1;-				len_j = p1.points.length ();-				for (j = 0; j < p1.points.length (); j++) {-					-					ix = p1.points.nth ((j + offset_j) % len_j).data;-					-					// add-					if (np.has_edit_point (ix)) {-						// SPLIT-						warning ("Merged path need split");-						np.close ();-						new_path = np;-						-						break;-					} else {-						en = ix.copy ();-						np.add_point (en);-						ix.recalculate_linear_handles ();-					}-					-					if (il.has_edit_point (ix)) {-						s = (!) il.get_intersection (ix);-						break;-					}-				}--				en.type = PointType.CURVE;-				en.right_handle.type = PointType.CURVE;-				en.right_handle.angle  = s.editpoint_a.right_handle.angle;-				en.right_handle.length = s.editpoint_a.right_handle.length;-								-				if (j == p0.points.length ()) {-					np.close ();-					new_path = np;-					return true;-				}--				// skip to next intersection-				int k;-				for (k = 0; k < p0.points.length (); k++) {-					ix = p0.points.nth (k).data; --					if (ix == s.editpoint_a) {-						break;-					}-				}-				-				if (k == p0.points.length ()) {-					new_path = np;-					return true;-				}-				-				offset_i = 0;-				i = k;-			}-		}--		new_path = np;-		-		return true;	-	}--}--public class PathList : GLib.Object {-	public List<Path> paths = new List<Path> ();-	-	public PathList () {-	}-	-	public void clear () {-		while (paths.length () > 0) {-			paths.remove_link (paths.first ());-		}-	}-}--}diff --git a/src/PenTool.vala b/src/PenTool.valadeleted file mode 100644index 931b4fa..0000000--- a/src/PenTool.vala+++ /dev/null@@ -1,1001 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--class PenTool : Tool {--	private static const double CONTACT_SURFACE = 50;--	public static bool move_selected = false;-	public static bool begin_new_point_on_path = false;-	public static bool move_point_on_path = false;--	public static bool edit_active_corner = false;-	-	public static List<EditPoint> selected_points = new List<EditPoint> (); --	public static EditPointHandle active_handle = new EditPointHandle.empty ();-	public static EditPointHandle selected_handle = new EditPointHandle.empty ();-	-	public static EditPoint? active_edit_point = new EditPoint ();--	/** Move handle vertical or horizontal. */-	public static bool tie_x_or_y_coordinates = false;--	private static bool move_selected_handle = false;--	private static double last_point_x = 0;-	private static double last_point_y = 0;--	public static double precision = 1;-	-	public PenTool (string name) {-		base (name, _("Right click to add new points, left click to move points") + " " + _("and double click to add new point on path."), ',', CTRL);-		-		select_action.connect ((self) => {-		});--		deselect_action.connect ((self) => {-			Glyph glyph = MainWindow.get_current_glyph ();-			-			CutTool.force_direction ();-			glyph.close_path ();-			-			move_point_on_path = false;-			begin_new_point_on_path = false;-		});-		-		press_action.connect ((self, b, x, y) => {-			last_point_x = x;-			last_point_y = y;--			press (b, x, y, false);-		});-		-		double_click_action.connect ((self, b, x, y) => {-			last_point_x = x;-			last_point_y = y;--			press (b, x, y, true);-		});--		release_action.connect ((self, b, ix, iy) => {-			double x = ix;-			double y = iy;-			Glyph g = MainWindow.get_current_glyph ();-			EditPoint selected_corner;-			-			if (move_selected && GridTool.is_visible () && selected_points.length () > 0) {-				selected_corner = selected_points.last ().data;-				GridTool.tie (ref x, ref y);-				g.move_selected_edit_point (selected_corner, x, y);-			}--			move_selected = false;-			move_selected_handle = false;-			edit_active_corner = false;-			-			active_handle = new EditPointHandle.empty ();-			-			move (x, y);-		});--		move_action.connect ((self, x, y) => {-			move (x, y);-		});-		-		key_press_action.connect ((self, keyval) => {-			Glyph g = MainWindow.get_current_glyph ();-			-			if (keyval == Key.DEL) {-				foreach (EditPoint p in selected_points) {-					g.delete_edit_point (p);-				}-				-				g.update_view ();-			}-		});-		-		key_release_action.connect ((self, keyval) => {-		});-	}-	-	public double get_precision () {-		return precision;-	}-	-	public void set_precision (double p) {-		precision = p;-		MainWindow.get_toolbox ().precision.set_value_round (p, false, false);-	}-	-	public void move (double x, double y) {-		Glyph glyph = MainWindow.get_current_glyph ();-		-		control_point_event (x, y);-		curve_active_corner_event (x, y);-		set_default_handle_positions ();--		// show new point on path-		if (glyph.new_point_on_path != null) {-			move_current_point_on_path (x, y);-		}-		-		// move curve handles-		if (move_selected_handle) {-			if (GridTool.is_visible ()) {-				GridTool.tie (ref x, ref y);-				selected_handle.set_point_type (PointType.CURVE);-				selected_handle.move_to (x, y);-			} else {-				selected_handle.set_point_type (PointType.CURVE);-				selected_handle.move_delta ((x - last_point_x) * precision, (y - last_point_y) * precision);-			}-			-			selected_handle.parent.recalculate_linear_handles ();-			-			// Fixa: redraw line only-			glyph.redraw_area (0, 0, glyph.allocation.width, glyph.allocation.height);-						-			last_point_x = x;-			last_point_y = y;-			-			return;-		}-		-		// move edit point-		if (move_selected) {-			foreach (EditPoint p in selected_points) {-				glyph.move_selected_edit_point_delta (p, (x - last_point_x) * precision, (y - last_point_y) * precision);-				-				if (tie_x_or_y_coordinates) {-					GridTool.tie_to_prev (p, x, y);-				}-				-				p.recalculate_linear_handles ();-			}-		}-		-		last_point_x = x;-		last_point_y = y;-	}-	-	public void press (int button, int x, int y, bool double_click) {-		Glyph? g = MainWindow.get_current_glyph ();-		Glyph glyph = (!) g;-		-		return_if_fail (g != null);--		if (button == 2) {-			if (glyph.is_open ()) {-				CutTool.force_direction ();-				glyph.close_path ();-			} else {-				glyph.open_path ();-			}-			-			return;-		}--		if (double_click) {-			glyph.insert_new_point_on_path (x, y);-			return;-		}--		if (is_new_point_from_path_selected ()) {-			new_point_on_path_at (x, y);-			return;	-		}--		// add new point on path -		if (is_new_point_from_path_selected ()) {-			move_selected = true;-			move_point_on_path = true;-			glyph.new_point_on_path = null;-			return;-		}-		-		// add new point-		if (button == 3) {-			remove_all_selected_points ();-			new_point_action (x, y);-			glyph.store_undo_state ();-			return;-		}-				-		control_point_event (x, y);-		curve_corner_event (x, y);-		-		if (!move_selected_handle) {-			select_active_point (x, y);-		}--		glyph.store_undo_state ();-	}-	-	public void select_active_point (double x, double y) {-		Glyph? g = MainWindow.get_current_glyph ();-		Glyph glyph = (!) g;-		-		if (KeyBindings.modifier != SHIFT) {-			if (active_edit_point != null) {-				((!)active_edit_point).set_active (false);-			}--			remove_all_selected_points ();-		}-		-		move_selected = true;-		move_point_on_path = true;--		if (active_edit_point != null) {-			((!)active_edit_point).set_selected (true);-		}--		if (!is_over_handle (x, y)) {-			edit_active_corner = true;-			set_default_handle_positions ();-			-			if (active_edit_point != null) {-				add_selected_point ((!) active_edit_point);-			}-		}-		-		// continue adding points from the selected one-		foreach (Path p in glyph.active_paths) {-			if (p.is_open () && p.points.length () > 0 && active_edit_point == p.points.first ().data) {-				p.reverse ();-			}-		}-	}-	-	public void move_current_point_on_path (double x, double y) {-		Glyph g = MainWindow.get_current_glyph ();--		EditPoint e;-		-		double rax, ray;-		double pax, pay;--		double distance, min;-		-		return_if_fail (g.new_point_on_path != null);-		-		if (GridTool.is_visible ()) {-			GridTool.tie (ref x, ref y);-		}--		min = double.MAX;--		foreach (Path p in g.path_list) {-			if (p.points.length () < 2) {-				continue;-			}-			-			e = new EditPoint ();-			-			pax = x * Glyph.ivz () + g.view_offset_x - Glyph.xc ();-			pay = y * Glyph.ivz () + g.view_offset_y - Glyph.yc ();--			pay *= -1;--			p.get_closest_point_on_path (e, pax, pay);-			-			distance = Math.sqrt (Math.pow (Math.fabs (pax - e.x), 2) + Math.pow (Math.fabs (pay - e.y), 2));-			-			if (distance < min) {-				min = distance;-				-				g.new_point_on_path = e;-				-				rax = (pax + g.view_offset_x - Glyph.xc ()) / Glyph.ivz ();-				ray = (pax + g.view_offset_x - Glyph.xc ()) / Glyph.ivz ();-				-				g.redraw_area (rax - 5, ray - 5, 10, 10);					-			}-		}-	}--	public void set_new_point_on_path (Path ap, int x, int y) {-		Glyph g = MainWindow.get_current_glyph ();-		-		return_if_fail (ap.is_editable ());-		-		g.clear_active_paths ();-		g.add_active_path (ap);--		if (g.new_point_on_path == null) {-			g.new_point_on_path = new EditPoint (0, 0, PointType.FLOATING);-		}-		-		move_current_point_on_path (x, y);-	}-		-	public static void set_active_edit_point (EditPoint? e) {-		Glyph g = MainWindow.get_current_glyph ();-		foreach (var p in g.path_list) {-			foreach (var ep in p.points) {-				ep.set_active (false);-			}-		}-		-		active_edit_point = e;-		-		if (e != null) {-			((!)e).set_active (true);-		}--		g.redraw_area (0, 0, g.allocation.width, g.allocation.height);-	}--	EditPoint? get_closest_point (double ex, double ey, out Path? path) {-		double x = Glyph.path_coordinate_x (ex);-		double y = Glyph.path_coordinate_y (ey);-		double d = double.MAX;-		double nd;-		EditPoint? ep = null;-		Glyph g = MainWindow.get_current_glyph ();-		-		path = null;-		-		foreach (Path current_path in g.path_list) {-			foreach (EditPoint e in current_path.points) {-				nd = e.get_distance (x, y);-				-				if (nd < d) {-					d = nd;-					ep = e;-					path = current_path;-				}-			}	-		}-		-		return ep;-	}--	public double get_distance_to_closest_edit_point (double event_x, double event_y) {-		Path? p;-		EditPoint e;-		EditPoint? ep = get_closest_point (event_x, event_y, out p);--		double x = Glyph.path_coordinate_x (event_x);-		double y = Glyph.path_coordinate_y (event_y);-		-		if (ep == null) {-			return double.MAX;-		}-		-		e = (!) ep;-		-		return e.get_distance (x, y);-	}--	public void control_point_event (double event_x, double event_y) {-		Path? p;-		EditPoint? ep = get_closest_point (event_x, event_y, out p);-		Glyph g = MainWindow.get_current_glyph ();-		double x = Glyph.path_coordinate_x (event_x);-		double y = Glyph.path_coordinate_y (event_y);-		double distance;-		EditPoint e;-		-		set_active_edit_point (null);-		-		if (ep == null) {-			return;	-		}-		-		e = (!) ep;-		distance = e.get_distance (x, y) * g.view_zoom;--		if (distance < CONTACT_SURFACE) {-			set_active_edit_point (ep);-			g.add_active_path (p);-		}-	}--	public static bool insert_new_point_on_path_selected () {-		Tool t = MainWindow.get_toolbox ().get_tool ("insert_point_on_path");-		return t.is_selected ();-	}--	public static bool is_new_point_from_path_selected () {-		if (!Supplement.experimental) {-			return false;-		}-		-		Tool t = MainWindow.get_toolbox ().get_tool ("new_point_on_path");-		return t.is_selected ();-	}--	public static bool is_erase_selected () {-		Tool t = MainWindow.get_toolbox ().get_tool ("erase_tool");-		return t.is_selected ();-	}-	-	public void new_point_action (int x, int y) {-		Glyph glyph;-		EditPoint new_point;-		glyph = MainWindow.get_current_glyph ();-		glyph.open_path ();-		-		new_point = glyph.add_new_edit_point (x, y);-		new_point.set_selected (true);-		-		if (KeyBindings.modifier != SHIFT) {-			remove_all_selected_points ();-		}-		-		add_selected_point (new_point);-		-		move_selected = true;-	}--	void set_linear_handles_for_last_point () {-		Glyph glyph;-		glyph = MainWindow.get_current_glyph ();-		return_if_fail (glyph.active_paths.length () > 0);-		return_if_fail (glyph.active_paths.last ().data.points.length () > 0);-		glyph.active_paths.last ().data.points.last ().data.right_handle.parent.recalculate_linear_handles ();-	}--	public static void set_default_handle_positions () {-		Glyph g = MainWindow.get_current_glyph ();-		foreach (var p in g.path_list) {-			if (p.is_editable ()) {-				p.create_list ();-				set_default_handle_positions_on_path (p);-			}-		}-	}--	static void set_default_handle_positions_on_path (Path path) {-		foreach (EditPoint e in path.points) {-			e.recalculate_linear_handles ();-		}-	}--	bool new_point_on_path_at (int x, int y) {-		EditPoint ep;-		Glyph glyph = MainWindow.get_current_glyph ();-		-		if (is_new_point_from_path_selected ()) {-			-			if (glyph.new_point_on_path != null) {-				ep = (!)glyph.new_point_on_path; -				-				begin_new_point_on_path = false;-				-				glyph.add_new_edit_point (glyph.reverse_path_coordinate_x (ep.x), glyph.reverse_path_coordinate_x (ep.y));-				glyph.new_point_on_path = null;-				-				MainWindow.get_toolbox ().select_tool_by_name ("new_point");--				return false;-			}-			-			start_from_new_point_on_path (x, y);-			begin_new_point_on_path = true;-			-			return false;-		} else {-			begin_new_point_on_path = false;-		}-		-		return true;-	}--	void start_from_new_point_on_path (double x, double y) {		-		Glyph g = MainWindow.get_current_glyph ();-		Path? p = null;-		Path pn;-		-		p = g.get_closeset_path (x, y);--		pn = (!)p;--		if (pn.points.length () >= 2) {-			pn.set_editable (true);-			set_new_point_on_path (pn, 0, 0);-			g.open_path ();-		}-		-		begin_new_point_on_path = false;-	}-	-	public void begin_from_new_point_on_path () {-		begin_new_point_on_path = true;-	}-	-	private bool is_over_handle (double event_x, double event_y) {-		double x = Glyph.path_coordinate_x (event_x);-		double y = Glyph.path_coordinate_y (event_y);-		Glyph g = MainWindow.get_current_glyph (); -		double d_point = g.view_zoom * get_distance_to_closest_edit_point (event_x, event_y);-		-		double dl, dr;-	-		foreach (EditPoint selected_corner in selected_points) {-			dl = g.view_zoom * selected_corner.get_left_handle ().get_point ().get_distance (x, y);-			dr = g.view_zoom * selected_corner.get_right_handle ().get_point ().get_distance (x, y);-			-			if (dl < CONTACT_SURFACE && dl < d_point) {-				return true;-			}--			if (dr < CONTACT_SURFACE && dr < d_point) {-				return true;-			}-		}-	-		return false;-	}--	EditPointHandle get_closest_handle (double event_x, double event_y) {-		EditPointHandle left, right;-		double x = Glyph.path_coordinate_x (event_x);-		double y = Glyph.path_coordinate_y (event_y);		-		EditPointHandle eh = new EditPointHandle.empty();--		double d = double.MAX;-		double dn;-				-		foreach (EditPoint selected_corner in selected_points) {-			left = selected_corner.get_left_handle ();-			right = selected_corner.get_right_handle ();--			dn = left.get_point ().get_distance (x, y);-			-			if (dn < d) {-				eh = left;-				d = dn;-			}--			dn = right.get_point ().get_distance (x, y);-			-			if (dn < d) {-				eh = right;-				d = dn;-			}-		}-		-		return eh;-	}--	private void curve_active_corner_event (double event_x, double event_y) {-		EditPointHandle eh;-		-		active_handle.active = false;-		-		if (!is_over_handle (event_x, event_y)) {-			return;-		}		-		-		eh = get_closest_handle (event_x, event_y);-		eh.active = true;-		active_handle = eh;-	}--	private void curve_corner_event (double event_x, double event_y) {-		MainWindow.get_current_glyph ().open_path ();--		if (!is_over_handle (event_x, event_y)) {-			return;-		}--		move_selected_handle = true;-		selected_handle = get_closest_handle (event_x, event_y);-	}--	void add_selected_point (EditPoint p) {-		foreach (EditPoint ep in selected_points) {-			if (p == ep) {-				return;-			}-		}-		-		selected_points.append (p);-	}-	-	void remove_all_selected_points () {-		foreach (EditPoint e in selected_points) {-			e.set_active (false);-			e.set_selected (false);-		}-			-		while (selected_points.length () > 0) {-			selected_points.remove_link (selected_points.first ());-		}-	}-	-	/** Draw a test glyph. */-	public override bool test () {-		test_select_action ();-		-		test_open_next_glyph ();-		-		// paint-		test_click_action (1, 30, 30); -		test_click_action (1, 60, 30);-		test_click_action (1, 60, 60);-		test_click_action (1, 30, 60);-		-		// close-		test_click_action (3, 0, 0);--		// reopen-		test_click_action (3, 35, 35);-		-		// move around-		test_move_action (100, 200);-		test_move_action (20, 300);-		test_move_action (0, 0);-		-		// add to path-		test_move_action (70, 50);-		-		test_click_action (1, 70, 50);-		test_click_action (1, 70, 50);-		test_click_action (1, 70, 100);-		test_click_action (1, 50, 100); -		test_click_action (1, 50, 50);-		-		// close-		test_click_action (3, 0, 0);-		-		// merge it-		-		-		this.yield ();-		-		return true;-	}--	public void test_active_edit_point () {-		Glyph g;-		EditPoint epa, epb;-		-		// paint-		test_select_action ();-		test_open_next_glyph ();--		g = MainWindow.get_current_glyph ();--		test_click_action (1, 130, 130); // open path-		test_click_action (1, 130, 130); // add point-		epa = g.get_last_edit_point ();-		-		test_click_action (1, 160, 130);-		test_click_action (1, 160, 160);-		epb = g.get_last_edit_point ();-		-		test_click_action (1, 130, 160);-		-		// validate active point-		test_move_action (130, 130);-		warn_if_fail (active_edit_point == epa);-		-		test_move_action (161, 161); // close but not on-		warn_if_fail (active_edit_point == epb);-		-		warn_if_fail (epa != epb);-		-		// TODO: Test move handle here.-	}--	/** Test path coordinates and reverse path coordinates. */-	public void test_coordinates () {-		int x, y, xc, yc;-		double px, py, mx, my;-		string n;-		-		Tool.test_open_next_glyph ();-		Glyph g = MainWindow.get_current_glyph ();-		-		xc = (int) (g.allocation.width / 2.0);-		yc = (int) (g.allocation.height / 2.0);--		g.default_zoom ();-		-		x = 10;-		y = 15;-		-		px = g.path_coordinate_x (x);-		py = g.path_coordinate_y (y);--		mx = x * g.ivz () - g.xc () + g.view_offset_x;-		my = g.yc () - y * g.ivz () - g.view_offset_y;-		-		if (mx != px || my != py) {-			warning (@"bad coordinate $mx != $px || $my != $py");-		}-			-		test_reverse_coordinate (x, y, px, py, "ten fifteen");-		test_click_action (1, x, y);-	-		// offset no zoom-		n = "Offset no zoom";-		g.reset_zoom ();-		-		px = g.path_coordinate_x (x);-		py = g.path_coordinate_y (y);-		-		test_reverse_coordinate (x, y, px, py, n);-		test_click_action (1, x, y);-		-		// close path-		test_click_action (3, x, y);-	}-	-	private void test_coordinate (double x, double y, double px, double py, string n) {-		if (!(px - 0.5 <= x <= px + 0.5|| py - 0.5 <= y <= py + 0.5)) {-			warning (@"Expecting (x == px || y == py) got ($x == $px || $y == $py) in \"$n\"\n");-		}-	}-	-	private void test_reverse_coordinate (int x, int y, double px, double py, string n) {-		Glyph g = MainWindow.get_current_glyph ();-		if (x != g.reverse_path_coordinate_x (px) || g.reverse_path_coordinate_y (py) != y) {-			warning (@"Reverse coordinates does not match current point for test case \"$n\".\n $x != $(g.reverse_path_coordinate_x (px)) || $(g.reverse_path_coordinate_y (py)) != $y (x != g.reverse_path_coordinate_x (px) || g.reverse_path_coordinate_y (py) != y)");-		}-	}--	private void test_last_is_counter_clockwise (string name) {-		bool d = ((!)MainWindow.get_current_glyph ().get_last_path ()).is_clockwise ();-		-		if (d) {-			critical (@"\nPath $name is clockwise, in test_last_is_counter_clockwise");-		}		-	}-	-	private void test_last_is_clockwise (string name) {-		bool d = ((!)MainWindow.get_current_glyph ().get_last_path ()).is_clockwise ();-		-		if (!d) {-				critical (@"\nPath $name is counter clockwise, in test_last_is_clockwise");-		}--	}-	-	private bool test_reverse_last (string name) -		requires (MainWindow.get_current_glyph ().get_last_path () != null)-	{-		Glyph g = MainWindow.get_current_glyph ();-		Path p = (!) g.get_last_path ();-		bool direction = p.is_clockwise ();--		p.reverse ();-		-		if (direction == p.is_clockwise ()) {-			critical (@"Direction did not change after reverseing path \"$name\"\n");-			stderr.printf (@"Path length: $(p.points.length ()) \n");-			return false;-		}--		this.yield ();-		return true;-	}-	-	-	class Point {-		-		public int x;-		public int y;-		-		public Point (int x, int y) {-			this.x = x;-			this.y = y;-		}-	}-	-	private Point p (int x, int y) {-		return new Point (x, y);-	}-	-	private void test_triangle (Point a, Point b, Point c, string name = "") {-		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");-		-		this.yield ();-		MainWindow.get_tab_bar ().select_overview ();--		this.yield ();-		MainWindow.get_overview ().open_current_glyph ();-		-		pen_tool.test_select_action ();-		-		pen_tool.test_click_action (1, 0, 0); // open path-		-		pen_tool.test_click_action (1, a.x, a.y);-		pen_tool.test_click_action (1, b.x, b.y);-		pen_tool.test_click_action (1, c.x, c.y);-		pen_tool.test_click_action (3, 0, 0);-		-		test_reverse_last (@"Triangle reverse \"$name\" ($(a.x), $(a.y)), ($(b.x), $(b.y)), ($(c.x), $(c.y)) failed.");-		-		this.yield ();-	}-	-	private void test_various_triangles () {-		test_triangle (p (287, 261), p (155, 81), p (200, 104), "First");-		test_triangle (p (65, 100), p (168, 100), p (196, 177), "Second");-		test_triangle (p (132, 68), p (195, 283), p (195, 222), "Third");-		test_triangle (p (144, 267), p (147, 27), p (296, 267), "Fourth");-	}-	-	public bool test_reverse_path () {-		// open a new glyph-		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");--		this.yield ();-		MainWindow.get_tab_bar ().select_overview ();--		this.yield ();-		MainWindow.get_overview ().open_current_glyph ();-		-		pen_tool.test_select_action ();-		-		// paint-		int x_offset = 10;-		int y_offset = 10;-		-		test_open_next_glyph ();-		test_various_triangles ();-		-		test_open_next_glyph ();-		// draw clockwise and check direction		--		y_offset += 160;-		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (1, 17 + x_offset, 17 + y_offset);-		pen_tool.test_click_action (1, 20 + x_offset, 0 + y_offset);-		pen_tool.test_click_action (3, 0, 0);-		test_last_is_clockwise ("Clockwise triangle 1.2");--		// draw paths clockwise / counter clockwise and reverse them-		-		pen_tool.test_click_action (1, 115, 137);-		pen_tool.test_click_action (1, 89, 74);-		pen_tool.test_click_action (1, 188, 232);-		pen_tool.test_click_action (3, 0, 0);-		test_reverse_last ("Triangle 0");--		// draw incomplete paths-		y_offset += 20;-		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (3, 0, 0);-		test_reverse_last ("Point");--		y_offset += 20;-		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (3, 0, 0);-		test_reverse_last ("Double point");--		y_offset += 20;-		pen_tool.test_click_action (1, 10 + x_offset, 30 + y_offset);-		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (3, 0, 0);-		test_reverse_last ("Vertical line");--		y_offset += 20;-		pen_tool.test_click_action (1, 30 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (3, 0, 0);-		test_reverse_last ("Horisontal line");--		// triangle 1-		y_offset += 20;-		pen_tool.test_click_action (1, 10 + x_offset, -10 + y_offset);-		pen_tool.test_click_action (1, 20 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (1, 30 + x_offset, 0 + y_offset);-		pen_tool.test_click_action (3, 0, 0);-		test_reverse_last ("Triangle reverse 1");--		// box-		y_offset += 20;-		pen_tool.test_click_action (1, 100 + x_offset, 150 + y_offset);-		pen_tool.test_click_action (1, 150 + x_offset, 150 + y_offset);-		pen_tool.test_click_action (1, 150 + x_offset, 100 + y_offset);-		pen_tool.test_click_action (1, 100 + x_offset, 100 + y_offset); -		pen_tool.test_click_action (3, 0, 0); // close-		test_reverse_last ("Box 1");-		-		return true;-	}-	-	private Tool select_pen () {-		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");-		pen_tool.test_select_action ();-		return pen_tool;-	}-	-	public bool test_delete_points () {-		PenTool pen;-		Tool delete_tool = MainWindow.get_toolbox ().get_tool ("erase_tool");-		-		test_open_next_glyph ();-				-		pen = (PenTool) select_pen ();-		pen.test_click_action (1, 0, 0); // open path-		-		// draw a line with ten points-		for (int i = 1; i <= 10; i++) {-			pen.test_click_action (1, 20*i, 20);-		}	-	-		// TODO: it would be nice to test if points were created here-		-		// delete points-		delete_tool.test_select_action ();-		for (int i = 1; i <= 10; i++) {-			pen.move (20*i, 20);-			pen.test_click_action (1, 20*i, 20);-		}-		-		return true;-	}-	-	public bool test_reverse_random_triangles () {-		Tool pen;-		-		int ax, bx, cx;-		int ay, by, cy;--		bool r = true;--		test_open_next_glyph ();-		pen = select_pen ();--		for (int i = 0; i < 30; i++) {-			this.yield ();-			-			ax = Random.int_range (0, 300);-			bx = Random.int_range (0, 300);-			cx = Random.int_range (0, 300);--			ay = Random.int_range (0, 300);-			by = Random.int_range (0, 300);-			cy = Random.int_range (0, 300);--			pen.test_click_action (1, ax, ay);-			pen.test_click_action (1, bx, by);-			pen.test_click_action (1, cx, cy);-			pen.test_click_action (3, 0, 0);-		-			r = test_reverse_last (@"Random triangle № $(i + 1) ($ax, $ay), ($bx, $by), ($cx, $cy)");-			if (!r) {-				test_open_next_glyph ();-				pen = select_pen ();--				pen.test_click_action (1, ax, ay);-				pen.test_click_action (1, bx, by);-				pen.test_click_action (1, cx, cy);-		-				return false;-			}-			-			test_open_next_glyph ();-		}-		-		if (r) test_open_next_glyph ();-		-		return true;-	}-}--}diff --git a/src/Preferences.vala b/src/Preferences.valadeleted file mode 100644index 6435d8e..0000000--- a/src/Preferences.vala+++ /dev/null@@ -1,175 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/-namespace Supplement {--public class Preferences {-		-	static HashTable<string, string> data;--	public Preferences () {-		data = new HashTable<string, string> (str_hash, str_equal);-	}--	public static void set_last_file (string fn) {-		set ("last_file", fn);-	}--	public static string @get (string k) {-		string? s;-		-		lock (data) {-			s = data.lookup (k);-		}-		-		return (s != null) ? (!) s : "";-	}--	public static void @set (string k, string v) {-		lock (data) {-			data.replace (k, v);-			save (); // Fixa: save in separate io thread instead.-		}-	}--	public static string[] get_recent_files () {-		string recent = get ("recent_files");-		string[] files = recent.split ("\t");-		-		for (uint i = 0; i < files.length; i++) {-			files[i] = files[i].replace ("\\t", "\t");-		}-		-		return files;-	}--	public static void add_recent_files (string file) {-		string escaped_string = file.replace ("\t", "\\t");-		StringBuilder recent = new StringBuilder ();--		foreach (string f in get_recent_files ()) {-			if (f != file) {-				recent.append (f.replace ("\t", "\\t"));-				recent.append ("\t");-			}-		}--		recent.append (escaped_string);--		set ("recent_files", @"$(recent.str)");-	}--	public static int get_window_width() {-		string wp = get ("window_width");-		int w = int.parse (wp);-		return (w == 0) ? 860 : w;-	}--	public static int get_window_height() {-		int h = int.parse (get ("window_height"));-		return (h == 0) ? 500 : h;-	}-	-	public static void load () {-		File app_dir = Supplement.get_settings_directory ();-		File settings = app_dir.get_child ("settings");--		data = new HashTable<string, string> (str_hash, str_equal);--		if (!settings.query_exists ()) {-			stderr.printf ("No settings file.\n");-			return;-		}--		FileStream? settings_file = FileStream.open ((!) settings.get_path (), "r");-		-		if (settings_file == null) {-			stderr.printf ("Failed to load settings from file %s.\n", (!) settings.get_path ());-			return;-		}-		-		return_if_fail (settings_file != null);-		-		unowned FileStream b = (!) settings_file;-		-		string? l;-		l = b.read_line ();-		while ((l = b.read_line ())!= null) {-			string line;-			-			line = (!) l;-			-			if (line.get_char (0) == '#') {-				continue;-			}-			-			int i = 0;-			int s = 0;-			-			i = line.index_of_char(' ', s);-			string key = line.substring (s, i - s);--			s = i + 1;-			i = line.index_of_char('"', s);-			s = i + 1;-			i = line.index_of_char('"', s);-			string val = line.substring (s, i - s);-			-			// (key, val, description);-			-			data.insert (key, val);-		}-	}-	-	public static void save () {-		try {-			File app_dir = Supplement.get_settings_directory ();-			File settings = app_dir.get_child ("settings");--			return_if_fail (app_dir.query_exists ());-		-			if (settings.query_exists ()) {-				settings.delete ();-			}--			DataOutputStream os = new DataOutputStream(settings.create(FileCreateFlags.REPLACE_DESTINATION));-			uint8[] d;-			long written = 0;-			-			StringBuilder sb = new StringBuilder ();-			-			sb.append ("# Supplement settings\n");-			sb.append ("# Version: 1.0\n");-			-			foreach (var k in data.get_keys ()) {-				sb.append (k);-				sb.append (" \"");-				sb.append (data.lookup (k));-				sb.append ("\"\n");-			}-			-			d = sb.str.data;-				-			while (written < d.length) { -				written += os.write (d[written:d.length]);-			}-		} catch (Error e) {-			stderr.printf ("Can not save key settings. (%s)", e.message);	-		}	-	}-}--}diff --git a/src/Preview.vala b/src/Preview.valadeleted file mode 100644index 39f5da8..0000000--- a/src/Preview.vala+++ /dev/null@@ -1,55 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using WebKit;--namespace Supplement {--public class Preview : FontDisplay {	-	-	public Preview () {-	}-	-	public override string get_name () {-		return "Preview";-	}-	-	public override bool is_html_canvas () {-		return true;-	}--	public override void selected_canvas () {-	}--	public File get_html_file () {-		Font font = Supplement.get_current_font ();-		string path = @"$(font.get_name ()).html";-		File dir = font.get_folder ();-		File file = dir.get_child (path);-		-		if (!file.query_exists ()) {-			ExportTool.generate_html_document ((!)file.get_path (), font);				-		}-		-		return file;	-	}--	public override string get_uri () {-		return path_to_uri ((!) get_html_file ().get_path ());-	}-}-}diff --git a/src/ResizeTool.vala b/src/ResizeTool.valadeleted file mode 100644index a63a312..0000000--- a/src/ResizeTool.vala+++ /dev/null@@ -1,249 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--class ResizeTool : Tool {--	ImageSurface move_icon;-	ImageSurface move_icon_active;--	ImageSurface rotate_icon;-	ImageSurface rotate_icon_active;-		-	int active_handle = -1;-	-	Path selected_path = new Path ();-	-	bool resize = false;-	double resize_begin_x = 0;-	double resize_begin_y = 0;-	Path? original_path = null;-	-	public ResizeTool (string n) {-		base (n, "resize paths");--		select_action.connect((self) => {-			active_handle = -1;-			redraw_handle ();-		});-			-		deselect_action.connect((self) => {-			active_handle = -1;-			redraw_handle ();-		});-		-		press_action.connect((self, b, x, y) => {-			if (get_handle_at (x, y) > -1) {-				resize_begin_x = x;-				resize_begin_y = y;-				resize = true;-				original_path = null;-			}-		});--		release_action.connect((self, b, x, y) => {-			resize = false;-			original_path = null;-		});-		-		move_action.connect ((self, x, y)	 => {-			high_light (x, y);-			-			if (resize) {-				resize_process (x, y);-			}-		});-		-		draw_action.connect ((self, cairo_context, glyph) => {-				draw_handle (cairo_context, glyph);-			});-			-		move_icon = (!) Icons.get_icon ("move_handle.png");-		move_icon_active = (!) Icons.get_icon ("move_handle_active.png");--		rotate_icon = (!) Icons.get_icon ("rotate_icon.png");-		rotate_icon_active = (!) Icons.get_icon ("rotate_icon_active.png");		-		-	}--	private void resize_process (double x, double y) {-		double a, b, c, nc, tx, ty;-		-		Path p;-		-		// TODO: this function resizes only one path not a group of paths-		Path po = selected_path;-		-		if (po.empty ()) return;-				-		if (original_path == null) original_path = po.copy ();-		-		p = ((!)original_path).copy ();-		-		a = p.xmax - p.xmin;-		b = p.ymax - p.ymin;-		-		tx = x - resize_begin_x;-		ty = y - resize_begin_y;-		-		c  =  Math.sqrt (Math.pow (a, 2) + Math.pow (b, 2));-		nc =  Math.sqrt (Math.pow (a + tx, 2) + Math.pow (b + ty, 2));--		p.resize (nc / c);-		po.replace_path (p);--		MainWindow.get_current_glyph ().queue_redraw_path (po);-		MainWindow.get_current_glyph ().queue_redraw_path (p);-	}--	private void high_light (double x, double y) {-		int i = get_handle_at (x, y);--		if (active_handle != i) {-			redraw_handle ();-			active_handle = i;-		}-	}--	private int get_handle_at (double x, double y) {-		Glyph g = MainWindow.get_current_glyph ();-		int hx, hy;-		double ivz = 1/g.view_zoom;-				-		int i = 0;-		foreach (var p in g.path_list) {-			hx = (int) (p.xmax * ivz + g.view_offset_x + (g.allocation.width / 2) * ivz);-			hy = (int) (p.ymax * ivz + g.view_offset_y + (g.allocation.height / 2) * ivz);-		-			if (p.points.length () > 0 && hx <= x <= hx + move_icon.get_width () && hy <= y <= hy + move_icon.get_height ()) {-				selected_path = p;-				return i;-			}-			-			i++;-		}-		-		selected_path = new Path ();-		return -1;-	}--	private void draw_handle (Context cr, Glyph g) {-		double x, y;-		int i = 0;	-		double ivz = 1/g.view_zoom;--		if (resize) return;-						-		foreach (var p in g.path_list) {-			x = p.xmax * ivz + g.view_offset_x + (g.allocation.width / 2) * ivz;-			y = p.ymax * ivz + g.view_offset_y + (g.allocation.height / 2) * ivz;-			-			if (p.points.length () > 0) {-				ImageSurface s = (i == active_handle) ? move_icon_active : move_icon;-				cr.set_source_surface (s, x, y);-				cr.paint ();--				ImageSurface r = (i == active_handle) ? rotate_icon_active : rotate_icon;-				cr.set_source_surface (r, x, y - 20);-				cr.paint ();-			}-			-			i++;-		}-	}--	private void redraw_handle () {-		Glyph g = MainWindow.get_current_glyph ();-		int x, y;-		double ivz = 1/g.view_zoom;-		-		foreach (var p in g.path_list) {-			x = (int) (p.xmax * ivz + g.view_offset_x + (g.allocation.width / 2) * ivz);-			y = (int) (p.ymax * ivz + g.view_offset_y + (g.allocation.height / 2) * ivz);--			g.redraw_area (x, y, (int) move_icon.get_width (), (int) move_icon.get_height ());-			g.redraw_area (0, 0, g.allocation.width, g.allocation.height);-		}-	}--	public override bool test () {-		// draw and resize glyphs -		-		draw_test_glyph (0, 100); // 1:1--		draw_test_glyph (0, 89); -		resize_test_glyph (0.99); // 99%-				-		for (int i = 2; i < 10; i++) {-			draw_test_glyph (10 * (i - 1), 100);-			resize_test_glyph (1.0 / i);-		}--		int m = 100;-		for (int i = 2; i < 6; i++) {-			m += ((i - 1) * 30) + 5;-			draw_test_glyph (0, m);-			resize_test_glyph (i);-		}--		this.yield ();-		-		return true;-	}--	private void resize_test_glyph (double ratio_percent) {-		test_select_action ();-		-		this.yield ();-		-		Glyph g = MainWindow.get_current_glyph ();-		Path? pn = g.get_last_path ();-		return_if_fail (pn != null);--		Path p = (!) pn;		-		p.resize (ratio_percent);--	}-	-	private void draw_test_glyph (int x_offset, int y_offset) {-		// open a new glyph-		Tool pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");--		this.yield ();-		MainWindow.get_tab_bar ().select_overview ();--		this.yield ();-		MainWindow.get_overview ().open_current_glyph ();-		-		pen_tool.test_select_action ();-		-		// paint-		pen_tool.test_click_action (1, 10 + x_offset, 10 + y_offset); -		pen_tool.test_click_action (1, 20 + x_offset, 10 + y_offset);-		pen_tool.test_click_action (1, 20 + x_offset, 20 + y_offset);-		pen_tool.test_click_action (1, 10 + x_offset, 20 + y_offset);-		-		// close-		pen_tool.test_click_action (3, 0, 0);-	}-	-}--}diff --git a/src/SaveDialog.vala b/src/SaveDialog.valadeleted file mode 100644index 2a8ac81..0000000--- a/src/SaveDialog.vala+++ /dev/null@@ -1,98 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--public class SaveDialog : FontDisplay {--	public signal void finished ();--	public SaveDialog () {-		add_html_callback ("save_dialog", (val) => {-			if (val == "save") {-				if (MenuTab.save ()) {-					finished ();-				}-			}-			-			if (val == "save_as") {-				if (MenuTab.save_as ()) {-					finished ();-				}-			}-			-			if (val == "discard") {-				// discard it-				finished ();-			}-			-			if (val == "cancel") {-				MainWindow.get_tab_bar ().close_display (this);-			}			-			-		});-	}--	public override string get_name () {-		return "Save?";-	}--	public override bool is_html_canvas () {-		return true;-	}--	public override string get_html () {-		Font f = Supplement.get_current_font ();-		string fn = f.get_file_name ();-		-		if (fn == "") {-			fn = f.get_name () + ".ffi";-		}-				-		return """-<html>-<head>-	<script type="text/javascript" src="supplement.js"></script>-	<style type="text/css">@import url("style.css");</style>-</head>-<body>-	-	<div style="width:300px; margin: 50px auto 0 auto;">-		<div class="heading"><h2>""" + _("Save?") + """</h2></div>-		-		<p>""" + fn + """</p>-		-		<form>-			<input class="button" type="button" value=""" + "\"" + _("Save") + "\"" + """    onclick="call ('save_dialog:save');"/>-			<input class="button" type="button" value=""" + "\"" + _("Save as") + "\"" + """ onclick="call ('save_dialog:save_as');"/>-			<input class="button" type="button" value=""" + "\"" + _("Discard") + "\"" + """ onclick="call ('save_dialog:discard');"/>-			<input class="button" type="button" value=""" + "\"" + _("Cancel") + "\"" + """  onclick="call ('save_dialog:cancel');"/>-			<br />-		</form>-	</div>-</body>-</html>""";--		// FIXME:-		// tell xgettext to extract this:-		_("Save?");-	}-}-	-}diff --git a/src/Scrollbar.vala b/src/Scrollbar.valadeleted file mode 100644index 8ddd580..0000000--- a/src/Scrollbar.vala+++ /dev/null@@ -1,141 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--class Scrollbar : GLib.Object {-	Allocation alloc;--	double pos = 0;-	double height = 0.1;-	-	double y = 0;-	double h = 0;-	-	double y_begin = 0;-	double y_last = 0;-	bool move = false;-	bool visible = true;--	public signal void signal_scroll (double delta, double delta_last, double absolute);--	public Scrollbar () {-	}-	-	public void set_handle_size (double s) {-		height = (s < 0.01) ? 0.01 : s;-		-		if (height > 1) {-			height = 1;-		}-		-		h = height * alloc.height;-	}--	public void set_handle_position (double p) {-		pos = (p > 0) ? p : 0;-		y = pos * alloc.height;-	}-	-	public void button_press (uint button, double x, double y) {-		move = true;-		y_begin = y;-		y_last = y;-	}-	-	public void button_release (int button, double ex, double ey) {-		move = false;-	}-	-	public void motion_notify (double ex, double ey) {-		double d = 0;-		double dl = 0;-		double m;-		-		h = height * alloc.height;-		m = alloc.height;-				-		if (move && 0 < ey < m) {-			d = ey - y_begin;-			dl = ey - y_last;-			y += dl;-			y_last = ey;-		}--		if (y < 0) {-			y = 0;-		}--		if (y > alloc.height - h) {-			y = alloc.height - h;-			d = 0;-			dl = 0;-		}-		-		if (move) {-			pos = y / alloc.height;-		}-		-		if (move) {-			signal_scroll (d / (m - h), dl / (m -h), y / (m - h));-			pos = y / m;-		}--	}-		-	public void draw (Context cr, Allocation alloc) {-		double ax, ay, aw, ah;--		this.alloc = alloc;--		ax = alloc.width - 10;-		ay = 0;-		aw = alloc.width;-		ah = alloc.height;-						-		if (!visible) {-			cr.save ();-			cr.rectangle (ax, ay, aw, ah);-			cr.set_line_width (0);-			cr.set_source_rgba (194/255.0, 194/255.0, 194/255.0, 1);-			cr.fill_preserve ();-			cr.stroke ();-			cr.restore ();-			return;-		}-		-		cr.save ();-		cr.rectangle (ax, ay, aw, ah);-		cr.set_line_width (0);-		cr.set_source_rgba (183/255.0, 200/255.0, 223/255.0, 1);-		cr.fill_preserve ();-		cr.stroke ();-		cr.restore ();	-		-		cr.save ();-		cr.rectangle (ax, pos * alloc.height, alloc.width, ah * height);-		cr.set_line_width (0);-		cr.set_source_rgba (133/255.0, 143/255.0, 174/255.0, 1);-		cr.fill_preserve ();-		cr.stroke ();-		cr.restore ();			-	}-}--}diff --git a/src/ShrinkTool.vala b/src/ShrinkTool.valadeleted file mode 100644index cea88be..0000000--- a/src/ShrinkTool.vala+++ /dev/null@@ -1,113 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--// TODO contrast--class ShrinkTool : Tool {--	public ShrinkTool (string n) {-		base (n, "shrink path");--		select_action.connect((self) => {-			Glyph g = MainWindow.get_current_glyph ();-		-			foreach (Path p in g.path_list) {-				shrink_copy (p, 0.25);-			}-			-			MainWindow.get_glyph_canvas ().redraw ();-		});--		select_action.connect((self) => {-		});-			-		press_action.connect((self, b, x, y) => {-		});--		move_action.connect((self, x, y) => {-		});-	}-	-	public static int path_index (Path p) {-		Glyph g = MainWindow.get_current_glyph ();-		int i = 0;-		-		foreach (Path pt in g.path_list) {-			if (p == pt) {-				return i;-			}-			i++;-		}-		-		return -1;-	}-	-	public static void shrink_copy (Path px, double step) {		-		Path p = px.copy ();-		shrink (px, step);-		p.set_editable (true);-	}	-	-	public static void shrink (Path p, double step) {		-		if (p.points.length () < 3) return;--		p.create_list ();-		-		EditPoint ep;--		p.create_list ();--		foreach (EditPoint pep in p.points) {-			unowned List<EditPoint>? lep = pep.get_next ();-			-			if (unlikely (lep == null)) {-				warning ("Bad list.");-				stderr.printf (@"No links in path $(path_index (p)). Length: $(p.points.length ())\n");-				p.print_boundries ();-				continue;-			}-			-			ep = ((!)lep).data;-			move_point (p, pep, ep, step);-		}-		-		move_point (p, p.points.last ().data, p.points.first ().data, step);-		-		p.set_editable (true);-	}-	-	static void move_point (Path p, EditPoint pep, EditPoint ep, double step) {	-		double istep = 1 - step;-		-		double mx = ep.get_right_handle ().x () / 2 + ep.get_left_handle ().x () / 2;-		double my = ep.get_right_handle ().y () / 2 + ep.get_left_handle ().y () / 2;-			-		ep.x = mx * step + ep.x * istep;-		ep.y = my * step + ep.y * istep;--	}-	-	public override bool test () {-		test_select_action ();-		return true;-	}--}-	-}diff --git a/src/SpinButton.vala b/src/SpinButton.valadeleted file mode 100644index fb417b6..0000000--- a/src/SpinButton.vala+++ /dev/null@@ -1,228 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--public class SpinButton : Tool {--	public signal void new_value_action (SpinButton selected);-	-	public int8 deka = 2;-	public int8 deci = 0;-	public int8 centi = 0;-	public int8 milli = 0;-	-	bool value_from_motion = false;-	double begin_x = 0;-	double begin_y = 0;-	double begin_value = 0;-	-	double max = 9.999;-	double min = 0;-	-	public SpinButton (string? name = null, string tip = "", unichar key = '\0', uint modifier_flag = 0) {-		base (null , tip, key, modifier_flag);-		-		if (name != null) {-			base.name = (!) name;-		}-		-		set_icon ("spin_button");-		-		panel_press_action.connect ((selected, button, tx, ty) => {-			double py = Math.fabs (y - ty);-			int n;-			-			if (is_selected ()) {-				n = (button == 3) ? 10 : 1;-					-				for (int i = 0; i < n; i++) {-					if (py < 28) increase ();-					if (py > 35) decrease ();-				}-			}-			-			value_from_motion = true;-			begin_x = tx;-			begin_y = ty;-			-			begin_value = double.parse (get_display_value ());-		});--		panel_move_action.connect ((selected, button, tx, ty) => {-			double d;-			if (value_from_motion) {-			 d = (begin_y - ty) / 200;-			 d = (d < 0) ? -Math.pow (d, 2) : Math.pow (d, 2);-			 set_value_round (begin_value + d);-			}-		});--		panel_release_action.connect ((selected, button, tx, ty) => {-			value_from_motion = false;-		});-	}-	-	public void set_max (double max) {-		this.max = max;-	}--	public void set_min (double min) {-		this.min = min;-	}-	-	public void increase () {	-		if (deka == 9 && deci == 9 && centi == 9 && milli == 9) {-			return;-		}-		-		milli++;--		if (milli == 10) {-			centi++;-			milli = 0;-		}-				-		if (centi == 10) {-			deci++;-			centi = 0;-		}--		if (deci == 10) {-			deka++;-			deci = 0;-		}-		-		if (get_value () > max) {-			set_value_round (max, false);-		}--		new_value_action (this);-	}--	public void decrease () {-		if (deka == 0 && deci == 0 && centi == 0) {-			return;-		}-		-		milli--;-		-		if (milli == -1) {-			centi--;-			milli = 9;-		}-		-		if (centi == -1) {-			deci--;-			centi = 9;-		}--		if (deci == -1) {-			deka--;-			deci = 9;-		}-		-		if (get_value () < 0.05) { // lower limit-			set_value_round (0.05);-		}--		if (get_value () < min) {-			set_value_round (min, false);-		}-				-		new_value_action (this);-	}--	public void set_value (string new_value, bool check_boundries = true, bool emit_signal = true) {-		string v = new_value;-		-		while (!(v.char_count () >= 5)) {-			if (v.index_of (".") == -1) {-				v += ".";-			} else {-				v += "0";-			}-			-			return;-		}-		-		deka = (int8) int.parse (v.substring (0, 1));-		deci = (int8) int.parse (v.substring (2, 1));-		centi = (int8) int.parse (v.substring (3, 1));-		milli = (int8) int.parse (v.substring (4, 1));-		-		if (emit_signal) {-			new_value_action (this);-		}-		-		if (check_boundries && get_value () > max) {-			set_value_round (max, false);-		}--		if (check_boundries && get_value () < min) {-			set_value_round (min, false);-		}-	}--	public void set_value_round (double v, bool check_boundries = true, bool emit_signal = true) {-		int8 m;-		-		v += 0.005;-		-		if (v > 10) v = 9.999;-		if (v < 0.001) v = 0.001;-				-		m = milli; // ignore milli value-		set_value (@"$v", check_boundries, emit_signal);-		milli = m;-	}-	-	public double get_value () {-		return deka + (deci / 10.0) + (centi / 100.0) + (milli / 1000.0);-	}--	public string get_display_value () {-		return @"$deka.$deci$centi$milli";-	}--	public override void draw (Context cr) {-		double xt = x + 3 + w;-		double yt = y + h + 17;--		double text_x = -4.5;-		double text_y = 11;--		base.draw (cr);-	-		cr.save ();-	-		cr.set_source_rgba (99/255.0, 99/255.0, 99/255.0, 1);-		-		cr.set_font_size (10);-		cr.select_font_face ("Cantarell", FontSlant.NORMAL, FontWeight.NORMAL);--		cr.move_to (xt + text_x, yt + text_y);-		-		cr.show_text (get_display_value ());-		-		cr.restore ();-	}-}--}diff --git a/src/Supplement.vala b/src/Supplement.valadeleted file mode 100644index 7aa2912..0000000--- a/src/Supplement.vala+++ /dev/null@@ -1,223 +0,0 @@-/*
-    Copyright (C) 2012 Johan Mattsson
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-using Supplement;
-using Birdfont;
-
-const string GETTEXT_PACKAGE = "birdfont"; 
-
-namespace Supplement {
-
-public class Supplement {
-	public static Argument args;
-	internal static bool experimental = false;
-	internal static bool show_coordinates = false;
-	internal static bool fatal_wanings = false;
-	public static bool win32 = false;
-	internal static string exec_path = "";
-
-	internal static Font current_font;
-	public static Glyph current_glyph;
-	
-	public void init (string[] arg) {
-		int err_arg;
-		int i;
-		File font_file;
-		IdleSource idle;
-		string exec_path;
-
-		stdout.printf ("birdfont version %s\n", VERSION);
-		stdout.printf ("built on %s\n", BUILD_TIMESTAMP);
-		
-		init_gettext ();		
-		
-		args = new Argument.command_line (arg);
-
-		if (args.has_argument ("--help")) {
-			args.print_help ();
-			Process.exit (0);
-		}
-
-		err_arg = args.validate ();
-		if (err_arg != 0) {
-			stdout.printf (@"Unknown parameter $(arg [err_arg])\n\n");
-			args.print_help ();
-			Process.exit (0);
-		}
-
-		current_font = new Font ();
-		current_glyph = new Glyph ("");
-
-		experimental = args.has_argument ("--test");
-		show_coordinates = args.has_argument ("--show-coordinates");
-		fatal_wanings = args.has_argument ("--fatal-warning");
-		win32 = (arg[0].index_of (".exe") > -1) || arg[0] == "wine";
-		exec_path = "";
-
-		if (win32) {
-			// wine hack to get "." folder in win32 environment
-			i = arg[0].last_index_of ("\\");
-			
-			if (i != -1) {	
-				exec_path = arg[0];
-				exec_path = exec_path.substring (0, i);
-				exec_path = wine_to_unix_path (exec_path);			
-			}
-		} else {
-			exec_path = "./";
-		}
-		
-		Preferences preferences = new Preferences ();
-		preferences.load ();
-		
-		if (args.get_file () != "") {
-			font_file = File.new_for_path (args.get_file ());
-			
-			if (!font_file.query_exists ()) {
-				stderr.printf (@"File $(args.get_file ()) not found.");
-				Process.exit (-1);
-			}
-		}
-
-		if (fatal_wanings) {
-			LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
-			Log.set_handler (null, levels, fatal_warning);
-		}
-		
-		preferences.set_last_file (get_current_font ().get_path ());
-	}
-
-	static void init_gettext () {
-		File f;
-
-		Intl.setlocale (LocaleCategory.MESSAGES, "");
-		Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "utf-8");
-
-		f = File.new_for_path ("./build/locale/sv/LC_MESSAGES/birdfont.mo");
-		if (f.query_exists ()) {
-			Intl.bindtextdomain (GETTEXT_PACKAGE, "./build/locale");
-			return;
-		}
-		
-		f = File.new_for_path ("./locale/sv/LC_MESSAGES/birdfont.mo");
-		if (f.query_exists ()) {
-			Intl.bindtextdomain (GETTEXT_PACKAGE, "./locale");
-			return;
-		}
-
-		f = File.new_for_path ("../locale/sv/LC_MESSAGES/birdfont.mo");
-		if (f.query_exists ()) {
-			Intl.bindtextdomain (GETTEXT_PACKAGE, "../locale");
-			return;
-		}
-
-		f = File.new_for_path ("/usr/share/locale/sv/LC_MESSAGES/birdfont.mo");
-		if (f.query_exists ()) {
-			Intl.bindtextdomain (GETTEXT_PACKAGE, "/usr/local/share/locale");
-			return;
-		}		
-
-		f = File.new_for_path ("/usr/share/locale/sv/LC_MESSAGES/birdfont.mo");
-		if (f.query_exists ()) {
-			Intl.bindtextdomain (GETTEXT_PACKAGE, "/usr/share/locale");
-			return;
-		}
-		
-		warning ("translations not found");
-	}
-	
-	public static Font get_current_font () {
-		return current_font;
-	}
-
-	internal static void fatal_warning (string? log_domain, LogLevelFlags log_levels, string message) {		
-		bool fatal = true;
-		
-		if (log_domain != null) {
-			stderr.printf ("%s: \n", (!) log_domain);
-		}
-		
-		stderr.printf ("\n%s\n\n", message);
-		assert (!fatal);
-	}
-	
-	internal static void new_font () {
-		current_font = new Font ();
-	}
-
-	public static File get_preview_directory () {
-		File settings = get_settings_directory ();
-		File backup = settings.get_child ("preview");
-		
-		if (!backup.query_exists ()) {
-			DirUtils.create ((!) backup.get_path (), 0xFFFFFF);
-		}
-			
-		return backup;
-	}
-
-	internal static File get_thumbnail_directory () {
-		File thumbnails = get_settings_directory ().get_child ("thumbnails");
-		
-		if (!thumbnails.query_exists ()) {
-			DirUtils.create ((!) thumbnails.get_path (), 0xFFFFFF);
-		}
-		
-		return thumbnails;
-	}
-		
-	internal static File get_settings_directory () {
-		File home = File.new_for_path (Environment.get_home_dir ());
-		File settings = home.get_child (".birdfont");
-		
-		if (!settings.query_exists ()) {
-			DirUtils.create ((!) settings.get_path (), 0xFFFFFF);
-		}
-			
-		return settings;
-	}
-
-	internal static File get_backup_directory () {
-		File settings = get_settings_directory ();
-		File backup = settings.get_child ("backup");
-		
-		if (!backup.query_exists ()) {
-			DirUtils.create ((!) backup.get_path (), 0xFFFFFF);
-		}
-			
-		return backup;
-	}
-
-	internal static bool has_argument (string param) {
-		if (is_null (args)) {
-			warning ("args is null");
-			return false;
-		}
-		
-		return args.has_argument (param);
-	}
-	
-	internal static string? get_argument (string param) {
-		return args.get_argument (param);
-	}	
-}
-
-internal bool is_null (void* n) {
-	return n == null;
-}
-
-}
diff --git a/src/Svg.vala b/src/Svg.valadeleted file mode 100644index 50dca9a..0000000--- a/src/Svg.vala+++ /dev/null@@ -1,345 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--class Svg {--	/** Export to svg glyph data. */-	public static string to_svg_glyph (Glyph g, double scale = 1) {	-		StringBuilder svg = new StringBuilder ();--		foreach (Path pl in g.path_list) {-			write_path_as_glyph (pl, svg, g, scale);-		}-		-		return svg.str;-	}--	/** Export to regular svg path data. */-	public static string to_svg_path (Path pl, Glyph g, double scale = 1) {	-		StringBuilder svg = new StringBuilder ();-		pl.create_list ();-		write_path (pl, svg, g, false, scale);-		return svg.str;-	}--	private static void write_path_as_glyph (Path pl, StringBuilder svg, Glyph g, double scale = 1) {-		write_path (pl, svg, g, true, scale);-	}--	private static void write_path (Path pl, StringBuilder svg, Glyph g, bool do_glyph, double scale = 1) {-		int i = 0;-		EditPoint? n = null;-		EditPoint m;-		-		if (pl.points.length () <= 2) {-			return;-		}-		-		pl.create_list ();-		-		foreach (var e in pl.points) {-			if (i == 0) {-				add_abs_start (e, svg, g, do_glyph, scale);-				i++;-				n = e;-				continue;-			}-			-			m = (!) n;--			add_abs_next (m, e, svg, g, do_glyph, scale);-			-			n = e;-			i++;-		}--		m = pl.points.first ().data;-		-		add_abs_next ((!) n, m, svg, g, do_glyph, scale);--		close_path (svg);-	}--	private static void add_abs_next (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool do_glyph, double scale = 1) {-		if (start.right_handle.type == PointType.LINE && end.left_handle.type == PointType.LINE) {-			add_abs_line_to (end, start, svg, g, do_glyph, scale);-		} else if (end.get_left_handle ().type == PointType.NONE) {-			add_quadratic_abs_path (end, start, svg, g, do_glyph, scale);-		} else {-			add_cubic_abs_path (end, start, svg, g, do_glyph, scale);-		}-	}--	private static void add_abs_start (EditPoint ep, StringBuilder svg, Glyph g, bool do_glyph, double scale = 1) {		-		double left = g.left_limit;-		double baseline = Supplement.get_current_font ().base_line;-		-		svg.append_printf ("M");--		svg.append_printf ("%s ",  round ((ep.x - left) * scale));-		svg.append_printf ("%s ",  round ((ep.y + baseline) * scale));-	}-		-	private static void close_path (StringBuilder svg) {-		svg.append ("z");-	}	-	-	private static void add_abs_line_to (EditPoint start, EditPoint stop, StringBuilder svg, Glyph g, bool to_glyph, double scale = 1) {-		double baseline = Supplement.get_current_font ().base_line;-		double left = g.left_limit;-		-		double xa, ya, xb, yb;-		-		Path.get_line_points (start, stop, out xa, out ya, out xb, out yb);--		double height = Supplement.get_current_font ().get_height (); // no probably not--		double center_x = Glyph.xc ();-		double center_y = Glyph.yc ();-	-		if (!to_glyph) {-			svg.append ("L");-						-			svg.append_printf ("%s ", round ((xa - center_x - left) * scale));-			svg.append_printf ("%s ", round ((ya - center_y - baseline + height) * scale));		--			svg.append ("L");-			-			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));-			svg.append_printf ("%s ", round ((yb - center_y - baseline + height) * scale));	--		} else {-			svg.append ("L");-			-			svg.append_printf ("%s ", round ((xa - center_x - left) * scale));-			svg.append_printf ("%s ", round ((-ya + center_y + baseline) * scale));--			svg.append ("L");-			-			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));-			svg.append_printf ("%s ", round ((-yb + center_y + baseline) * scale));				-		}-	}-	-	private static void add_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph, double scale = 1) {-		double left = g.left_limit;-		double baseline = Supplement.get_current_font ().base_line;-		double height = Supplement.get_current_font ().get_height (); // no probably not-		-		double xa, ya, xb, yb, xc, yc, xd, yd;-		-		Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);--		double center_x = Glyph.xc ();-		double center_y = Glyph.yc ();-		-		// cubic path-		if (!to_glyph) {-			svg.append_printf ("Q");--			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));-			svg.append_printf ("%s ", round ((yb - center_y - baseline + height) * scale));-			-			svg.append_printf ("%s ", round ((xc - center_x - left) * scale));-			svg.append_printf ("%s ", round ((yc - center_y - baseline + height) * scale));--		} else {		-			svg.append_printf ("Q");--			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));-			svg.append_printf ("%s ", round ((-yb + center_y + baseline) * scale));-			-			svg.append_printf ("%s ", round ((xc - center_x - left) * scale));-			svg.append_printf ("%s ", round ((-yc + center_y + baseline) * scale));	-		}-	}-			-	private static void add_cubic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph, double scale = 1) {-		double left = g.left_limit;-		double baseline = Supplement.get_current_font ().base_line;-		double height = Supplement.get_current_font ().get_height (); // no probably not-		-		double xa, ya, xb, yb, xc, yc, xd, yd;-		-		Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);--		double center_x = Glyph.xc ();-		double center_y = Glyph.yc ();-		-		// cubic path-		if (!to_glyph) {-			svg.append_printf ("C");--			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));-			svg.append_printf ("%s ", round ((yb - center_y - baseline + height) * scale));-			-			svg.append_printf ("%s ", round ((xc - center_x - left) * scale));-			svg.append_printf ("%s ", round ((yc - center_y - baseline + height) * scale));-			-			svg.append_printf ("%s ", round ((xd - center_x - left) * scale));-			svg.append_printf ("%s ", round ((yd - center_y - baseline + height) * scale));	--		} else {		-			svg.append_printf ("C");--			svg.append_printf ("%s ", round ((xb - center_x - left) * scale));-			svg.append_printf ("%s ", round ((-yb + center_y + baseline) * scale));-			-			svg.append_printf ("%s ", round ((xc - center_x - left) * scale));-			svg.append_printf ("%s ", round ((-yc + center_y + baseline) * scale));	-			-			svg.append_printf ("%s ", round ((xd - center_x - left) * scale));-			svg.append_printf ("%s ", round ((-yd + center_y + baseline) * scale));	-		}-	}-	-	/** Draw path from svg font data. */-	public static void draw_svg_path (Context cr, string svg, double x, double y, double scale = 1) {-		double x1, x2, x3;-		double y1, y2, y3;--		string[] d = svg.split (" ");-		-		x /= scale;-		y /= scale;--		if (d.length == 0) {-			return;-		}-		-		cr.save ();--		cr.set_line_width (1);-		cr.set_source_rgba (0, 0, 0, 1);-		-		if (svg == "") {-			return;-		}-		-		for (int i = 0; i < d.length; i++) {-			-			// trim off leading white space	-			while (d[i].index_of (" ") == 0) { -				d[i] = d[i].substring (1);-			}-			-			if (d[i].index_of ("L") == 0) {-				x1 = double.parse (d[i].substring (1)) + x;-				y1 = -double.parse (d[i+1]) + y;-				-				x1 *= scale;-				y1 *= scale;-				-				cr.line_to (x1, y1);-				continue;-			}--			if (d[i].index_of ("Q") == 0) {-				x1 = double.parse (d[i].substring (1)) + x;-				y1 = -double.parse (d[i+1]) + y;--				x2 = double.parse (d[i+2]) + x;-				y2 = -double.parse (d[i+3]) + y;--				x1 *= scale;-				y1 *= scale;--				x2 *= scale;-				y2 *= scale;-																-				cr.curve_to (x1, y1, x2, y2, x2, y2);-				continue;-			}-			-			if (d[i].index_of ("C") == 0) {-				x1 = double.parse (d[i].substring (1)) + x;-				y1 = -double.parse (d[i+1]) + y;--				x2 = double.parse (d[i+2]) + x;-				y2 = -double.parse (d[i+3]) + y;--				x3 = double.parse (d[i+4]) + x;-				y3 = -double.parse (d[i+5]) + y;--				x1 *= scale;-				y1 *= scale;--				x2 *= scale;-				y2 *= scale;-				-				x3 *= scale;-				y3 *= scale;-																-				cr.curve_to (x1, y1, x2, y2, x3, y3);-				continue;-			}--			if (d[i].index_of ("M") == 0) {-				x1 = double.parse (d[i].substring (1)) + x;-				y1 = -double.parse (d[i+1]) + y;-				-				x1 *= scale;-				y1 *= scale;-				-				cr.move_to (x1, y1);-				continue;-			}-								-			if (d[i].index_of ("zM") == 0) {-				cr.close_path ();-				-				x1 = double.parse (d[i].substring (2)) + x;-				y1 = -double.parse (d[i+1]) + y;-				-				x1 *= scale;-				y1 *= scale;-				-				cr.move_to (x1, y1);-				continue;-			}--			if (d[i].index_of ("z") == 0) {-				cr.close_path ();-				continue;-			}-		-		}-		-		cr.fill ();-		cr.restore ();-	}--}--internal static string round (double p) {-	string v = p.to_string ();-	char[] c = new char [501];-	-	v = p.format (c, "%3.15f");-	-	if (v.index_of ("e") != -1) {	-		return "0.0";-	}-	-	return v;-}-	-}diff --git a/src/SvgFontFormatWriter.vala b/src/SvgFontFormatWriter.valadeleted file mode 100644index 2b8c2a0..0000000--- a/src/SvgFontFormatWriter.vala+++ /dev/null@@ -1,132 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--class SvgFontFormatWriter : Object  {--	DataOutputStream os;--	public SvgFontFormatWriter () {-	}--	public void open (File file) throws Error {-		if (file.query_exists ()) {-			throw new FileError.EXIST ("SvgFontFormatWriter: file exists.");-		}-		-		os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));-	}--	public void close () throws Error {-		os.close ();-	}-	-	public void write_font_file (Font font) throws Error {-		string font_name = font.get_name ();-		-		int units_per_em = 100;-		-		int ascent = 80;-		int descent = -20;-		-		StringBuilder b;-		-		Glyph? g;-		Glyph glyph;-		unichar indice = 0;-		-		string uni;-		-		put ("""<?xml version="1.0" standalone="no"?>""");-		put ("""<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >""");-		put ("""<svg xmlns="http://www.w3.org/2000/svg">""");--		// (metadata goes here)--		put ("<defs>");--		put (@"<font id=\"$font_name\" horiz-adv-x=\"250\" >");-		put (@"<font-face units-per-em=\"$units_per_em\" ascent=\"$ascent\" descent=\"$descent\" />");--		// (missing-glyph goes here)--		while (true) {-			g = font.get_glyph_indice (indice++);-			-			if (g == null) {-				break;-			}-			-			glyph = (!) g;-			-			b = new StringBuilder ();-			b.append_unichar (glyph.get_unichar ());--			if (glyph.get_unichar () >= ' ' && b.str.validate ()) {-				if (b.str == "\"" || b.str == "&" || b.str == "<") {-					uni = Font.to_hex_code (glyph.get_unichar ());-					put (@"<glyph unicode=\"&#x$(uni);\" horiz-adv-x=\"$(glyph.get_width ())\" d=\"$(glyph.get_svg_data ())\" />");			-				} else {-					put (@"<glyph unicode=\"$(b.str)\" horiz-adv-x=\"$(glyph.get_width ())\" d=\"$(glyph.get_svg_data ())\" />");-				}-			}-		}--		while (true) {-			g = font.get_glyph_indice (indice++);-			-			if (g == null) {-				break;-			}-			-			glyph = (!) g;-			-			foreach (Kerning k in glyph.kerning) {-				string l, r;-				Font f = Supplement.get_current_font ();-				Glyph? gr = f.get_glyph (k.glyph_right);-				Glyph glyph_right;-				-				if (gr == null) {-					warning ("kerning glyph that does not exist.");-				}-				-				glyph_right = (!) gr;-				-				l = Font.to_hex_code (glyph.unichar_code);-				r = Font.to_hex_code (glyph_right.unichar_code);-								-				os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(k.val)\"/>\n");-			}-		}		--		put ("</font>");-		put ("</defs>");-		put ("</svg>");-	}--	/** Write a new line */-	private void put (string line) throws Error {-		os.put_string (line);-		os.put_string ("\n");-	}--}---}diff --git a/src/Tab.vala b/src/Tab.valadeleted file mode 100644index fc9a582..0000000--- a/src/Tab.vala+++ /dev/null@@ -1,49 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--public class Tab : GLib.Object {--	bool always_open;-	double width; -	FontDisplay display;--	public Tab (FontDisplay glyph, double tab_width, bool always_open) {-		width = tab_width;-		this.display = glyph;-		this.always_open = always_open;-	}--	public bool has_close_button () {-		return !always_open;-	}--	public FontDisplay get_display () {-		return display;-	}-	-	public string get_label () {-		return display.get_name ();-	}-	-	public double get_width () {-		return width;-	}-}--}diff --git a/src/TabBar.vala b/src/TabBar.valadeleted file mode 100644index fb10a5f..0000000--- a/src/TabBar.vala+++ /dev/null@@ -1,583 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--public class TabBar : GLib.Object {-	-	int width = 0;-	int height = 0;-	-	public List<Tab> tabs = new List<Tab> ();--	static const int NO_TAB = -1;-	static const int NEXT_TAB = -2;-	static const int PREVIOUS_TAB = -3;--	int first_tab = 0;-	int selected = 0;-	int over = NO_TAB;-	int over_close = NO_TAB;-	-	public signal void signal_tab_selected (Tab selected_tab);--	Tab? previous_tab = null;-	Tab? current_tab = null;--	ImageSurface? tab1_left = null;-	ImageSurface? tab1_right = null;--	ImageSurface? tab2_left = null;-	ImageSurface? tab2_right = null;--	ImageSurface? tab3_left = null;-	ImageSurface? tab3_right = null;-	-	ImageSurface? bar_background = null;-	ImageSurface next_tab;-	ImageSurface to_previous_tab;-			-	public TabBar () {-		tab1_left = Icons.get_icon ("tab1_left.png");-		tab1_right = Icons.get_icon ("tab1_right.png");--		tab2_left = Icons.get_icon ("tab2_left.png");-		tab2_right = Icons.get_icon ("tab2_right.png");--		tab3_left = Icons.get_icon ("tab3_left.png");-		tab3_right = Icons.get_icon ("tab3_right.png");-		-		bar_background = Icons.get_icon ("tabbar_background.png");-		-		next_tab = (!) Icons.get_icon ("next_tab.png");-		to_previous_tab = (!) Icons.get_icon ("previous_tab.png");-		-		return_if_fail (!is_null (next_tab));-	}-	-	public void motion (double x, double y) {-		TooltipArea status = MainWindow.get_tool_tip ();-		is_over_close (x, y, out over, out over_close);-	}-	-	private bool is_over_next (double x) {-		return x > width - 19 && has_scroll ();-	}--	private bool is_over_previous (double x) {-		return x < 19  && has_scroll ();-	}--	private void is_over_close (double x, double y, out int over, out int over_close) {-		int i = 0;-		double offset = 19;-		-		if (x < 19 && has_scroll ()) {-			over_close = NO_TAB;-			over = PREVIOUS_TAB;-			return;-		}-		-		foreach (Tab t in tabs) {-			-			if (i < first_tab) {-				i++;-				continue;-			}-			-			if (offset + t.get_width () + 3 > width && has_scroll ()) {-				over_close = NO_TAB;-				over = NEXT_TAB;-				return;-			}-			-			if (offset < x < offset + t.get_width ()) {-				over = i;-				-				if (8 < y < 20 && x > offset + t.get_width () - 16) {-					over_close =  i;-				} else {-					over_close =  NO_TAB;-				}-				-				return;-			}-			-			offset += t.get_width () + 3;-			i++;-		}--		over_close = NO_TAB;		-		over = NO_TAB;-	}	-	-	-	/** Select tab for a glyph by charcode or name.-	 * @return true if the tab was found-	 */-	public bool select_char (string s) {-		int i = 0;-		foreach (Tab t in tabs) {-			if (t.get_label () == s) {-				select_tab (i);-				return true;-			}-			i++;-		}-		-		return false;-	}--	public bool select_tab_name (string s) {-		return select_char (s);-	}--	public void select_overview () {-		select_tab_name ("Overview");-	}--	private void select_previous_tab () {-		Tab t;-		bool open;-		-		if (previous_tab == null) {-			return;-		}-		-		t = (!) previous_tab;-		open = selected_open_tab (t);-		-		if (!open) {-			select_tab ((int) tabs.length () - 1);-		}-	}-		-	public void close_display (FontDisplay f) {-		int i = -1;-		foreach (var t in tabs) {-			++i;-			-			if (t.get_display () == f) {-				close_tab (i) ;-				return;-			}-		}-		-		return_if_fail (i != -1);-	} --	public bool close_tab (int index, bool background_tab = false) {	-		unowned List<Tab?>? lt;-		Tab t;-		-		if (!(0 <= index < tabs.length ())) {-			return false;-		}-		-		lt = tabs.nth(index);-		-		if (lt == null || ((!) lt).data == null) {-			return false;-		}--		if (first_tab > 0) {-			first_tab--;-		}--		t = (!) ((!) lt).data;--		if (t.has_close_button ()) {-			tabs.delete_link (tabs.nth(index));-			-			if (!background_tab) {-				select_previous_tab ();-			}-			-			return true;-		}-		-		select_tab (index);-		return false;-	}-	-	public bool close_by_name (string name, bool background_tab = false) {-		int i = 0;-		-		foreach (var t in tabs) {-			if (t.get_label () == name) {-				return close_tab (i, background_tab);-			}-			-			i++;-		}-		-		return false;-	}-	-	public void close_background_tab_by_name (string name) {-		close_by_name (name, true);-	}-	-	/** Select a tab and return true if it is open. */-	public bool selected_open_tab (Tab t) {-		int i = 0;-		foreach (var n in tabs) {-			if (n == t) {-				select_tab (i);-				return true;-			}-			-			i++;-		}-		-		return false;-	}--	public Tab? get_tab (string name) {-		foreach (var n in tabs) {-			if (n.get_label () == name) {-				return n;-			}-		}-		-		return null;-	}--	public bool selected_open_tab_by_name (string t) {-		int i = 0;-		foreach (var n in tabs) {-			if (n.get_label () == t) {-				select_tab (i);-				return true;-			}-			-			i++;-		}-		-		return false;-	}-		-	public Tab get_selected_tab () {-		return tabs.nth (get_selected ()).data;-	}-	-	public uint get_length () {-		return tabs.length ();-	}--	public int get_selected () {-		return selected;-	}-	-	public void select_tab (int index) {-		Tab t;--		if (index == NEXT_TAB) {-			selected++;-			-			if (selected >=  tabs.length ()) {-				selected = (int) tabs.length () - 1;-			}-			-			scroll_to_tab (selected);-			return;-		}-		-		if (index == PREVIOUS_TAB) {--			if (selected > 0) {-				selected--;-			}-			-			scroll_to_tab (selected);-			return;-		}-		-		if (!(0 <= index < tabs.length ())) {-			return;-		}--		selected = index;-		-		unowned List<Tab?>? lt = tabs.nth(index);-		-		return_if_fail(lt != null);-		t = (!) ((!) lt).data;-		-		previous_tab = current_tab;-		current_tab = t;--		scroll_to_tab (selected);-	}-	-	private bool has_scroll () {-		int i = 0;-		double offset = 19;-		-		if (first_tab > 0) {-			return true;-		}-		-		foreach (Tab t in tabs) {	-			if (i < first_tab) {-				i++;-				continue;-			}-			-			if (offset + t.get_width () + 3 > width - 19) {-				return true;-			}--			offset += t.get_width () + 3;-			i++;-		}-		-		return false;		-	}-	-	private void signal_selected (int index) {-		unowned List<Tab?>? lt;-		Tab t;-		-		lt = tabs.nth(index);-		return_if_fail(lt != null);		-		t = (!) ((!) lt).data;-		-		signal_tab_selected (t);		-	}-	-	private void scroll_to_tab (int index) {-		double offset = 19;-		int i = 0;-		-		if (index < first_tab) {-			first_tab = index;-			signal_selected (index);-			return;-		}-		-		foreach (Tab t in tabs) {-			-			if (i < first_tab) {-				i++;-				continue;-			}-			-			// out of view-			if (offset + t.get_width () + 3 > width - 19) {-				first_tab++;-				scroll_to_tab (index);-				return;-			}--			// in view-			if (i == index) {-				signal_selected (index);-				return;-			}--			offset += t.get_width () + 3;-			i++;-		}-		-		warning ("");-	}-	-	public void select_tab_click (double x, double y, int width, int height) {-		int over, close;-		-		this.width = width;-		this.height = height;-		-		is_over_close (x, y, out over, out close);--		if (over_close >= 0 && over == selected) {-			close_tab (over_close);-		} else {-			select_tab (over);-		}-	}-	-	public void add_tab (FontDisplay display_item, double tab_width = -1, bool always_open = false) {-		int s = (tabs.length () == 0) ? 0 : selected + 1;-		-		if (tab_width < 0) {-			//cr.text_extents (display_item.get_name (), out te); // this is not a good estimation, pango might solve it-			//tab_width = te.width + 30;-			-			tab_width = 9 * display_item.get_name ().char_count ();-			tab_width += 30;-		}-				-		tabs.insert (new Tab (display_item, tab_width, always_open), s);-		select_tab (s);-	}-	-	/** Returns true if the new item was added to the bar. */-	public bool add_unique_tab (FontDisplay display_item, double tab_width = -1, bool always_open = false) {-		bool i = select_tab_name (display_item.get_name ());--		if (!i) {-			add_tab (display_item, tab_width, always_open);-			return true;-		}-		-		return false;-	}-	-	public void draw (Context cr, int width, int height) {-		this.width = width;-		this.height = height;-		-		cr.save ();-		cr.rectangle (0, 0, width, height);-		cr.set_line_width (0);-		cr.set_source_rgba (183/255.0, 200/255.0, 223/255.0, 1);-		cr.fill_preserve ();-		cr.stroke ();-		cr.restore ();--		for (int j = 0; j < width; j++) {-			cr.set_source_surface ((!) bar_background, j, 0);-			cr.paint ();-		}--		if (has_scroll ()) {-			// left arrow-			cr.set_source_surface (to_previous_tab, 3, (height - to_previous_tab.get_height ()) / 2.0);-			cr.paint ();--			// right arrow-			cr.set_source_surface (next_tab, width - 19, (height - next_tab.get_height ()) / 2.0);-			cr.paint ();-		}-		-		draw_tabs (cr);-		-	}-	-	// this can be removed when tabs are not referenced by name-	string translate (string s) {-		switch (s) {-			case "Overview":-				return _("Overview");-				break;-			case "Kerning":-				return _("Kerning");-				break;-			case "Menu":-				return _("Menu");-				break;-			default:-				return s;-				break;-		}-	}-	-	private void draw_tabs (Context cr) {-		double close_opacity;-		double offset = 19;-		int i = 0;-		-		foreach (Tab t in tabs) {-			-			if (i < first_tab) {-				i++;-				continue;-			}--			cr.save ();-			cr.translate (offset, 0);-						-			if (offset + t.get_width () + next_tab.get_width () + 3 > width) {-				break;-			}-		-			// background-			if (i == selected) {-				for (int j = 0; j < t.get_width (); j++) {-					cr.set_source_surface ((!) tab3_right, j, 2);-					cr.paint ();-				}							-			} else if (i == over) {-				for (int j = 0; j < t.get_width (); j++) {-					cr.set_source_surface ((!) tab2_right, j, 2);-					cr.paint ();-				}				-			} else {-				for (int j = 0; j < t.get_width (); j++) {-					cr.set_source_surface ((!) tab1_right, j, 2);-					cr.paint ();-				}-			}-			-			// close-			if (t.has_close_button ()) {-				cr.set_line_width (1);-				-				close_opacity = (over_close == i) ? 1 : 0.2; -				cr.set_source_rgba (0, 0, 0, close_opacity);-				-				cr.move_to (t.get_width () - 5, 11);-				cr.line_to (t.get_width () - 10, 16);--				cr.move_to (t.get_width () - 10, 11);-				cr.line_to (t.get_width () - 5, 16);-				-				cr.stroke ();	-			}--			cr.set_source_rgba (0, 0, 0, 1);-			cr.set_font_size (14);-			cr.move_to (8, 18);-			cr.show_text (translate (t.get_label ()));-			cr.stroke ();-			-			// edges-			if (tab1_left != null  && tab1_right != null && tab2_left != null  && tab2_right != null && tab3_left != null  && tab3_right != null) {-				if (i == selected) {-					cr.set_source_surface ((!) tab3_left, 0, 2);-					cr.paint ();-					-					cr.set_source_surface ((!) tab3_right, t.get_width () - 2, 2);-					cr.paint ();-				} else if (i == over) {-					cr.set_source_surface ((!) tab2_left, 0, 2);-					cr.paint ();--					cr.set_source_surface ((!) tab2_right, t.get_width () - 2, 2);-					cr.paint ();-				} else {-					cr.set_source_surface ((!) tab1_left, 0, 2);-					cr.paint ();--					cr.set_source_surface ((!) tab1_right, t.get_width () - 2, 2);-					cr.paint ();-				}-			}--			cr.restore ();-			-			offset += t.get_width () + 3;-			i++;-		}-	}-}--}diff --git a/src/TestCases.vala b/src/TestCases.valadeleted file mode 100644index 29730a9..0000000--- a/src/TestCases.vala+++ /dev/null@@ -1,527 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--/** All the things we want to test listed is here. */-class TestCases {-	-	public List<Test> test_cases;--	public TestCases () {-		add (test_overview, "Overview");-		add (test_data_reader, "Font data reader");-		add (test_argument, "Argument list");-		add (test_glyph_ranges, "Glyph ranges");-		add (test_glyph_table, "Glyph table");-		add (test_active_edit_point, "Active edit point");-		add (test_hex, "Unicode hex values");-		add (test_reverse_path, "Reverse path");-		add (test_reverse_random_paths, "Reverse random paths");-		add (test_coordinates, "Coordinates");-		add (test_drawing, "Pen tool");-		add (test_delete_points, "Delete edit points");-		add (test_save_backup, "Save backup");-		add (test_convert_to_quadratic_bezier_path, "Convert to quadratic path");-		add (test_notdef, "Notdef");-		add (test_merge, "Merge");-		add (test_over_path, "Over path");-		add (test_preview, "Preview");-		add (test_export, "Export");-		add (test_background_coordinates, "Background coordinates");-	}--	public static void test_background_coordinates () {-		GlyphBackgroundImage bg = new GlyphBackgroundImage ("");-		-		bg.set_position (100, 100);-		bg.set_img_offset (bg.img_offset_x, bg.img_offset_y);-		warn_if_fail (bg.img_x == 100 && bg.img_y == 100);--		bg.set_img_offset (100, 100);-		bg.set_position (bg.img_x, bg.img_y);-		warn_if_fail (bg.img_offset_x == 100 && bg.img_offset_y == 100);-	}--	public static void test_export () {-		Glyph g;-		Path p = new Path ();-		-		Tool.test_open_next_glyph ();-		g = MainWindow.get_current_glyph ();-				-		p.add (-10, -10);-		p.add (50, 50);-		p.add (10, -10);-		-		p.close ();-		g.add_path (p);	--		MenuTab.preview ();--		// TODO: run this many times on big font-		for (int i = 0; i < 10; i++) {-			ExportTool.export_all ();-			Tool.yield ();-		}		-	}---	public static void test_preview () {-		Glyph g;-		Path p = new Path ();-		-		Tool.test_open_next_glyph ();-		g = MainWindow.get_current_glyph ();-				-		p.add (-10, 10);-		p.add (10, 10);-		p.add (10, -10);-		p.add (-10, -10);-		p.close ();-		g.add_path (p);	--		MenuTab.preview ();--		// TODO: run this many times on big font-		for (int i = 0; i < 100; i++) {-			MainWindow.get_tab_bar ().select_tab_name ("Menu");-			Tool.yield ();-			-			MainWindow.get_tab_bar ().select_tab_name ("Preview");-			Tool.yield ();-		}-		-	}--	public static void test_over_path () {-		Glyph g;-		Path p = new Path ();-		Tool pen_tool;-		-		pen_tool = MainWindow.get_toolbox ().get_tool ("pen_tool");-		pen_tool.test_select_action ();-		Tool.test_open_next_glyph ();-		-		g = MainWindow.get_current_glyph ();--		pen_tool.test_click_action (1, 10, 10);-		pen_tool.test_click_action (1, 10, 10);-		pen_tool.test_click_action (1, 100, 10);-		pen_tool.test_click_action (1, 100, 100);-		pen_tool.test_click_action (1, 10, 100);-		pen_tool.test_click_action (3, 0, 0);--		warn_if_fail (g.active_paths.length () == 0);--		g.select_path (50, 50);--		warn_if_fail (g.active_paths.length () == 1);-		-		p.add (-10, 10);-		p.add (10, 10);-		p.add (10, -10);-		p.add (-10, -10);-		p.update_region_boundries ();-		g.add_path (p);-		g.close_path ();--		if (!p.is_over_coordinate (0, 0)) {-			warning ("Coordinate 0, 0 is not in path.");-		}-		-		if (!p.is_over_coordinate (-10, 10)) {-			warning ("Corner corrdinate -10, 10 is not in path.");-		}-		-		warn_if_fail (!p.is_over_coordinate (-20, -20));-		-		for (double x = -10; x <= 10; x += 0.1) {-			for (double y = 10; y <= 10; y += 0.1) {-				warn_if_fail (p.is_over_coordinate (x, y));-			} -		} -		-	}--	public static void test_merge () {-		Glyph g;-		Path p = new Path ();-		Path p2 = new Path ();-		-		Tool.test_open_next_glyph ();-		g = MainWindow.get_current_glyph ();-				-		p.add (-10, 10);-		p.add (10, 10);-		p.add (10, -10);-		p.add (-10, -10);-		p.close ();-		g.add_path (p);--		p2.add (10, 10);-		p2.add (10, -10);		-		p2.add (20, -10);-		p2.add (20, 10);-		p2.reverse ();-		p2.close ();-		g.add_path (p2);-		-		g.add_active_path (p);-		g.add_active_path (p);-		-		g.add_active_path (p);-		g.merge_all	();-	}--	public static void test_notdef () {-		Font f = Supplement.get_current_font ();-		Glyph n = f.get_not_def_character ();-		Glyph g;-		Path pn;-		-		f.add_glyph (n);-		-		Tool.test_open_next_glyph ();-		g = MainWindow.get_current_glyph ();-		foreach (Path p in n.path_list) {-			pn = p.copy ().get_quadratic_points ();-			g.path_list.append (pn);-			pn.move (50, 0);-			g.path_list.append (p.copy ());-		}-	}--	public static void test_convert_to_quadratic_bezier_path () {-		Glyph g;-		Path gqp;-		Path gqp_points;-		-		Path p = new Path ();-		Path p1 = new Path ();-		List<Path> qpl = new List<Path> (); -		-		EditPoint e0, e1, e2, e3;-		-		g = MainWindow.get_current_glyph ();-		-		// split_all_cubic_in_half-		foreach (Path gp in g.path_list) {-			gqp_points = gp.copy ();-			gqp_points.split_cubic_in_parts (gqp_points);-			gqp_points.move (50, 0);-			qpl.append (gqp_points);-		}-		-		// convert to quadratic points-		foreach (Path gp in g.path_list) {-			gqp = gp.get_quadratic_points ();-			gqp.move (100, 0);-			qpl.append (gqp);-		}--		foreach (Path gp in qpl) {-			g.add_path (gp);-		}--		Tool.test_open_next_glyph ();-		-		g = MainWindow.get_current_glyph ();-		-		p.add (-10, 10);-		p.add (10, 10);-		p.add (10, -10);-		p.add (-10, -10);-		p.close ();-		g.add_path (p);-		g.add_path (p1.get_quadratic_points ());--		e0 = new EditPoint (20, 40);-		e1 = new EditPoint (40, 40);-		e2 = new EditPoint (40, 20);-		e3 = new EditPoint (20, 20);--		p1.add_point (e0);-		p1.add_point (e1);-		p1.add_point (e2);-		p1.add_point (e3);-		p1.close ();--		e0.set_tie_handle (true);-		e1.set_tie_handle (true);-		e2.set_tie_handle (true);-		e3.set_tie_handle (true);--		e0.process_tied_handle ();-		e1.process_tied_handle ();-		e2.process_tied_handle ();-		e3.process_tied_handle ();--		g.add_path (p1);-		g.add_path (p1.get_quadratic_points ());-	}--	public static void test_overview () {-		OverView o = MainWindow.get_overview ();--		warn_if_fail (o.selected_char_is_visible ());-	-		for (int i = 0; i < 10; i++) {-			o.key_down ();-			warn_if_fail (o.selected_char_is_visible ());-		}--		for (int i = 0; i < 15; i++) {-			o.key_up ();-			warn_if_fail (o.selected_char_is_visible ());-		}--		for (int i = 0; i < 6; i++) {-			o.key_down ();-			warn_if_fail (o.selected_char_is_visible ());-		}--		for (int i = 0; i < 3; i++) {-			o.key_down ();-			warn_if_fail (o.selected_char_is_visible ());-		}-		-		for (int i = 0; i < 2000; i++) {-			o.scroll_adjustment (5);-		}--		for (int i = 0; i < 2000; i++) {-			o.scroll_adjustment (-5);-		}-	}--	public static void test_data_reader () {-		FontData fd = new FontData ();-		uint len;--		try {-			fd.add (7);-			fd.add_ulong (0x5F0F3CF5);-			fd.add_ulong (9);-			-			warn_if_fail (fd.table_data[0] == 7);-			warn_if_fail (fd.read () == 7);-			warn_if_fail (fd.read_ulong () == 0x5F0F3CF5);-			warn_if_fail (fd.read_ulong () == 9);-			-			fd = new FontData ();-			for (int16 i = 0; i < 2048; i++) {-				fd.add_short (i);-			}-			-			fd.seek (2 * 80);-			warn_if_fail (fd.read_short () == 80);-			-			fd.seek (100);-			fd.add_short (7);-			fd.seek (100);-			warn_if_fail (fd.read_short () == 7);-			-			fd.seek_end ();-			len = fd.length ();-			fd.add (0);-			warn_if_fail (len + 1 == fd.length ());-		} catch (GLib.Error e) {-			warning (e.message);-		}-	}--	public static void test_argument () {-		Argument arg = new Argument ("supplement -t \"Argument list\" --unknown -unknown --help -s");-		-		return_if_fail (arg.has_argument ("--test"));-		return_if_fail ((!) arg.get_argument ("--test") == "\"Argument list\"" );-		return_if_fail (arg.has_argument ("--unknown"));-		return_if_fail (arg.has_argument ("--help"));-		return_if_fail (arg.has_argument ("--slow"));-		return_if_fail (arg.validate () != 0);--		arg = new Argument ("supplement --test \"Argument list\"");-		return_if_fail ((!) arg.get_argument ("--test") == "\"Argument list\"" );-		return_if_fail (!arg.has_argument ("--help"));-		return_if_fail (!arg.has_argument ("--slow"));-		return_if_fail (arg.validate () == 0);-		-	}--	public static void test_glyph_ranges () {-		GlyphRange gr = new GlyphRange ();-		-		gr.add_range ('b', 'c');-		gr.add_single ('d');-		gr.add_range ('e', 'h');-		gr.add_range ('k', 'm');-		gr.add_range ('o', 'u');-		gr.add_range ('a', 'd');-		gr.add_range ('f', 'z');-		gr.add_range ('b', 'd');-			-		return_if_fail (gr.length () == 'z' - 'a' + 1);-		return_if_fail (gr.get_ranges ().length () == 1);-		return_if_fail (gr.get_ranges ().first ().data.length () == 'z' - 'a' + 1);-		-		for (unichar i = 'a'; i <= 'z'; i++) {-			uint index = i - 'a';-			string c = gr.get_char (index);-			StringBuilder s = new StringBuilder ();-			s.append_unichar (i);-			-			if (c != s.str) {-				warning (@"wrong glyph in glyph range got \"$c\" expected \"$(s.str)\" for index $(index).");-			}-		}-		-		gr = new GlyphRange ();-		gr.add_single ('a');-		gr.add_range ('c', 'e');-		gr.add_single ('◊');-		return_if_fail (gr.get_char (0) == "a");-		return_if_fail (gr.get_char (1) == "c");-		return_if_fail (gr.get_char (2) == "d");-		return_if_fail (gr.get_char (3) == "e");-		return_if_fail (gr.get_char (4) == "◊");-	}--	public static void test_glyph_table () {-		GlyphTable table = new GlyphTable ();-		List<GlyphCollection> gc = new List<GlyphCollection> ();-		List<unowned GlyphCollection> gc_copy;-		GlyphCollection g;-		-		return_if_fail (table.length () == 0);-		return_if_fail (table.get ("Some glyph") == null);-		-		// generate test data-		for (uint i = 0; i < 1000; i++) {-			gc.append (new GlyphCollection (new Glyph (@"TEST $i", i + 'a')));-		}--		// insert in random order-		gc_copy = gc.copy ();-		for (uint i = 0; gc_copy.length () > 0; i++) {-			int t = (int) ((gc_copy.length () - 1) * Random.next_double ());-			g = gc_copy.nth (t).data;-			-			if (!table.insert (g)) {-				warning (@"Failed to insert $(g.get_name ())");-				return;-			}-			-			gc_copy.remove_all (g);-			-			if (!table.validate_index ()) {-				table.print_all ();-				warning ("index is invalid");-				return;-			}-		}-		-		return_if_fail (table.length () == gc.length ());-		-		// validate table-		for (uint i = 0; i > 1000; i++) {-			g = (!) table.get (gc.nth (i).data.get_name ());-			return_if_fail (gc.nth (i).data == g);-		}-		-		// search -		for (int i = 0; i < 2000; i++) {-			int t = (int) (999 * Random.next_double ());-			if (table.get (@"TEST $t") == null) {-				table.print_all ();-				warning (@"Did't find TEST $t in glyph table.");-				return;-			}-		}--		// remove-		table.remove ("TEST 0");-		table.remove ("TEST 53");-		return_if_fail (table.get ("TEST 0") == null);-		return_if_fail (table.get ("TEST 53") == null);-		-		// search -		return_if_fail (table.get ("TEST 52") != null);-		return_if_fail (table.get ("TEST 54") != null);-	}--	public static void test_delete_points () {-		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");-		tool.test_delete_points ();-	}--	public static void test_active_edit_point () {-		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");-		tool.test_active_edit_point ();-	}-	-	public static void test_save_backup () {-		// TODO draw various things and assert that they are restored correctly-		Supplement.get_current_font ().save_backup ();-		Supplement.get_current_font ().restore_backup ();-	}--	public static void test_hex () {-		test_hex_conv ('H', "U+48", 72);-		test_hex_conv ('1', "U+31", 49);-		test_hex_conv ('å', "U+e5", 229);-		test_hex_conv ('◊', "U+25ca", 9674);-	}--	private static void test_hex_conv (unichar h, string sr, int r) {-		string s = Font.to_hex (h);-		unichar t = Font.to_unichar (sr);-		-		if (s != sr) warning (@"($s != \"$sr\")");-		if ((int)t != r || t != h) warning (@"$((int)t) != $r || $t != '$h'");-	}--	public static void test_coordinates () {-		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");-		tool.test_coordinates ();		-	}--	public static void test_reverse_random_paths () {-		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");-		tool.test_reverse_random_triangles ();		-	}--	public static void test_reverse_path () {-		PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");-		tool.test_reverse_path ();-	}--	public static void test_drawing () {-		Tool tool = MainWindow.get_toolbox ().get_tool ("pen_tool");-		tool.test ();-	}-		-	private void add (Callback callback, string name) {-		test_cases.append (new Test (callback, name));-	}-	-	public unowned List<Test> get_test_functions () {-		return test_cases;-	}-}	-	-}diff --git a/src/TestSupplement.vala b/src/TestSupplement.valadeleted file mode 100644index e03b659..0000000--- a/src/TestSupplement.vala+++ /dev/null@@ -1,262 +0,0 @@-/*
-    Copyright (C) 2012 Johan Mattsson
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-namespace Supplement {
-
-class Test : Object {
-	public Callback callback;
-	public string name;
-	
-	public Test (Callback callback, string name) {
-		this.callback = callback;
-		this.name = name;
-	}
-}
-
-/** Class for executing tests cases. */
-class TestSupplement : GLib.Object {
-
-	const int NOT_STARTED = 0;
-	const int RUNNING = 1;
-	const int PAUSED = 2;
-	const int DONE = 3;
-	
-	static int state = NOT_STARTED;
-
-	TestCases tests;
-	
-	unowned List<Test> test_cases;
-	unowned List<Test?>? current_case = null;
-
-	unowned List<Test> passed;
-	unowned List<Test> failed;
-	unowned List<Test> skipped;
-
-	static TestSupplement? singleton = null;
-
-	bool has_failed = false;
-	bool has_skipped = false;
-		
-	static bool slow_test = false;
-	
-	string test_cases_to_run; // name of specific test case or all to run all test cases
-	
-	public TestSupplement () {
-		assert (singleton == null);
-		tests = new TestCases ();
-		test_cases = tests.get_test_functions ();
-		current_case = test_cases.first ();
-		test_cases_to_run = "All";
-		
-		from_command_line ();
-	}
-	
-	public static bool is_slow_test () {
-		return slow_test;
-	}
-	
-	public static void set_slow_test (bool s) {
-		slow_test = s;
-	}
-	
-	public static TestSupplement get_singleton () {
-		if (singleton == null) {
-			singleton = new TestSupplement ();
-		}
-		
-		return (!) singleton;
-	}
-
-	private bool has_test_case (string s) {
-		foreach (var t in test_cases) {
-			if (t.name == s) return true;
-		}	
-		
-		if (s == "" || s == "All") {
-			print ("No specific tescase given run all test cases.\n");
-			return true;
-		}
-		
-		return false;
-	}
-
-	/** Run only test specified on the command line. */
-	private void from_command_line () {
-		string? stn = Supplement.get_argument ("--test");
-	
-		if (stn != null) {
-			string st = (!) stn;
-			
-			if (!has_test_case (st)) {
-				stderr.printf (@"Test case \"$st\" does not exist.\n");
-				stderr.printf ("\nAvaliable test cases:\n");
-				
-				foreach (var t in test_cases) {
-					stderr.printf (t.name);
-					stderr.printf ("\n");
-				}
-				
-				Process.exit(1);
-			}
-
-			if (st == "All" || st == "") {
-				return;
-			} else {
-				stderr.printf  (@"Run only test case \"$st\" \n");
-			}
-			
-			test_cases_to_run = st;
-		}
-	}
-
-	public static void log (string? log_domain, LogLevelFlags log_levels, string message) {		
-		Test t = (!)((!)get_singleton ().current_case).data;
-		
-		if (log_domain != null) {
-			stderr.printf ("%s: \n", (!) log_domain);
-		}
-		
-		stderr.printf ("Testcase \"%s\" failed because:\n", t.name);
-		stderr.printf ("%s\n\n", message);	
-		
-		get_singleton ().has_failed = true;
-		
-		assert (!Supplement.fatal_wanings);
-	}
-
-	public static void @continue () {
-		int s = AtomicInt.get (ref state);
-		if (s == DONE) {
-			singleton = null;
-		}
-
-		TestSupplement t = get_singleton ();
-		
-		LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
-		Log.set_handler (null, levels, log);
-				
-		AtomicInt.set (ref state, RUNNING);
-		t.run_all_tests ();
-	}
-
-	public static bool is_running () {
-		int r = AtomicInt.get (ref state);
-		return (r == RUNNING);
-	}
-
-	public static void pause () {
-		AtomicInt.set (ref state, PAUSED);
-		Log.set_handler (null, LogLevelFlags.LEVEL_MASK, Log.default_handler);
-	}
-
-	private static void pad (int t) {
-		for (int i = 0; i < t; i++) stdout.printf (" ");
-	}
-
-	public void print_result () {
-		stdout.printf ("\n");
-		stdout.printf ("Testcase results:\n");
-
-		foreach (var t in skipped) {
-			stdout.printf ("%s", t.name);
-			pad (40 - t.name.char_count());
-			stdout.printf ("Skipped\n");
-		}
-		
-		if (skipped.length () > 0) {
-			stdout.printf ("\n");
-		}
-		
-		foreach (var t in passed) {
-			stdout.printf ("%s", t.name);
-			pad (40 - t.name.char_count());
-			stdout.printf ("Passed\n");
-		}
-		
-		foreach (var t in failed) {
-			stdout.printf ("%s", t.name);
-			pad (40 - t.name.char_count());
-			stdout.printf ("Failed\n");
-		}
-		
-		stdout.printf ("\n");
-		
-		stdout.printf ("Total %u test cases executed, %u passed and %u failed.\n", (passed.length () + failed.length ()), passed.length (), failed.length ());
-	}
-
-	/** Run tests in main loop. */
-	public void run_all_tests () {
-		var idle = new TimeoutSource (20);
-		
-		idle.set_callback(() => {
-			int s = AtomicInt.get (ref state);
-			
-			if (s != RUNNING || current_case == null) {
-				return false;
-			}
-
-			Test test = (!)((!) current_case).data;
-					
-			has_failed = false;
-			has_skipped = false;
-
-			if (test_cases_to_run != "All" && test_cases_to_run != test.name) {
-				has_skipped = true;
-			} else {
-				test.callback ();
-			}
-			
-			if (has_failed) {
-				failed.append ((!) test);
-				
-				if (Supplement.has_argument ("--exit")) {
-					print_result ();
-					Process.exit (1);
-				}
-	
-			} else if (has_skipped) {
-				skipped.append ((!) test);
-			} else {
-				passed.append ((!) test);
-			}
-
-			if (unlikely (current_case == test_cases.last ())) {
-				stdout.printf ("Finished running test suite.\n");			
-				
-				AtomicInt.set (ref state, DONE);
-				Log.set_handler (null, LogLevelFlags.LEVEL_MASK, Log.default_handler);
-
-				print_result ();
-				
-				if (Supplement.has_argument ("--exit")) {
-					print_result ();
-					Process.exit ((failed.length () == 0) ? 0 : 1);
-				}
-				return false;
-			}
-			
-			current_case = ((!) current_case).next;
-			
-			return true;
-		});
-    
-		idle.attach (null);
-	}
-
-}
-
-}
diff --git a/src/Tool.vala b/src/Tool.valadeleted file mode 100644index 6d3e31f..0000000--- a/src/Tool.vala+++ /dev/null@@ -1,353 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;--namespace Supplement {--public class Tool : GLib.Object {-	-	public double x = 0;-	public double y = 0;-	public double w = 0;-	public double h = 0;--	protected bool active = false;-	protected bool selected = false;-	-	double r_default_selected = 131/255.0;-	double g_default_selected = 179/255.0;-	double b_default_selected = 231/255.0;-	double a_default_selected = 1;-	-	double r_default = 185/255.0;-	double g_default = 207/255.0;-	double b_default = 231/255.0;-	double a_default = 1;-	-	double r;-	double g;-	double b;-	double a;-	-	ImageSurface? icon = null;-		-	public signal void select_action (Tool selected);-	public signal void deselect_action (Tool selected);-	-	public signal void press_action (Tool selected, int button, int x, int y);-	public signal void double_click_action (Tool selected, int button, int x, int y);-	public signal void move_action (Tool selected, int x, int y);-	public signal void release_action (Tool selected, int button, int x, int y);-	-	public signal void key_press_action (Tool selected, uint32 keyval);-	public signal void key_release_action (Tool selected, uint32 keyval);-	-	public signal void panel_press_action (Tool selected, uint button, double x, double y);-	public signal void panel_release_action (Tool selected, uint button, double x, double y);-	public signal void panel_move_action (Tool selected, double x, double y);-	-	public signal void draw_action (Tool selected, Context cr, Glyph glyph);-	-	protected string name = "";-	-	static int next_id = 1;-	-	int id;-	-	public bool new_selection = false;-	-	bool show_bg = true;-	-	string tip = "";--	// keyboard bindings-	public uint modifier_flag;-	public unichar key;-	-	private static ImageSurface? selected_button = null;-	private static ImageSurface? active_selected_button = null;-	private static ImageSurface? deselected_button = null;-	private static ImageSurface? active_deselected_button = null;-	-	/** Create tool with a certain name and load icon "name".png */-	public Tool (string? name = null, string tip = "", unichar key = '\0', uint modifier_flag = 0) {-		this.tip = tip;-		-		if (selected_button == null) {-			selected_button = Icons.get_icon ("tool_button_selected.png");-			active_selected_button = Icons.get_icon ("tool_button_selected_active.png");-			deselected_button = Icons.get_icon ("tool_button_deselected.png");-			active_deselected_button = Icons.get_icon ("tool_button_deselected_active.png");-		}-		-		if (name != null) {-			set_icon ((!) name);-			this.name = (!) name;-		}-		-		this.key = key;-		this.modifier_flag = modifier_flag;-				-		id = next_id;-		next_id++;-		-		panel_press_action.connect ((self, button, x, y) => {-			MainWindow.get_tool_tip ().set_text_from_tool ();-		});-		-		r = r_default;-		g = g_default;-		b = b_default;-		a = a_default;-	}-	-	public void set_icon (string name) {-		StringBuilder n = new StringBuilder ();-		n.append ((!) name);-		n.append (".png");-		icon = Icons.get_icon (n.str);-	}-	-	public bool is_active () {-		return active;-	}-	-	public void set_show_background (bool bg) {-		show_bg = bg;-	}-	-	public int get_id () {-		return id;-	}--	public string get_name () {-		return name;-	}--	public bool is_selected () {-			return selected;-	}-		-	public virtual void set_properties (double tx, double ty, double tw, double th) {-		x = tx;-		y = ty;-		w = tw;-		h = th;-	}--	public string get_tip () {-		return tip;-	}--	public bool is_over (double xp, double yp) {	-		return (x <= xp <= x + w  + 12 + 15 && y + 15 <= yp <= y + w + 12 + 30);  -	}-	-	public bool set_selected (bool a) {-		TooltipArea? tpa = null;					-		tpa = MainWindow.get_tool_tip ();-							-		new_selection = true;-		selected = a;-		set_active (a);-		-		if (!a) {-			deselect_action (this);-		} else {-			((!)tpa).update_text ();-		}-		-		return true;-	}-	-	public bool set_active (bool ac) {-		bool ret = (active != ac);--		if (selected) {-			if (ac) {-				r = r_default_selected;-				g = g_default_selected;-				b = b_default_selected;-				a = a_default_selected * 0.5;-			} else {-				r = r_default_selected;-				g = g_default_selected;-				b = b_default_selected;-				a = a_default_selected;-			}			-		} else {-			if (ac) {-				r = r_default;-				g = g_default;-				b = b_default;-				a = a_default * 0.2;-			} else {-				r = r_default;-				g = g_default;-				b = b_default;-				a = a_default;-			}-		}--		active = ac;-		return ret;-	}-		-	public virtual void draw (Context cr) {-		double xt = x + 3 + w;-		double yt = y + h + 17;--		double bgx, bgy;-		-		bgx = xt - 6;-		bgy = yt - 7;--		cr.save ();--		// Button in four states-		if (selected && selected_button != null) {-			cr.set_source_surface ((!) selected_button, bgx, bgy);-			cr.paint ();-		}--		if (selected && active && active_selected_button != null) {-			cr.set_source_surface ((!) active_selected_button, bgx, bgy);-			cr.paint ();-		}--		if (!selected && deselected_button != null) {-			cr.set_source_surface ((!) deselected_button, bgx, bgy);-			cr.paint ();-		}--		if (!selected && active && active_deselected_button != null) {-			cr.set_source_surface ((!) active_deselected_button, bgx, bgy);-			cr.paint ();-		}-				-		if (icon != null) {-			ImageSurface i = (!) icon;-			-			if (likely (i.status () == Cairo.Status.SUCCESS)) {-				cr.set_source_surface (i, xt + (15 - i.get_width () / 2) - 5.7, yt + (15 - i.get_height ()) / 2.0);-				cr.paint ();-			} else {-				warning (@"Falied to load icon for $name");-			}-		}-		-		cr.restore ();-	}--	/** Run a test case for this tool. */-	public virtual bool test () {-		stderr.printf (@"$(get_name ()) does not have a test case, implement one by overriding test method in Tool base class.");-		return false;-	}-	-	/** Help function to test button press actions. */-	public void test_click_action (int b, int x, int y) {-		Tool.yield ();-		press_action (this, b, x, y);-		-		Tool.yield ();-		release_action (this, b, x, y);-	}--	/** Help function to test select action for this tool. */-	public void test_select_action () {-		Tool.yield ();-		MainWindow.get_toolbox ().select_tool (this);-	}--	public void test_move_action (int x, int y) {-		Tool.yield ();-		move_action (this, x, y);-	}--	public void test_press_action (int b, int x, int y) {-		Tool.yield ();-		press_action (this, b, x, y);-	}--	public void test_release_action (int b, int x, int y) {-		Tool.yield ();-		release_action (this, b, x, y);-	}--	public static void test_open_next_glyph () {-		Tool.yield ();-		OverView o = MainWindow.get_overview ();-		-		MainWindow.get_tab_bar ().select_overview ();-		MainWindow.get_toolbox ().select_tool_by_name ("utf_8");-		-		o.select_next_glyph ();-		Tool.yield ();-		-		o.open_current_glyph ();-		Tool.yield ();-	}-	-	/** Run pending events in main loop before continue. */-	public static void @yield () {-		int t = 0;-		var time = new TimeoutSource(500);-		bool timeout;--		if (TestSupplement.is_slow_test ()) {-			timeout = false;-			-			time.set_callback(() => {-				timeout = true;-				return false;-			});--			time.attach(null);		-		} else {-			timeout = true;-		}-    -		unowned MainContext c = MainContext.default ();-		bool a = c.acquire ();-		-		while (unlikely (!a)) {-			warning ("Failed to acquire main loop.\n");-			return;-		}--		while (c.pending () || TestSupplement.is_slow_test ()) {-			c.iteration (true);-			t++;--			if (!c.pending () && TestSupplement.is_slow_test ()) {-				if (timeout) break;-			}-						-			if (unlikely (t > 100000)) {-				assert (false);-				break;-			}-		}-		-		c.release ();-	}-	-}--}diff --git a/src/Toolbox.vala b/src/Toolbox.valadeleted file mode 100644index b5c6089..0000000--- a/src/Toolbox.vala+++ /dev/null@@ -1,868 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Math;--namespace Supplement {--public class Toolbox : GLib.Object  {-	GlyphCanvas glyph_canvas;-	-	bool expanded = true;-	-	ToolboxExpander toolbox_expander;-	-	public List<Expander> expanders;-	-	Tool current_tool = new Tool ("no_icon");-	-	Expander draw_tools;-	Expander grid_expander;-		-	BackgroundTool move_background;-	CutBackgroundTool cut_background;-	-	public Tool press_tool = new Tool (null); // activate the pressed button on release-	-	public SpinButton background_scale = new SpinButton ();-	public SpinButton precision;-	-	public signal void redraw (int x, int y, int w, int h);-	-	public int allocation_width = 0;-	public int allocation_height = 0;-	-	ImageSurface? toolbox_background = null;-	-	public Toolbox (GlyphCanvas main_glyph_canvas) {-		glyph_canvas = main_glyph_canvas;--		toolbox_background = Icons.get_icon ("toolbox_background.png");--		toolbox_expander = new ToolboxExpander ();-		-		add_expander (toolbox_expander);-		-		draw_tools = new Expander ();-			-		Expander path_tool_modifiers = new Expander ();-		Expander draw_tool_modifiers = new Expander ();-		Expander characterset_tools = new Expander ();-		Expander test_tools = new Expander ();-		Expander guideline_tools = new Expander ();-		Expander view_tools = new Expander ();-		Expander grid = new Expander ();-		Expander background_tools = new Expander ();-		Expander trace = new Expander ();-		-		grid_expander = grid;--		// Draw tools-		PenTool pen_tool = new PenTool ("pen_tool");-		draw_tools.add_tool (pen_tool);-	-		ZoomTool zoom_tool = new ZoomTool ("zoom_tool");-		draw_tools.add_tool (zoom_tool);--		CutTool cut_tool = new CutTool ("cut");-		draw_tools.add_tool (cut_tool);	--		Tool move_tool = new MoveTool ("move");-		draw_tools.add_tool (move_tool);--		Tool shrink_tool = new ShrinkTool ("shrink");-		// Fixa: draw_tools.add_tool (shrink_tool);	-		-		// Draw tool modifiers-		// DELETE tools:-		-		Tool new_point = new Tool ("new_point", _("Right click to add new point, left click to move points") + " " + _("and double click to add new point on path"), 'a');-		new_point.select_action.connect ((self) => {-				select_draw_tool ();-			});-		-		// draw_tool_modifiers.add_tool (new_point);--		Tool insert_point_on_path = new Tool ("insert_point_on_path", _("Add new point on path"), 'n');-		insert_point_on_path.select_action.connect ((self) => {-			select_draw_tool ();-			pen_tool.begin_from_new_point_on_path ();-		});-		// draw_tool_modifiers.add_tool (insert_point_on_path);-		-		if (Supplement.experimental) {-			Tool new_point_on_path = new Tool ("new_point_on_path", _("Begin new line from point on path"), 'n');-			new_point_on_path.select_action.connect ((self) => {-					select_draw_tool ();-					pen_tool.begin_from_new_point_on_path ();-				});-			draw_tool_modifiers.add_tool (new_point_on_path);	-		}--		Tool tie_editpoint_tool = new Tool ("tie_point", _("Tie curve handles for selected edit point"), 'w');-		tie_editpoint_tool.select_action.connect ((self) => {-			bool tie;-			-			select_draw_tool ();-			-			foreach (EditPoint ep in pen_tool.selected_points) {-				tie = !ep.tie_handles;--				if (tie) {-					ep.process_tied_handle ();-				}--				ep.set_tie_handle (tie);-				-				MainWindow.get_current_glyph ().update_view ();				-			}-		});-		draw_tool_modifiers.add_tool (tie_editpoint_tool);	--		Tool tie_x_or_y = new Tool ("tie_x_or_y", _("Tie coordinates to previous edit point"), 'q');-		tie_x_or_y.select_action.connect((self) => {-			select_draw_tool ();-			pen_tool.tie_x_or_y_coordinates = !pen_tool.tie_x_or_y_coordinates;-		});-		// Fixa: draw_tool_modifiers.add_tool (tie_x_or_y);	--		Tool erase_tool = new Tool ("erase_tool", _("Delete edit points"));-		erase_tool.select_action.connect((self) => {-			select_draw_tool ();-		});-		// draw_tool_modifiers.add_tool (erase_tool);	--		// adjust precision-		string precision_value = Preferences.get ("precision");-		-		precision = new SpinButton ("precision", _("Set precision"));-		-		if (precision_value != "") {-			precision.set_value (precision_value);-		} else {-			precision.set_value_round (1);-		}-		-		precision.new_value_action.connect ((self) => {-			select_tool (precision);-			-			Preferences.set ("precision", self.get_display_value ());-			redraw ((int) precision.x, (int) precision.y, 70, 70);-		});--		precision.select_action.connect((self) => {-			pen_tool.set_precision (((SpinButton)self).get_value ());-		});-		-		precision.set_min (0.001);-		precision.set_max (1);-		-		draw_tool_modifiers.add_tool (precision);-				-		// path tools-		Tool union_paths_tool = new MergeTool ("union_paths");-		path_tool_modifiers.add_tool (union_paths_tool);-		-		Tool reverse_path_tool = new Tool ("reverse_path", _("Create counter from outline"), 'r');-		reverse_path_tool.select_action.connect ((self) => {-			Glyph g = MainWindow.get_current_glyph ();-			-			foreach (Path p in g.active_paths) {-				p.reverse ();-			}-		-			g.redraw_area (0, 0, g.allocation.width, g.allocation.height);-		});-		path_tool_modifiers.add_tool (reverse_path_tool);--		Tool move_layer = new Tool ("move_layer", _("Move to path to the bottom layer"), 'd');-		move_layer.select_action.connect ((self) => {-			Glyph g = MainWindow.get_current_glyph ();--			foreach (Path p in g.active_paths) {-				g.path_list.remove (p);-				g.path_list.prepend (p);-			}-		});-		path_tool_modifiers.add_tool (move_layer);-		-		// Character set tools-		Tool full_unicode = new Tool ("utf_8", _("Show full unicode characters set"), 'f', CTRL);-		full_unicode.select_action.connect ((self) => {-				MainWindow.get_tab_bar ().add_unique_tab (new OverView (), 100, false);	-				OverView o = MainWindow.get_overview ();-				GlyphRange gr = new GlyphRange ();-				gr.use_full_unicode_range ();-				o.set_glyph_range (gr);-				MainWindow.get_tab_bar ().select_tab_name ("Overview");-			});-		characterset_tools.add_tool (full_unicode);--		Tool custom_character_set = new Tool ("custom_character_set", _("Show default characters set"), 'r', CTRL);-		custom_character_set.select_action.connect ((self) => {-			MainWindow.get_tab_bar ().add_unique_tab (new OverView (), 100, false);-			OverView o = MainWindow.get_overview ();-			GlyphRange gr = new GlyphRange ();-			gr.use_default_range ();-			o.set_glyph_range (gr);-			MainWindow.get_tab_bar ().select_tab_name ("Overview");-		});-		characterset_tools.add_tool (custom_character_set);--		Tool avalilable_characters = new Tool ("available_characters", _("Show all characters in font"), 'd', CTRL);-		avalilable_characters.select_action.connect ((self) => {-			MainWindow.get_tab_bar ().add_unique_tab (new OverView (), 100, false);-			OverView o = MainWindow.get_overview ();-			o.display_all_available_glyphs ();-			MainWindow.get_tab_bar ().select_tab_name ("Overview");-		});-		characterset_tools.add_tool (avalilable_characters);--		Tool delete_glyph = new Tool ("delete_selected_glyph", _("Delete selected glyph"));-		delete_glyph.select_action.connect ((self) => {-			OverView o = MainWindow.get_overview ();-			-			if (MainWindow.get_current_display () is OverView) {-				o.delete_selected_glyph ();-			}-			-			MainWindow.get_tab_bar ().select_tab_name ("Overview");-		});-		characterset_tools.add_tool (delete_glyph);-						-		if (Supplement.has_argument ("--test")) {-			Tool test_case = new Tool ("test_case");-			test_case.select_action.connect((self) => {-					if (self.is_selected ()) {-						if (TestSupplement.is_running ()) {-							TestSupplement.pause ();-						} else {-							TestSupplement.continue ();-						}-					}-				});-			test_tools.add_tool (test_case);--			Tool slow_test = new Tool ("slow_test");-			slow_test.select_action.connect((self) => {-					bool s = TestSupplement.is_slow_test ();-					TestSupplement.set_slow_test (!s);-					s = TestSupplement.is_slow_test ();-					self.set_selected (s);-				});-		-			test_tools.add_tool (slow_test);-					-			// Run from commad line-			string? st = Supplement.get_argument ("--test");-			if (st != null && ((!)st).char_count () > 0) {-				IdleSource idle = new IdleSource ();--				idle.set_callback (() => {			-					select_tool (test_case);-					return false;-				});-				-				idle.attach (null);-			}-			-			if (Supplement.has_argument ("--slow")) {-				select_tool (slow_test);-			}-			-		}-		-		// help lines, grid and other guidlines-		Tool help_lines = new Tool ("help_lines", "Show help lines", 'l');-		help_lines.select_action.connect ((self) => {-				bool h;-				h = glyph_canvas.get_current_glyph ().get_show_help_lines ();-				glyph_canvas.get_current_glyph ().set_show_help_lines (!h);-				self.set_selected (!h);-				glyph_canvas.get_current_glyph ().redraw_help_lines ();-			});	--		guideline_tools.add_tool (help_lines);--		Tool xheight_help_lines = new Tool ("show_xheight_helplines", _("Show help lines for x-height and baseline"), 'x');-		xheight_help_lines.select_action.connect ((self) => {-			Glyph g = MainWindow.get_current_glyph ();-			bool v = !g.get_xheight_lines_visible ();-			g.set_xheight_lines_visible (v);-			self.set_selected (v);-			MainWindow.get_glyph_canvas ().redraw ();-			-			if (v && !help_lines.is_selected ()) {-				select_tool (help_lines);-			}-			-		});-		guideline_tools.add_tool (xheight_help_lines);--		Tool background_help_lines = new Tool ("background_help_lines", _("Show help lines at top and bottom margin"), 't');-		background_help_lines.select_action.connect ((self) => {-			Glyph g = MainWindow.get_current_glyph ();-			bool v = !g.get_margin_lines_visible ();-			g.set_margin_lines_visible (v);-			self.set_selected (v);-			MainWindow.get_glyph_canvas ().redraw ();-			-			if (v && !help_lines.is_selected ()) {-				select_tool (help_lines);-			}-		});-		guideline_tools.add_tool (background_help_lines);--		Tool new_grid = new GridTool ("new_grid");-		guideline_tools.add_tool (new_grid);--		// Zoom tools -		Tool zoom_in = new Tool ("zoom_in", _("Zoom in"), '+', CTRL);-		zoom_in.select_action.connect ((self) => {-			zoom_tool.store_current_view ();-			glyph_canvas.get_current_display ().zoom_in ();-		});-		view_tools.add_tool (zoom_in);--		Tool zoom_out = new Tool ("zoom_out", _("Zoom out"), '-', CTRL);-		zoom_out.select_action.connect ((self) => {-			zoom_tool.store_current_view ();-			glyph_canvas.get_current_display ().zoom_out ();-		});-		view_tools.add_tool (zoom_out);--		Tool reset_zoom = new Tool ("zoom_1_1", _("Zoom to scale 1:1"), '0', CTRL);-		reset_zoom.select_action.connect ((self) => {-				zoom_tool.store_current_view ();-				glyph_canvas.get_current_display ().reset_zoom ();-				glyph_canvas.redraw_area(0, 0, glyph_canvas.allocation.width, glyph_canvas.allocation.height);-			});-		view_tools.add_tool (reset_zoom);--		Tool full_glyph = new Tool ("full_glyph", _("Show full glyph"));-		full_glyph.select_action.connect((self) => {-			zoom_tool.store_current_view ();-			zoom_tool.zoom_full_glyph ();-		});-		view_tools.add_tool (full_glyph);--		Tool zoom_boundries = new Tool ("zoom_boundries", _("Zoom in at region boundries"), 'v');-		zoom_boundries.select_action.connect((self) => {-			zoom_tool.store_current_view ();-			glyph_canvas.get_current_display ().zoom_max ();-		});-		view_tools.add_tool (zoom_boundries);--		Tool zoom_bg = new Tool ("zoom_background_image", _("Zoom in background image"), 'b');-		zoom_bg.select_action.connect((self) => {-			if (MainWindow.get_current_glyph ().get_background_image () != null) {-				zoom_tool.store_current_view ();					-				glyph_canvas.get_current_display ().reset_zoom ();-				-				zoom_tool.zoom_full_background_image ();-				-				glyph_canvas.redraw_area(0, 0, glyph_canvas.allocation.width, glyph_canvas.allocation.height);-			}-		});-		view_tools.add_tool (zoom_bg);--		Tool zoom_prev = new Tool ("prev", _("Previous view"), 'j', CTRL);-		zoom_prev.select_action.connect((self) => {-			zoom_tool.previous_view ();-		});-		view_tools.add_tool (zoom_prev);--		Tool zoom_next = new Tool ("next", _("Next view"), 'l', CTRL);-		zoom_next.select_action.connect((self) => {-			zoom_tool.next_view ();-		});-		view_tools.add_tool (zoom_next);-				-		// background tools-		background_scale = new SpinButton ("scale_background", _("Set size for background image"));-		background_scale.set_value_round (1);-		-		background_scale.new_value_action.connect((self) => {-			background_scale.select_action (self);-		});-		-		background_scale.select_action.connect((self) => {-			SpinButton sb = (SpinButton) self;-			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? img = g.get_background_image ();-			double s = sb.get_value ();-			-			if (img != null) {-				((!)img).set_img_scale (s, s);-			}-			-			MainWindow.get_glyph_canvas ().redraw ();-		});-		-		background_tools.add_tool (background_scale);		--		BackgroundTool move_background = new BackgroundTool ("move_background");			-		background_tools.add_tool (move_background);-		this.move_background = move_background;-		-		CutBackgroundTool cut_background = new CutBackgroundTool ("cut_background");-		background_tools.add_tool (cut_background);-		this.cut_background = cut_background;-		-		Tool show_bg = new Tool ("show_background", _("Show/hide background image"));-		show_bg.select_action.connect ((self) => {-			Glyph g = MainWindow.get_current_glyph ();-			g.set_background_visible (!g.get_background_visible ());-			MainWindow.get_glyph_canvas ().redraw ();-		});-		background_tools.add_tool (show_bg);--		Tool bg_selection = new Tool ("insert_background", _("Insert a new background image"));-		bg_selection.select_action.connect((self) => {-			Glyph? g = null;-			FontDisplay fd = MainWindow.get_current_display ();-			TooltipArea tp = MainWindow.get_tool_tip ();-			tp.show_text (_("Creating thumbnails"));-			-			bg_selection.yield ();-		-			if (fd is Glyph) {-				g = (Glyph) fd;-			} else {-				return;-			}-			-			BackgroundSelection bgs = new BackgroundSelection ();-			MainWindow.get_tab_bar ().add_unique_tab (bgs , 120, false);-			-			tp.show_text ("");-		});-		-		bg_selection.set_show_background (true);-		background_tools.add_tool (bg_selection);-		-		SpinButton background_contrast = new SpinButton ("background_contrast", _("Set contrast for background image"));-		background_contrast.set_value_round (1);--		background_contrast.new_value_action.connect ((self) => {-			background_contrast.select_action (self);-		});-		-		background_contrast.select_action.connect ((self) => {		-			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? bg = g.get_background_image ();-			GlyphBackgroundImage b;-			-			if (bg != null) {-				b = (!) bg;-				b.set_contrast (background_contrast.get_value ());-			}-		});-		-		// Fixa: background_tools.add_tool (background_contrast);--		SpinButton background_threshold = new SpinButton ("background_threshold", _("Set threshold"));-		background_threshold.set_value_round (1);--		background_threshold.new_value_action.connect ((self) => {-			background_threshold.select_action (self);-		});-		-		background_threshold.select_action.connect ((self) => {		-			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? bg = g.get_background_image ();-			GlyphBackgroundImage b;-			-			if (bg != null) {-				b = (!) bg;-				b.set_threshold (background_threshold.get_value ());-			}-			-		});-		trace.add_tool (background_threshold);-		-		draw_tools.set_open (true);-		draw_tool_modifiers.set_open (true);-		path_tool_modifiers.set_open (true);-		view_tools.set_open (true);-		grid.set_open (true);-		characterset_tools.set_open (true);-		test_tools.set_open (true);-		guideline_tools.set_open (true);-		background_tools.set_open (true);-		trace.set_open (true);-		-		add_expander (draw_tools);-		add_expander (draw_tool_modifiers);-		add_expander (path_tool_modifiers);	-		-		add_expander (characterset_tools);-		add_expander (guideline_tools);-		add_expander (grid);-		add_expander (view_tools);-		add_expander (background_tools);-		-		// Fixa: add_expander (trace);-		-		add_expander (test_tools);-		-		draw_tools.set_persistent (true);-		draw_tools.set_unique (true);-		-		draw_tool_modifiers.set_persistent (true);-		draw_tool_modifiers.set_unique (true);--		path_tool_modifiers.set_persistent (false);-		path_tool_modifiers.set_unique (false);--		characterset_tools.set_persistent (true);-		characterset_tools.set_unique (true);-		-		test_tools.set_persistent (true);-	-		guideline_tools.set_persistent (true);-		guideline_tools.set_unique (false);-		-		grid.set_persistent (true);-		grid.set_unique (true);-		grid.set_open (false);-		-		background_tools.set_persistent (true);-		background_tools.set_unique (true);-		-		trace.set_persistent (false);-		trace.set_unique (false);--		update_expanders ();-		reset_active_tool ();--		// Default selection-		var idle = new IdleSource();-		idle.set_callback (() => {-			new_point.set_selected (true);-			pen_tool.set_selected (true);-			select_draw_tool ();			-			-			if (glyph_canvas.get_current_glyph ().get_show_help_lines ()) {-				help_lines.set_selected (true);-				help_lines.set_active (false);-			}--			add_new_grid ();-			add_new_grid ();-			-			move (0, 0);-			-			return false;-		});-		-		idle.attach (null);-	}-	-	public void key_press (uint keyval) {-		print ("key_press\n");-		foreach (var exp in MainWindow.get_toolbox ().expanders) {-			foreach (Tool t in exp.tool) {-				t.set_active (false);-				-				if (t.key == keyval && t.modifier_flag == NONE) {-					MainWindow.get_toolbox ().select_tool (t);-				}-			}-		}-	}-	-	public void press (uint button, double x, double y) {-		foreach (var exp in expanders) {-			foreach (Tool t in exp.tool) {-				if (t.is_over (x, y)) {-					t.panel_press_action (t, button, x, y);-					press_tool = t;-				}-			}-		}-	}-	-	public void release (uint button, double x, double y) {-		foreach (var exp in expanders) {-			if (exp.is_over (x, y)) {-				exp.set_open (! exp.is_open ());					-				update_expanders ();			-				redraw ((int) exp.x - 10, (int) exp.y - 10, allocation_width, (int) (allocation_height - exp.y + 10));-			}-			-			if (exp.is_open ()) {-				foreach (Tool t in exp.tool) {-					bool active = t.is_over (x, y);-					-					if (active) {-						if (press_tool == t) {-							select_tool (t);-						}-					}-					-					t.panel_release_action (t, button, x, y);-				}-			}-		}-	}-	-	private void select_draw_tool () {-		select_tool_by_name ("pen_tool");-	}-	-	public void move (double x, double y) {-		bool update;-		bool a;-		foreach (var exp in expanders) {-			a = exp.is_over (x, y);-			update = exp.set_active (a);-			-			if (update) {-				MainWindow.get_toolbox ().redraw ((int) exp.x - 10, (int) exp.y - 10, (int) (exp.x + exp.w + 10), (int) (exp.y + exp.h + 10));-			}-			-			if (exp.is_open ()) {-				foreach (Tool t in exp.tool) {-					bool active = t.is_over (x, y);-					TooltipArea? tpa = null;-					-					update = t.set_active (active);-					tpa = MainWindow.get_tool_tip ();-					-					if (active && tpa != null) {-						((!)tpa).update_text ();-					}-					-					if (update) {-						MainWindow.get_toolbox ().redraw (0, 0, allocation_width, allocation_height);-					}-					-					t.panel_move_action (t, x, y);-				}-			}-		}-	}-	-	public void remove_all_grid_buttons () {-		while (grid_expander.tool.length () > 0) {-			grid_expander.tool.remove_link (grid_expander.tool.last ());-		}-		-		while (GridTool.sizes.length () > 0) {-			GridTool.sizes.remove_link (GridTool.sizes.first ());-		}-		-		grid_expander.set_open (true);-		update_expanders ();-		redraw (0, 0, allocation_width, allocation_height);		-	}-	-	public void parse_grid (string spin_button_value) {-		SpinButton sb = add_new_grid ();-		sb.set_value (spin_button_value);-		select_tool (sb);-	}--	public SpinButton add_new_grid () {-		SpinButton grid_width = new SpinButton ("grid_width", _("Set size for grid"));-		-		grid_width.new_value_action.connect((self) => {-			grid_width.select_action (grid_width);-		});--		grid_width.select_action.connect((self) => {-			SpinButton sb = (SpinButton) self;-			GridTool.set_grid_width (sb.get_value ());-			MainWindow.get_glyph_canvas ().redraw ();-		});-				-		grid_expander.add_tool (grid_width);--		GridTool.sizes.append (grid_width);--		grid_expander.set_open (true);-		update_expanders ();-		-		redraw (0, 0, allocation_width, allocation_height);-		-		select_tool (grid_width);-		grid_width.set_active (false);-		-		return grid_width;-	}--	public void remove_current_grid () {-		Tool grid_width;-		-		foreach (Tool t in grid_expander.tool) {-			if (t.is_selected () && t is SpinButton) {-				GridTool.sizes.remove ((SpinButton)t);-				grid_expander.tool.remove (t);-				break;-			}-		}-		-		if (grid_expander.tool.length () > 0) {-			grid_width = grid_expander.tool.last ().data;-			select_tool (grid_width);-			grid_width.set_active (false);-		}-		-		update_expanders ();-		redraw (0, 0, allocation_width, allocation_height);-	}--	public void reset_active_tool () {-		foreach (var exp in expanders) {-			foreach (Tool t in exp.tool) {-				t.set_active (false);-			}-		}-	}--	public Tool? get_active_tool () {-		foreach (var exp in expanders) {-			foreach (Tool t in exp.tool) {-				if (t.is_active ()) {-					return t;-				}-			}-		}-		-		return null;-	}--	public Tool get_current_tool () {-		return current_tool;-	}-	-	public void select_tool (Tool tool) {-		foreach (var exp in expanders) {-			-			foreach (Tool t in exp.tool) {-				if (tool.get_id () == t.get_id ()) {-					exp.set_open (true);-					-					bool update;-					-					update = tool.set_selected (true);-					update = tool.set_active (true);-					-					tool.select_action (tool); // execute command-					-					if (update) {							-						redraw ((int) exp.x - 10, (int) exp.y - 10, allocation_width, (int) (allocation_height - exp.y + 10));-					}-					-					if (exp == draw_tools || t == move_background || t == cut_background) {-						current_tool = tool;-					}-				}-			}-			-		}-	}-	-	public Tool get_tool (string name) {-		foreach (var e in expanders) {-			foreach (var t in e.tool) {-				if (t.get_name () == name) {-					return t;-				}-			}-		}--		if (name == cut_background.get_name ()) {-			return cut_background;-		}-				-		warning ("No tool found for name \"%s\".\n", name);-		-		return new Tool ("no_icon");-	}-	-	public static void select_tool_by_name (string name) {-		Toolbox b = MainWindow.get_toolbox ();-		b.select_tool (b.get_tool (name));-	}-		-	private void update_expanders () {-		Expander? p = null; -		Expander pp;-		foreach (var e in expanders) {-			if (p != null) {-				pp = (!) p;-				e.set_offset (pp.y + pp.margin + 12);-			}-			-			p = e;-		}-	}-	-	private void add_expander (Expander e) {-		expanders.append (e);-	}-	-	private void draw_expanders (int w, int h, Context cr) {-		foreach (Expander e in expanders) {-			e.draw (w, h, cr);-			if (e.is_open ()) {-				e.draw_content (w, h, cr);-			}-		}-	}-	-	public void draw (int w, int h, Context cr) { -		cr.save ();-		-		cr.rectangle(0, 0, w, h);-		cr.set_line_width(0);-		cr.set_source_rgba(255/255.0, 255/255.0, 255/255.0, 1);-		cr.fill();--		for (int i = 0; i < w; i++) {-			cr.set_source_surface ((!) toolbox_background, i, 0);-			cr.paint ();-		}--		cr.rectangle(0, 0, 1, h);-		cr.set_line_width(0);-		cr.set_source_rgba(0/255.0, 0/255.0, 0/255.0, 1);-		cr.fill();-		-		draw_expanders (w, h, cr);-		-		cr.restore ();-	}-}--class ToolboxExpander : Expander {-	-	public ToolboxExpander () {-	}-	-	public override bool set_open (bool o) {-		bool r = (open != o);-		rotation = (o) ? 0 : Math.PI;-		open = o;-		return r;-	}-}--}diff --git a/src/TooltipArea.vala b/src/TooltipArea.valadeleted file mode 100644index c70d7c5..0000000--- a/src/TooltipArea.vala+++ /dev/null@@ -1,122 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Gdk;-using Gtk;--namespace Supplement {--public class TooltipArea : GLib.Object {--	string tool_tip;-	public signal void redraw ();--	public TooltipArea () {-		set_text_from_tool ();-	}--	public void update_text () {-		set_text_from_current_tool ();-		redraw ();-	}-	-	public void show_text (string text) {-		tool_tip = text;-		redraw ();-	}-	-	public void set_text_from_tool () {-		set_text_from_current_tool ();-		redraw ();-	}-	-	private void set_text_from_current_tool () {-		Toolbox? tb = MainWindow.get_toolbox ();-		Tool? t;-		Tool tool;-		StringBuilder sb;-		-		if (tb == null) {-			return;-		}-		-		t = ((!)tb).get_active_tool ();-		-		if (t == null) {-			return;-		}-		-		tool = (!) t;-		-		if (tool.key != '\0') {-			sb = new StringBuilder ();--			sb.append ("(");-			-			if (tool.modifier_flag == CTRL) {-				sb.append ("Ctrl+");-			} else if (tool.modifier_flag == SHIFT) {-				sb.append ("Shift+");-			} --			sb.append_unichar (tool.key);-			sb.append (") ");-			sb.append (tool.get_tip ());-			-			show_text (sb.str);-		} else {-			show_text (tool.get_tip ());-		}-		-	}-	-	public void draw (Context cr, Allocation alloc) {-		cr.save ();-		cr.rectangle (0, 0, alloc.width, alloc.height);-		cr.set_line_width (0);-		cr.set_source_rgba (183/255.0, 200/255.0, 223/255.0, 1);-		cr.fill_preserve ();-		cr.stroke ();-		cr.restore ();--		cr.save ();-		cr.rectangle (0, 0, alloc.width, 1);-		cr.set_line_width (0);-		cr.set_source_rgba (107/255.0, 127/255.0, 168/255.0, 1);-		cr.fill_preserve ();-		cr.stroke ();-		cr.restore ();-		-		cr.save ();-		cr.rectangle (0, 1, alloc.width, 1);-		cr.set_line_width (0);-		cr.set_source_rgba (145/255.0, 160/255.0, 190/255.0, 1);-		cr.fill_preserve ();-		cr.stroke ();-		cr.restore ();-		-		cr.save ();-		cr.set_font_size (14);-		cr.move_to (5, 15);-		cr.show_text (tool_tip);-		cr.restore ();--	}-}--}diff --git a/src/UniRange.vala b/src/UniRange.valadeleted file mode 100644index fcfdc11..0000000--- a/src/UniRange.vala+++ /dev/null@@ -1,49 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {-	-public class UniRange : GLib.Object {-	-	public unichar start;-	public unichar stop;-	-	public UniRange (unichar start, unichar stop) {-		this.start = start;-		this.stop = stop;-	}-	-	public unichar length () {-		return stop - start + 1;-	}--	public unichar get_char (unichar index) {-		unichar result = start + index;-		-		if (unlikely (index < 0)) {-			warning ("Index is negative in UniRange.");-		}-		-		if (unlikely (!(start <= result <= stop))) {-			warning ("Index out of range in UniRange (%u <= %u <= %u) (index: %u)\n", start, result, stop, index);-		}-		-		return result;-	}-}--}diff --git a/src/UserActions.vala b/src/UserActions.valadeleted file mode 100644index d7946eb..0000000--- a/src/UserActions.vala+++ /dev/null@@ -1,52 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--namespace Supplement {--/** User interface action for unit testing and undo command.- */-interface UserActions {-	public abstract void do_action ();-	public abstract void undo ();-	public abstract void reundo ();-	public abstract void test ();-}--interface UserClickActions {-	public abstract void do_click (int button, int x, int y);-}--class NewPointAction : UserActions, UserClickActions {--	public void do_action () {-	}--	public void undo () {-	}-	-	public void reundo () {-	}-	-	public void do_click (int button, int x, int y) {-	}-	-	public void test () {-	}-	-}--}diff --git a/src/VersionList.vala b/src/VersionList.valadeleted file mode 100644index 3f11a88..0000000--- a/src/VersionList.vala+++ /dev/null@@ -1,113 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Cairo;-using Math;--namespace Supplement {--public class VersionList : DropMenu {-	int versions = 1;-	int current_version = 0;-	public List<Glyph> glyphs = new List<Glyph> ();-	public static bool suppress_signal = false;--	public VersionList (Glyph? g = null) {-		base ("version");-		set_direction (MenuDirection.POP_UP);-		-		MenuAction ma = add_item (_("New version"));-		ma.action = (self) => {-			return_if_fail (self.parent != null);-			return_if_fail (glyphs.length () > 0);-			-			Supplement.get_current_font ().touch ();-			-			add_new_version ();-			current_version = (int) glyphs.length () - 1;-		};-		-		if (g != null) {-			add_glyph ((!) g);-		}-	}-	-	public static void set_suppress_signal (bool s) {-		suppress_signal = s;-	}-	-	public Glyph get_current () {-		if (unlikely (current_version >= glyphs.length ())) {-			warning (@"current_version >= glyphs.length ($current_version >= $(glyphs.length ()))");-			return new Glyph ("");-		}-		-		return glyphs.nth (current_version).data;-	}--	public void add_new_version () {-		Glyph g = get_current ();-		Glyph new_version = g.copy ();-		add_glyph (new_version);-	}-	-	private void set_selected_item (MenuAction ma) {-		int i = ma.index;-				-		return_if_fail (0 <= i < glyphs.length ());--		current_version = i;-		-		return_if_fail (ma.parent != null);-		-		((!)ma.parent).deselect_all ();-		ma.set_selected (true);		-		-		if (!is_null (MainWindow.get_tab_bar ())) {-			MainWindow.get_tab_bar ().close_by_name (get_current ().name);-		}	-	}-	-	public void add_glyph (Glyph new_version, bool selected = true) {-		MenuAction ma;-		-		versions++;-		glyphs.append (new_version);--		ma = add_item (_("Version") + @" $(versions - 1)");-		ma.index = (int) glyphs.length () - 1;-		-		ma.action = (self) => {-			Font font;-			-			if (suppress_signal) {-				return;-			}-			-			font = Supplement.get_current_font ();-			set_selected_item (self);-			font.touch ();-		};--		if (selected && !suppress_signal) {-			set_selected_item (ma);-		}-	}--}--}diff --git a/src/ZoomTool.vala b/src/ZoomTool.valadeleted file mode 100644index 25996cc..0000000--- a/src/ZoomTool.vala+++ /dev/null@@ -1,157 +0,0 @@-/*-    Copyright (C) 2012 Johan Mattsson--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program 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 General Public License for more details.--    You should have received a copy of the GNU General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.-*/--using Gtk;--namespace Supplement {--class ZoomTool : Tool {--	int zoom_area_begin_x = -1;-	int zoom_area_begin_y = -1;-	-	int view_index = 0;-	List<Tab> views = new List<Tab> ();-	-	public ZoomTool (string n) {-		base (n, "Zoom");--		select_action.connect((self) => {-		});--		select_action.connect((self) => {-		});-			-		press_action.connect((self, b, x, y) => {-			if (b == 1) {-				zoom_area_begin_x = x;-				zoom_area_begin_y = y;-			}-		});--		move_action.connect((self, x, y) => {-			if (zoom_area_begin_x > 0) {-				Glyph g =	MainWindow.get_current_glyph ();-				g.show_zoom_area (zoom_area_begin_x, zoom_area_begin_y, x, y);-			}-		});-		-		release_action.connect((self, b, x, y) => {-			Glyph g;-						-			if (b == 1) {-				store_current_view ();-				-				g =	MainWindow.get_current_glyph ();-				-				if (zoom_area_begin_x == x && zoom_area_begin_y == y) { // zero width center this point but don't zoom in-					g.set_center (x, y);-				} else { -					g.set_zoom_from_area ();-				}-				-				zoom_area_begin_x = -1;-				zoom_area_begin_y = -1;-			}-		});-	}--	public void zoom_full_background_image () {-		GlyphBackgroundImage bg;-		Glyph g = MainWindow.get_current_glyph ();-		int x, y;-		-		if (g.get_background_image () == null) {-			return;-		}-		-		bg = (!) g.get_background_image ();-		-		x = (int) (bg.img_offset_x);-		y = (int) (bg.img_offset_y);-		-		g.set_zoom_area (x, y, (int) (x + bg.size_margin * bg.img_scale_x), (int) (y + bg.size_margin * bg.img_scale_y));-		g.set_zoom_from_area ();-	}--	public void zoom_full_glyph () {-		store_current_view ();-		-		MainWindow.get_current_display ().zoom_min ();-	}-	-	public override bool test () {-		test_select_action ();-		return true;-	}-	-	/** Add an item to zoom view list. */-	public void store_current_view () {	-		if (views.length () - 1 > view_index) {-			unowned List<Tab> i = views.nth (view_index + 1);-			while (i != i.last ()) {-				i.delete_link (i.next);-			}-		}-		-		views.append (MainWindow.get_current_tab ());-		view_index = (int)views.length () - 1;-		MainWindow.get_current_display ().store_current_view ();-	}--	/** Redo last zoom.*/-	public void next_view () {-		if (view_index + 1 >= (int) views.length ()) {-			return;-		}-		-		view_index++;-	-		MainWindow.select_tab (views.nth (view_index).data);-		MainWindow.get_current_display ().next_view ();-		MainWindow.get_glyph_canvas ().redraw ();-	}-	-	/** Undo last zoom. */-	public void previous_view () {-		if (view_index == 0) {-			return;-		}-		-		view_index--;-	-		MainWindow.select_tab (views.nth (view_index).data);-		MainWindow.get_current_display ().restore_last_view ();-		MainWindow.get_glyph_canvas ().redraw ();-	}-}--class ZoomView : GLib.Object {-	public double x;-	public double y;-	public double zoom;-	public Allocation allocation;-	-	public ZoomView (double x, double y, double zoom, Allocation allocation) {-		this.x = x;-		this.y = y;-		this.zoom = zoom;-		this.allocation = allocation;-	}-}-	-}diff --git a/src/wscript b/src/wscriptdeleted file mode 100644index 101720e..0000000--- a/src/wscript+++ /dev/null@@ -1,139 +0,0 @@-BIRDFONT = [-	'Allocation.vala',-	'Argument.vala',-	'Font.vala',-	'BackgroundTool.vala',-	'BackgroundSelection.vala',-	'Birdfont.vala',-	'EotWriter.vala',-	'Expander.vala',-	'ExportTool.vala',-	'FontDisplay.vala',-	'CharDatabase.vala',-	'Config.vala',-	'Context.vala',-	'CutBackgroundTool.vala',-	'CutTool.vala',-	'ClipTool.vala',-	'DropMenu.vala',-	'EditPoint.vala',-	'EditPointHandle.vala',-	'Glyph.vala',-	'GlyphBackgroundImage.vala',-	'GlyphCanvas.vala',-	'GlyphCollection.vala',-	'GlyphRange.vala',-	'GlyphTable.vala',-	'GridTool.vala',-	'Icons.vala',-	'Kerning.vala', -	'KeyBindings.vala', -	'Line.vala',-	'MainWindow.vala',-	'MenuAction.vala',-	'MenuTab.vala',-	'MergeTool.vala',-	'MoveTool.vala',-	'NativeWindow.vala',-	'Intersection.vala',-	'IntersectionList.vala',-	'IOThread.vala',-	'OpenFontFormatWriter.vala',-	'OpenFontFormatReader.vala',-	'OverView.vala',-	'Path.vala',-	'PenTool.vala',-	'Preview.vala',-	'ResizeTool.vala',-	'SaveDialog.vala',-	'Scrollbar.vala',-	'ShrinkTool.vala',-	'SpinButton.vala',-	'Supplement.vala',-	'Svg.vala',-	'SvgFontFormatWriter.vala',-	'Preferences.vala',-	'Tab.vala',-	'TabBar.vala',-	'TestCases.vala', -	'TestSupplement.vala', -	'Tool.vala',-	'Toolbox.vala',-	'TooltipArea.vala',-	'UniRange.vala',-	'VersionList.vala',-	'ZoomTool.vala',-]--PARAM = [-	'-X -D\'GETTEXT_PACKAGE="birdfont"\' '-	'--thread',-	'--disable-warnings',-	'--enable-experimental',-	'--enable-experimental-non-null',-	'--vapidir=./',-]--def build (bld):--	bld.shlib (-		target        = 'birdfont',-		param         = PARAM,-		packages      = 'libxml-2.0',-		uselib        = 'CAIRO GLIB GIO PIXBUF XML',-		source        = BIRDFONT,-		name          = "libbirdfont",-	)--	bld (-		rule          = "mv src/birdfont.h ./",-		depends       = "libbirdfont",-		name          = "birdfont_h"-	)-	-	bld (-		rule = 'valac -C ./src/birdfont.vapi ../src/Main.vala ../src/GtkWindow.vala -X ../build/src/libbirdfont.so -X ./birdfont.h --pkg gtk+-2.0 --pkg libxml-2.0 --pkg gdk-2.0 --pkg webkit-1.0',-		name = 'main_c',-		source  = ['../src/Main.vala', '../src/GtkWindow.vala'],-		target = '../Main.c',-		depends = 'libbirdfont'-	)--	bld (-		rule = "valac -C ./src/birdfont.vapi ../src/BirdfontExport.vala -X ../build/src/libbirdfont.so -X ./birdfont.h --pkg gtk+-2.0 --pkg libxml-2.0 --pkg gdk-2.0 --pkg webkit-1.0",-		depends = 'libbirdfont',-		target = '../BirdfontExport.c',-		name = 'birdfont_export'-	)--	bld (-		rule = """gcc ../build/Main.c ../build/GtkWindow.c \-				-D 'GETTEXT_PACKAGE="birdfont"' \-				-I ../build/ -I ../build/src -L ../build/src -l birdfont \-				-o ${TGT} \-				$(pkg-config --cflags --libs glib-2.0) \-				$(pkg-config --cflags --libs libxml-2.0) \-				$(pkg-config --cflags --libs gio-2.0) \-				$(pkg-config --cflags --libs libsoup-2.4) \-				$(pkg-config --cflags --libs gtk+-2.0) \-				$(pkg-config --cflags --libs webkit-1.0)""",-		source  = ['../Main.c'],-		target  = 'birdfont',-		depends = 'main_c'-	)--	bld (-		rule = 'gcc  ../build/BirdfontExport.c -I../build/ -L  ../build/src -l birdfont -o ${TGT} \-				$(pkg-config --cflags --libs glib-2.0) \-				$(pkg-config --cflags --libs libxml-2.0) \-				$(pkg-config --cflags --libs gio-2.0) \-				$(pkg-config --cflags --libs libsoup-2.4) \-				$(pkg-config --cflags --libs gtk+-2.0) \-				$(pkg-config --cflags --libs webkit-1.0)',-		source  = ['../BirdfontExport.c'],-		target  = 'birdfont-export',-		depends = 'birdfont_export'-	)--	bld.install_files('${PREFIX}/bin/', ['../build/src/birdfont'], chmod=755)-	bld.install_files('${PREFIX}/bin/', ['../build/src/birdfont-export'], chmod=755)diff --git a/wscript b/wscriptindex 5874915..431dc20 100644--- a/wscript+++ b/wscript@@ -53,8 +53,10 @@ def build(bld): 	
 	bld.add_pre_fun(pre)
 	bld.add_post_fun(post)
-	
-	bld.recurse('src')
+
+	bld.recurse('libbirdfont')
+	bld.recurse('birdfont')
+	bld.recurse('birdfont-export')
 
 	start_dir = bld.path.find_dir('./')
 	bld.install_files('${PREFIX}/share/birdfont/', start_dir.ant_glob('layout/*'), cwd=start_dir, relative_trick=True)
@@ -142,14 +144,14 @@ def compile_translations (bld): 		)
 		
 		bld.install_files(
-			'${PREFIX}/locale/" + loc + "/LC_MESSAGES/', 
+			'${PREFIX}/share/locale/' + loc + '/LC_MESSAGES/', 
 			['build/locale/'  + loc + '/LC_MESSAGES/birdfont.mo']
 		)
 	
 def write_config (cfg):
 	print ("Writing Config.vala")	
 
-	f = open('./src/Config.vala', 'w+')
+	f = open('./libbirdfont/Config.vala', 'w+')
 	f.write("// Don't edit this file – it's generated by wscript\n")
 	f.write("namespace Supplement {\n")
 	

Contact

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