.
1 /*
2 Copyright (C) 2012 2013 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 Math;
16 using Cairo;
17
18 namespace BirdFont {
19
20 public class MoveTool : Tool {
21
22 static bool move_path = false;
23 static bool moved = false;
24 static double last_x = 0;
25 static double last_y = 0;
26
27 static double selection_x = 0;
28 static double selection_y = 0;
29 static bool group_selection= false;
30
31 public static static double selection_box_width = 0;
32 public static static double selection_box_height = 0;
33 public static static double selection_box_center_x = 0;
34 public static static double selection_box_center_y = 0;
35
36 public signal void selection_changed ();
37 public signal void objects_moved ();
38 public signal void objects_deselected ();
39
40 public MoveTool (string n) {
41 base (n, t_("Move paths"));
42
43 select_action.connect((self) => {
44 Glyph glyph = MainWindow.get_current_glyph ();
45 glyph.close_path ();
46 });
47
48 deselect_action.connect((self) => {
49 });
50
51 press_action.connect((self, b, x, y) => {
52 press (b, x, y);
53 });
54
55 release_action.connect((self, b, x, y) => {
56 release (b, x, y);
57 });
58
59 move_action.connect ((self, x, y) => {
60 move (x, y);
61 });
62
63 key_press_action.connect ((self, keyval) => {
64 key_press (keyval);
65 });
66
67 draw_action.connect ((self, cr, glyph) => {
68 draw_actions (cr);
69 });
70 }
71
72 public static void draw_actions (Context cr) {
73 if (group_selection) {
74 draw_selection_box (cr);
75 }
76 }
77
78 public void key_press (uint32 keyval) {
79 Glyph g = MainWindow.get_current_glyph ();
80
81 // delete selected paths
82 if (keyval == Key.DEL || keyval == Key.BACK_SPACE) {
83 foreach (Path p in g.active_paths) {
84 g.path_list.remove (p);
85 g.update_view ();
86 }
87
88 g.active_paths.clear ();
89 }
90
91 if (is_arrow_key (keyval)) {
92 move_selected_paths (keyval);
93 }
94 }
95
96 public void move (int x, int y) {
97 Glyph glyph = MainWindow.get_current_glyph ();
98 double dx = last_x - x;
99 double dy = last_y - y;
100 double p = PenTool.precision;
101
102 if (!move_path) {
103 return;
104 }
105
106 if (move_path && (fabs(dx) > 0 || fabs (dy) > 0)) {
107 moved = true;
108 foreach (Path path in glyph.active_paths) {
109 path.move (Glyph.ivz () * -dx * p, Glyph.ivz () * dy * p);
110 }
111 }
112
113 last_x = x;
114 last_y = y;
115
116 GlyphCanvas.redraw ();
117
118 update_selection_boundaries ();
119
120 if (glyph.active_paths.size > 0) {
121 objects_moved ();
122 }
123
124 BirdFont.get_current_font ().touch ();
125 }
126
127 public void release (int b, int x, int y) {
128 Glyph glyph = MainWindow.get_current_glyph ();
129
130 move_path = false;
131
132 if (GridTool.is_visible () && moved) {
133 tie_paths_to_grid (glyph);
134 } else if (GridTool.has_ttf_grid ()) {
135 foreach (Path p in glyph.active_paths) {
136 tie_path_to_ttf_grid (p);
137 }
138 }
139
140 if (group_selection) {
141 select_group ();
142 }
143
144 group_selection = false;
145 moved = false;
146
147 if (glyph.active_paths.size > 0) {
148 objects_moved ();
149 DrawingTools.resize_tool.signal_objects_rotated ();
150 } else {
151 objects_deselected ();
152 }
153 }
154
155 public static void press (int b, int x, int y) {
156 Glyph glyph = MainWindow.get_current_glyph ();
157
158 glyph.store_undo_state ();
159 group_selection = false;
160
161 if (!glyph.is_over_selected_path (x, y)) {
162 if (!glyph.select_path (x, y)) {
163 glyph.clear_active_paths ();
164 }
165 }
166
167 move_path = true;
168
169 update_selection_boundaries ();
170
171 last_x = x;
172 last_y = y;
173
174 if (glyph.active_paths.size == 0) {
175 group_selection = true;
176 selection_x = x;
177 selection_y = y;
178 }
179
180 update_boundaries_for_selection ();
181 }
182
183
184 void select_group () {
185 double x1 = Glyph.path_coordinate_x (Math.fmin (selection_x, last_x));
186 double y1 = Glyph.path_coordinate_y (Math.fmin (selection_y, last_y));
187 double x2 = Glyph.path_coordinate_x (Math.fmax (selection_x, last_x));
188 double y2 = Glyph.path_coordinate_y (Math.fmax (selection_y, last_y));
189 Glyph glyph = MainWindow.get_current_glyph ();
190
191 glyph.clear_active_paths ();
192
193 foreach (Path p in glyph.path_list) {
194 if (p.xmin > x1 && p.xmax < x2 && p.ymin < y1 && p.ymax > y2) {
195 if (p.points.size > 0) {
196 glyph.add_active_path (p);
197 }
198 }
199 }
200
201 selection_changed ();
202 }
203
204 public static void update_selection_boundaries () {
205 get_selection_box_boundaries (out selection_box_center_x,
206 out selection_box_center_y, out selection_box_width,
207 out selection_box_height);
208 }
209
210 public void move_to_baseline () {
211 Glyph glyph = MainWindow.get_current_glyph ();
212 Font font = BirdFont.get_current_font ();
213 double x, y, w, h;
214
215 get_selection_box_boundaries (out x, out y, out w, out h);
216
217 foreach (Path path in glyph.active_paths) {
218 path.move (glyph.left_limit - x + w / 2, font.base_line - y + h / 2);
219 }
220
221 update_selection_boundaries ();
222 objects_moved ();
223 GlyphCanvas.redraw ();
224 }
225
226 static void draw_selection_box (Context cr) {
227 double x = Math.fmin (selection_x, last_x);
228 double y = Math.fmin (selection_y, last_y);
229
230 double w = Math.fabs (selection_x - last_x);
231 double h = Math.fabs (selection_y - last_y);
232
233 cr.save ();
234
235 Theme.color (cr, "Background 4");
236 cr.set_line_width (2);
237 cr.rectangle (x, y, w, h);
238 cr.stroke ();
239
240 cr.restore ();
241 }
242
243 public static void get_selection_box_boundaries (out double x, out double y, out double w, out double h) {
244 double px, py, px2, py2;
245 Glyph glyph = MainWindow.get_current_glyph ();
246
247 px = 10000;
248 py = 10000;
249 px2 = -10000;
250 py2 = -10000;
251
252 foreach (Path p in glyph.active_paths) {
253 if (px > p.xmin) {
254 px = p.xmin;
255 }
256
257 if (py > p.ymin) {
258 py = p.ymin;
259 }
260
261 if (px2 < p.xmax) {
262 px2 = p.xmax;
263 }
264
265 if (py2 < p.ymax) {
266 py2 = p.ymax;
267 }
268 }
269
270 w = px2 - px;
271 h = py2 - py;
272 x = px + (w / 2);
273 y = py + (h / 2);
274 }
275
276 void move_selected_paths (uint key) {
277 Glyph glyph = MainWindow.get_current_glyph ();
278 double x, y;
279
280 x = 0;
281 y = 0;
282
283 switch (key) {
284 case Key.UP:
285 y = 1;
286 break;
287 case Key.DOWN:
288 y = -1;
289 break;
290 case Key.LEFT:
291 x = -1;
292 break;
293 case Key.RIGHT:
294 x = 1;
295 break;
296 default:
297 break;
298 }
299
300 foreach (Path path in glyph.active_paths) {
301 path.move (x * Glyph.ivz (), y * Glyph.ivz ());
302 }
303
304 update_selection_boundaries ();
305 objects_moved ();
306 glyph.redraw_area (0, 0, glyph.allocation.width, glyph.allocation.height);
307 }
308
309 static void tie_path_to_ttf_grid (Path p) {
310 double sx, sy, qx, qy;
311
312 sx = p.xmax;
313 sy = p.ymax;
314 qx = p.xmin;
315 qy = p.ymin;
316
317 GridTool.ttf_grid_coordinate (ref sx, ref sy);
318 GridTool.ttf_grid_coordinate (ref qx, ref qy);
319
320 if (Math.fabs (qy - p.ymin) < Math.fabs (sy - p.ymax)) {
321 p.move (0, qy - p.ymin);
322 } else {
323 p.move (0, sy - p.ymax);
324 }
325
326 if (Math.fabs (qx - p.xmin) < Math.fabs (sx - p.xmax)) {
327 p.move (qx - p.xmin, 0);
328 } else {
329 p.move (sx - p.xmax, 0);
330 }
331 }
332
333 static void tie_paths_to_grid (Glyph g) {
334 double sx, sy, qx, qy;
335 double dx_min, dx_max, dy_min, dy_max;;
336 double maxx, maxy, minx, miny;
337
338 update_selection_boundaries ();
339
340 // tie to grid
341 maxx = selection_box_center_x + selection_box_width / 2;
342 maxy = selection_box_center_y + selection_box_height / 2;
343 minx = selection_box_center_x - selection_box_width / 2;
344 miny = selection_box_center_y - selection_box_height / 2;
345
346 sx = maxx;
347 sy = maxy;
348 qx = minx;
349 qy = miny;
350
351 GridTool.tie_coordinate (ref sx, ref sy);
352 GridTool.tie_coordinate (ref qx, ref qy);
353
354 dy_min = Math.fabs (qy - miny);
355 dy_max = Math.fabs (sy - maxy);
356 dx_min = Math.fabs (qx - minx);
357 dx_max = Math.fabs (sx - maxx);
358
359 foreach (Path p in g.active_paths) {
360 if (dy_min < dy_max) {
361 p.move (0, qy - miny);
362 } else {
363 p.move (0, sy - maxy);
364 }
365
366 if (dx_min < dx_max) {
367 p.move (qx - minx, 0);
368 } else {
369 p.move (sx - maxx, 0);
370 }
371 }
372
373 update_selection_boundaries ();
374 }
375
376 public static void update_boundaries_for_selection () {
377 Glyph glyph = MainWindow.get_current_glyph ();
378 foreach (Path p in glyph.active_paths) {
379 p.update_region_boundaries ();
380 }
381 }
382
383 public static void flip_vertical () {
384 flip (true);
385 }
386
387 public static void flip_horizontal () {
388 flip (false);
389 }
390
391 public static void flip (bool vertical) {
392 double xc, yc, xc2, yc2, w, h;
393 double dx, dy;
394 Glyph glyph = MainWindow.get_current_glyph ();
395
396 update_selection_boundaries ();
397
398 xc = selection_box_center_x;
399 yc = selection_box_center_y;
400
401 foreach (Path p in glyph.active_paths) {
402 if (vertical) {
403 p.flip_vertical ();
404 } else {
405 p.flip_horizontal ();
406 }
407
408 p.reverse ();
409 }
410
411 get_selection_box_boundaries (out xc2, out yc2, out w, out h);
412
413 dx = -(xc2 - xc);
414 dy = -(yc2 - yc);
415
416 foreach (Path p in glyph.active_paths) {
417 p.move (dx, dy);
418 }
419
420 update_selection_boundaries ();
421
422 BirdFont.get_current_font ().touch ();
423 }
424
425 public void select_all_paths () {
426 Glyph g = MainWindow.get_current_glyph ();
427
428 g.clear_active_paths ();
429
430 foreach (Path p in g.path_list) {
431 if (p.points.size > 0) {
432 g.add_active_path (p);
433 }
434 }
435
436 g.update_view ();
437
438 update_selection_boundaries ();
439 objects_moved ();
440 }
441 }
442
443 }
444