The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

ForesightTool.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/ForesightTool.vala.
Show cursor in dialog
1 /* 2 Copyright (C) 2014 2015 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 /** Create Beziér curves and preview the path. */ 21 public class ForesightTool : Tool { 22 23 public const uint NONE = 0; 24 public const uint MOVE_POINT = 1; 25 public const uint MOVE_HANDLES = 2; 26 public const uint MOVE_LAST_HANDLE = 3; 27 public const uint MOVE_FIRST_HANDLE = 4; 28 29 uint state = NONE; 30 bool move_right_handle = true; 31 int previous_point = 0; 32 33 Path current_path = new Path (); 34 35 int last_move_x = 0; 36 int last_move_y = 0; 37 38 public bool skip_deselect = false; 39 40 public ForesightTool (string name) { 41 base (name, t_ ("Create Beziér curves")); 42 43 select_action.connect ((self) => { 44 PenTool p = (PenTool) PointTool.pen (); 45 46 if (state != NONE) { 47 p.release_action (p, 1, last_move_x, last_move_y); 48 } 49 50 state = NONE; 51 }); 52 53 deselect_action.connect ((self) => { 54 PenTool p = (PenTool) PointTool.pen (); 55 56 if (state != NONE) { 57 p.release_action (p, 1, last_move_x, last_move_y); 58 } 59 60 state = NONE; 61 }); 62 63 press_action.connect ((self, b, x, y) => { 64 PenTool p = (PenTool) PointTool.pen (); 65 PointSelection ps; 66 EditPoint first_point; 67 bool clockwise; 68 69 if (b == 2) { 70 p.release_action (p, 1, x, y); 71 p.press_action (p, 2, x, y); 72 p.release_action (p, 2, x, y); 73 current_path.hide_end_handle = true; 74 state = NONE; 75 MainWindow.set_cursor (NativeWindow.VISIBLE); 76 return; 77 } 78 79 BirdFont.get_current_font ().touch (); 80 MainWindow.get_current_glyph ().store_undo_state (); 81 82 last_move_x = x; 83 last_move_y = y; 84 85 if (previous_point > 0) { 86 previous_point = 0; 87 state = MOVE_POINT; 88 } else { 89 if (state == NONE) { 90 state = MOVE_POINT; 91 add_new_point (x, y); 92 93 PenTool.last_point_x = Glyph.path_coordinate_x (x); 94 PenTool.last_point_y = Glyph.path_coordinate_y (y); 95 96 move_action (this, x, y); 97 state = MOVE_FIRST_HANDLE; 98 release_action(this, b, x, y); 99 } else if (state == MOVE_POINT) { 100 state = MOVE_HANDLES; 101 102 if (p.has_join_icon ()) { 103 if (unlikely (PenTool.active_path.points.size == 0)) { 104 warning ("No point to join."); 105 return; 106 } 107 108 ps = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - 1), PenTool.active_path); 109 ps.point.set_tie_handle (false); 110 111 ps = new PointSelection (PenTool.active_path.points.get (0), PenTool.active_path); 112 ps.point.set_tie_handle (false); 113 114 first_point = PenTool.active_path.points.get (0); 115 clockwise = PenTool.active_path.is_clockwise (); 116 117 PenTool.move_selected = false; 118 p.release_action (p, 2, x, y); 119 120 PenTool.move_selected = false; 121 p.press_action (p, 2, x, y); 122 123 if (ps.path.has_point (first_point)) { 124 ps.path.reverse (); 125 } 126 127 if (clockwise && PenTool.active_path.is_clockwise ()) { 128 ps = new PointSelection (PenTool.active_path.points.get (0), PenTool.active_path); 129 } else { 130 ps = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - 1), PenTool.active_path); 131 } 132 133 PenTool.selected_points.clear (); 134 PenTool.selected_points.add (ps); 135 PenTool.selected_point = ps.point; 136 137 state = MOVE_LAST_HANDLE; 138 } 139 } 140 } 141 }); 142 143 double_click_action.connect ((self, b, x, y) => { 144 Tool p = PointTool.pen (); 145 146 if (!BirdFont.android) { 147 p.double_click_action (p, b, x, y); 148 } 149 }); 150 151 release_action.connect ((self, b, x, y) => { 152 PenTool p = (PenTool) PointTool.pen (); 153 PointSelection last; 154 155 if (state == MOVE_HANDLES || state == MOVE_FIRST_HANDLE) { 156 if (state != MOVE_FIRST_HANDLE) { // FIXME: 157 last = add_new_point (x, y); 158 } 159 160 state = MOVE_POINT; 161 move_action (this, x, y); 162 } else if (state == MOVE_LAST_HANDLE) { 163 previous_point = 0; 164 165 if (unlikely (PenTool.selected_points.size == 0)) { 166 warning ("No point in move last handle."); 167 return; 168 } 169 170 last = PenTool.selected_points.get (PenTool.selected_points.size - 1); 171 172 p.release_action (p, 2, x, y); 173 174 PenTool.selected_points.add (last); 175 PenTool.active_path.highlight_last_segment = false; 176 177 last.path.direction_is_set = false; 178 PenTool.force_direction (); 179 180 state = NONE; 181 } else if (state == MOVE_POINT) { 182 } else if (state == NONE) { 183 } else { 184 warning (@"Unknown state $state."); 185 } 186 187 current_path.hide_end_handle = true; 188 }); 189 190 move_action.connect ((self, x, y) => { 191 Tool p = PointTool.pen (); 192 PointSelection last; 193 bool lh; 194 EditPointHandle h; 195 196 last_move_x = x; 197 last_move_y = y; 198 199 if (MainWindow.dialog.visible && state != NONE) { 200 state = NONE; 201 p.release_action (p, 1, last_move_x, last_move_y); 202 } 203 204 if (state == NONE) { 205 MainWindow.set_cursor (NativeWindow.VISIBLE); 206 } 207 208 PenTool.active_path = current_path; 209 PenTool.active_path.hide_end_handle = (state == MOVE_POINT); 210 211 if (state == MOVE_HANDLES || state == MOVE_LAST_HANDLE) { 212 if (previous_point > 0) { 213 return_if_fail (PenTool.active_path.points.size >= previous_point + 1); 214 last = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - (previous_point + 1)), PenTool.active_path); 215 } else { 216 if (unlikely (PenTool.selected_points.size == 0)) { 217 warning ("No point to move in state %u", state); 218 return; 219 } 220 last = PenTool.selected_points.get (PenTool.selected_points.size - 1); 221 222 if (last.point.get_right_handle ().is_line () || last.point.get_left_handle ().is_line ()) { 223 last.point.convert_to_curve (); 224 last.point.get_right_handle ().length = 0.1; 225 last.point.get_left_handle ().length = 0.1; 226 } 227 } 228 229 PenTool.move_selected_handle = true; 230 PenTool.move_selected = false; 231 232 lh = (state == MOVE_LAST_HANDLE); 233 234 if (!move_right_handle) { 235 lh = !lh; 236 } 237 238 if (previous_point > 0) { 239 lh = false; 240 } 241 242 h = (lh) ? last.point.get_left_handle () : last.point.get_right_handle (); 243 PenTool.selected_handle = h; 244 PenTool.active_handle = h; 245 246 if (previous_point == 0 247 && !last.point.reflective_point 248 && !last.point.tie_handles) { 249 250 last.point.convert_to_curve (); 251 last.point.set_reflective_handles (true); 252 last.point.get_right_handle ().length = 0.1; 253 last.point.get_left_handle ().length = 0.1; 254 } 255 256 if (previous_point == 0) { 257 last.point.set_reflective_handles (true); 258 } 259 260 if (previous_point > 0) { 261 PenTool.retain_angle = last.point.tie_handles; 262 } 263 264 if (h.is_line ()) { 265 last.point.convert_to_curve (); 266 last.point.get_right_handle ().length = 0.1; 267 last.point.get_left_handle ().length = 0.1; 268 } 269 270 p.move_action (p, x, y); 271 last.point.set_reflective_handles (false); 272 273 PenTool.selected_handle = h; 274 PenTool.active_handle = h; 275 276 PenTool.move_selected_handle = false; 277 last.path.highlight_last_segment = true; 278 279 if (previous_point == 0) { 280 last.point.set_tie_handle (true); 281 p.move_action (p, x, y); 282 } 283 284 PenTool.retain_angle = false; 285 } else { 286 if (DrawingTools.get_selected_point_type () != PointType.QUADRATIC) { 287 p.move_action (p, x, y); 288 } else { 289 PenTool.move_point_independent_of_handle = true; 290 p.move_action (p, x, y); 291 PenTool.move_point_independent_of_handle = false; 292 } 293 } 294 295 if (PenTool.active_path.points.size < 3) { 296 PenTool.active_edit_point = null; 297 } 298 }); 299 300 key_press_action.connect ((self, keyval) => { 301 PenTool p = (PenTool) PointTool.pen (); 302 p.key_press_action (p, keyval); 303 }); 304 305 key_release_action.connect ((self, keyval) => { 306 Tool p = PointTool.pen (); 307 p.key_release_action (p, keyval); 308 }); 309 310 draw_action.connect ((tool, cairo_context, glyph) => { 311 Tool p = PointTool.pen (); 312 p.draw_action (p, cairo_context, glyph); 313 }); 314 } 315 316 public void switch_to_line_mode () { 317 EditPoint ep; 318 EditPoint last; 319 320 if (PenTool.active_path.points.size > 2) { 321 ep = PenTool.active_path.points.get (PenTool.active_path.points.size - 2); 322 ep.get_right_handle ().convert_to_line (); 323 ep.set_tie_handle (false); 324 325 last = PenTool.active_path.points.get (PenTool.active_path.points.size - 1); 326 last.convert_to_line (); 327 328 move_action (this, last_move_x, last_move_y); 329 } 330 } 331 332 PointSelection add_new_point (int x, int y) { 333 PointSelection last; 334 double handle_x, handle_y; 335 336 PenTool p = (PenTool) PointTool.pen (); 337 338 if (PenTool.active_path.points.size == 0) { 339 last = p.new_point_action (x, y); 340 } else { 341 if (PenTool.selected_points.size == 0) { 342 warning ("No selected points."); 343 return new PointSelection.empty (); 344 } 345 346 last = PenTool.selected_points.get (PenTool.selected_points.size - 1); 347 348 PenTool.selected_points.clear (); 349 PenTool.selected_handle = new EditPointHandle.empty (); 350 351 if (DrawingTools.get_selected_point_type () != PointType.QUADRATIC) { 352 last = p.new_point_action (x, y); 353 } else { 354 last.point.get_right_handle ().length *= 0.999999; 355 handle_x = last.point.get_right_handle ().x; 356 handle_y = last.point.get_right_handle ().y; 357 358 last = p.new_point_action (x, y); 359 360 last.point.get_left_handle ().x = handle_x; 361 last.point.get_left_handle ().y = handle_y; 362 } 363 364 PenTool.move_selected = true; 365 p.move_action (p, x, y); 366 } 367 368 PenTool.selected_points.clear (); 369 PenTool.selected_points.add (last); 370 PenTool.selected_point = last.point; 371 PenTool.active_edit_point = null; 372 PenTool.show_selection_box = false; 373 374 PenTool.move_selected_handle = false; 375 PenTool.move_selected = true; 376 377 PenTool.active_path.hide_end_handle = (state == MOVE_POINT); 378 379 current_path = last.path; 380 381 return last; 382 } 383 384 // FIXME: solve the straight line issue in undo 385 public override void before_undo () { 386 EditPoint last; 387 388 if (PenTool.active_path.points.size > 1) { 389 last = PenTool.active_path.points.get (PenTool.active_path.points.size - 2); 390 last.convert_to_curve (); 391 392 print (@"Before $(last.x) $(last.y)\n"); 393 } 394 } 395 396 public override void after_undo () { 397 PenTool.selected_points.clear (); 398 PenTool.active_edit_point = null; 399 state = NONE; 400 401 EditPoint last; 402 403 if (PenTool.active_path.points.size > 0) { 404 last = PenTool.active_path.points.get (PenTool.active_path.points.size - 1); 405 last.convert_to_curve (); 406 407 print (@"After $(last.x) $(last.y)\n"); 408 } 409 } 410 411 } 412 413 } 414