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 }); 55 56 deselect_action.connect((self) => { 57 }); 58 59 press_action.connect((self, b, x, y) => { 60 press (b, x, y); 61 }); 62 63 release_action.connect((self, b, x, y) => { 64 release (b, x, y); 65 }); 66 67 move_action.connect ((self, x, y) => { 68 move (x, y); 69 }); 70 71 key_press_action.connect ((self, keyval) => { 72 key_down (keyval); 73 }); 74 75 draw_action.connect ((self, cr, glyph) => { 76 draw_actions (cr); 77 }); 78 } 79 80 public static void draw_actions (Context cr) { 81 if (group_selection) { 82 draw_selection_box (cr); 83 } 84 } 85 86 public void key_down (uint32 keyval) { 87 Glyph g = MainWindow.get_current_glyph (); 88 89 // delete selected paths 90 if (keyval == Key.DEL || keyval == Key.BACK_SPACE) { 91 92 if (g.active_paths.size > 0) { 93 g.store_undo_state (); 94 } 95 96 foreach (Path p in g.active_paths) { 97 g.layers.remove_path (p); 98 g.update_view (); 99 } 100 101 g.active_paths.clear (); 102 } 103 104 if (is_arrow_key (keyval)) { 105 move_selected_paths (keyval); 106 } 107 } 108 109 public void move (int x, int y) { 110 Glyph glyph = MainWindow.get_current_glyph (); 111 double dx = last_x - x; 112 double dy = last_y - y; 113 double p = PenTool.precision; 114 115 if (!move_path) { 116 return; 117 } 118 119 if (move_path && (fabs(dx) > 0 || fabs (dy) > 0)) { 120 moved = true; 121 foreach (Path path in glyph.active_paths) { 122 path.move (Glyph.ivz () * -dx * p, Glyph.ivz () * dy * p); 123 } 124 } 125 126 last_x = x; 127 last_y = y; 128 129 update_selection_boundaries (); 130 131 if (glyph.active_paths.size > 0) { 132 objects_moved (); 133 } 134 135 BirdFont.get_current_font ().touch (); 136 137 GlyphCanvas.redraw (); 138 PenTool.reset_stroke (); 139 } 140 141 public void release (int b, int x, int y) { 142 Glyph glyph = MainWindow.get_current_glyph (); 143 144 move_path = false; 145 146 if (GridTool.is_visible () && moved) { 147 tie_paths_to_grid (glyph); 148 } else if (GridTool.has_ttf_grid ()) { 149 foreach (Path p in glyph.active_paths) { 150 tie_path_to_ttf_grid (p); 151 } 152 } 153 154 if (group_selection) { 155 select_group (); 156 } 157 158 group_selection = false; 159 moved = false; 160 161 if (glyph.active_paths.size > 0) { 162 selection_changed (); 163 objects_moved (); 164 DrawingTools.resize_tool.signal_objects_rotated (); 165 166 foreach (Path p in glyph.active_paths) { 167 p.create_full_stroke (); 168 } 169 } else { 170 objects_deselected (); 171 } 172 } 173 174 public void press (int b, int x, int y) { 175 Glyph glyph = MainWindow.get_current_glyph (); 176 Path p; 177 bool selected = false; 178 Layer? group; 179 Layer g; 180 181 glyph.store_undo_state (); 182 group_selection = false; 183 184 group = glyph.get_path_at (x, y); 185 186 if (group != null) { 187 g = (!) group; 188 return_if_fail (g.paths.paths.size > 0); 189 p = g.paths.paths.get (0); 190 selected = glyph.active_paths.contains (p); 191 192 if (!selected && !KeyBindings.has_shift ()) { 193 glyph.clear_active_paths (); 194 } 195 196 foreach (Path lp in g.paths.paths) { 197 if (selected && KeyBindings.has_shift ()) { 198 glyph.active_paths.remove (lp); 199 } else { 200 glyph.add_active_path (lp); 201 } 202 } 203 } else if (!KeyBindings.has_shift ()) { 204 glyph.clear_active_paths (); 205 } 206 207 move_path = true; 208 209 update_selection_boundaries (); 210 211 last_x = x; 212 last_y = y; 213 214 if (glyph.active_paths.size == 0) { 215 group_selection = true; 216 selection_x = x; 217 selection_y = y; 218 } 219 220 update_boundaries_for_selection (); 221 selection_changed (); 222 GlyphCanvas.redraw (); 223 } 224 225 226 void select_group () { 227 double x1 = Glyph.path_coordinate_x (Math.fmin (selection_x, last_x)); 228 double y1 = Glyph.path_coordinate_y (Math.fmin (selection_y, last_y)); 229 double x2 = Glyph.path_coordinate_x (Math.fmax (selection_x, last_x)); 230 double y2 = Glyph.path_coordinate_y (Math.fmax (selection_y, last_y)); 231 Glyph glyph = MainWindow.get_current_glyph (); 232 233 glyph.clear_active_paths (); 234 235 foreach (Path p in glyph.get_paths_in_current_layer ()) { 236 if (p.xmin > x1 && p.xmax < x2 && p.ymin < y1 && p.ymax > y2) { 237 if (p.points.size > 0) { 238 glyph.add_active_path (p); 239 } 240 } 241 } 242 243 selection_changed (); 244 } 245 246 public static void update_selection_boundaries () { 247 get_selection_box_boundaries (out selection_box_center_x, 248 out selection_box_center_y, out selection_box_width, 249 out selection_box_height); 250 } 251 252 public void move_to_baseline () { 253 Glyph glyph = MainWindow.get_current_glyph (); 254 Font font = BirdFont.get_current_font (); 255 double x, y, w, h; 256 257 get_selection_box_boundaries (out x, out y, out w, out h); 258 259 foreach (Path path in glyph.active_paths) { 260 path.move (glyph.left_limit - x + w / 2, font.base_line - y + h / 2); 261 } 262 263 update_selection_boundaries (); 264 objects_moved (); 265 GlyphCanvas.redraw (); 266 } 267 268 static void draw_selection_box (Context cr) { 269 double x = Math.fmin (selection_x, last_x); 270 double y = Math.fmin (selection_y, last_y); 271 272 double w = Math.fabs (selection_x - last_x); 273 double h = Math.fabs (selection_y - last_y); 274 275 cr.save (); 276 277 Theme.color (cr, "Foreground 1"); 278 cr.set_line_width (2); 279 cr.rectangle (x, y, w, h); 280 cr.stroke (); 281 282 cr.restore (); 283 } 284 285 public static void get_selection_box_boundaries (out double x, out double y, out double w, out double h) { 286 double px, py, px2, py2; 287 Glyph glyph = MainWindow.get_current_glyph (); 288 289 px = 10000; 290 py = 10000; 291 px2 = -10000; 292 py2 = -10000; 293 294 foreach (Path p in glyph.active_paths) { 295 if (px > p.xmin) { 296 px = p.xmin; 297 } 298 299 if (py > p.ymin) { 300 py = p.ymin; 301 } 302 303 if (px2 < p.xmax) { 304 px2 = p.xmax; 305 } 306 307 if (py2 < p.ymax) { 308 py2 = p.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 (Path 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 glyph.redraw_area (0, 0, glyph.allocation.width, glyph.allocation.height); 351 } 352 353 static void tie_path_to_ttf_grid (Path 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 (Path 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 foreach (Path p in glyph.active_paths) { 423 p.update_region_boundaries (); 424 } 425 } 426 427 public static void flip_vertical () { 428 flip (true); 429 } 430 431 public static void flip_horizontal () { 432 flip (false); 433 } 434 435 public static void flip (bool vertical) { 436 double xc, yc, xc2, yc2, w, h; 437 double dx, dy; 438 Glyph glyph = MainWindow.get_current_glyph (); 439 440 update_selection_boundaries (); 441 442 xc = selection_box_center_x; 443 yc = selection_box_center_y; 444 445 foreach (Path p in glyph.active_paths) { 446 if (vertical) { 447 p.flip_vertical (); 448 } else { 449 p.flip_horizontal (); 450 } 451 452 p.reverse (); 453 } 454 455 get_selection_box_boundaries (out xc2, out yc2, out w, out h); 456 457 dx = -(xc2 - xc); 458 dy = -(yc2 - yc); 459 460 foreach (Path p in glyph.active_paths) { 461 p.move (dx, dy); 462 } 463 464 update_selection_boundaries (); 465 PenTool.reset_stroke (); 466 467 BirdFont.get_current_font ().touch (); 468 } 469 470 public void select_all_paths () { 471 Glyph g = MainWindow.get_current_glyph (); 472 473 g.clear_active_paths (); 474 foreach (Path p in g.get_paths_in_current_layer ()) { 475 if (p.points.size > 0) { 476 g.add_active_path (p); 477 } 478 } 479 480 g.update_view (); 481 482 update_selection_boundaries (); 483 objects_moved (); 484 } 485 } 486 487 } 488