.
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 public Gee.ArrayList<Ligature> ligatures = new Gee.ArrayList<Ligature> ();
23 public 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, GlyphSequence ligature);
27
28 public delegate void ContextualLigatureIterator (ContextualLigature lig);
29
30 unowned Font font;
31
32 public Ligatures (Font font) {
33 this.font = font;
34
35 font.font_deleted.connect (() => {
36 this.font = Font.empty;
37 });
38 }
39
40 public void get_ligatures (LigatureIterator iter) {
41 foreach (Ligature l in ligatures) {
42 iter (l.substitution, l.ligature);
43 }
44 }
45
46 public void get_contextual_ligatures (ContextualLigatureIterator iter) {
47 foreach (ContextualLigature l in contextual_ligatures) {
48 iter (l);
49 }
50 }
51
52 public void get_single_substitution_ligatures (SingleLigatureIterator iter) {
53 get_ligatures ((substitution, ligature) => {
54 GlyphCollection? gc;
55 GlyphSequence lig;
56 GlyphSequence gs;
57 string[] subst_names = substitution.split (" ");
58
59 lig = new GlyphSequence ();
60 foreach (string n in font.get_names (ligature)) {
61 gc = font.get_glyph_collection_by_name (n);
62
63 if (gc == null) {
64 return;
65 }
66
67 lig.add (((!) gc).get_current ());
68 }
69
70 gs = new GlyphSequence ();
71 foreach (string s in subst_names) {
72 gc = font.get_glyph_collection_by_name (s);
73
74 if (gc == null) {
75 return;
76 }
77
78 gs.glyph.add (((!) gc).get_current ());
79 }
80
81 iter (gs, lig);
82 });
83 }
84
85 public int count () {
86 return ligatures.size;
87 }
88
89 public int count_contextual_ligatures () {
90 return contextual_ligatures.size;
91 }
92
93 public void remove_at (int i) {
94 return_if_fail (0 <= i < ligatures.size);
95 ligatures.remove_at (i);
96 }
97
98 public void remove_contextual_ligatures_at (int i) {
99 return_if_fail (0 <= i < contextual_ligatures.size);
100 contextual_ligatures.remove_at (i);
101 }
102
103 public void set_beginning (int index) {
104 ContextualLigature lig;
105 TextListener listener;
106
107 return_if_fail (0 <= index < contextual_ligatures.size);
108
109 lig = contextual_ligatures.get (index);
110 listener = new TextListener (t_("Beginning"), lig.backtrack, t_("Set"));
111
112 listener.signal_text_input.connect ((text) => {
113 lig.backtrack = text;
114 });
115
116 listener.signal_submit.connect (() => {
117 TabContent.hide_text_input ();
118 MainWindow.get_ligature_display ().update_rows ();
119 sort_ligatures ();
120 });
121
122 TabContent.show_text_input (listener);
123 }
124
125 public void set_middle (int index) {
126 ContextualLigature lig;
127 TextListener listener;
128
129 return_if_fail (0 <= index < contextual_ligatures.size);
130
131 lig = contextual_ligatures.get (index);
132 listener = new TextListener (t_("Middle"), lig.input, t_("Set"));
133
134 listener.signal_text_input.connect ((text) => {
135 lig.input = text;
136 });
137
138 listener.signal_submit.connect (() => {
139 TabContent.hide_text_input ();
140 MainWindow.get_ligature_display ().update_rows ();
141 sort_ligatures ();
142 });
143
144 TabContent.show_text_input (listener);
145 }
146
147 public void set_end (int index) {
148 ContextualLigature lig;
149 TextListener listener;
150
151 return_if_fail (0 <= index < contextual_ligatures.size);
152
153 lig = contextual_ligatures.get (index);
154 listener = new TextListener (t_("End"), lig.lookahead, t_("Set"));
155
156 listener.signal_text_input.connect ((text) => {
157 lig.lookahead = text;
158 });
159
160 listener.signal_submit.connect (() => {
161 TabContent.hide_text_input ();
162 MainWindow.get_ligature_display ().update_rows ();
163 sort_ligatures ();
164 });
165
166 TabContent.show_text_input (listener);
167 }
168
169 public void set_ligature (int index) {
170 Ligature lig;
171
172 return_if_fail (0 <= index < ligatures.size);
173
174 lig = ligatures.get (index);
175 lig.set_ligature ();
176 }
177
178 public void set_contextual_ligature (int index) {
179 ContextualLigature lig;
180 TextListener listener;
181
182 return_if_fail (0 <= index < contextual_ligatures.size);
183
184 lig = contextual_ligatures.get (index);
185 listener = new TextListener (t_("Ligature"), lig.ligatures, t_("Set"));
186
187 listener.signal_text_input.connect ((text) => {
188 lig.ligatures = text;
189 });
190
191 listener.signal_submit.connect (() => {
192 TabContent.hide_text_input ();
193 MainWindow.get_ligature_display ().update_rows ();
194 sort_ligatures ();
195 });
196
197 TabContent.show_text_input (listener);
198
199 }
200
201 public void set_substitution (int index) {
202 Ligature lig;
203
204 return_if_fail (0 <= index < ligatures.size);
205
206 lig = ligatures.get (index);
207 lig.set_substitution ();
208 }
209
210 public void add_ligature (string subst, string liga) {
211 ligatures.insert (0, new Ligature (liga, subst));
212 sort_ligatures ();
213 }
214
215 public void add_contextual_ligature (string ligature, string backtrack, string input, string lookahead) {
216 ContextualLigature l = new ContextualLigature (font, ligature, backtrack, input, lookahead);
217 contextual_ligatures.insert (0, l);
218 sort_ligatures ();
219 }
220
221 public void sort_ligatures () {
222 ligatures.sort ((a, b) => {
223 Ligature first, next;
224 int chars_first, chars_next;
225
226 first = (Ligature) a;
227 next = (Ligature) b;
228
229 chars_first = first.substitution.split (" ").length;
230 chars_next = next.substitution.split (" ").length;
231
232 return chars_next - chars_first;
233 });
234
235 contextual_ligatures.sort ((a, b) => {
236 ContextualLigature first, next;
237 int chars_first, chars_next;
238
239 first = (ContextualLigature) a;
240 next = (ContextualLigature) b;
241
242 chars_first = first.backtrack.split (" ").length;
243 chars_first += first.input.split (" ").length;
244 chars_first += first.lookahead.split (" ").length;
245
246 chars_next = next.backtrack.split (" ").length;
247 chars_next += next.input.split (" ").length;
248 chars_next += next.lookahead.split (" ").length;
249
250 return chars_next - chars_first;
251 });
252 }
253 }
254
255 }
256