The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

MoveTool.vala in libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/MoveTool.vala.
Merge branch 'master' of github.com:johanmattssonm/birdfont
1 /* 2 Copyright (C) 2012 2013 2015 2016 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 using SvgBird; 18 19 namespace BirdFont { 20 21 public class MoveTool : Tool { 22 23 static bool move_path = false; 24 static bool moved = false; 25 static double last_x = 0; 26 static double last_y = 0; 27 28 static double selection_x = 0; 29 static double selection_y = 0; 30 static bool group_selection= false; 31 32 public static double selection_box_width = 0; 33 public static double selection_box_height = 0; 34 public static double selection_box_center_x = 0; 35 public static double selection_box_center_y = 0; 36 public static double selection_box_left = 0; 37 public static double selection_box_top = 0; 38 39 public signal void selection_changed (); 40 public signal void objects_moved (); 41 public signal void objects_deselected (); 42 43 public MoveTool (string name) { 44 base (name, t_("Move paths")); 45 46 selection_changed.connect (() => { 47 update_selection_boundaries (); 48 redraw(); 49 }); 50 51 objects_deselected.connect (() => { 52 update_selection_boundaries (); 53 redraw(); 54 }); 55 56 select_action.connect((self) => { 57 MainWindow.get_current_glyph ().close_path (); 58 }); 59 60 deselect_action.connect((self) => { 61 }); 62 63 press_action.connect((self, b, x, y) => { 64 press (b, x, y); 65 }); 66 67 release_action.connect((self, b, x, y) => { 68 release (b, x, y); 69 }); 70 71 move_action.connect ((self, x, y) => { 72 move (x, y); 73 }); 74 75 key_press_action.connect ((self, keyval) => { 76 key_down (keyval); 77 }); 78 79 draw_action.connect ((self, cr, glyph) => { 80 draw_actions (cr); 81 }); 82 } 83 84 public static void draw_actions (Context cr) { 85 if (group_selection) { 86 draw_selection_box (cr); 87 } 88 } 89 90 public void key_down (uint32 keyval) { 91 Glyph g = MainWindow.get_current_glyph (); 92 93 // delete selected paths 94 if (keyval == Key.DEL || keyval == Key.BACK_SPACE) { 95 96 if (g.active_paths.size > 0) { 97 g.store_undo_state (); 98 } 99 100 foreach (SvgBird.Object p in g.active_paths) { 101 g.layers.remove (p); 102 g.update_view (); 103 } 104 105 g.active_paths.clear (); 106 } 107 108 if (is_arrow_key (keyval)) { 109 move_selected_paths (keyval); 110 } 111 } 112 113 public void move (int x, int y) { 114 Glyph glyph = MainWindow.get_current_glyph (); 115 double dx = Glyph.path_coordinate_x (last_x) - Glyph.path_coordinate_x (x); 116 double dy = Glyph.path_coordinate_y (last_y) - Glyph.path_coordinate_y (y); 117 double delta_x, delta_y; 118 119 if (!move_path) { 120 return; 121 } 122 123 if (move_path && (fabs(dx) > 0 || fabs (dy) > 0)) { 124 moved = true; 125 126 delta_x = -dx; 127 delta_y = -dy; 128 129 foreach (SvgBird.Object object in glyph.active_paths) { 130 object.move (delta_x, delta_y); 131 } 132 } 133 134 last_x = x; 135 last_y = y; 136 137 update_selection_boundaries (); 138 139 if (glyph.active_paths.size > 0) { 140 objects_moved (); 141 } 142 143 BirdFont.get_current_font ().touch (); 144 145 GlyphCanvas.redraw (); 146 PenTool.reset_stroke (); 147 } 148 149 public void release (int b, int x, int y) { 150 Glyph glyph = MainWindow.get_current_glyph (); 151 152 move_path = false; 153 154 if (GridTool.is_visible () && moved) { 155 tie_paths_to_grid (glyph); 156 } else if (GridTool.has_ttf_grid ()) { 157 foreach (SvgBird.Object p in glyph.active_paths) { 158 tie_path_to_ttf_grid (p); 159 } 160 } 161 162 if (group_selection) { 163 select_group (); 164 } 165 166 group_selection = false; 167 moved = false; 168 169 if (glyph.active_paths.size > 0) { 170 selection_changed (); 171 objects_moved (); 172 173 foreach (SvgBird.Object o in glyph.active_paths) { 174 if (o is PathObject) { 175 PathObject path = (PathObject) o; 176 path.get_path ().create_full_stroke (); 177 } 178 } 179 } else { 180 objects_deselected (); 181 } 182 } 183 184 public void press (int b, int x, int y) { 185 Glyph glyph = MainWindow.get_current_glyph (); 186 SvgBird.Object object; 187 bool selected = false; 188 SvgBird.Object? o; 189 190 glyph.store_undo_state (); 191 double px = Glyph.path_coordinate_x (x); 192 double py = Glyph.path_coordinate_y (y); 193 o = glyph.get_object_at (px, py); 194 195 if (o != null) { 196 object = (!) o; 197 selected = glyph.active_paths_contains (object); 198 199 if (!selected && !KeyBindings.has_shift ()) { 200 glyph.clear_active_paths (); 201 } 202 203 if (selected && KeyBindings.has_shift ()) { 204 glyph.active_paths.remove (object); 205 } else { 206 glyph.add_active_object (object); 207 } 208 } else if (!KeyBindings.has_shift ()) { 209 glyph.clear_active_paths (); 210 } 211 212 update_selection_boundaries (); 213 214 move_path = true; 215 216 last_x = x; 217 last_y = y; 218 219 if (glyph.active_paths.size == 0) { 220 group_selection = true; 221 selection_x = x; 222 selection_y = y; 223 } 224 225 update_boundaries_for_selection (); 226 selection_changed (); 227 GlyphCanvas.redraw (); 228 } 229 230 void select_group () { 231 double x1 = Glyph.path_coordinate_x (Math.fmin (selection_x, last_x)); 232 double y1 = Glyph.path_coordinate_y (Math.fmin (selection_y, last_y)); 233 double x2 = Glyph.path_coordinate_x (Math.fmax (selection_x, last_x)); 234 double y2 = Glyph.path_coordinate_y (Math.fmax (selection_y, last_y)); 235 Glyph glyph = MainWindow.get_current_glyph (); 236 237 glyph.clear_active_paths (); 238 239 foreach (SvgBird.Object p in glyph.get_objects_in_current_layer ()) { 240 if (p.xmin > x1 && p.xmax < x2 && p.ymin < y1 && p.ymax > y2) { 241 if (!p.is_empty ()) { 242 glyph.add_active_object (p); 243 } 244 } 245 } 246 247 selection_changed (); 248 } 249 250 public static void update_selection_boundaries () { 251 get_selection_box_boundaries (out selection_box_center_x, 252 out selection_box_center_y, out selection_box_width, 253 out selection_box_height, out selection_box_left, 254 out selection_box_top); 255 } 256 257 public void move_to_baseline () { 258 Glyph glyph = MainWindow.get_current_glyph (); 259 Font font = BirdFont.get_current_font (); 260 double x, y, w, h, l, t; 261 262 get_selection_box_boundaries (out x, out y, out w, out h, out l, out t); 263 264 foreach (SvgBird.Object path in glyph.active_paths) { 265 path.move (glyph.left_limit - x + w / 2, font.base_line - y + h / 2); 266 } 267 268 update_selection_boundaries (); 269 objects_moved (); 270 GlyphCanvas.redraw (); 271 } 272 273 public static void get_selection_box_boundaries (out double x, out double y, out double w, out double h, 274 out double left, out double top) { 275 276 double px, py, px2, py2; 277 Glyph glyph = MainWindow.get_current_glyph (); 278 279 px = 10000; 280 py = 10000; 281 px2 = -10000; 282 py2 = -10000; 283 top = 10000; 284 left = 10000; 285 286 foreach (SvgBird.Object o in glyph.active_paths) { 287 if (top > o.top) { 288 top = o.top; 289 } 290 291 if (left > o.left) { 292 left = o.left; 293 } 294 295 if (px > o.xmin) { 296 px = o.xmin; 297 } 298 299 if (py > o.ymin) { 300 py = o.ymin; 301 } 302 303 if (px2 < o.xmax) { 304 px2 = o.xmax; 305 } 306 307 if (py2 < o.ymax) { 308 py2 = o.ymax; 309 } 310 } 311 312 w = px2 - px; 313 h = py2 - py; 314 x = px + (w / 2); 315 y = py + (h / 2); 316 } 317 318 void move_selected_paths (uint key) { 319 Glyph glyph = MainWindow.get_current_glyph (); 320 double x, y; 321 322 x = 0; 323 y = 0; 324 325 switch (key) { 326 case Key.UP: 327 y = 1; 328 break; 329 case Key.DOWN: 330 y = -1; 331 break; 332 case Key.LEFT: 333 x = -1; 334 break; 335 case Key.RIGHT: 336 x = 1; 337 break; 338 default: 339 break; 340 } 341 342 foreach (SvgBird.Object path in glyph.active_paths) { 343 path.move (x * Glyph.ivz (), y * Glyph.ivz ()); 344 } 345 346 BirdFont.get_current_font ().touch (); 347 PenTool.reset_stroke (); 348 update_selection_boundaries (); 349 objects_moved (); 350 GlyphCanvas.redraw (); 351 } 352 353 static void tie_path_to_ttf_grid (SvgBird.Object p) { 354 double sx, sy, qx, qy; 355 356 sx = p.xmax; 357 sy = p.ymax; 358 qx = p.xmin; 359 qy = p.ymin; 360 361 GridTool.ttf_grid_coordinate (ref sx, ref sy); 362 GridTool.ttf_grid_coordinate (ref qx, ref qy); 363 364 if (Math.fabs (qy - p.ymin) < Math.fabs (sy - p.ymax)) { 365 p.move (0, qy - p.ymin); 366 } else { 367 p.move (0, sy - p.ymax); 368 } 369 370 if (Math.fabs (qx - p.xmin) < Math.fabs (sx - p.xmax)) { 371 p.move (qx - p.xmin, 0); 372 } else { 373 p.move (sx - p.xmax, 0); 374 } 375 } 376 377 static void tie_paths_to_grid (Glyph g) { 378 double sx, sy, qx, qy; 379 double dx_min, dx_max, dy_min, dy_max;; 380 double maxx, maxy, minx, miny; 381 382 update_selection_boundaries (); 383 384 // tie to grid 385 maxx = selection_box_center_x + selection_box_width / 2; 386 maxy = selection_box_center_y + selection_box_height / 2; 387 minx = selection_box_center_x - selection_box_width / 2; 388 miny = selection_box_center_y - selection_box_height / 2; 389 390 sx = maxx; 391 sy = maxy; 392 qx = minx; 393 qy = miny; 394 395 GridTool.tie_coordinate (ref sx, ref sy); 396 GridTool.tie_coordinate (ref qx, ref qy); 397 398 dy_min = Math.fabs (qy - miny); 399 dy_max = Math.fabs (sy - maxy); 400 dx_min = Math.fabs (qx - minx); 401 dx_max = Math.fabs (sx - maxx); 402 403 foreach (SvgBird.Object p in g.active_paths) { 404 if (dy_min < dy_max) { 405 p.move (0, qy - miny); 406 } else { 407 p.move (0, sy - maxy); 408 } 409 410 if (dx_min < dx_max) { 411 p.move (qx - minx, 0); 412 } else { 413 p.move (sx - maxx, 0); 414 } 415 } 416 417 update_selection_boundaries (); 418 } 419 420 public static void update_boundaries_for_selection () { 421 Glyph glyph = MainWindow.get_current_glyph (); 422 glyph.layers.update_boundaries_for_object (); 423 } 424 425 public static void flip_vertical () { 426 DrawingTools.move_tool.flip (true); 427 } 428 429 public static void flip_horizontal () { 430 DrawingTools.move_tool.flip (false); 431 } 432 433 public void flip (bool vertical) { 434 Glyph glyph = MainWindow.get_current_glyph (); 435 436 update_selection_boundaries (); 437 438 foreach (SvgBird.Object object in glyph.active_paths) { 439 Matrix matrix = Matrix.identity (); 440 double x = 0; 441 double y = 0; 442 443 if (object is EmbeddedSvg) { 444 EmbeddedSvg svg = (EmbeddedSvg) object; 445 x = selection_box_left - svg.x + selection_box_width / 2; 446 y = selection_box_top + svg.y + selection_box_height / 2; 447 } else { 448 x = selection_box_center_x; 449 y = selection_box_center_y; 450 } 451 452 matrix.translate (x, y); 453 454 if (vertical) { 455 matrix.scale (1, -1); 456 } else { 457 matrix.scale (-1, 1); 458 } 459 460 matrix.translate (-x, -y); 461 462 SvgTransform transform = new SvgTransform.for_matrix (matrix); 463 object.transforms.add (transform); 464 object.transforms.collapse_transforms (); 465 466 if (object is PathObject) { 467 Path path = ((PathObject) object).get_path (); 468 Matrix m = object.transforms.get_matrix (); 469 object.transforms.clear (); 470 path.transform (m); 471 path.reverse (); 472 object.move (0, 0); 473 } 474 } 475 476 update_selection_boundaries (); 477 objects_moved (); 478 479 PenTool.reset_stroke (); 480 BirdFont.get_current_font ().touch (); 481 } 482 483 public void select_all_paths () { 484 Glyph g = MainWindow.get_current_glyph (); 485 486 g.clear_active_paths (); 487 foreach (SvgBird.Object p in g.get_objects_in_current_layer ()) { 488 if (!p.is_empty ()) { 489 g.add_active_object (p); 490 } 491 } 492 493 g.update_view (); 494 495 update_selection_boundaries (); 496 objects_moved (); 497 498 ResizeTool.update_selection_box (); 499 } 500 501 static void draw_selection_box (Context cr) { 502 double x = Math.fmin (selection_x, last_x); 503 double y = Math.fmin (selection_y, last_y); 504 505 double w = Math.fabs (selection_x - last_x); 506 double h = Math.fabs (selection_y - last_y); 507 508 cr.save (); 509 Theme.color (cr, "Foreground 1"); 510 cr.set_line_width (2); 511 cr.rectangle (x, y, w, h); 512 cr.stroke (); 513 cr.restore (); 514 } 515 516 public void convert_svg_to_monochrome () { 517 ObjectGroup embedded_paths; 518 Glyph glyph = MainWindow.get_current_glyph (); 519 520 embedded_paths = new ObjectGroup (); 521 522 foreach (SvgBird.Object object in glyph.active_paths) { 523 if (object is EmbeddedSvg) { 524 embedded_paths.add (object); 525 } 526 } 527 528 convert_objects_to_monochrome_glyph (glyph, embedded_paths); 529 530 glyph.add_help_lines (); 531 glyph.fix_curve_orientation (); 532 533 GlyphCanvas.redraw (); 534 } 535 536 public void convert_glyph_to_monochrome (Glyph glyph) { 537 ObjectGroup embedded_paths; 538 539 embedded_paths = new ObjectGroup (); 540 541 foreach (SvgBird.Object object in glyph.get_visible_objects ()) { 542 if (object is EmbeddedSvg) { 543 embedded_paths.add (object); 544 } 545 } 546 547 convert_objects_to_monochrome_glyph (glyph, embedded_paths); 548 549 glyph.add_help_lines (); 550 glyph.fix_curve_orientation (); 551 } 552 553 public void convert_objects_to_monochrome_glyph (Glyph glyph, ObjectGroup embedded_paths) { 554 Font font = BirdFont.get_current_font (); 555 556 foreach (SvgBird.Object object in embedded_paths) { 557 if (object is EmbeddedSvg) { 558 EmbeddedSvg svg = (EmbeddedSvg) object; 559 glyph.clear_active_paths (); 560 string transformed_svg_data = svg.get_transformed_svg_data (); 561 SvgParser svg_parser = new SvgParser (); 562 PathList path_list = svg_parser.import_svg_data_in_glyph (transformed_svg_data, glyph); 563 glyph.delete_object (svg); 564 565 foreach (Path path in path_list.paths) { 566 path.move (svg.x - glyph.left_limit, svg.y - font.top_limit); 567 } 568 } 569 } 570 } 571 } 572 573 } 574