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