Clone in a terminal:
Ligatures.vala
/*
	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 Ligatures : GLib.Object {
	
	public Gee.ArrayList<Ligature> ligatures = new Gee.ArrayList<Ligature> ();
	public Gee.ArrayList<ContextualLigature> contextual_ligatures = new Gee.ArrayList<ContextualLigature> ();
	
	public delegate void LigatureIterator (string substitution, string ligature);
	public delegate void SingleLigatureIterator (GlyphSequence substitution, GlyphSequence ligature);

	public delegate void ContextualLigatureIterator (ContextualLigature lig);

	unowned Font font;

	public Ligatures (Font font) {
		this.font = font;

		font.font_deleted.connect (() => {
			this.font = Font.empty;
		});
	}
	
	public void get_ligatures (LigatureIterator iter) {
		foreach (Ligature l in ligatures) {
			iter (l.substitution, l.ligature);
		}	
	}

	public void get_contextual_ligatures (ContextualLigatureIterator iter) {
		foreach (ContextualLigature l in contextual_ligatures) {
			iter (l);
		}	
	}

	public void get_single_substitution_ligatures (SingleLigatureIterator iter) {
		get_ligatures ((substitution, ligature) => {
			GlyphCollection? gc;
			GlyphSequence lig;
			GlyphSequence gs;
			string[] subst_names = substitution.split (" ");
			
			lig = new GlyphSequence ();
			foreach (string n in font.get_names (ligature)) {
				gc = font.get_glyph_collection_by_name (n);
				
				if (gc == null) {
					return;
				}
				
				lig.add (((!) gc).get_current ());
			}
			
			gs = new GlyphSequence ();
			foreach (string s in subst_names) {
				gc = font.get_glyph_collection_by_name (s);
				
				if (gc == null) {
					return;
				}
				
				gs.glyph.add (((!) gc).get_current ());
			}
			
			iter (gs, lig);
		});
	}
		
	public int count () {
		return ligatures.size;
	}

	public int count_contextual_ligatures () {
		return contextual_ligatures.size;
	}
	
	public void remove_at (int i) {
		return_if_fail (0 <= i < ligatures.size);
		ligatures.remove_at (i);
	}

	public void remove_contextual_ligatures_at (int i) {
		return_if_fail (0 <= i < contextual_ligatures.size);
		contextual_ligatures.remove_at (i);
	}
		
	public void set_beginning (int index) {
		ContextualLigature lig;
		TextListener listener;
		
		return_if_fail (0 <= index < contextual_ligatures.size);

		lig = contextual_ligatures.get (index);
		listener = new TextListener (t_("Beginning"), lig.backtrack, t_("Set"));
		
		listener.signal_text_input.connect ((text) => {
			lig.backtrack = text;
		});
		
		listener.signal_submit.connect (() => {
			TabContent.hide_text_input ();
			MainWindow.get_ligature_display ().update_rows ();
			sort_ligatures ();
			GlyphCanvas.redraw ();
		});
		
		TabContent.show_text_input (listener);			
	}
	
	public void set_middle (int index) {
		ContextualLigature lig;
		TextListener listener;
		
		return_if_fail (0 <= index < contextual_ligatures.size);

		lig = contextual_ligatures.get (index);
		listener = new TextListener (t_("Middle"), lig.input, t_("Set"));
		
		listener.signal_text_input.connect ((text) => {
			lig.input = text;
		});
		
		listener.signal_submit.connect (() => {
			TabContent.hide_text_input ();
			MainWindow.get_ligature_display ().update_rows ();
			sort_ligatures ();
			GlyphCanvas.redraw ();
		});
		
		TabContent.show_text_input (listener);		
	}

	public void set_end (int index) {
		ContextualLigature lig;
		TextListener listener;
		
		return_if_fail (0 <= index < contextual_ligatures.size);

		lig = contextual_ligatures.get (index);
		listener = new TextListener (t_("End"), lig.lookahead, t_("Set"));
		
		listener.signal_text_input.connect ((text) => {
			lig.lookahead = text;
		});
		
		listener.signal_submit.connect (() => {
			TabContent.hide_text_input ();
			MainWindow.get_ligature_display ().update_rows ();
			sort_ligatures ();
			GlyphCanvas.redraw ();
		});
		
		TabContent.show_text_input (listener);
	}
				
	public void set_ligature (int index) {
		Ligature lig;
		
		return_if_fail (0 <= index < ligatures.size);
		
		lig = ligatures.get (index);
		lig.set_ligature ();
	}

	public void set_contextual_ligature (int index) {
		ContextualLigature lig;
		TextListener listener;
		
		return_if_fail (0 <= index < contextual_ligatures.size);

		lig = contextual_ligatures.get (index);
		listener = new TextListener (t_("Ligature"), lig.ligatures, t_("Set"));
		
		listener.signal_text_input.connect ((text) => {
			lig.ligatures = text;
		});
		
		listener.signal_submit.connect (() => {
			TabContent.hide_text_input ();
			MainWindow.get_ligature_display ().update_rows ();
			sort_ligatures ();
			GlyphCanvas.redraw ();
		});
		
		TabContent.show_text_input (listener);			

	}
		
	public void set_substitution (int index) {
		Ligature lig;
		
		return_if_fail (0 <= index < ligatures.size);
		
		lig = ligatures.get (index);
		lig.set_substitution ();
	}
	
	public void add_ligature (string subst, string liga) {
		ligatures.insert (0, new Ligature (liga, subst));
		sort_ligatures ();
	}

	public void add_contextual_ligature (string ligature, string backtrack, string input, string lookahead) {
		ContextualLigature l = new ContextualLigature (font, ligature, backtrack, input, lookahead);
		contextual_ligatures.insert (0, l);
		sort_ligatures ();
	}
	
	public void sort_ligatures () {
		ligatures.sort ((a, b) => {
			Ligature first, next;
			int chars_first, chars_next;
			
			first = (Ligature) a;
			next = (Ligature) b;
			
			chars_first = first.substitution.split (" ").length;
			chars_next = next.substitution.split (" ").length;
							
			return chars_next - chars_first;
		});
		
		contextual_ligatures.sort ((a, b) => {
			ContextualLigature first, next;
			int chars_first, chars_next;
			
			first = (ContextualLigature) a;
			next = (ContextualLigature) b;
			
			chars_first = first.backtrack.split (" ").length;
			chars_first += first.input.split (" ").length;
			chars_first += first.lookahead.split (" ").length;
			
			chars_next = next.backtrack.split (" ").length;
			chars_next += next.input.split (" ").length;
			chars_next += next.lookahead.split (" ").length;		
								
			return chars_next - chars_first;
		});
	}
}

}

Contact

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