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