Clone in a terminal:
Commit 963e81c
Johan Mattsson - 2014-09-02T11:37:18+00:00
963e81cd72b12b644a1394621622053918ad3d4cjohan.mattsson.m@gmail.com

Spacing classes Update side bearings and all kerning pairs for one or more glyphs at the same time.

Changed files (18)

Diff

diff --git a/birdfont/GtkWindow.vala b/birdfont/GtkWindow.valaindex 4f733e5..fb3ff3c 100644--- a/birdfont/GtkWindow.vala+++ b/birdfont/GtkWindow.vala@@ -717,6 +717,14 @@ public class GtkWindow : Gtk.Window, NativeWindow { 			 MenuTab.list_all_kerning_pairs (); 		}); +		Gtk.MenuItem spacing_kerning_tab = new Gtk.MenuItem.with_mnemonic (t_("Show _Spacing Tab"));+		kerning_menu.append (spacing_kerning_tab);+		spacing_kerning_tab.activate.connect (() => { +			 MenuTab.show_spacing_tab ();+		});+		spacing_kerning_tab.add_accelerator ("activate", accel_group, 'k', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);++ 		kerning_menu.append (new SeparatorMenuItem ());  		Gtk.MenuItem select_next_kerning_pair = new Gtk.MenuItem.with_mnemonic (" Ctrl+→  " + t_("Select _Next Kerning Pair"));diff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex b2d8040..8c07ef6 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -162,7 +162,7 @@ class BirdFontFile : GLib.Object { 					 				TooltipArea.show_text (t_("Saving")); 			});-		+			 			font.glyph_cache.for_each ((gc) => { 				BackgroundImage bg; 				@@ -193,6 +193,7 @@ class BirdFontFile : GLib.Object { 				TooltipArea.show_text (t_("Saving")); 			}); 			+			write_spacing_classes (os); 			write_kerning (os); 			write_closing_root_tag (os); 			@@ -216,6 +217,23 @@ class BirdFontFile : GLib.Object { 		os.put_string ("</font>\n"); 	} 	+	public void write_spacing_classes (DataOutputStream os)  throws GLib.Error {+		SpacingClassTab s = MainWindow.get_spacing_class_tab ();+		+		foreach (SpacingClass sc in s.classes) {+				os.put_string ("<spacing ");+				os.put_string ("first=\"");+				os.put_string (Font.to_hex (sc.first.get_char ()));+				os.put_string ("\" ");+				+				os.put_string ("next=\"");+				os.put_string (Font.to_hex (sc.next.get_char ()));+				os.put_string ("\" ");+				+				os.put_string (" />\n");+		}+	}+		 	public void write_kerning (DataOutputStream os)  throws GLib.Error { 			uint num_kerning_pairs; 			string range;@@ -657,7 +675,11 @@ class BirdFontFile : GLib.Object { 			if (iter->name == "kerning") { 				parse_kerning (iter); 			}-						++			if (iter->name == "spacing") {+				parse_spacing_class (iter);+			}+									 			TooltipArea.show_text (t_("Loading XML data.")); 		} @@ -696,6 +718,31 @@ class BirdFontFile : GLib.Object { 		return GlyphRange.get_serialized_char (c); 	} 	+	private void parse_spacing_class (Xml.Node* node) {+		string attr_name;+		string attr_content;+		string first, next;+		SpacingClassTab spacing_class_tab = MainWindow.get_spacing_class_tab ();+		+		first = "";+		next = "";+		+		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+			attr_name = prop->name;+			attr_content = prop->children->content;+			+			if (attr_name == "first") {+				first = (!) Font.to_unichar (attr_content).to_string ();+			}++			if (attr_name == "next") {+				next = (!) Font.to_unichar (attr_content).to_string ();+			}		+		}+		+		spacing_class_tab.add_class (first, next);+	}+	 	private void parse_kerning (Xml.Node* node) { 		string attr_name; 		string attr_content;@@ -742,7 +789,7 @@ class BirdFontFile : GLib.Object { 			warning (e.message); 		} 	}-		+	 	private void parse_old_kerning (Xml.Node* node) { 		string attr_name; 		string attr_content;diff --git a/libbirdfont/BirdFontPart.vala b/libbirdfont/BirdFontPart.valaindex 2502c51..31e6abf 100644--- a/libbirdfont/BirdFontPart.vala+++ b/libbirdfont/BirdFontPart.vala@@ -172,6 +172,12 @@ class BirdFontPart : GLib.Object{ 			bf.write_closing_root_tag (os); 			os.close (); +			os = create_file ("spacing.bfp");+			bf.write_root_tag (os);+			bf.write_spacing_classes (os);+			bf.write_closing_root_tag (os);+			os.close ();+ 			font.glyph_cache.for_each ((gc) => { 				try { 					string selected_file_name;diff --git a/libbirdfont/DrawingTools.vala b/libbirdfont/DrawingTools.valaindex ebe291e..56f426c 100644--- a/libbirdfont/DrawingTools.vala+++ b/libbirdfont/DrawingTools.vala@@ -20,7 +20,7 @@ namespace BirdFont { public class DrawingTools : ToolCollection  { 	GlyphCanvas glyph_canvas; 	-	public List<Expander> expanders;+	public Gee.ArrayList<Expander> expanders = new Gee.ArrayList<Expander> (); 	 	Expander draw_tools; 	Expander grid_expander;@@ -92,7 +92,6 @@ public class DrawingTools : ToolCollection  { 		Expander view_tools = new Expander (); 		Expander grid = new Expander (); 		-		Expander background_tools = new Expander (); 		Expander style_tools = new Expander (); 		 		grid_expander = grid;@@ -1354,7 +1353,7 @@ public class DrawingTools : ToolCollection  { 		idle.attach (null); 	} 	-	public override unowned List<Expander> get_expanders () {+	public override Gee.ArrayList<Expander> get_expanders () { 		return expanders; 	} 	@@ -1433,7 +1432,7 @@ public class DrawingTools : ToolCollection  { 	} 	 	private void add_expander (Expander e) {-		expanders.append (e);+		expanders.add (e); 	} } diff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex 9e58147..0e7862a 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -450,13 +450,15 @@ public class Glyph : FontDisplay { 							 		Line left_line = new Line ("left", left_limit, true); 		left_line.position_updated.connect ((pos) => {-				left_limit = pos;-			});+			left_limit = pos;+			update_other_spacing_classes ();+		}); 		 		Line right_line = new Line ("right", right_limit, true); 		right_line.position_updated.connect ((pos) => {-				right_limit = pos;-			});+			right_limit = pos;+			update_other_spacing_classes ();+		}); 		 		// lists of lines are sorted and lines are added only if  		// they are relevant for a particular glyph.@@ -2015,6 +2017,50 @@ public class Glyph : FontDisplay { 			last_tap1_y = y; 		} 	}+	+	public void update_spacing_class () {+		Font font = BirdFont.get_current_font ();+		GlyphCollection? g;+		GlyphCollection gc;+		Glyph glyph;+		+		foreach (string l in MainWindow.get_spacing_class_tab ()+				.get_all_connections ((!) unichar_code.to_string ())) {+			if (l != (!) unichar_code.to_string ()) {+				g = font.get_glyph_collection (l);+				if (g != null) {+					gc = (!) g;+					glyph = gc.get_current ();+					left_limit = glyph.left_limit;+					right_limit = glyph.right_limit;+					break;+				}+			}+		}+		+		add_help_lines ();+	}++	public void update_other_spacing_classes () {+		Font font = BirdFont.get_current_font ();+		GlyphCollection? g;+		GlyphCollection gc;+		Glyph glyph;+		+		foreach (string l in MainWindow.get_spacing_class_tab ()+				.get_all_connections ((!) unichar_code.to_string ())) {+			if (l != (!) unichar_code.to_string ()) {+				g = font.get_glyph_collection (l);+				if (g != null) {+					gc = (!) g;+					glyph = gc.get_current ();+					glyph.left_limit = left_limit;+					glyph.right_limit = right_limit;+					glyph.add_help_lines ();+				}+			}+		}	+	} }  }diff --git a/libbirdfont/KerningClasses.vala b/libbirdfont/KerningClasses.valaindex 1d35288..4f113d5 100644--- a/libbirdfont/KerningClasses.vala+++ b/libbirdfont/KerningClasses.vala@@ -52,15 +52,43 @@ public class KerningClasses : GLib.Object { 		return BirdFont.get_current_font ().get_kerning_classes (); 	} -	public double? get_kerning_for_single_glyphs (string l, string r) {-		string left = GlyphRange.serialize (l);-		string right = GlyphRange.serialize (r);-		return single_kerning.get (@"$left - $right");+	public void update_space_class (string c) {+		double? k;+		+		foreach (string l in single_kerning_letters_left) {	+			k = get_kerning_for_single_glyphs (l, c);+			+			if (k != null) {+				set_kerning_for_single_glyphs (l, c, (!) k);+			}+		}++		foreach (string r in single_kerning_letters_right) {	+			k = get_kerning_for_single_glyphs (c, r);+			+			if (k != null) {+				set_kerning_for_single_glyphs (c, r, (!) k);+			}+		}		+	}++	public double? get_kerning_for_single_glyphs (string first, string next) {+		double? k = null;+		string left = GlyphRange.unserialize (first);+		string right = GlyphRange.unserialize (next);++		foreach (string l in get_spacing_class (left)) {+			foreach (string r in get_spacing_class (right)) {+				k = single_kerning.get (@"$l - $r");+			}+		}+		+		return k; 	}  -	public void set_kerning_for_single_glyphs (string l, string r, double k) {-		string left = GlyphRange.serialize (l);-		string right = GlyphRange.serialize (r);+	public void set_kerning_for_single_glyphs (string le, string ri, double k) {+		string left = GlyphRange.serialize (le);+		string right = GlyphRange.serialize (ri); 		string cleft = (!)GlyphRange.unserialize (left).get_char ().to_string (); 		string cright = (!)GlyphRange.unserialize (right).get_char ().to_string (); 		@@ -68,16 +96,22 @@ public class KerningClasses : GLib.Object { 			warning ("Map is protected."); 			return; 		}-		-		if (!single_kerning_letters_left.contains (cleft)) {-			single_kerning_letters_left.add (cleft);-		} -		if (!single_kerning_letters_right.contains (cright)) {-			single_kerning_letters_right.add (cright);+		foreach (string l in get_spacing_class (cleft)) {+			foreach (string r in get_spacing_class (cright)) {+				if (!single_kerning_letters_left.contains (cleft)) {+					single_kerning_letters_left.add (cleft);+				}++				if (!single_kerning_letters_right.contains (cright)) {+					single_kerning_letters_right.add (cright);+				}++				left = GlyphRange.serialize (l);+				right = GlyphRange.serialize (r);+				single_kerning.set (@"$left - $right", k);+			} 		}-			-		single_kerning.set (@"$left - $right", k); 	}   	public void set_kerning (GlyphRange left_range, GlyphRange right_range, double k, int class_index = -1) {@@ -118,14 +152,20 @@ public class KerningClasses : GLib.Object { 	}  	public bool has_kerning (string first, string next) {-		string f = GlyphRange.serialize (first);-		string n = GlyphRange.serialize (next);		+		string f = "";+		string n = "";		 		GlyphRange gf, gn; -		if (single_kerning.has_key (@"$f - $n")) {-			return true;+		foreach (string l in get_spacing_class (first)) {+			foreach (string r in get_spacing_class (next)) {+				f = GlyphRange.serialize (l);+				n = GlyphRange.serialize (r);	+				if (single_kerning.has_key (@"$f - $n")) {+					return true;+				}+			} 		}-+			 		gf = new GlyphRange (); 		gn = new GlyphRange (); 		@@ -171,7 +211,7 @@ public class KerningClasses : GLib.Object { 		GlyphRange l; 		int len = (int) classes_first.size; 		-		len = (int)classes_first.size;+		len = (int) classes_first.size; 		return_val_if_fail (len == classes_last.size, 0); 		return_val_if_fail (len == classes_kerning.size, 0); @@ -236,19 +276,23 @@ public class KerningClasses : GLib.Object { 			return -1; 		} 		-		for (int i = len - 1; i >= 0; i--) {-			l = classes_first.get (i);-			r = classes_last.get (i);-			-			if (l.get_all_ranges () == left_range.get_all_ranges ()-				&& r.has_character (right_char)) {-				return classes_kerning.get (i).val;+		foreach (string right in get_spacing_class (right_char)) {+			for (int i = len - 1; i >= 0; i--) {+				l = classes_first.get (i);+				r = classes_last.get (i);+				+				if (l.get_all_ranges () == left_range.get_all_ranges ()+					&& r.has_character (right)) {+					return classes_kerning.get (i).val;+				} 			} 		} 		 		return 0; 	} ++ 	public double get_kern_for_char_to_range (string left_char, GlyphRange right_range) { 		GlyphRange r; 		GlyphRange l;@@ -263,13 +307,15 @@ public class KerningClasses : GLib.Object { 			return 0; 		} 		-		for (int i = len - 1; i >= 0; i--) {-			l = classes_first.get (i);-			r = classes_last.get (i);-			-			if (l.has_character (left_char)-				&& r.get_all_ranges () == right_range.get_all_ranges ()) {-				return classes_kerning.get (i).val;+		foreach (string left in get_spacing_class (left_char)) {+			for (int i = len - 1; i >= 0; i--) {+				l = classes_first.get (i);+				r = classes_last.get (i);+				+				if (l.has_character (left)+					&& r.get_all_ranges () == right_range.get_all_ranges ()) {+					return classes_kerning.get (i).val;+				} 			} 		} 		@@ -333,6 +379,7 @@ public class KerningClasses : GLib.Object { 				kerningIterator (chars[0], chars[1], k); 			} 		}+		 		set_protect_map (false); 	} 	@@ -457,8 +504,20 @@ public class KerningClasses : GLib.Object { 		classes_last.remove_at (index); 		classes_kerning.remove_at (index); 	}-+	+	private Gee.ArrayList<string> get_spacing_class (string c) {+		return MainWindow.get_spacing_class_tab ().get_all_connections (c);+	}+	 	public void delete_kerning_for_pair (string left, string right) {+		foreach (string l in get_spacing_class (left)) {+			foreach (string r in get_spacing_class (right)) {+				delete_kerning_for_one_pair (l, r);+			}+		}+	}++	private void delete_kerning_for_one_pair (string left, string right) { 		bool has_left, has_right; 		string[] p; 		diff --git a/libbirdfont/KerningDisplay.vala b/libbirdfont/KerningDisplay.valaindex 747a68a..c5eb572 100644--- a/libbirdfont/KerningDisplay.vala+++ b/libbirdfont/KerningDisplay.vala@@ -379,6 +379,7 @@ public class KerningDisplay : FontDisplay { 			left_active_glyph.right_limit += val; 			left_active_glyph.remove_lines (); 			left_active_glyph.add_help_lines ();+			left_active_glyph.update_other_spacing_classes (); 		} 	} diff --git a/libbirdfont/KerningRange.vala b/libbirdfont/KerningRange.valaindex 88be7b3..0c6752d 100644--- a/libbirdfont/KerningRange.vala+++ b/libbirdfont/KerningRange.vala@@ -58,20 +58,37 @@ public class KerningRange : Tool { 	} 	 	public void set_ranges (string r) {-			ranges = r;-			name = r;--			try {-				glyph_range.empty ();-				glyph_range.parse_ranges (r);-				glyph_range.set_class (true);+		GlyphRange glyph_range = new GlyphRange ();+		string new_range;+		string ch;+		try {	+			glyph_range.parse_ranges (r);+			new_range = glyph_range.get_all_ranges ();+			+			for (int i = 0; i < glyph_range.get_length (); i++) {+				ch = glyph_range.get_char (i); 				-				malformed = false;-			} catch (MarkupError e) {-				KerningClasses.get_instance ().print_all ();-				warning (e.message);-				malformed = true;+				foreach (string c in MainWindow.get_spacing_class_tab ().get_all_connections (ch)) {+					new_range += " " + GlyphRange.serialize (c);+				} 			}+			+			set_one_range (new_range);+			malformed = false;+		} catch (MarkupError e) {+			KerningClasses.get_instance ().print_all ();+			warning (e.message);+			malformed = true;+		}+	}+		+	private void set_one_range (string r) throws MarkupError {+		ranges = r;+		name = r;++		glyph_range.empty ();+		glyph_range.parse_ranges (r);+		glyph_range.set_class (true); 	} 	 	public void update_kerning_classes () {diff --git a/libbirdfont/KerningTools.vala b/libbirdfont/KerningTools.valaindex c853258..53e3ee7 100644--- a/libbirdfont/KerningTools.vala+++ b/libbirdfont/KerningTools.vala@@ -18,7 +18,7 @@ using Math; namespace BirdFont {  public class KerningTools : ToolCollection  {-	public static List<Expander> expanders;+	public static Gee.ArrayList<Expander> expanders = new Gee.ArrayList<Expander> (); 	public static int next_class = 0; 	public static Expander classes; 	public static bool adjust_side_bearings = false;@@ -118,8 +118,8 @@ public class KerningTools : ToolCollection  { 		classes.set_persistent (true); 		classes.set_unique (true); 		-		expanders.append (kerning_tools);-		expanders.append (classes);+		expanders.add (kerning_tools);+		expanders.add (classes); 	} 	 	public static void add_unique_class (KerningRange kerning_class) {@@ -210,7 +210,7 @@ public class KerningTools : ToolCollection  { 		} 	} 	-	public override unowned List<Expander> get_expanders () {+	public override Gee.ArrayList<Expander> get_expanders () { 		return expanders; 	} }diff --git a/libbirdfont/MainWindow.vala b/libbirdfont/MainWindow.valaindex c680ba0..c923716 100644--- a/libbirdfont/MainWindow.vala+++ b/libbirdfont/MainWindow.vala@@ -28,6 +28,7 @@ public class MainWindow : GLib.Object { 	public static KerningDisplay kerning_display;
 	public static CharDatabase character_database;
 	public static LigatureList ligature_display;
+	public static SpacingClassTab spacing_class_tab;
 	
 	/** Number of pixels per mm */
 	public static double units = 1;
@@ -45,6 +46,7 @@ public class MainWindow : GLib.Object { 		kerning_display = new KerningDisplay ();
 		character_database = new CharDatabase ();
 		ligature_display = new LigatureList ();
+		spacing_class_tab = new SpacingClassTab ();
 	}
 
 	/** Set the number of picels per millimeter for the current screen. */
@@ -145,6 +147,10 @@ public class MainWindow : GLib.Object { 		return over_view;
 	}
 	
+	public static SpacingClassTab get_spacing_class_tab () {
+		return spacing_class_tab;
+	}
+	
 	public static void update_glyph_sequence () {
 		TextListener listener = new TextListener (t_("Glyph sequence"), Preferences.get ("glyph_sequence"), t_("Close"));
 		listener.signal_text_input.connect ((text) => {
diff --git a/libbirdfont/MenuTab.vala b/libbirdfont/MenuTab.valaindex dffb71c..99a380b 100644--- a/libbirdfont/MenuTab.vala+++ b/libbirdfont/MenuTab.vala@@ -221,6 +221,8 @@ public class MenuTab : FontDisplay { 			return; 		} +		MainWindow.native_window.hide_text_input ();+ 		SaveDialogListener dialog = new SaveDialogListener (); 		Font font = BirdFont.get_current_font (); 		@@ -448,6 +450,11 @@ public class MenuTab : FontDisplay { 		g.active_paths.clear (); 		g.update_view (); 	}+	+	public static void show_spacing_tab () {+		SpacingClassTab t = MainWindow.get_spacing_class_tab ();+		MainWindow.get_tab_bar ().add_unique_tab (t);+	} }  }diff --git a/libbirdfont/OverView.vala b/libbirdfont/OverView.valaindex 2d2d799..83647cb 100644--- a/libbirdfont/OverView.vala+++ b/libbirdfont/OverView.vala@@ -80,9 +80,7 @@ public class OverView : FontDisplay { 		this.open_new_glyph_signal.connect ((character) => { 			StringBuilder name = new StringBuilder (); 			TabBar tabs = MainWindow.get_tab_bar ();-			Font font = BirdFont.get_current_font (); 			bool selected;-			GlyphCollection? fg; 			Glyph glyph; 			GlyphCollection glyph_collection; 			GlyphCanvas canvas;@@ -114,13 +112,11 @@ public class OverView : FontDisplay { 	 	public GlyphCollection add_character_to_font (unichar character, bool empty = false) { 		StringBuilder name = new StringBuilder ();-		TabBar tabs = MainWindow.get_tab_bar (); 		Font font = BirdFont.get_current_font (); 		GlyphCollection? fg; 		Glyph glyph; 		GlyphCollection glyph_collection;-		GlyphCanvas canvas;-			+ 		name.append_unichar (character);  		if (all_available) {diff --git a/libbirdfont/PenTool.vala b/libbirdfont/PenTool.valaindex 89f5294..a4e76f9 100644--- a/libbirdfont/PenTool.vala+++ b/libbirdfont/PenTool.vala@@ -2003,7 +2003,7 @@ public class PenTool : Tool { 	public static Path simplify (Path path, bool selected_segments = false, double threshold = 0.3) { 		PointSelection ps; 		EditPoint ep;-		Path p1, p2, new_path;+		Path p1, new_path; 		double d, sumd; 		int i; diff --git a/libbirdfont/PreviewTools.vala b/libbirdfont/PreviewTools.valaindex 43e23aa..48c1b0d 100644--- a/libbirdfont/PreviewTools.vala+++ b/libbirdfont/PreviewTools.vala@@ -15,7 +15,7 @@ namespace BirdFont {  public class PreviewTools : ToolCollection  {-	public List<Expander> expanders;+	public Gee.ArrayList<Expander> expanders = new Gee.ArrayList<Expander> (); 	public Expander classes; 	 	public PreviewTools () {@@ -39,7 +39,7 @@ public class PreviewTools : ToolCollection  { 		}); 		webview_tools.add_tool (generate_html_button); 		-		expanders.append (webview_tools);+		expanders.add (webview_tools); 	}  	/** Export fonts and update html canvas. */@@ -70,7 +70,7 @@ public class PreviewTools : ToolCollection  { 		Preview.generate_html_document (); 	} 		-	public override unowned List<Expander> get_expanders () {+	public override Gee.ArrayList<Expander> get_expanders () { 		return expanders; 	} }diff --git a/libbirdfont/Row.vala b/libbirdfont/Row.valanew file mode 100644index 0000000..a4a939d--- /dev/null+++ b/libbirdfont/Row.vala@@ -0,0 +1,63 @@+/*+    Copyright (C) 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library is distributed in the hope that it will be useful, but +    WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +    Lesser General Public License for more details.+*/+using Cairo;+using Math;++namespace BirdFont {++public class Row : GLib.Object {++	int index = 0;+	bool delete_button = true;+	+	Gee.ArrayList<string> columns_labels = new Gee.ArrayList<string> ();+	+	public int columns {+		get {+			return columns_labels.size;+		}+	}+	+	public Row (string label, int index, bool delete_button = true) {+		this.index = index;+		columns_labels.add (label);+		this.delete_button = delete_button;+	}+	+	public Row.columns_3 (string label0, string label1, string label2, int index) {+		columns_labels.add (label0);+		columns_labels.add (label1);+		columns_labels.add (label2);+		this.index = index;+	}+	+	public bool has_delete_button () {+		return delete_button;+	}+	+	public string get_column (int i) {+		return_val_if_fail (0 <= i < columns, "".dup ());+		return columns_labels.get (i);+	}++	public int get_index () {+		return index;+	}++	public void set_index (int index) {	+		this.index = index;+	}+}++}diff --git a/libbirdfont/SpacingClass.vala b/libbirdfont/SpacingClass.valanew file mode 100644index 0000000..9b92826--- /dev/null+++ b/libbirdfont/SpacingClass.vala@@ -0,0 +1,71 @@+/*+    Copyright (C) 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library is distributed in the hope that it will be useful, but +    WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +    Lesser General Public License for more details.+*/+using Cairo;+using Math;++namespace BirdFont {++public class SpacingClass : GLib.Object  {++	public string first;+	public string next;++	bool update_first = true;+	TextListener listener;+	+	public signal void updated (SpacingClass s);+	+	public SpacingClass (string first, string next) {+		this.first = first;+		this.next = next;+	}+	+	public void set_first () {	+		update_first = true;+		update (first);+	}+	+	public void set_next () {	+		update_first = false;+		update (next);+	}+	+	void update (string val) {+		listener = new TextListener (t_("Character"), val, t_("Set"));+		+		listener.signal_text_input.connect ((text) => {+			string v = text;+			+			if (v.has_prefix ("U+") || v.has_prefix ("u+")) {+				v = (!) Font.to_unichar (val).to_string ();+			}+			+			if (update_first) {+				first = v.dup ();+			} else {+				next = v.dup ();+			}+			+			updated (this);+		});+		+		listener.signal_submit.connect (() => {+			MainWindow.native_window.hide_text_input ();+		});+		+		MainWindow.native_window.set_text_listener (listener);+	}+}++}diff --git a/libbirdfont/SpacingClassTab.vala b/libbirdfont/SpacingClassTab.valanew file mode 100644index 0000000..45954a6--- /dev/null+++ b/libbirdfont/SpacingClassTab.vala@@ -0,0 +1,325 @@+/*+    Copyright (C) 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library is distributed in the hope that it will be useful, but +    WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +    Lesser General Public License for more details.+*/+using Cairo;+using Math;++namespace BirdFont {++public class SpacingClassTab : FontDisplay {++	int scroll = 0;+	int visible_rows = 0;+	WidgetAllocation allocation = new WidgetAllocation ();+	Gee.ArrayList<Row> rows = new Gee.ArrayList<Row> ();+	+	public Gee.ArrayList<SpacingClass> classes = new Gee.ArrayList<SpacingClass> ();+	+	public static int NEW_CLASS = -1;+	+	Gee.ArrayList<string> connections = new Gee.ArrayList<string> ();+	+	public SpacingClassTab () {+	}++	public Gee.ArrayList<string> get_all_connections (string glyph) {+		Gee.ArrayList<string> c = new Gee.ArrayList<string> ();+		+		connections.clear ();+		add_connections (glyph);+		+		foreach (string t in connections) {+			c.add (t);+		}+		+		connections.clear ();+		+		return c;+	}+	+	public void add_connections (string glyph) {+		connections.add (glyph);+		+		foreach (SpacingClass s in classes) {+			if (s.first == glyph) {+				if (connections.index_of (s.next) == -1) {+					add_connections (s.next);+				}+			}++			if (s.next == glyph) {+				if (connections.index_of (s.first) == -1) {+					add_connections (s.first);+				}+			}+		}+		+		connections.sort ((a, b) => {+			return strcmp (a, b);+		});+	}++	public void add_class (string first, string next) {+		SpacingClass s = new SpacingClass (first, next);+		s.updated.connect (update_all_rows);+		s.updated.connect (update_kerning);+		classes.add (s);+		update_kerning (s);+	}++	public void update_kerning (SpacingClass s) {+		Font font = BirdFont.get_current_font ();+		GlyphCollection? g;+		GlyphCollection gc;+		+		if (s.next != "?") {+			KerningClasses.get_instance ().update_space_class (s.next);+			g = font.get_glyph_collection (s.next);+			if (g != null) {+				gc = (!) g;+				gc.get_current ().update_spacing_class ();+			}+		}+		+		if (s.first != "?") {+			KerningClasses.get_instance ().update_space_class (s.first);+			g = font.get_glyph_collection (s.first);+			if (g != null) {+				gc = (!) g;+				gc.get_current ().update_spacing_class ();+			}+		}+	}++	public void selected_row (Row row, int column, bool delete_button) {+		Font font = BirdFont.get_current_font ();+		+		if (row.get_index () == -1) {+			add_class ("?", "?");+			MainWindow.native_window.hide_text_input ();+			update_rows ();+			update_scrollbar ();+			font.touch ();+		} else if (classes.size != 0) {+			if (delete_button) {+				return_if_fail (0 <= row.get_index () < classes.size);+				classes.remove_at (row.get_index ());+				MainWindow.native_window.hide_text_input ();+				update_rows ();+				update_scrollbar ();+				font.touch ();+			} else if (column == 0) {+				if (!(0 <= row.get_index () < classes.size)) {+					warning (@"Index: $(row.get_index ()) classes.size: $(classes.size)");+					return;+				}+				classes.get (row.get_index ()).set_first ();+				font.touch ();+			} else if (column == 2) {+				return_if_fail (0 <= row.get_index () < classes.size);+				classes.get (row.get_index ()).set_next ();+				font.touch ();+			}+		}+	}++	public override void selected_canvas () {+		update_rows ();+		update_scrollbar ();+	}+	+	void update_all_rows (SpacingClass s) {+		update_rows ();+	}+	+	void update_rows () {+		int i = 0;+		+		rows.clear ();+		rows.add (new Row (t_("New spacing class"), NEW_CLASS, false));+		+		foreach (SpacingClass c in classes) {+			rows.add (new Row.columns_3 (@"$(c.first)", "=",  @"$(c.next)", i));+			i++;+		}+		+		GlyphCanvas.redraw ();+	}+	+	public override void draw (WidgetAllocation allocation, Context cr) {+		double y = 20 * MainWindow.units;+		int s = 0;+		bool color = (scroll + 1 % 2) == 0;+		+		if (allocation.width != this.allocation.width+				|| allocation.height != this.allocation.height) {+			this.allocation = allocation;+			update_rows ();+			update_scrollbar ();+		}+		+		visible_rows = (int) (allocation.height / 18.0);+		+		cr.save ();+		cr.set_source_rgba (1, 1, 1, 1);+		cr.rectangle (0, 0, allocation.width, allocation.height);+		cr.fill ();+		cr.restore ();+		+		cr.save ();+		cr.set_source_rgba (0.3, 0.3, 0.3, 1);+		cr.set_font_size (12);++		foreach (Row r in rows) {+			if (s++ >= scroll) {+				draw_row (allocation, cr, r, y, color, true);+				y += 18 * MainWindow.units;+				color = !color;+			}+		}+		+		cr.restore ();+	}	++	private static void draw_row (WidgetAllocation allocation, Context cr,+			Row row, double y, bool color, bool dark) {+		+		double margin;+	+		if (color) {+			cr.save ();+			cr.set_source_rgba (224/255.0, 224/255.0, 224/255.0, 1);+			cr.rectangle (0, y - 14 * MainWindow.units, allocation.width, 18 * MainWindow.units);+			cr.fill ();+			cr.restore ();+		}++		if (row.has_delete_button ()) {+			cr.save ();+			cr.set_line_width (1);+			cr.move_to (10 * MainWindow.units, y - 8 * MainWindow.units);+			cr.line_to (15 * MainWindow.units, y - 3 * MainWindow.units);+			cr.move_to (10 * MainWindow.units, y - 3 * MainWindow.units);+			cr.line_to (15 * MainWindow.units, y - 8 * MainWindow.units);		+			cr.stroke ();+			cr.restore ();+		}+		+		for (int i = 0; i < row.columns; i++) {+			cr.save ();+			margin = (row.has_delete_button ()) ? 120 * MainWindow.units : 3* MainWindow.units;+			cr.move_to (margin + i * 120 * MainWindow.units, y);+			cr.set_font_size (12 * MainWindow.units);+			cr.show_text (row.get_column (i));+			cr.restore ();+		}+	}++	public override void button_release (int button, double ex, double ey) {+		int s = 0;+		double y = 0;+		int colum = -1;+		Row? selected = null;+		bool over_delete = false;++		foreach (Row r in rows) {+			if (s++ >= scroll) {+				y += 18 * MainWindow.units;+				+				if (y - 10 * MainWindow.units <= ey * MainWindow.units <= y + 5 * MainWindow.units) {+					+					if (r.has_delete_button ()) {+						colum = (int) ((ex - 120) / 120 * MainWindow.units);+					} else {+						colum = (int) ((ex) / 120 * MainWindow.units);+					}+					+					over_delete = (ex < 18 && r.has_delete_button ());+					selected = r;+					break;+				}+			}+		}+		+		if (selected != null) {+			selected_row ((!) selected, colum, over_delete);	+		}+		+		update_scrollbar ();+		redraw_area (0, 0, allocation.width, allocation.height);+	}++	public override string get_label () {+		return t_("Spacing Classes");+	}++	public override string get_name () {+		return "SpacingClasses";+	}++	public override bool has_scrollbar () {+		return true;+	}+	+	public override void scroll_wheel_down (double x, double y) {+		int nrows = rows.size;+		scroll += 3;++		if (scroll > nrows - visible_rows) {+			scroll = (int) (nrows - visible_rows);+		}+		+		if (visible_rows > nrows) {+			scroll = 0;+		} +		+		update_scrollbar ();+		redraw_area (0, 0, allocation.width, allocation.height);+	}+	+	public override void scroll_wheel_up (double x, double y) {+		scroll -= 3;+		+		if (scroll < 0) {+			scroll = 0;+		}+		+		update_scrollbar ();+		redraw_area (0, 0, allocation.width, allocation.height);+	}+	+	public void update_scrollbar () {+		uint rows = rows.size;++		if (rows == 0 || visible_rows == 0) {+			MainWindow.set_scrollbar_size (0);+			MainWindow.set_scrollbar_position (0);+		} else {+			MainWindow.set_scrollbar_size ((double) visible_rows / rows);+			MainWindow.set_scrollbar_position ((double) scroll /  rows);+		}+	}++	public override void scroll_to (double percent) {+		uint rows = rows.size;+		scroll = (int) (percent * rows);+		+		if (scroll > rows - visible_rows) {+			scroll = (int) (rows - visible_rows);+		}+		+		redraw_area (0, 0, allocation.width, allocation.height);+	}+}++}diff --git a/libbirdfont/ToolCollection.vala b/libbirdfont/ToolCollection.valaindex dcbc89e..9861b93 100644--- a/libbirdfont/ToolCollection.vala+++ b/libbirdfont/ToolCollection.vala@@ -20,7 +20,7 @@ namespace BirdFont { public abstract class ToolCollection : GLib.Object  { 	public double scroll = 0; 	public double content_height = 0;-	public abstract unowned List<Expander> get_expanders ();+	public abstract Gee.ArrayList<Expander> get_expanders (); }  }

Contact

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