.
1 /*
2 Copyright (C) 2013 2014 2015 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 KerningTools : ToolCollection {
21 public static Gee.ArrayList<Expander> expanders;
22 public static int next_class = 0;
23 public static Expander classes;
24
25 public static double font_size = 1;
26 public static ZoomBar zoom_bar;
27
28 public static Tool previous_kerning_string;
29 public static Tool next_kerning_string;
30
31 public static Expander otf_features;
32
33 static OtfTags active_otf_features;
34
35 public KerningTools () {
36 selected ();
37 }
38
39 public override void selected () {
40 init ();
41 }
42
43 public static OtfTags get_otf_tags () {
44 if (is_null (active_otf_features)) {
45 return new OtfTags ();
46 }
47
48 return active_otf_features;
49 }
50
51 public static void init () {
52 Font font = BirdFont.get_current_font ();
53
54 active_otf_features = new OtfTags ();
55
56 Expander kerning_tools = new Expander (t_("Kerning Tools"));
57
58 if (is_null (classes)) {
59 classes = new Expander ();
60 update_kerning_classes ();
61 }
62
63 expanders = new Gee.ArrayList<Expander> ();
64
65 Expander font_name = new Expander ();
66 font_name.add_tool (new FontName ());
67
68 Expander zoom_expander = new Expander (t_("Font Size"));
69
70 zoom_bar = new ZoomBar ();
71 zoom_bar.new_zoom.connect ((z) => {
72 Font f;
73
74 font_size = 3 * z;
75
76 if (font_size < 0.1) {
77 font_size = 0.1;
78 }
79
80 f = BirdFont.get_current_font ();
81 f.settings.set_setting ("kerning_zoom", @"$z");
82
83 GlyphCanvas.redraw ();
84 });
85 zoom_expander.add_tool (zoom_bar);
86
87 Tool new_kerning_class = new Tool ("kerning_class", t_("Create new kerning class."));
88 new_kerning_class.select_action.connect ((self) => {
89 Font f = BirdFont.get_current_font ();
90 string label = t_("Kerning class");
91 KerningRange kr = new KerningRange (f, @"$label $(++next_class)");
92 classes.add_tool (kr);
93 self.set_selected (false);
94 classes.clear_cache ();
95 classes.redraw ();
96 });
97 kerning_tools.add_tool (new_kerning_class);
98
99 Tool text_kerning = new Tool ("kerning_text_input", t_("Use text input to enter kerning values."));
100 text_kerning.select_action.connect ((self) => {
101 KerningDisplay d = MainWindow.get_kerning_display ();
102 d.set_kerning_by_text ();
103 self.set_selected (false);
104 });
105 kerning_tools.add_tool (text_kerning);
106
107 Tool insert_last = new Tool ("insert_glyph_from_overview", t_("Insert glyph from overview"));
108 insert_last.select_action.connect ((self) => {
109 KerningDisplay d = MainWindow.get_kerning_display ();
110 GlyphSelection gs = new GlyphSelection ();
111
112 gs.selected_glyph.connect ((gc) => {
113 d.inser_glyph (gc.get_current ());
114 MainWindow.get_tab_bar ().select_tab_name ("Kerning");
115 });
116
117 GlyphCanvas.set_display (gs);
118 self.set_selected (false);
119 });
120 kerning_tools.add_tool (insert_last);
121
122 Tool insert_unicode = new Tool ("insert_unichar", t_("Insert character by unicode value"));
123 insert_unicode.select_action.connect ((self) => {
124 KerningDisplay d = MainWindow.get_kerning_display ();
125 d.insert_unichar ();
126 self.set_selected (false);
127 });
128 kerning_tools.add_tool (insert_unicode);
129
130 Tool right_to_left = new Tool ("right_to_left", t_("Right to left"));
131 right_to_left.select_action.connect ((self) => {
132 KerningDisplay.right_to_left = !KerningDisplay.right_to_left;
133 right_to_left.set_selected (KerningDisplay.right_to_left);
134 GlyphCanvas.redraw ();
135 });
136 kerning_tools.add_tool (right_to_left);
137
138 string empty_kerning_text = t_("Open a text file with kerning strings first.");
139
140 previous_kerning_string = new Tool ("previous_kerning_string", t_("Previous kerning string"));
141 previous_kerning_string.select_action.connect ((self) => {
142 FontDisplay fd = MainWindow.get_current_display ();
143 KerningDisplay d = (KerningDisplay) fd;
144 Font f = BirdFont.get_current_font ();
145 string w = f.kerning_strings.previous ();
146
147 if (f.kerning_strings.is_empty ()) {
148 MainWindow.show_message (empty_kerning_text);
149 } else if (w == "") {
150 MainWindow.show_message (t_("You have reached the beginning of the list."));
151 } else {
152 d.new_line ();
153 d.add_text (w);
154 }
155 self.set_selected (false);
156 });
157 kerning_tools.add_tool (previous_kerning_string);
158
159 next_kerning_string = new Tool ("next_kerning_string", t_("Next kerning string"));
160 next_kerning_string.select_action.connect ((self) => {
161 FontDisplay fd = MainWindow.get_current_display ();
162 KerningDisplay d = (KerningDisplay) fd;
163 Font f = BirdFont.get_current_font ();
164 string w = f.kerning_strings.next ();
165
166 if (f.kerning_strings.is_empty ()) {
167 MainWindow.show_message (empty_kerning_text);
168 } else if (w == "") {
169 MainWindow.show_message (t_("You have reached the end of the list."));
170 } else {
171 d.new_line ();
172 d.add_text (w);
173 }
174 self.set_selected (false);
175 });
176 kerning_tools.add_tool (next_kerning_string);
177
178 otf_features = new Expander (t_("Substitutions"));
179
180 foreach (string tag in font.alternates.get_all_tags ()) {
181 add_otf_label (tag);
182 }
183
184 kerning_tools.set_persistent (false);
185 kerning_tools.set_unique (false);
186
187 classes.set_persistent (true);
188 classes.set_unique (true);
189
190 expanders.add (font_name);
191 expanders.add (zoom_expander);
192 expanders.add (kerning_tools);
193 expanders.add (otf_features);
194 expanders.add (classes);
195 }
196
197 public static void add_otf_label (string tag) {
198 OtfLabel otf_label = new OtfLabel (tag);
199 FontSettings fs = BirdFont.get_current_font ().settings;
200
201 otf_features.add_tool (otf_label);
202 otf_label.otf_feature_activity.connect ((enable, tag) => {
203 OtfTags tags = active_otf_features.copy ();
204 KerningDisplay kd = MainWindow.get_kerning_display ();
205 kd.new_segment ();
206
207 // create a new feature set in order to keep the features
208 // for other parts of the text in kerning tab
209 active_otf_features = tags;
210
211 if (enable) {
212 tags.add (tag);
213 fs.set_setting (@"kerning_$(tag)", "true");
214 } else {
215 tags.remove (tag);
216 fs.set_setting (@"kerning_$(tag)", "false");
217 }
218
219 kd.get_last_segment ().set_otf_tags (tags);
220
221 GlyphCanvas.redraw ();
222 });
223
224 bool enable = fs.get_setting (@"kerning_$(tag)") == "true";
225 otf_label.set_selected_tag (enable);
226 }
227
228 public static void add_unique_class (KerningRange kerning_class) {
229 KerningRange k;
230
231 if (is_null (classes)) { // FIXME: export without tools
232 init ();
233 }
234
235 foreach (Tool t in classes.tool) {
236 if (!(t is KerningRange)) {
237 warning ("Tool is not kerning range");
238 return;
239 }
240
241 k = (KerningRange) t;
242 if (k.glyph_range.get_all_ranges () == kerning_class.glyph_range.get_all_ranges ()) {
243 return;
244 }
245 }
246
247 classes.add_tool (kerning_class);
248 }
249
250 public static GlyphRange get_kerning_class (int index) {
251 if (likely (0 <= index < classes.tool.size)) {
252 return ((KerningRange) classes.tool.get (index)).glyph_range;
253 } else {
254 warning ("Index out of bounds.");
255 return new GlyphRange ();
256 }
257 }
258
259 public static void update_kerning_classes () {
260 Font font = BirdFont.get_current_font ();
261 KerningClasses k = font.get_kerning_classes ();
262 KerningRange kr;
263 GlyphRange r;
264 int i;
265
266 remove_all_kerning_classes ();
267
268 for (i = 0; i < k.classes_first.size; i++) {
269 r = k.classes_first.get (i);
270 if (r.is_class ()) {
271 kr = new KerningRange (font);
272 kr.set_ranges (r.get_all_ranges ());
273 add_unique_class (kr);
274 }
275
276 r = k.classes_last.get (i);
277 if (r.is_class ()) {
278 kr = new KerningRange (font);
279 kr.set_ranges (r.get_all_ranges ());
280 add_unique_class (kr);
281 }
282 }
283
284 classes.clear_cache ();
285 classes.redraw ();
286 }
287
288 private static void remove_all_kerning_classes () {
289 if (is_null (classes)) { // FIXME: export without tools
290 init ();
291 }
292
293 classes.tool.clear ();
294
295 if (!is_null (MainWindow.get_toolbox ())) {
296 MainWindow.get_toolbox ().update_expanders ();
297 }
298 }
299
300 public static void remove_empty_classes () {
301 KerningRange kr;
302 int i;
303
304 if (classes.tool.size == 0) {
305 return;
306 }
307
308 i = 0;
309 foreach (Tool t in classes.tool) {
310 return_if_fail (t is KerningRange);
311
312 kr = (KerningRange) t;
313 if (kr.glyph_range.is_empty ()) {
314 classes.tool.remove_at (i);
315 remove_empty_classes ();
316 Toolbox.redraw_tool_box ();
317 return;
318 }
319
320 i++;
321 }
322 }
323
324 public override Gee.ArrayList<Expander> get_expanders () {
325 return expanders;
326 }
327
328 public static void update_spacing_classes () {
329 KerningRange kr;
330
331 if (classes.tool.size == 0) {
332 return;
333 }
334
335 foreach (Tool t in classes.tool) {
336 return_if_fail (t is KerningRange);
337
338 kr = (KerningRange) t;
339 kr.update_spacing_class ();
340 }
341 }
342
343 public override Gee.ArrayList<string> get_displays () {
344 Gee.ArrayList<string> d = new Gee.ArrayList<string> ();
345 d.add ("Kerning");
346 d.add ("Spacing");
347 return d;
348 }
349
350 }
351
352 }
353