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