Clone in a terminal:
Commit e48648e
Johan Mattsson - 2014-03-31T13:18:19+00:00
e48648e0adbafc7ada939c34b888841bc1f51006johan.mattsson.m@gmail.com

Split .bf files in several smaller .bfp files This commit adds support for the .bfp file format.

Changed files (15)

Diff

diff --git a/birdfont/GtkWindow.vala b/birdfont/GtkWindow.valaindex e3fc39e..bcca5ec 100644--- a/birdfont/GtkWindow.vala+++ b/birdfont/GtkWindow.vala@@ -414,6 +414,7 @@ public class GtkWindow : Gtk.Window, NativeWindow { 		Gtk.Menu tab_menu = new Gtk.Menu (); 		Gtk.Menu tool_menu = new Gtk.Menu (); 		Gtk.Menu kerning_menu = new Gtk.Menu ();+		Gtk.Menu git_menu = new Gtk.Menu ();  		AccelGroup accel_group = new Gtk.AccelGroup(); 		add_accel_group (accel_group);@@ -704,6 +705,13 @@ public class GtkWindow : Gtk.Window, NativeWindow { 		kerning_menu.append (select_previous_kerning_pair); 		select_previous_kerning_pair.activate.connect (() => { KerningDisplay.previous_pair (); }); 		+		// Git+		Gtk.MenuItem save_as_bfp = new Gtk.MenuItem.with_mnemonic (t_("_Save as .bfp"));+		git_menu.append (save_as_bfp);+		save_as_bfp.activate.connect (() => { +			 MenuTab.save_as_bfp ();+		});+		 		// Add menus 		Gtk.MenuItem file_launcher = new Gtk.MenuItem.with_mnemonic (t_("_File")); 		file_launcher.set_submenu (file_menu);@@ -719,13 +727,20 @@ public class GtkWindow : Gtk.Window, NativeWindow {  		Gtk.MenuItem kerning_launcher = new Gtk.MenuItem.with_mnemonic (t_("_Kerning")); 		kerning_launcher.set_submenu (kerning_menu);-						++		Gtk.MenuItem git_launcher = new Gtk.MenuItem.with_mnemonic ("_Git");+		git_launcher.set_submenu (git_menu);+	 		menubar.append (file_launcher); 		menubar.append (edit_launcher); 		menubar.append (tab_launcher); 		menubar.append (tool_launcher); 		menubar.append (kerning_launcher); 		+		if (BirdFont.has_argument ("--test")) {+			menubar.append (git_launcher);+		}+		 		return menubar;	 	} diff --git a/libbirdfont/BirdFont.vala b/libbirdfont/BirdFont.valaindex 5ee8263..1c31029 100644--- a/libbirdfont/BirdFont.vala+++ b/libbirdfont/BirdFont.vala@@ -410,7 +410,7 @@ public class BirdFont { 		return backup; 	} -	internal static bool has_argument (string param) {+	public static bool has_argument (string param) { 		if (is_null (args)) { 			warning ("args is null"); 			return false;diff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex 8c9cb76..c83e868 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -19,7 +19,7 @@ namespace BirdFont {  * BirdFont file format. This class can parse both the old ffi format   * and the new bf format.  */-class BirdFontFile {+class BirdFontFile : GLib.Object { 	 	Font font; 	@@ -36,6 +36,10 @@ class BirdFontFile { 		 		Parser.init (); 		+		while (font.background_images.length () > 0) {+			font.background_images.remove_link (font.background_images.first ());+		}+		 		font.font_file = path; 		tr = new TextReader.filename (path); 		ok = load_xml (tr);@@ -45,6 +49,17 @@ class BirdFontFile { 		return ok; 	} +	public bool load_part (string bfp_file) {+		TextReader tr;+		bool ok;+		Parser.init ();+		tr = new TextReader.filename (bfp_file);+		ok = load_xml (tr);+		Parser.cleanup ();+				+		return ok;+	}+ 	/** Load a new .bf file. 	 * @param xml_data data for a valid .bf file 	 */@@ -76,7 +91,6 @@ class BirdFontFile {  		create_background_files (root); 		ok = parse_file (root);-		 		tr.close (); 			 		return ok;@@ -85,12 +99,10 @@ class BirdFontFile { 	public bool write_font_file (string path, bool backup = false) { 		try { 			File file = File.new_for_path (path);-			uint num_kerning_pairs; 			uint progress;-			string range; 			 			if (file.query_file_type (0) == FileType.DIRECTORY) {-				warning (@"Can not save font. $path is a directory.");+				warning (@"Can't save font. $path is a directory."); 				return false; 			} 			@@ -98,12 +110,9 @@ class BirdFontFile { 				file.delete (); 			} 			-			DataOutputStream os = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION));+			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");+			write_root_tag (os); 			 			// this a backup of another font 			if (backup) {@@ -113,38 +122,17 @@ class BirdFontFile { 			} 		 			os.put_string ("\n");-			os.put_string (@"<postscript_name>$(font.postscript_name)</postscript_name>\n");-			os.put_string (@"<name>$(font.name)</name>\n");-			os.put_string (@"<subfamily>$(font.subfamily)</subfamily>\n");-			os.put_string (@"<bold>$(font.bold)</bold>\n");-			os.put_string (@"<italic>$(font.italic)</italic>\n");			-			os.put_string (@"<full_name>$(font.full_name)</full_name>\n");-			os.put_string (@"<unique_identifier>$(font.unique_identifier)</unique_identifier>\n");-			os.put_string (@"<version>$(font.version)</version>\n");-			os.put_string (@"<description>$(font.description)</description>\n");-			os.put_string (@"<copyright>$(font.copyright)</copyright>\n");+			write_description (os); 			 			os.put_string ("\n");-			os.put_string ("<horizontal>\n");-			os.put_string (@"\t<top_limit>$(font.top_limit)</top_limit>\n");-			os.put_string (@"\t<top_position>$(font.top_position)</top_position>\n");-			os.put_string (@"\t<x-height>$(font.xheight_position)</x-height>\n");-			os.put_string (@"\t<base_line>$(font.base_line)</base_line>\n");-			os.put_string (@"\t<bottom_position>$(font.bottom_position)</bottom_position>\n");-			os.put_string (@"\t<bottom_limit>$(font.bottom_limit)</bottom_limit>\n");-			os.put_string ("</horizontal>\n\n");+			write_lines (os); -			foreach (string gv in font.grid_width) {-				os.put_string (@"<grid width=\"$(gv)\"/>\n");-			}-			-			if (GridTool.sizes.length () > 0) {-				os.put_string ("\n");-			}+			os.put_string ("\n");+			write_settings (os); 			-			os.put_string (@"<background scale=\"$(font.background_scale)\" />\n"); 			os.put_string ("\n"); 			+			// FIXME: add this to a font specific settings file 			if (font.background_images.length () > 0) { 				os.put_string (@"<images>\n"); 				@@ -203,6 +191,35 @@ class BirdFontFile { 				TooltipArea.show_text (t_("Saving")); 			}); 			+			write_kerning (os);+			write_closing_root_tag (os);+			+			os.close ();+		} catch (GLib.Error e) {+			warning (@"Failed to save $path \n");+			warning (@"$(e.message) \n");+			return false;+		}+		+		TooltipArea.show_text ("");+		return true;+	}+	+	public void write_root_tag (DataOutputStream os) throws GLib.Error {+		os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>""");+		os.put_string ("\n");+		os.put_string ("<font>\n");+	}+	+	public void write_closing_root_tag (DataOutputStream os) throws GLib.Error {+		os.put_string ("</font>\n");+	}+	+	public void write_kerning (DataOutputStream os)  throws GLib.Error {+			uint num_kerning_pairs;+			uint progress;+			string range;+			 			num_kerning_pairs = KerningClasses.get_instance ().classes_first.length (); 			progress = num_kerning_pairs; 			ProgressBar.set_progress (1);@@ -252,20 +269,80 @@ class BirdFontFile { 				ProgressBar.set_progress (0); 				TooltipArea.show_text (t_("Saving")); 			});-			-			os.put_string ("</font>");-			-		} catch (GLib.Error e) {-			warning (@"Failed to save $path \n");-			warning (@"$(e.message) \n");-			return false;+	}+	+	public void write_settings (DataOutputStream os) throws GLib.Error {+		foreach (string gv in font.grid_width) {+			os.put_string (@"<grid width=\"$(gv)\"/>\n"); 		} 		-		TooltipArea.show_text ("");-		return true;+		if (GridTool.sizes.length () > 0) {+			os.put_string ("\n");+		}+		+		os.put_string (@"<background scale=\"$(font.background_scale)\" />\n");	+	}++	public void write_description (DataOutputStream os) throws GLib.Error {+		os.put_string (@"<postscript_name>$(font.postscript_name)</postscript_name>\n");+		os.put_string (@"<name>$(font.name)</name>\n");+		os.put_string (@"<subfamily>$(font.subfamily)</subfamily>\n");+		os.put_string (@"<bold>$(font.bold)</bold>\n");+		os.put_string (@"<italic>$(font.italic)</italic>\n");			+		os.put_string (@"<full_name>$(font.full_name)</full_name>\n");+		os.put_string (@"<unique_identifier>$(font.unique_identifier)</unique_identifier>\n");+		os.put_string (@"<version>$(font.version)</version>\n");+		os.put_string (@"<description>$(font.description)</description>\n");+		os.put_string (@"<copyright>$(font.copyright)</copyright>\n"); 	} -	private string float_point (double d) {+	public void write_lines (DataOutputStream os) throws GLib.Error {+		os.put_string ("<horizontal>\n");+		os.put_string (@"\t<top_limit>$(font.top_limit)</top_limit>\n");+		os.put_string (@"\t<top_position>$(font.top_position)</top_position>\n");+		os.put_string (@"\t<x-height>$(font.xheight_position)</x-height>\n");+		os.put_string (@"\t<base_line>$(font.base_line)</base_line>\n");+		os.put_string (@"\t<bottom_position>$(font.bottom_position)</bottom_position>\n");+		os.put_string (@"\t<bottom_limit>$(font.bottom_limit)</bottom_limit>\n");+		os.put_string ("</horizontal>\n");+	}++	public void write_glyph_collection_start (GlyphCollection gc, DataOutputStream os)  throws GLib.Error {+		os.put_string (@"<collection unicode=\"$(Font.to_hex (gc.get_current ().unichar_code))\">\n");		+	}++	public void write_glyph_collection_end (DataOutputStream os)  throws GLib.Error {+		os.put_string ("</collection>\n");+	}++	public void write_selected (GlyphCollection gc, DataOutputStream os)  throws GLib.Error {+		os.put_string (@"\t<selected id=\"$(gc.get_selected_id ())\"/>\n");+	}++	public void write_glyph_collection (GlyphCollection gc, DataOutputStream os)  throws GLib.Error {+		write_glyph_collection_start (gc, os);+		write_selected (gc, os);+		foreach (Glyph g in gc.get_version_list ().glyphs) {+			write_glyph (g, gc, os);+		}+		write_glyph_collection_end (os);+	} ++	public void write_glyph (Glyph g, GlyphCollection gc, DataOutputStream os) throws GLib.Error {+		string data;+		+		os.put_string (@"\t<glyph id=\"$(g.version_id)\" left=\"$(g.left_limit)\" right=\"$(g.right_limit)\">\n");+		foreach (Path p in g.path_list) {+			data = get_point_data (p);+			if (data != "") {+				os.put_string (@"\t\t<path stroke=\"$(p.stroke)\" data=\"$(data)\" />\n");+			}+		}+		write_glyph_background (g, os);+		os.put_string ("\t</glyph>\n");+	}+			+	public static string float_point (double d) { 		string s = @"$d"; 		return s.replace (",", "."); 	}@@ -419,32 +496,7 @@ class BirdFontFile { 			data.append (@"T"); 		}	 	}--	private void write_glyph_collection (GlyphCollection gc, DataOutputStream os)  throws GLib.Error {-		os.put_string (@"<collection unicode=\"$(Font.to_hex (gc.get_current ().unichar_code))\">\n");-		os.put_string (@"\t<selected id=\"$(gc.get_selected_id ())\"/>\n");-		foreach (Glyph g in gc.get_version_list ().glyphs) {-			write_glyph (g, gc, os);-		}-		os.put_string ("</collection>\n");-	} --	private void write_glyph (Glyph g, GlyphCollection gc, DataOutputStream os) throws GLib.Error {-		string data;-		-		os.put_string (@"\t<glyph id=\"$(g.version_id)\" left=\"$(g.left_limit)\" right=\"$(g.right_limit)\">\n");-		foreach (Path p in g.path_list) {-			data = get_point_data (p);-			if (data != "") {-				os.put_string (@"\t\t<path stroke=\"$(p.stroke)\" data=\"$(data)\" />\n");-			}-		}-		-		write_glyph_background (g, os);-		-		os.put_string ("\t</glyph>\n");-	}-+	 	private void write_glyph_background (Glyph g, DataOutputStream os) throws GLib.Error { 		GlyphBackgroundImage? bg; 		GlyphBackgroundImage background_image;@@ -452,6 +504,7 @@ class BirdFontFile { 		 		bg = g.get_background_image (); 		+		// FIXME: use the coordinate system 		if (bg != null) { 			background_image = (!) bg; @@ -574,12 +627,7 @@ class BirdFontFile { 	} 	 	private void create_background_files (Xml.Node* root) requires (root != null) {-		while (font.background_images.length () > 0) {-			font.background_images.remove_link (font.background_images.first ());-		}-	-		for (Xml.Node* iter = root->children; iter != null; iter = iter->next) {-			+		for (Xml.Node* iter = root->children; iter != null; iter = iter->next) {			 			if (iter->name == "name" && iter->children != null) { 				font.set_name (iter->children->content); 			}@@ -895,7 +943,7 @@ class BirdFontFile { 		string attr_content; 		StringBuilder b; 		string name = "";-		int selected_id = 1;+		int selected_id = -1; 				 		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) { 			attr_name = prop->name;@@ -915,7 +963,7 @@ class BirdFontFile {  		current_gc = font.get_glyph_collection_by_name (name); 		new_glyph_collection = (current_gc == null);-		gc = (!new_glyph_collection) ? (!) current_gc : new GlyphCollection ();+		gc = (!new_glyph_collection) ? (!) current_gc : new GlyphCollection (unicode, name);  		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { 			if (iter->name == "selected") {@@ -1372,7 +1420,8 @@ class BirdFontFile { 		} 				 		if (gc == null) {-			gc = new GlyphCollection (g);+			gc = new GlyphCollection (uni, name);+			((!) gc).insert_glyph (g, selected); 			font.add_glyph_collection ((!) gc); 		} else { 			((!)gc).insert_glyph (g, selected);@@ -1463,7 +1512,7 @@ class BirdFontFile { 			} 		} 		-		img.set_position(img.img_x, img.img_y);	+		img.set_position (img.img_x, img.img_y);	 	} 	 	private void parse_point (Path p, Xml.Node* iter) {diff --git a/libbirdfont/BirdFontPart.vala b/libbirdfont/BirdFontPart.valanew file mode 100644index 0000000..9f2cca9--- /dev/null+++ b/libbirdfont/BirdFontPart.vala@@ -0,0 +1,404 @@+/*+    Copyright (C) 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library is distributed in the hope that it will be useful, but +    WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +    Lesser General Public License for more details.+*/++namespace BirdFont {++/** BirdFontPart is a class for parsing .bfp files. The file format is + * identical to .bf but the font is split in many parts. Each part+ * contains a few elements and all parent nodes from the root node and+ * downwards. The .bfp files can be parsed in any order. The root directory+ * of a .bfp tree must have a file with the name "description.bfp", this file + * tells the parser that bfp files in parent directories should be excluded.+ */+class BirdFontPart : GLib.Object{+	Font font;+	List<string> parts;+	string root_directory;++	static string FILE_ATTRIBUTES = "standard::*";++	public BirdFontPart (Font font) {	+		this.font = font;+		parts = new List<string> ();+		root_directory = "";+	}++	public bool load (string bfp_file) {+		BirdFontFile bf = new BirdFontFile (font);+		File bfp_dir;+		File image_dir;+		+		try {+			find_all_parts (bfp_file);+			font.set_bfp (true);+			+			while (font.background_images.length () > 0) {+				font.background_images.remove_link (font.background_images.first ());+			}+			+			bfp_dir = File.new_for_path (root_directory);+			image_dir = bfp_dir.get_child ("images");+			copy_backgrounds ((!) image_dir.get_path ());++			foreach (string fn in parts) {+				bf.load_part (fn);+			}+		} catch (GLib.Error e) {+			warning (e.message);+			return false;+		}+		+		return true;+	}+	+	public string get_path () {+		string path = "";+		+		try {+			path = (!) get_destination_file (@"$(font.full_name).bfp").get_path ();+		} catch (GLib.Error e) {+			warning (e.message);+		}+		+		return path;+	}+	+	public bool save () {+		DataOutputStream os;+		BirdFontFile bf = new BirdFontFile (font);+		bool error = false;+		+		if (root_directory == "") {+			warning ("No directory is created for this birdfont part.");+			return false;+		}+		+		try {+			os = create_file (@"$(font.full_name).bfp");+			bf.write_root_tag (os);+			bf.write_closing_root_tag (os);+			os.close ();+			+			os = create_file ("description.bfp");+			bf.write_root_tag (os);+			bf.write_description (os);+			bf.write_closing_root_tag (os);+			os.close ();++			os = create_file ("lines.bfp");+			bf.write_root_tag (os);+			bf.write_lines (os);+			bf.write_closing_root_tag (os);+			os.close ();++			os = create_file ("settings.bfp");+			bf.write_root_tag (os);+			bf.write_settings (os);+			bf.write_closing_root_tag (os);+			os.close ();++			font.glyph_cache.for_each ((gc) => {+				try {+					string file_name;+					string dir_name;+			+					if (is_null (gc)) {+						warning ("No glyph collection");+					}++					file_name = get_first_number_in_unicode (((!)gc).get_current ());+					dir_name = (!)file_name.get_char ().to_string ();+								+					os = create_file (@"selected_$(file_name).bfp", "glyphs", dir_name);+					bf.write_root_tag (os);+					bf.write_glyph_collection_start (gc, os);+					bf.write_selected ((!) gc, os);+					bf.write_glyph_collection_end (os);+					bf.write_closing_root_tag (os);+					os.close ();+			+					foreach (Glyph g in gc.get_version_list ().glyphs) {+						try {+							write_glyph (bf, gc, g, os);+							write_glyph_background_image (bf, gc, g, os);+						} catch (GLib.Error e) {+							warning (e.message);+						}+					}+				} catch (GLib.Error e) {+					warning (@"Can not save bfp files to $root_directory\n");+					warning (@"$(e.message) \n");+					error = true;+				}+			});++			os = create_file ("kerning.bfp");+			bf.write_root_tag (os);+			bf.write_kerning (os);+			bf.write_closing_root_tag (os);+			os.close ();+			+		} catch (GLib.Error e) {+			warning (@"Failed to save bfp files to $root_directory\n");+			warning (@"$(e.message) \n");+			return false;+		}+		+		return error;+	}++	void copy_backgrounds (string folder) throws GLib.Error {+		FileInfo info;+		FileInfo? fi;+		FileEnumerator e;+		string name;+		File image_dir;+		GlyphBackgroundImage bg;+		File found;+		File dest;+		+		image_dir = File.new_for_path (folder);+		+		if (image_dir.query_exists ()) {+			info = image_dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+			if (info.get_file_type () != FileType.DIRECTORY) {+				warning (@"$((!) image_dir.get_path ()) is not a directory.");+				throw new FileError.NOTDIR ("Not a directory.");+			}++			e = image_dir.enumerate_children (FILE_ATTRIBUTES, 0);+			while ((fi = e.next_file ()) != null) {+				info = (!) fi;+				name = info.get_name ();+				+				if (info.get_file_type () == FileType.DIRECTORY) {+					found = image_dir.get_child (name);+					copy_backgrounds ((!) found.get_path ());+				}+				+				if (name.has_suffix (".png")) {+					found = image_dir.get_child (name);+					dest = font.get_backgrounds_folder ().get_child ("parts").get_child (name);+					bg = new GlyphBackgroundImage ((!) found.get_path ());+					bg.create_background_folders (font);+					bg.copy_if_new (dest);+				}+			}+		}+	}++	string get_first_number_in_unicode (Glyph g) throws GLib.Error {+		string s = Font.to_hex (g.unichar_code);+		s = s.replace ("U+", "");+		return s;+	}+	+	string get_glyph_base_file_name (Glyph g) throws GLib.Error {+		string s = get_first_number_in_unicode (g);+		s = @"$(s)_$(g.version_id)";+		return s;+	}++	void write_glyph (BirdFontFile bf, GlyphCollection gc, Glyph g, DataOutputStream os) throws GLib.Error {+		string file_name;+		string dir_name;+	+		file_name = get_glyph_base_file_name (g);+		dir_name = (!)file_name.get_char ().to_string ();+					+		os = create_file (@"$(file_name).bfp", "glyphs", dir_name);+		bf.write_root_tag (os);+		bf.write_glyph_collection_start (gc, os);+		bf.write_glyph (g, gc, os);+		bf.write_glyph_collection_end (os);+		bf.write_closing_root_tag (os);+		os.close ();+	}++	void write_glyph_background_image (BirdFontFile bf, GlyphCollection gc, Glyph g, DataOutputStream os) throws GLib.Error {+		string file_name;+		string dir_name;+		GlyphBackgroundImage bg;+		File file;+		+		if (g.get_background_image () != null) {+			bg = (!) g.get_background_image ();++			if (bg.is_valid ()) {+				file_name = @"$(bg.get_sha1 ()).png";+				dir_name = (!)file_name.get_char ().to_string ();+				file = get_destination_file (file_name, "images", dir_name);+				bg.copy_if_new (file);				+				+				// FIXME: GIT ADD+			}			+		}+	}+		+	public void create_directory (string directory) throws GLib.Error {	+		File dir = File.new_for_path (directory);+		File bfp_dir;+		int i = 2;+		+		if (directory.has_suffix (font.get_full_name ())) {+			bfp_dir = dir;+		} else {+			bfp_dir = dir.get_child (font.get_full_name ());+		}+		+		while (bfp_dir.query_exists ()) {+			bfp_dir = dir.get_child (@"$(font.get_full_name ())_$(i)");+			i++;+		}+		+		if (!dir.query_exists ()) {+			DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+		}+		+		root_directory = (!) bfp_dir.get_path ();+		DirUtils.create (root_directory, 0xFFFFFF);+	}++	private void find_all_parts (string bfp_file) throws GLib.Error {	+		File start = File.new_for_path (bfp_file);+		FileInfo info;+		File root;+		+		info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+		if (info.get_file_type () != FileType.DIRECTORY) {+			start = (!) start.get_parent ();+		}++		root = find_root ((!)start.get_path ());+		root_directory = (!)root.get_path ();++		find_parts (root_directory);+	}++	private void find_parts (string directory) throws GLib.Error {	+		File start = File.new_for_path (directory);+		File found;+		FileInfo info;+		FileInfo? fi;+		FileEnumerator e;+		string name;+		+		+		info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+		if (info.get_file_type () != FileType.DIRECTORY) {+			warning (@"$directory is not a directory.");+			throw new FileError.NOTDIR ("Not a directory.");+		}++		e = start.enumerate_children (FILE_ATTRIBUTES, 0);+		while ((fi = e.next_file ()) != null) {+			info = (!) fi;+			name = info.get_name ();+			if (info.get_file_type () == FileType.DIRECTORY) {+				find_parts ((!) ((!) start.get_child (name)).get_path ());+			} else if (name.has_suffix (".bfp")) {+				found = start.get_child (name);+				parts.append ((!) found.get_path ());+			}+		}+	}+		+	private File find_root (string directory) throws GLib.Error {+		File start = File.new_for_path (directory);+		FileInfo info;+		FileInfo? fi;+		FileEnumerator e;+		+		info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+		if (info.get_file_type () != FileType.DIRECTORY) {+			warning ("Not a directory.");+			throw new FileError.NOTDIR ("Not a directory.");+		}++		e = start.enumerate_children (FILE_ATTRIBUTES, 0);+		while ((fi = e.next_file ()) != null) {+			info = (!) fi;+			if (info.get_name () == "description.bfp") {+				return start;+			}+		}+		+		if (start.get_parent () == null) {+			warning ("description.bfp not found");+			throw new FileError.FAILED ("description.bfp not found");+		}+		+		return find_root ((!)((!) start.get_parent ()).get_path ());+	}+	+	private File new_subdirectory (File d, string subdir) throws GLib.Error {+		FileInfo info;+		File dir;+		+		dir = d;+		dir = dir.get_child (subdir);+		+		if (!dir.query_exists ()) {+			DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+		} else {+			info = dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+			if (info.get_file_type () != FileType.DIRECTORY) {+				throw new FileError.FAILED (@"Can't save font, $subdir is not a directory.");+			}+		}+		return dir;+	}+	+	private File get_destination_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error {+		File file;+		File dir;++		dir = File.new_for_path (root_directory);+		+		if (subdir1 != "") {+			dir = new_subdirectory (dir, subdir1);+		}++		if (subdir2 != "") {+			dir = new_subdirectory (dir, subdir2);+		}+				+		file = dir.get_child (name);++		if (file.query_file_type (0) == FileType.DIRECTORY) {+			throw new FileError.FAILED (@"Can't save font, $name is a directory.");+		}+		+		return file;+	}+	+	private DataOutputStream create_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error {+		DataOutputStream os;+		File file;+		+		file = get_destination_file (name, subdir1, subdir2);+		+		if (file.query_exists ()) {+			file.delete ();+		}+		+		os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION));+		+		// FIXME: GIT ADD+		+		return os;+	}+}++}diff --git a/libbirdfont/DropMenu.vala b/libbirdfont/DropMenu.valaindex 1aeb235..95b1233 100644--- a/libbirdfont/DropMenu.vala+++ b/libbirdfont/DropMenu.vala@@ -49,6 +49,14 @@ public class DropMenu : GLib.Object { 		} 	} 	+	public MenuAction get_action_index (int index) {+		if (!(0 <= index < actions.length ())) {+			warning (@"No action for index $index.");+			return new MenuAction ("None");+		}+		return actions.nth (index).data;+	}+	 	public void recreate_index () { 		int i = -1; 		foreach (MenuAction a in actions) {diff --git a/libbirdfont/FileTab.vala b/libbirdfont/FileTab.valaindex 883bd22..482d1aa 100644--- a/libbirdfont/FileTab.vala+++ b/libbirdfont/FileTab.vala@@ -44,7 +44,11 @@ public class FileTab : FontDisplay { 		dialog.signal_discard.connect (() => { 			Font f; 			bool loaded;-			++			if (MenuTab.suppress_event) {+				return;+			}+					 			f = BirdFont.new_font (); 			f.delete_backup (); 			@@ -69,6 +73,10 @@ public class FileTab : FontDisplay { 		});  		dialog.signal_save.connect (() => {+			if (MenuTab.suppress_event) {+				return;+			}+			 			MenuTab.save (); 			dialog.signal_discard (); 		});@@ -115,6 +123,10 @@ public class FileTab : FontDisplay { 	public override void button_release (int button, double ex, double ey) { 		int r, i; 		+		if (MenuTab.suppress_event) {+			return;+		}+		 		return_if_fail (!is_null(this)); 		 		r = (int) rint ((ey - 17) / row_height) + scroll;diff --git a/libbirdfont/Font.vala b/libbirdfont/Font.valaindex 472e287..94e1595 100644--- a/libbirdfont/Font.vala+++ b/libbirdfont/Font.vala@@ -1,5 +1,5 @@ /*-    Copyright (C) 2012, 2013 Johan Mattsson+    Copyright (C) 2012, 2013, 2014 Johan Mattsson      This library is free software; you can redistribute it and/or modify      it under the terms of the GNU Lesser General Public License as @@ -18,6 +18,7 @@ namespace BirdFont {  public enum FontFormat { 	BIRDFONT,+	BIRDFONT_PART, 	FFI, 	SVG, 	FREETYPE@@ -87,6 +88,10 @@ public class Font : GLib.Object { 	 	bool read_only = false; 	+	/** Save font as many .bfp files instead of one big .bf */+	bool bfp = false;+	BirdFontPart bfp_file;+	 	public Font () { 		postscript_name = "Typeface"; 		name = "Typeface";@@ -105,14 +110,14 @@ public class Font : GLib.Object { 	 		kerning_classes = new KerningClasses (); 		-		// positions in pixels at first zoom level-		// default x-height should be 60 in 1:1 		top_limit = 84 ; 		top_position = 72; 		xheight_position = 56; 		base_line = 0; 		bottom_position = -20; 		bottom_limit = -27;+		+		bfp_file = new BirdFontPart (this); 	}  	public void set_weight (string w) {@@ -324,25 +329,29 @@ public class Font : GLib.Object {  		return g; 	}-		+	+	// FIXME: DELETE? 	public void add_glyph (Glyph glyph) { 		GlyphCollection? gc = get_glyph_collection (glyph.get_name ());-+		GlyphCollection c;+		 		if (gc == null) {-			add_glyph_collection (new GlyphCollection (glyph));+			c = new GlyphCollection (glyph.get_unichar (), glyph.get_name ());+			c.insert_glyph (glyph, false);+			add_glyph_collection (c); 		} 	}  	public void add_glyph_collection (GlyphCollection glyph_collection) { 		GlyphCollection? gc; 		-		if (glyph_collection.get_name () == "") {-			warning ("Refusing to insert glyph with name \"\", null character should be named null.");+		if (unlikely (glyph_collection.get_name () == "")) {+			warning ("Refusing to add glyph with name \"\", null character should be named null."); 			return; 		} 		 		gc = glyph_name.get (glyph_collection.get_name ());-		if (gc != null) {+		if (unlikely (gc != null)) { 			warning ("glyph has already been added"); 			return; 		}@@ -352,8 +361,6 @@ public class Font : GLib.Object { 		} 		 		glyph_cache.insert (glyph_collection.get_unicode (), glyph_collection);-		-		printd (@"Adding $(glyph_collection.get_current ().get_name ())\n"); 	} 	 	public string get_name_for_character (unichar c) {@@ -389,7 +396,7 @@ public class Font : GLib.Object { 		ligature.remove (glyph.get_current ().get_ligature_string ()); 	} -	// FIXME: order of ligature substitutions is important+	// FIXME: the order of ligature substitutions 	public GlyphCollection? get_ligature (uint indice) { 		return ligature.nth (indice); 	}@@ -404,7 +411,6 @@ public class Font : GLib.Object { 			g = otf.read_glyph (glyph); 			 			if (g != null) {-				add_glyph_callback ((!) g); 				return get_cached_glyph_collection (glyph); 			} 		}@@ -514,6 +520,31 @@ public class Font : GLib.Object { 		return backup_file; 	} 	+	public void init_bfp (string directory) {+		try {+			bfp_file = new BirdFontPart (this);+			bfp_file.create_directory (directory);+			bfp_file.save ();+			this.bfp = true;+			Preferences.add_recent_files (bfp_file.get_path ());+		} catch (GLib.Error e) {+			warning (e.message);+			// FIXME: notify user+		}+	}++	public void set_bfp (bool bfp) {+		this.bfp = bfp;+	}++	public bool is_bfp () {+		return bfp;+	}+	+	public bool save_bfp () {+		return bfp_file.save ();+	}+	 	public bool save (string path) { 		Font font; 		BirdFontFile birdfont_file = new BirdFontFile (this);@@ -594,6 +625,12 @@ public class Font : GLib.Object { 			loaded = parse_bf_file (path); 			format = FontFormat.BIRDFONT; 		}++		if (path.has_suffix (".bfp")) {+			font_file = path;+			loaded = parse_bfp_file (path);+			format = FontFormat.BIRDFONT_PART;+		}		 		 		if (path.has_suffix (".ttf")) { 			font_file = path;@@ -626,15 +663,7 @@ public class Font : GLib.Object { 			 			format = FontFormat.FREETYPE; 		}	-					-		/* // TODO: Remove the old way of loading ttfs when testing of the OTF writer is complete.-		if (BirdFont.experimental) {-			if (path.has_suffix (".ttf")) {-				font_file = path;-				loaded = parse_otf_file (path);-			}-		}*/-		+ 		if (recent) { 			Preferences.add_recent_files (get_path ()); 		}@@ -642,6 +671,10 @@ public class Font : GLib.Object { 		return loaded; 	} 	+	private bool parse_bfp_file (string path) {+		return bfp_file.load (path);+	}+	 	private bool parse_bf_file (string path) { 		BirdFontFile font = new BirdFontFile (this); 		return font.load (path);@@ -684,31 +717,6 @@ public class Font : GLib.Object { 		return true; 	} -	/** Callback function for loading glyph in a separate thread. */-	public void add_glyph_callback (Glyph g) {-		GlyphCollection? gcl;-		GlyphCollection gc;-		string liga;-							-		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);-		}--		gc = new GlyphCollection (g);-		add_glyph_collection (gc);--		if (g.is_ligature ()) {-			liga = g.get_ligature_string ();-			ligature.insert (liga, gc);-		}-	}- 	public bool parse_otf_file (string path) throws GLib.Error { 		otf = new OpenFontFormatReader (); 		otf_font = true;diff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex ea9dd99..99b0dd9 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -53,7 +53,9 @@ public class Glyph : FontDisplay { 	public double move_offset_y = 0;  	public WidgetAllocation allocation = new WidgetAllocation ();-	++	bool unassigned = false;+	public unichar unichar_code = 0; // FIXME: name and unichar should be moved to to glyph collection  	public string name;  	public double left_limit;@@ -67,9 +69,6 @@ public class Glyph : FontDisplay { 	bool xheight_lines_visible = false; 	bool margin_boundaries_visible = false; 	-	bool unassigned = false;-	public unichar unichar_code = 0;-	 	List<Glyph> undo_list = new List<Glyph> (); 	 	string glyph_sequence = "";@@ -296,12 +295,11 @@ public class Glyph : FontDisplay { 		 		if (b == null) { 			background_image = null;+		} else {		+			bg = (!) b;+			background_image = bg; 		} 		-		bg = (!) b;-		-		background_image = bg;-		 		BirdFont.get_current_font ().touch (); 	} 	diff --git a/libbirdfont/GlyphBackgroundImage.vala b/libbirdfont/GlyphBackgroundImage.valaindex 5614b62..8fc6568 100644--- a/libbirdfont/GlyphBackgroundImage.vala+++ b/libbirdfont/GlyphBackgroundImage.vala@@ -64,8 +64,8 @@ public class GlyphBackgroundImage { 		} 	} 		-	public GlyphBackgroundImage (string fn) {-		path = fn;+	public GlyphBackgroundImage (string file_name) {+		path = file_name; 	}  	public GlyphBackgroundImage copy () {@@ -105,7 +105,7 @@ public class GlyphBackgroundImage { 	} 			 	public ImageSurface get_img () {-		if (path.index_of (".png") == -1) {+		if (!path.has_suffix (".png")) { 			create_png (); 		} 		@@ -148,12 +148,12 @@ public class GlyphBackgroundImage { 			DataInputStream png_stream; 			 			if (!file.query_exists ()) {-				warning (@"Failed to save image $path, file does not exist.");+				warning (@"Can't 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.");+				warning (@"Can not allocate a buffer of $(file_info.get_size ()) bytes to store $path."); 				return ""; 			} 			@@ -168,6 +168,65 @@ public class GlyphBackgroundImage { 		} 		 		return "";++	}++	public void create_background_folders (Font font) {+		File dir;+		try {+			dir = BirdFont.get_settings_directory ();+			if (!dir.query_exists ()) {+				DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+			}+			+			dir = font.get_backgrounds_folder ();+			if (!dir.query_exists ()) {+				DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+			}++			dir = font.get_backgrounds_folder ().get_child ("parts");+			if (!dir.query_exists ()) {+				DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+			}+		} catch (GLib.Error e) {+			warning (e.message);+		}+	}++	public void copy_if_new (File destination) {+		if (!destination.query_exists ()) {+			copy_file (destination);+		}+	}++	public void copy_file (File destination) {+		File source;+		FileInfo info;+		+		try {+			if (destination.query_exists ()) {+				info = destination.query_info ("standard::*", FileQueryInfoFlags.NONE);+				if (info.get_file_type () == FileType.DIRECTORY) {+					warning (@"$((!) destination.get_path ()) is a directory.");+					return;+				}+			}+			+			if (!((!)destination.get_parent ()).query_exists ()) {+				warning (@"Directory for file $((!) destination.get_path ()) is not created.");+				return;+			}+			+			if (destination.query_exists ()) {+				warning (@"Image $((!) destination.get_path ()) is already created.");+				return;+			}+			+			source = File.new_for_path (path);+			source.copy (destination, FileCopyFlags.NONE);+		} catch (GLib.Error e) {+			warning (e.message);+		} 	}  	public string get_sha1 () {@@ -291,7 +350,7 @@ public class GlyphBackgroundImage { 		double wc, hc;  		if (unlikely (get_img ().status () != Cairo.Status.SUCCESS)) {-			stderr.printf (@"Background image is invalid. (\"$path\")\n");+			warning (@"Background image is invalid. (\"$path\")\n"); 			MainWindow.get_current_glyph ().set_background_visible (false); 			return; 		}@@ -734,7 +793,7 @@ public class GlyphBackgroundImage { 		h = img.get_height();  		if (unlikely (img.status () != Cairo.Status.SUCCESS)) {-			warning ("Err");+			warning ("Error"); 			return p; 		} 		diff --git a/libbirdfont/GlyphCollection.vala b/libbirdfont/GlyphCollection.valaindex d239118..11b1029 100644--- a/libbirdfont/GlyphCollection.vala+++ b/libbirdfont/GlyphCollection.vala@@ -19,9 +19,13 @@ namespace BirdFont {  public class GlyphCollection : GLib.Object { 	VersionList versions;+	unichar unicode_character;+	string name; -	public GlyphCollection (Glyph? current = null) {-		versions = new VersionList (current);+	public GlyphCollection (unichar unicode_character, string name) {+		this.unicode_character = unicode_character;+		this.name = name;+		versions = new VersionList (null); 	}  	public VersionList get_version_list () {@@ -43,12 +47,12 @@ public class GlyphCollection : GLib.Object { 	 	public string get_unicode () { 		StringBuilder unicode = new StringBuilder ();-		unicode.append_unichar (get_current ().unichar_code);+		unicode.append_unichar (unicode_character); 		return unicode.str; 	} 	 	public string get_name () {-		return get_current ().get_name ();+		return name; 	} 	 	public int get_selected_id () {diff --git a/libbirdfont/MenuTab.vala b/libbirdfont/MenuTab.valaindex b77ddd4..17e133f 100644--- a/libbirdfont/MenuTab.vala+++ b/libbirdfont/MenuTab.vala@@ -102,6 +102,7 @@ public class MenuTab : FontDisplay { 			save (); 			saved = true; 		}+		 		return saved; 	} @@ -112,39 +113,68 @@ public class MenuTab : FontDisplay {  		if (suppress_event) { 			return false;-		}	+		} 		 		if (!set_suppress_event (true)) { 			return false; 		}  		f = BirdFont.get_current_font ();-		f.delete_backup ();-		-		fn = f.get_path ();-		-		if (f.font_file != null && fn.has_suffix (".bf")) {-			f.background_scale = MainWindow.get_drawing_tools ().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;++		if (f.is_bfp ()) {+			saved = f.save_bfp (); 			set_suppress_event (false); 		} else {-			set_suppress_event (false);-			saved = save_as ();+			f.delete_backup ();+			fn = f.get_path ();+			+			if (f.font_file != null && fn.has_suffix (".bf")) {+				set_font_setting_from_tools (f); +				saved = f.save (fn);+				set_suppress_event (false);+			} else {+				set_suppress_event (false);+				saved = save_as ();+			} 		} 		 		return saved; 	} 	+	public static void set_font_setting_from_tools (Font f) {	+		f.background_scale = MainWindow.get_drawing_tools ().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 ());+		}+	}+	+	public static void save_as_bfp () {+		Font f;+		string? fn;+		+		if (suppress_event) {+			return;+		}	+		+		if (!set_suppress_event (true)) {+			return;+		}+		+		f = BirdFont.get_current_font ();	+		fn = MainWindow.file_chooser_save (t_("Save"));+		+		if (fn != null) {+			f.init_bfp ((!) fn);+		}+		+		set_suppress_event (false);+	}+	 	public static void new_file () { 		Font font; 		SaveDialogListener dialog = new SaveDialogListener ();diff --git a/libbirdfont/OverView.vala b/libbirdfont/OverView.valaindex e624761..5c7d1d6 100644--- a/libbirdfont/OverView.vala+++ b/libbirdfont/OverView.vala@@ -244,6 +244,11 @@ public class OverView : FontDisplay { 	} 	 	OverViewItem get_selected_item () {+		if (visible_items.length () == 0) {+			warning ("No items.");+			return new OverViewItem (null, '\0', 0, 0);+		}+		 		if (unlikely (!(0 <= selected < visible_items.length ()))) {  			warning (@"0 <= $selected < $(visible_items.length ())"); 			new OverViewItem (null, '\0', 0, 0);diff --git a/libbirdfont/SvgFont.vala b/libbirdfont/SvgFont.valaindex c7b1968..c40c1b8 100644--- a/libbirdfont/SvgFont.vala+++ b/libbirdfont/SvgFont.vala@@ -273,7 +273,7 @@ class SvgFont : GLib.Object { 			glyph.set_ligature_substitution (ligature); 		} 		-		font.add_glyph_callback (glyph);+		font.add_glyph (glyph); 	} } diff --git a/libbirdfont/TabBar.vala b/libbirdfont/TabBar.valaindex 1da73c1..bbebeb3 100644--- a/libbirdfont/TabBar.vala+++ b/libbirdfont/TabBar.vala@@ -125,6 +125,11 @@ public class TabBar : GLib.Object { 	 */ 	public bool select_char (string s) { 		int i = 0;+		+		if (MenuTab.suppress_event) {+			return false;+		}+		 		foreach (Tab t in tabs) { 			if (t.get_display ().get_name () == s) { 				select_tab (i);@@ -137,10 +142,18 @@ public class TabBar : GLib.Object { 	}  	public bool select_tab_name (string s) {+		if (MenuTab.suppress_event) {+			return false;+		}+		 		return select_char (s); 	}  	public void select_overview () {+		if (MenuTab.suppress_event) {+			return;+		}+		 		select_tab_name ("Overview"); 	} @@ -148,6 +161,10 @@ public class TabBar : GLib.Object { 		Tab t; 		bool open; 		+		if (MenuTab.suppress_event) {+			return;+		}+		 		if (previous_tab == null) { 			return; 		}@@ -163,6 +180,10 @@ public class TabBar : GLib.Object { 	public void close_display (FontDisplay f) { 		int i = -1; 		+		if (MenuTab.suppress_event) {+			return;+		}+		 		if (tabs.length () > 1) { 			foreach (var t in tabs) { 				++i;@@ -178,6 +199,10 @@ public class TabBar : GLib.Object { 	}   	public void close_all_tabs () {+		if (MenuTab.suppress_event) {+			return;+		}+		 		for (int i = 0; i < get_length (); i++) { 			if (close_tab (i, false, true)) { 				close_all_tabs ();@@ -190,7 +215,11 @@ public class TabBar : GLib.Object { 		Tab t; 		EmptyTab empty_tab_canvas; 		Tab empty_tab;-		+	+		if (MenuTab.suppress_event) {+			return false;+		}+				 		if (!(0 <= index < tabs.length ())) { 			return false; 		}@@ -233,7 +262,11 @@ public class TabBar : GLib.Object { 	 	public bool close_by_name (string name, bool background_tab = false) { 		int i = 0;-		++		if (MenuTab.suppress_event) {+			return false;+		}+				 		foreach (var t in tabs) { 			if (t.get_display ().get_name () == name) { 				return close_tab (i, background_tab);@@ -246,12 +279,21 @@ public class TabBar : GLib.Object { 	} 	 	public void close_background_tab_by_name (string name) {+		if (MenuTab.suppress_event) {+			return;+		}+		 		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;++		if (MenuTab.suppress_event) {+			return false;+		}+		 		foreach (var n in tabs) { 			if (n == t) { 				select_tab (i);@@ -284,6 +326,11 @@ public class TabBar : GLib.Object {  	public bool selected_open_tab_by_name (string t) { 		int i = 0;+	+		if (MenuTab.suppress_event) {+			return false;+		}+		 		foreach (var n in tabs) { 			if (n.get_display ().get_name () == t) { 				select_tab (i);@@ -310,7 +357,11 @@ public class TabBar : GLib.Object { 	 	public void select_tab (int index, bool signal_selected = true) { 		Tab t;-	++		if (MenuTab.suppress_event) {+			return;+		}+			 		// always close any pending text input if the user switches tab 		MainWindow.native_window.hide_text_input (); @@ -455,7 +506,11 @@ public class TabBar : GLib.Object { 		double tab_width = -1; 		bool always_open = false; 		int s = (tabs.length () == 0) ? 0 : selected + 1;-		++		if (MenuTab.suppress_event) {+			return;+		}+				 		if (tab_width < 0) { 			tab_width = 9 * display_item.get_label ().char_count (); 			tab_width += 36;@@ -467,7 +522,13 @@ public class TabBar : GLib.Object { 	 	/** Returns true if the new item was added to the bar. */ 	public bool add_unique_tab (FontDisplay display_item, bool signal_selected = true) {-		bool i = select_tab_name (display_item.get_name ());+		bool i;+		+		if (MenuTab.suppress_event) {+			return false;+		}+				+		i = select_tab_name (display_item.get_name ());  		if (!i) { 			add_tab (display_item, signal_selected);diff --git a/libbirdfont/VersionList.vala b/libbirdfont/VersionList.valaindex 2ee8e40..68b0bf4 100644--- a/libbirdfont/VersionList.vala+++ b/libbirdfont/VersionList.vala@@ -77,6 +77,7 @@ public class VersionList : DropMenu {  	public void set_selected_version (int version_id) { 		current_version_id = version_id;+		update_selection (); 	} 	 	public Glyph get_current () {@@ -195,9 +196,7 @@ public class VersionList : DropMenu { 		ma.index = (int) glyphs.length () - 1; 		 		ma.action = (self) => {-			Font font;-			-			font = BirdFont.get_current_font ();+			Font font = BirdFont.get_current_font (); 			set_selected_item (self); 			font.touch (); 		};@@ -205,6 +204,23 @@ public class VersionList : DropMenu { 		if (selected) { 			set_selected_item (ma); 		}+		+		update_selection ();+	}+	+	bool has_version (int id) {+		foreach (Glyph g in glyphs) {+			if (g.version_id == id) {+				return true;+			}+		}+		return false;+	}+	+	void update_selection () {+		if (has_version (current_version_id)) {+			set_selected_item (get_action_index (get_current_version_index () + 1)); // the first item is the "new version"+		} 	}  }

Contact

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