.
1 /*
2 Copyright (C) 2012 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
17 namespace BirdFont {
18
19 /** Help line */
20 public class Line : GLib.Object {
21 public static const bool VERTICAL = true;
22 public static const bool HORIZONTAL = false;
23
24 public bool dashed { get; set; }
25
26 string label;
27 bool vertical;
28 string metrics;
29
30 public double pos;
31
32 bool active = false;
33 bool move = false;
34
35 public signal void queue_draw_area (int x, int y, int w, int h);
36 public signal void position_updated (double pos);
37
38 double r;
39 double g;
40 double b;
41 double a;
42
43 bool visible = true;
44 bool moveable = true;
45
46 public Line (string label = "No label set", double position = 10, bool vertical = false) {
47 this.label = label;
48 this.vertical = vertical;
49 this.pos = position;
50
51 dashed = false;
52 metrics = "";
53
54 r = 0.7;
55 g = 0.7;
56 b = 0.8;
57 a = 1;
58 }
59
60 public Line copy () {
61 Line l = new Line (label, pos, vertical);
62
63 l.r = r;
64 l.g = g;
65 l.b = b;
66 l.a = a;
67
68 l.visible = visible;
69 l.dashed = dashed;
70
71 return l;
72 }
73
74 public void set_metrics (double m) {
75 string t = @"$m";
76 string s = "";
77
78 int i;
79 unichar c;
80
81 i = 0;
82 while (t.get_next_char (ref i, out c)) {
83 s = s + (!) c.to_string ();
84
85 if (i >= 5) {
86 break;
87 }
88 }
89
90 metrics = s;
91 }
92
93 public void set_visible (bool v) {
94 visible = v;
95 }
96
97 public bool is_visible () {
98 return visible;
99 }
100
101 public void set_moveable (bool m) {
102 moveable = m;
103 }
104
105 public void set_color (double r, double g, double b, double a) {
106 this.r = r;
107 this.g = g;
108 this.b = b;
109 this.a = a;
110 }
111
112 public bool is_moving () {
113 return move;
114 }
115
116 public bool set_move (bool moving) {
117 bool r = move;
118 move = moving;
119 return (r == moving);
120 }
121
122 public bool button_press (uint button) {
123 Glyph g;
124 TextListener listener;
125 string position;
126
127 if (get_active ()) {
128 if (button == 3 || KeyBindings.has_shift ()) {
129 move = false;
130 position = @"$pos";
131
132 listener = new TextListener (t_("Position"), position, t_("Move"));
133
134 listener.signal_text_input.connect ((text) => {
135 string submitted_value;
136 double parsed_value;
137
138 submitted_value = text.replace (",", ".");
139 parsed_value = double.parse (submitted_value);
140
141 pos = parsed_value;
142
143 position_updated (parsed_value);
144 GlyphCanvas.redraw ();
145 });
146
147 listener.signal_submit.connect (() => {
148 MainWindow.native_window.hide_text_input ();
149 });
150
151 MainWindow.native_window.set_text_listener (listener);
152 } else {
153 move = true;
154 }
155
156 g = MainWindow.get_current_glyph ();
157 g.store_undo_state ();
158 } else {
159 move = false;
160 active = false;
161 }
162
163 return move;
164 }
165
166 void redraw_line () {
167 double p;
168 Glyph g = MainWindow.get_current_glyph ();
169
170 if (vertical) {
171 p = Glyph.reverse_path_coordinate_x (pos);
172 queue_draw_area ((int)p - 100, 0, 200, g.allocation.height);
173 } else {
174 p = Glyph.reverse_path_coordinate_y (pos);
175 queue_draw_area (0, (int)p - 100, g.allocation.width, 300);
176 }
177 }
178
179 public void move_line_to (int x, int y, WidgetAllocation allocation) {
180 set_move (true);
181 event_move_to (x, y, allocation);
182 }
183
184 public bool event_move_to (int x, int y, WidgetAllocation allocation) {
185 double p, c;
186 bool a = false;
187 Glyph g = MainWindow.get_current_glyph ();
188 double ivz = 1/g.view_zoom;
189 double margin = 10;
190 double none = 0;
191
192 if (!moveable) {
193 return false;
194 }
195
196 if (is_vertical ()) { // over line handle (y)
197 if (y > g.allocation.height - 10) {
198 p = pos;
199 c = Glyph.path_coordinate_x (x);
200 a = (p - margin * ivz <= c <= p + margin * ivz);
201 }
202
203 if (a != get_active ()) {
204 redraw_line ();
205 }
206
207 set_active (a);
208
209 } else { // over line handle (x)
210 if (x > g.allocation.width - 10) {
211 p = pos;
212 c = Glyph.path_coordinate_y (y);
213 a = (p - margin * ivz <= c <= p + margin * ivz);
214 }
215
216 if (a != get_active ()) {
217 redraw_line ();
218 }
219
220 set_active (a);
221 }
222
223 // move the line
224 if (move) {
225 double np = pos;
226 redraw_line (); // clear old position
227
228 if (is_vertical ()) {
229 pos = Glyph.path_coordinate_x (x);
230
231 if (GridTool.is_visible ()) {
232 GridTool.tie_coordinate (ref pos, ref none);
233 }
234 redraw_line (); // draw at new position
235 } else {
236 pos = Glyph.path_coordinate_y (y);
237
238 if (GridTool.is_visible ()) {
239 GridTool.tie_coordinate (ref none, ref pos);
240 }
241 redraw_line ();
242 }
243
244 if (Math.fabs (np - pos) > 10) {
245 queue_draw_area (0, 0, g.allocation.width, g.allocation.height);
246 }
247
248 position_updated (pos); // signal update
249
250 BirdFont.get_current_font ().touch ();
251 }
252
253 if (GridTool.is_visible ()) {
254 GridTool.update_lines ();
255 }
256
257 return move;
258 }
259
260 public bool get_active () {
261 return active;
262 }
263
264 public void set_active (bool active) {
265 this.active = active;
266 }
267
268 public string get_label () {
269 return label;
270 }
271
272 public bool is_vertical () {
273 return vertical;
274 }
275
276 public int get_position_pixel () {
277 if (is_vertical ()) {
278 return Glyph.reverse_path_coordinate_x (pos);
279 }
280
281 return Glyph.reverse_path_coordinate_y (pos) ;
282 }
283
284 public double get_pos () {
285 return pos;
286 }
287
288 public void draw (Context cr, WidgetAllocation allocation) {
289 Glyph g = MainWindow.get_current_glyph ();
290 double p, h, w;
291 double size = (active) ? 8 : 5;
292 Text glyph_metrics;
293
294 if (!visible) {
295 return;
296 }
297
298 cr.save ();
299 cr.set_line_width (1);
300
301 if (dashed) {
302 cr.set_dash ({20, 20}, 0);
303 }
304
305 if (active) {
306 cr.set_source_rgba (0, 0, 0.3, 1);
307 } else {
308 cr.set_source_rgba (r, this.g, b, a);
309 }
310
311 // Line
312 if (is_vertical ()) {
313 p = Glyph.reverse_path_coordinate_x (pos);
314 h = g.allocation.height;
315
316 cr.move_to (p, 0);
317 cr.line_to (p, h);
318 cr.stroke ();
319
320 cr.scale (1, 1);
321
322 if (moveable) {
323 cr.new_path ();
324 cr.move_to (p - size, h);
325 cr.line_to (p, h - size);
326 cr.line_to (p + size, h);
327 cr.close_path();
328 cr.fill ();
329
330 if (get_active ()) {
331 glyph_metrics = new Text (metrics, 17);
332 glyph_metrics.set_source_rgba (72 / 255.0, 72 / 255.0, 72 / 255.0, 1);
333 glyph_metrics.widget_x = p + 10;
334 glyph_metrics.widget_y = h - 25;
335 glyph_metrics.draw (cr);
336 }
337 }
338
339 } else {
340 p = Glyph.reverse_path_coordinate_y (pos);
341 w = g.allocation.width;
342
343 cr.move_to (0, p);
344 cr.line_to (w, p);
345 cr.stroke ();
346
347 if (moveable) {
348 cr.new_path ();
349 cr.move_to (w, p - size);
350 cr.line_to (w - size, p);
351 cr.line_to (w, p + size);
352 cr.close_path();
353 cr.fill ();
354 }
355 }
356
357 // Label
358 if (get_active ()) {
359 if (is_vertical ()) {
360 h = g.allocation.height;
361 cr.move_to (p + 8 , h - 30);
362 } else {
363 w = g.allocation.width;
364 cr.move_to (w - 70, p + 15);
365 }
366
367 cr.set_font_size (12);
368 cr.select_font_face ("Cantarell", FontSlant.NORMAL, FontWeight.BOLD);
369
370 cr.show_text (get_label ());
371 cr.stroke ();
372 }
373
374 cr.restore ();
375 }
376
377
378
379 }
380
381 }
382