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.
Move embedded SVG files
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 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 selection_changed.connect (() => { 44 update_selection_boundaries (); 45 redraw(); 46 }); 47 48 objects_deselected.connect (() => { 49 update_selection_boundaries (); 50 redraw(); 51 }); 52 53 select_action.connect((self) => { 54 MainWindow.get_current_glyph ().close_path (); 55 }); 56 57 deselect_action.connect((self) => { 58 }); 59 60 press_action.connect((self, b, x, y) => { 61 press (b, x, y); 62 }); 63 64 release_action.connect((self, b, x, y) => { 65 release (b, x, y); 66 }); 67 68 move_action.connect ((self, x, y) => { 69 move (x, y); 70 }); 71 72 key_press_action.connect ((self, keyval) => { 73 key_down (keyval); 74 }); 75 76 draw_action.connect ((self, cr, glyph) => { 77 draw_actions (cr); 78 }); 79 } 80 81 public static void draw_actions (Context cr) { 82 if (group_selection) { 83 draw_selection_box (cr); 84 } 85 } 86 87 public void key_down (uint32 keyval) { 88 Glyph g = MainWindow.get_current_glyph (); 89 90 // delete selected paths 91 if (keyval == Key.DEL || keyval == Key.BACK_SPACE) { 92 93 if (g.active_paths.size > 0) { 94 g.store_undo_state (); 95 } 96 97 foreach (Object p in g.active_paths) { 98 if (p is PathObject) { 99 g.layers.remove_path (((PathObject) p).get_path ()); 100 } else { 101 g.layers.remove (p); 102 } 103 104 g.update_view (); 105 } 106 107 g.active_paths.clear (); 108 } 109 110 if (is_arrow_key (keyval)) { 111 move_selected_paths (keyval); 112 } 113 } 114 115 public void move (int x, int y) { 116 Glyph glyph = MainWindow.get_current_glyph (); 117 double dx = Glyph.path_coordinate_x (last_x) - Glyph.path_coordinate_x (x); 118 double dy = Glyph.path_coordinate_y (last_y) - Glyph.path_coordinate_y (y); 119 double delta_x, delta_y; 120 121 if (!move_path) { 122 return; 123 } 124 125 if (move_path && (fabs(dx) > 0 || fabs (dy) > 0)) { 126 moved = true; 127 128 delta_x = -dx; 129 delta_y = -dy; 130 131 foreach (Layer group in glyph.selected_groups) { 132 if (group.gradient != null) { 133 Gradient g = (!) group.gradient; 134 g.x1 += delta_x; 135 g.x2 += delta_x; 136 g.y1 += delta_y; 137 g.y2 += delta_y; 138 } 139 } 140 141 foreach (Object object in glyph.active_paths) { 142 object.move (delta_x, delta_y); 143 } 144 } 145 146 last_x = x; 147 last_y = y; 148 149 update_selection_boundaries (); 150 151 if (glyph.active_paths.size > 0) { 152 objects_moved (); 153 } 154 155 BirdFont.get_current_font ().touch (); 156 157 GlyphCanvas.redraw (); 158 PenTool.reset_stroke (); 159 } 160 161 public void release (int b, int x, int y) { 162 Glyph glyph = MainWindow.get_current_glyph (); 163 164 move_path = false; 165 166 if (GridTool.is_visible () && moved) { 167 tie_paths_to_grid (glyph); 168 } else if (GridTool.has_ttf_grid ()) { 169 foreach (Object p in glyph.active_paths) { 170 tie_path_to_ttf_grid (p); 171 } 172 } 173 174 if (group_selection) { 175 select_group (); 176 } 177 178 group_selection = false; 179 moved = false; 180 181 if (glyph.active_paths.size > 0) { 182 selection_changed (); 183 objects_moved (); 184 DrawingTools.resize_tool.signal_objects_rotated (); 185 186 foreach (Object o in glyph.active_paths) { 187 if (o is PathObject) { 188 PathObject path = (PathObject) o; 189 path.get_path ().create_full_stroke (); 190 } 191 } 192 } else { 193 objects_deselected (); 194 } 195 } 196 197 public void press (int b, int x, int y) { 198 Glyph glyph = MainWindow.get_current_glyph (); 199 Object object; 200 bool selected = false; 201 Layer? group; 202 Layer g; 203 204 glyph.store_undo_state (); 205 group_selection = false; 206 207 group = glyph.get_path_at (x, y); 208 209 if (group != null) { 210 g = (!) group; 211 return_if_fail (g.objects.objects.size > 0); 212 object = g.objects.objects.get (0); 213 selected = glyph.active_paths_contains (object); 214 215 if (!selected && !KeyBindings.has_shift ()) { 216 glyph.clear_active_paths (); 217 } 218 219 foreach (Object lp in g.objects) { 220 if (selected && KeyBindings.has_shift ()) { 221 glyph.selected_groups.remove ((!) group); 222 glyph.active_paths.remove (lp); 223 } else { 224 glyph.add_active_object ((!) group, lp); 225 } 226 } 227 } else if (!KeyBindings.has_shift ()) { 228 glyph.clear_active_paths (); 229 } 230 231 update_selection_boundaries (); 232 233 move_path = true; 234 235 last_x = x; 236 last_y = y; 237 238 if (glyph.active_paths.size == 0) { 239 group_selection = true; 240 selection_x = x; 241 selection_y = y; 242 } 243 244 update_boundaries_for_selection (); 245 selection_changed (); 246 GlyphCanvas.redraw (); 247 } 248 249 void select_group () { 250 double x1 = Glyph.path_coordinate_x (Math.fmin (selection_x, last_x)); 251 double y1 = Glyph.path_coordinate_y (Math.fmin (selection_y, last_y)); 252 double x2 = Glyph.path_coordinate_x (Math.fmax (selection_x, last_x)); 253 double y2 = Glyph.path_coordinate_y (Math.fmax (selection_y, last_y)); 254 Glyph glyph = MainWindow.get_current_glyph (); 255 256 glyph.clear_active_paths (); 257 258 foreach (Object p in glyph.get_objects_in_current_layer ()) { 259 if (p.xmin > x1 && p.xmax < x2 && p.ymin < y1 && p.ymax > y2) { 260 if (!p.is_empty ()) { 261 glyph.add_active_object (null, p); 262 } 263 } 264 } 265 266 selection_changed (); 267 } 268 269 public static void update_selection_boundaries () { 270 get_selection_box_boundaries (out selection_box_center_x, 271 out selection_box_center_y, out selection_box_width, 272 out selection_box_height); 273 } 274 275 public void move_to_baseline () { 276 Glyph glyph = MainWindow.get_current_glyph (); 277 Font font = BirdFont.get_current_font (); 278 double x, y, w, h; 279 280 get_selection_box_boundaries (out x, out y, out w, out h); 281 282 foreach (Object path in glyph.active_paths) { 283 path.move (glyph.left_limit - x + w / 2, font.base_line - y + h / 2); 284 } 285 286 update_selection_boundaries (); 287 objects_moved (); 288 GlyphCanvas.redraw (); 289 } 290 291 public static void get_selection_box_boundaries (out double x, out double y, out double w, out double h) { 292 double px, py, px2, py2; 293 Glyph glyph = MainWindow.get_current_glyph (); 294 295 px = 10000; 296 py = 10000; 297 px2 = -10000; 298 py2 = -10000; 299 300 foreach (Object o in glyph.active_paths) { 301 if (o is PathObject) { 302 Path p = ((PathObject) o).get_path (); 303 p.update_region_boundaries (); 304 305 if (px > p.xmin) { 306 px = p.xmin; 307 } 308 309 if (py > p.ymin) { 310 py = p.ymin; 311 } 312 313 if (px2 < p.xmax) { 314 px2 = p.xmax; 315 } 316 317 if (py2 < p.ymax) { 318 py2 = p.ymax; 319 } 320 } 321 } 322 323 w = px2 - px; 324 h = py2 - py; 325 x = px + (w / 2); 326 y = py + (h / 2); 327 } 328 329 void move_selected_paths (uint key) { 330 Glyph glyph = MainWindow.get_current_glyph (); 331 double x, y; 332 333 x = 0; 334 y = 0; 335 336 switch (key) { 337 case Key.UP: 338 y = 1; 339 break; 340 case Key.DOWN: 341 y = -1; 342 break; 343 case Key.LEFT: 344 x = -1; 345 break; 346 case Key.RIGHT: 347 x = 1; 348 break; 349 default: 350 break; 351 } 352 353 foreach (Object path in glyph.active_paths) { 354 path.move (x * Glyph.ivz (), y * Glyph.ivz ()); 355 } 356 357 BirdFont.get_current_font ().touch (); 358 PenTool.reset_stroke (); 359 update_selection_boundaries (); 360 objects_moved (); 361 glyph.redraw_area (0, 0, glyph.allocation.width, glyph.allocation.height); 362 } 363 364 static void tie_path_to_ttf_grid (Object p) { 365 double sx, sy, qx, qy; 366 367 sx = p.xmax; 368 sy = p.ymax; 369 qx = p.xmin; 370 qy = p.ymin; 371 372 GridTool.ttf_grid_coordinate (ref sx, ref sy); 373 GridTool.ttf_grid_coordinate (ref qx, ref qy); 374 375 if (Math.fabs (qy - p.ymin) < Math.fabs (sy - p.ymax)) { 376 p.move (0, qy - p.ymin); 377 } else { 378 p.move (0, sy - p.ymax); 379 } 380 381 if (Math.fabs (qx - p.xmin) < Math.fabs (sx - p.xmax)) { 382 p.move (qx - p.xmin, 0); 383 } else { 384 p.move (sx - p.xmax, 0); 385 } 386 } 387 388 static void tie_paths_to_grid (Glyph g) { 389 double sx, sy, qx, qy; 390 double dx_min, dx_max, dy_min, dy_max;; 391 double maxx, maxy, minx, miny; 392 393 update_selection_boundaries (); 394 395 // tie to grid 396 maxx = selection_box_center_x + selection_box_width / 2; 397 maxy = selection_box_center_y + selection_box_height / 2; 398 minx = selection_box_center_x - selection_box_width / 2; 399 miny = selection_box_center_y - selection_box_height / 2; 400 401 sx = maxx; 402 sy = maxy; 403 qx = minx; 404 qy = miny; 405 406 GridTool.tie_coordinate (ref sx, ref sy); 407 GridTool.tie_coordinate (ref qx, ref qy); 408 409 dy_min = Math.fabs (qy - miny); 410 dy_max = Math.fabs (sy - maxy); 411 dx_min = Math.fabs (qx - minx); 412 dx_max = Math.fabs (sx - maxx); 413 414 foreach (Object p in g.active_paths) { 415 if (dy_min < dy_max) { 416 p.move (0, qy - miny); 417 } else { 418 p.move (0, sy - maxy); 419 } 420 421 if (dx_min < dx_max) { 422 p.move (qx - minx, 0); 423 } else { 424 p.move (sx - maxx, 0); 425 } 426 } 427 428 update_selection_boundaries (); 429 } 430 431 public static void update_boundaries_for_selection () { 432 Glyph glyph = MainWindow.get_current_glyph (); 433 foreach (Object o in glyph.active_paths) { 434 if (o is PathObject) { 435 ((PathObject)o).get_path ().update_region_boundaries (); 436 } 437 } 438 } 439 440 public static void flip_vertical () { 441 flip (true); 442 } 443 444 public static void flip_horizontal () { 445 flip (false); 446 } 447 448 public static void flip (bool vertical) { 449 double xc, yc, xc2, yc2, w, h; 450 double dx, dy; 451 Glyph glyph = MainWindow.get_current_glyph (); 452 453 update_selection_boundaries (); 454 455 xc = selection_box_center_x; 456 yc = selection_box_center_y; 457 458 foreach (Object p in glyph.active_paths) { 459 if (p is PathObject) { 460 Path path = ((PathObject) p).get_path (); 461 462 // FIXME: move to object 463 if (vertical) { 464 path.flip_vertical (); 465 } else { 466 path.flip_horizontal (); 467 } 468 469 path.reverse (); 470 } 471 } 472 473 get_selection_box_boundaries (out xc2, out yc2, out w, out h); 474 475 dx = -(xc2 - xc); 476 dy = -(yc2 - yc); 477 478 foreach (Object p in glyph.active_paths) { 479 p.move (dx, dy); 480 } 481 482 update_selection_boundaries (); 483 PenTool.reset_stroke (); 484 485 BirdFont.get_current_font ().touch (); 486 } 487 488 public void select_all_paths () { 489 Glyph g = MainWindow.get_current_glyph (); 490 491 g.clear_active_paths (); 492 foreach (Object p in g.get_objects_in_current_layer ()) { 493 if (!p.is_empty ()) { 494 g.add_active_object (null, p); 495 } 496 } 497 498 g.update_view (); 499 500 update_selection_boundaries (); 501 objects_moved (); 502 } 503 504 static void draw_selection_box (Context cr) { 505 double x = Math.fmin (selection_x, last_x); 506 double y = Math.fmin (selection_y, last_y); 507 508 double w = Math.fabs (selection_x - last_x); 509 double h = Math.fabs (selection_y - last_y); 510 511 Glyph glyph = MainWindow.get_current_glyph (); 512 513 cr.save (); 514 Theme.color (cr, "Foreground 1"); 515 cr.set_line_width (2); 516 cr.rectangle (x, y, w, h); 517 cr.stroke (); 518 cr.restore (); 519 } 520 } 521 522 } 523