.
1 /*
2 Copyright (C) 2014 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using Cairo;
16 using Math;
17
18 namespace BirdFont {
19
20 public class Ligatures : GLib.Object {
21
22 Gee.ArrayList<Ligature> ligatures = new Gee.ArrayList<Ligature> ();
23 Gee.ArrayList<ContextualLigature> contextual_ligatures = new Gee.ArrayList<ContextualLigature> ();
24
25 public delegate void LigatureIterator (string substitution, string ligature);
26 public delegate void SingleLigatureIterator (GlyphSequence substitution, GlyphCollection ligature);
27
28 public delegate void ContextualLigatureIterator (ContextualLigature lig);
29
30 public Ligatures () {
31 }
32
33 public void get_ligatures (LigatureIterator iter) {
34 foreach (Ligature l in ligatures) {
35 iter (l.substitution, l.ligature);
36 }
37 }
38
39 public void get_contextual_ligatures (ContextualLigatureIterator iter) {
40 foreach (ContextualLigature l in contextual_ligatures) {
41 iter (l);
42 }
43 }
44
45 public void get_single_substitution_ligatures (SingleLigatureIterator iter) {
46 get_ligatures ((substitution, ligature) => {
47 Font font = BirdFont.get_current_font ();
48 GlyphCollection? gc;
49 GlyphCollection li;
50 GlyphSequence gs;
51 string[] subst_names = substitution.split (" ");
52
53 gc = font.get_glyph_collection_by_name (ligature);
54
55 if (gc == null) {
56 return;
57 }
58
59 li = (!) gc;
60
61 gs = new GlyphSequence ();
62 foreach (string s in subst_names) {
63 gc = font.get_glyph_collection_by_name (s);
64
65 if (gc == null) {
66 return;
67 }
68
69 gs.glyph.add (((!) gc).get_current ());
70 }
71
72 iter (gs, li);
73 });
74 }
75
76 public int count () {
77 return ligatures.size;
78 }
79
80 public int count_contextual_ligatures () {
81 return contextual_ligatures.size;
82 }
83
84 public void remove_at (int i) {
85 return_if_fail (0 <= i < ligatures.size);
86 ligatures.remove_at (i);
87 }
88
89 public void remove_contextual_ligatures_at (int i) {
90 return_if_fail (0 <= i < contextual_ligatures.size);
91 contextual_ligatures.remove_at (i);
92 }
93
94 /** Add ligaure for a chained substitution. */
95 public void add_substitution_at (int index) {
96 return_if_fail (0 <= index < contextual_ligatures.size);
97 contextual_ligatures.get (index).add_ligature ();
98 }
99
100 public void set_beginning (int index) {
101 ContextualLigature lig;
102 TextListener listener;
103
104 return_if_fail (0 <= index < contextual_ligatures.size);
105
106 lig = contextual_ligatures.get (index);
107 listener = new TextListener (t_("Beginning"), lig.backtrack, t_("Set"));
108
109 listener.signal_text_input.connect ((text) => {
110 lig.backtrack = text;
111 });
112
113 listener.signal_submit.connect (() => {
114 MainWindow.native_window.hide_text_input ();
115 MainWindow.get_ligature_display ().update_rows ();
116 });
117
118 MainWindow.native_window.set_text_listener (listener);
119 }
120
121 public void set_middle (int index) {
122 ContextualLigature lig;
123 TextListener listener;
124
125 return_if_fail (0 <= index < contextual_ligatures.size);
126
127 lig = contextual_ligatures.get (index);
128 listener = new TextListener (t_("Middle"), lig.input, t_("Set"));
129
130 listener.signal_text_input.connect ((text) => {
131 lig.input = text;
132 });
133
134 listener.signal_submit.connect (() => {
135 MainWindow.native_window.hide_text_input ();
136 MainWindow.get_ligature_display ().update_rows ();
137 });
138
139 MainWindow.native_window.set_text_listener (listener);
140 }
141
142 public void set_end (int index) {
143 ContextualLigature lig;
144 TextListener listener;
145
146 return_if_fail (0 <= index < contextual_ligatures.size);
147
148 lig = contextual_ligatures.get (index);
149 listener = new TextListener (t_("End"), lig.lookahead, t_("Set"));
150
151 listener.signal_text_input.connect ((text) => {
152 lig.lookahead = text;
153 });
154
155 listener.signal_submit.connect (() => {
156 MainWindow.native_window.hide_text_input ();
157 MainWindow.get_ligature_display ().update_rows ();
158 });
159
160 MainWindow.native_window.set_text_listener (listener);
161 }
162
163 public void set_ligature (int index) {
164 Ligature lig;
165
166 return_if_fail (0 <= index < ligatures.size);
167
168 lig = ligatures.get (index);
169 lig.set_ligature ();
170 }
171
172 public void set_substitution (int index) {
173 Ligature lig;
174
175 return_if_fail (0 <= index < ligatures.size);
176
177 lig = ligatures.get (index);
178 lig.set_substitution ();
179 }
180
181 public void add_ligature (string subst, string liga) {
182 ligatures.insert (0, new Ligature (liga, subst));
183 sort_ligatures ();
184 }
185
186 public void add_contextual_ligature (string backtrack, string input, string lookahead) {
187 ContextualLigature l = new ContextualLigature (backtrack, input, lookahead);
188 contextual_ligatures.insert (0, l);
189 sort_ligatures ();
190 }
191
192 public void sort_ligatures () {
193 ligatures.sort ((a, b) => {
194 Ligature first, next;
195 int chars_first, chars_next;
196
197 first = (Ligature) a;
198 next = (Ligature) b;
199
200 chars_first = first.substitution.split (" ").length;
201 chars_next = next.substitution.split (" ").length;
202
203 return chars_next - chars_first;
204 });
205
206 contextual_ligatures.sort ((a, b) => {
207 ContextualLigature first, next;
208 int chars_first, chars_next;
209
210 first = (ContextualLigature) a;
211 next = (ContextualLigature) b;
212
213 chars_first = first.backtrack.split (" ").length;
214 chars_first += first.input.split (" ").length;
215 chars_first += first.lookahead.split (" ").length;
216
217 chars_next = next.backtrack.split (" ").length;
218 chars_next += next.input.split (" ").length;
219 chars_next += next.lookahead.split (" ").length;
220
221 return chars_next - chars_first;
222 });
223
224 foreach (ContextualLigature c in contextual_ligatures) {
225 c.sort ();
226 }
227 }
228 }
229
230 }
231