.
1 /*
2 Copyright (C) 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
17 namespace BirdFont {
18
19 /** Kerning context. */
20 public class SpacingTab : KerningDisplay {
21
22 double box_size = 122 * MainWindow.units;
23 double height = 44 * MainWindow.units;
24 double character_height = 20 * MainWindow.units;
25
26 WidgetAllocation allocation;
27
28 Glyph text_input_glyph;
29
30 public SpacingTab () {
31 allocation = new WidgetAllocation ();
32 adjust_side_bearings = true;
33 }
34
35 public override string get_label () {
36 return t_("Spacing");
37 }
38
39 public override string get_name () {
40 return "Spacing";
41 }
42
43 public override void draw (WidgetAllocation allocation, Context cr) {
44 base.draw (allocation, cr);
45 draw_spacing_metrix (allocation, cr);
46 }
47
48 void draw_spacing_metrix (WidgetAllocation allocation, Context cr) {
49 GlyphSequence row;
50 int index;
51 Font font = BirdFont.get_current_font ();
52
53 // background
54 cr.save ();
55 Theme.color (cr, "Background 1");
56 cr.rectangle (0, allocation.height - height, allocation.width, height);
57 cr.fill ();
58 cr.restore ();
59
60 // character bar
61 cr.save ();
62 Theme.color (cr, "Table Border");
63 cr.set_line_width (0.8);
64 cr.move_to (0, allocation.height - height);
65 cr.line_to (allocation.width, allocation.height - height);
66 cr.stroke ();
67
68 cr.move_to (0, allocation.height - height + character_height);
69 cr.line_to (allocation.width, allocation.height - height + character_height);
70 cr.stroke ();
71 cr.restore ();
72
73 row = get_first_row ().process_ligatures (font);
74 index = 0;
75 foreach (Glyph? g in row.glyph) {
76 draw_glyph_spacing (allocation, cr, g, index);
77 index++;
78 }
79 }
80
81 void draw_glyph_spacing (WidgetAllocation allocation, Context cr, Glyph? glyph, int index) {
82 Glyph g;
83 double end, middle;
84 double l, r;
85 Text left, right, cap;
86 unichar c;
87
88 this.allocation = allocation;
89
90 // end mark
91 end = (index + 1) * box_size;
92 cr.save ();
93 cr.set_source_rgba (0.5, 0.5, 0.5, 1);
94 cr.set_line_width (2);
95 cr.move_to (end, allocation.height - height);
96 cr.line_to (end, allocation.height);
97 cr.stroke ();
98 cr.restore ();
99
100 // boxes
101 middle = end - box_size / 2.0;
102 cr.save ();
103 cr.set_source_rgba (0.5, 0.5, 0.5, 1);
104 cr.set_line_width (0.8);
105 cr.move_to (middle, allocation.height - height + character_height);
106 cr.line_to (middle, allocation.height);
107 cr.stroke ();
108 cr.restore ();
109
110 if (glyph != null) {
111 g = (!) glyph;
112
113 c = g.get_unichar ();
114 cap = new Text ((!) c.to_string (), 17);
115 Theme.text_color (cap, "Table Border");
116 cap.widget_x = middle - cap.get_extent () / 2.0;
117 cap.widget_y = allocation.height - height + character_height - 4 * MainWindow.units;
118 cap.draw_at_baseline (cr, cap.widget_x, cap.widget_y);
119
120 l = g.get_left_side_bearing ();
121
122 if (Math.fabs (l) < 0.001) {
123 l = 0;
124 }
125
126 left = new Text (truncate (l, 5), 17);
127 Theme.text_color (left, "Foreground 1");
128 left.widget_x = middle - box_size / 2.0 + (box_size / 2.0 - left.get_extent ()) / 2.0;
129 left.widget_y = allocation.height - 7 * MainWindow.units;
130 left.draw_at_baseline (cr, left.widget_x, left.widget_y);
131
132 r = g.get_right_side_bearing ();
133
134 if (Math.fabs (r) < 0.001) {
135 r = 0;
136 }
137
138 right = new Text (truncate (r, 5), 17);
139 Theme.text_color (right, "Table Border");
140 right.widget_x = end - (box_size / 2.0 - right.get_extent ()) / 2.0 - right.get_extent ();
141 right.widget_y = allocation.height - 7 * MainWindow.units;
142 right.draw_at_baseline (cr, right.widget_x, right.widget_y);
143 }
144 }
145
146 public string truncate (double f, int digits) {
147 string t = @"$f";
148 string s = "";
149
150 int d = digits;
151 int i;
152 unichar c;
153
154 if (t.index_of ("-") != -1) {
155 d++;
156 }
157
158 if (t.index_of (".") != -1) {
159 d++;
160 }
161
162 i = 0;
163 while (t.get_next_char (ref i, out c)) {
164 s = s + (!) c.to_string ();
165
166 if (i >= d) {
167 break;
168 }
169 }
170
171 return s;
172 }
173
174 public override void button_press (uint button, double ex, double ey) {
175 if (button == 3) {
176 return;
177 }
178
179 if (!(ey >= allocation.height - height)) {
180 base.button_press (button, ex, ey);
181 }
182 }
183
184 public override void button_release (int button, double ex, double ey) {
185 GlyphSequence row;
186 double p;
187 Font font = BirdFont.get_current_font ();
188
189 if (button == 3) {
190 return;
191 }
192
193 if (ey >= allocation.height - height) {
194
195 TabContent.hide_text_input ();
196
197 // TODO: add button for processing ligatures
198 row = get_first_row ().process_ligatures (font);
199 p = 0;
200 foreach (Glyph? g in row.glyph) {
201 if (p < ex < p + box_size / 2.0) {
202 update_lsb (g);
203 }
204
205 if (p + box_size / 2.0 < ex < p + box_size) {
206 update_rsb (g);
207 }
208
209 p += box_size;
210 }
211 } else {
212 base.button_release (button, ex, ey);
213 }
214 }
215
216 void update_lsb (Glyph? g) {
217 TextListener listener;
218 string submitted_value = "";
219 double l;
220
221 if (g == null) {
222 return;
223 }
224
225 text_input_glyph = (!) g;
226 l = text_input_glyph.get_left_side_bearing ();
227
228 if (Math.fabs (l) < 0.001) {
229 l = 0;
230 }
231
232 listener = new TextListener (t_("Left"), @"$(l)", t_("Set"));
233
234 listener.signal_text_input.connect ((text) => {
235 submitted_value = text;
236
237 if (MenuTab.has_suppress_event ()) {
238 return;
239 }
240
241 GlyphCanvas.redraw ();
242 });
243
244 listener.signal_submit.connect (() => {
245 double v;
246 TabContent.hide_text_input ();
247
248 text_input = false;
249 suppress_input = false;
250
251 v = double.parse (submitted_value);
252 text_input_glyph.left_limit -= v - text_input_glyph.get_left_side_bearing ();
253 });
254
255 suppress_input = true;
256 text_input = true;
257 TabContent.show_text_input (listener);
258 }
259
260 void update_rsb (Glyph? g) {
261 TextListener listener;
262 string submitted_value = "";
263 double r;
264
265 if (g == null) {
266 return;
267 }
268
269 text_input_glyph = (!) g;
270 r = text_input_glyph.get_right_side_bearing ();
271
272 if (Math.fabs (r) < 0.001) {
273 r = 0;
274 }
275
276 listener = new TextListener (t_("Right"), @"$(r)", t_("Set"));
277
278 listener.signal_text_input.connect ((text) => {
279 submitted_value = text;
280
281 if (MenuTab.has_suppress_event ()) {
282 return;
283 }
284
285 GlyphCanvas.redraw ();
286 });
287
288 listener.signal_submit.connect (() => {
289 double v;
290 TabContent.hide_text_input ();
291
292 text_input = false;
293 suppress_input = false;
294
295 v = double.parse (submitted_value);
296 text_input_glyph.right_limit += v - text_input_glyph.get_right_side_bearing ();
297 });
298
299 suppress_input = true;
300 text_input = true;
301 TabContent.show_text_input (listener);
302 }
303
304 public override bool needs_modifier () {
305 return true;
306 }
307
308 }
309
310 }
311