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