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