The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

DrawingTools.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/DrawingTools.vala.
Fix quadratic double points for open paths
1 /* 2 Copyright (C) 2012, 2013, 2014 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 Cairo; 16 using Math; 17 18 namespace BirdFont { 19 20 public class DrawingTools : ToolCollection { 21 GlyphCanvas glyph_canvas; 22 23 public Gee.ArrayList<Expander> expanders = new Gee.ArrayList<Expander> (); 24 25 Expander draw_tools; 26 Expander grid_expander; 27 Expander shape_tools; 28 public static Expander draw_tool_modifiers; 29 public static Expander zoombar_tool; 30 public static Expander view_tools; 31 public static Expander guideline_tools; 32 33 public static PointType point_type = PointType.DOUBLE_CURVE; 34 35 public static SpinButton object_stroke; 36 37 public static MoveTool move_tool; 38 public static PenTool pen_tool; 39 40 public static ForesightTool foresight_tool; 41 PointTool point_tool; 42 public static ZoomTool zoom_tool; 43 public static ResizeTool resize_tool; 44 StrokeTool stroke_tool; 45 public static TrackTool track_tool; 46 public static BackgroundTool move_background; 47 public static Tool move_canvas; 48 49 Tool quadratic_points; 50 Tool cubic_points; 51 Tool double_points; 52 Tool convert_points; 53 54 public static CutBackgroundTool cut_background; 55 Tool show_bg; 56 Tool bg_selection; 57 SpinButton background_threshold; 58 public static SpinButton background_scale; 59 Tool high_contrast_background; 60 SpinButton auto_trace_resolution; 61 Tool auto_trace; 62 SpinButton auto_trace_simplify; 63 Tool delete_background; 64 65 Tool rectangle; 66 Tool circle; 67 68 Tool help_lines; 69 Tool xheight_help_lines; 70 Tool background_help_lines; 71 72 SpinButton x_coordinate; 73 SpinButton y_coordinate; 74 SpinButton rotation; 75 SpinButton width; 76 SpinButton height; 77 SpinButton skew; 78 79 Tool tie_handles; 80 Tool reflect_handle; 81 Tool create_line; 82 83 Tool delete_button; 84 public Tool insert_point_on_path_tool; 85 Tool undo_tool; 86 Tool select_all_button; 87 88 OrientationTool reverse_path_tool; 89 Tool move_layer; 90 Tool flip_vertical; 91 Tool flip_horizontal; 92 93 public ZoomBar zoom_bar; 94 95 public DrawingTools (GlyphCanvas main_glyph_canvas) { 96 bool selected_line; 97 98 glyph_canvas = main_glyph_canvas; 99 100 background_scale = new SpinButton (); 101 102 draw_tools = new Expander (t_("Drawing Tools")); 103 draw_tool_modifiers = new Expander (t_("Control Point")); 104 shape_tools = new Expander (t_("Geometrical Shapes")); 105 zoombar_tool = new Expander (t_("Zoom")); 106 view_tools = new Expander (); 107 guideline_tools = new Expander (t_("Guidelines & Grid")); 108 109 Expander font_name = new Expander (); 110 Expander key_tools = new Expander (); // tools on android 111 Expander test_tools = new Expander (); 112 Expander grid = new Expander (t_("Grid Size")); 113 114 grid_expander = grid; 115 116 // font name 117 font_name.add_tool (new FontName ()); 118 119 // Draw tools 120 foresight_tool = new ForesightTool ("foresight"); 121 foresight_tool.select_action.connect ((self) => { 122 update_drawing_and_background_tools (self); 123 }); 124 draw_tools.add_tool (foresight_tool); 125 126 Tool bezier_line = new Tool ("bezier_line", t_("Convert the last segment to a straight line")); 127 bezier_line.select_action.connect ((self) => { 128 foresight_tool.switch_to_line_mode (); 129 }); 130 bezier_line.is_tool_modifier = true; 131 draw_tools.add_tool (bezier_line); 132 bezier_line.set_tool_visibility (false); 133 134 pen_tool = new PenTool ("pen_tool"); 135 pen_tool.select_action.connect ((self) => { 136 update_drawing_and_background_tools (self); 137 }); 138 draw_tools.add_tool (pen_tool); 139 140 point_tool = new PointTool ("point_tool"); 141 point_tool.select_action.connect ((self) => { 142 update_drawing_and_background_tools (self); 143 }); 144 draw_tools.add_tool (point_tool); 145 146 zoom_tool = new ZoomTool ("zoom_tool"); 147 zoom_tool.select_action.connect ((self) => { 148 update_drawing_and_background_tools (self); 149 }); 150 draw_tools.add_tool (zoom_tool); 151 152 move_tool = new MoveTool ("move"); 153 move_tool.select_action.connect ((self) => { 154 update_drawing_and_background_tools (self); 155 }); 156 draw_tools.add_tool (move_tool); 157 158 resize_tool = new ResizeTool ("resize"); 159 resize_tool.select_action.connect ((self) => { 160 update_drawing_and_background_tools (self); 161 }); 162 draw_tools.add_tool (resize_tool); 163 164 track_tool = new TrackTool ("track"); // draw outline on freehand 165 track_tool.select_action.connect ((self) => { 166 update_drawing_and_background_tools (self); 167 }); 168 draw_tools.add_tool (track_tool); 169 170 move_background = new BackgroundTool ("move_background"); 171 move_background.select_action.connect ((self) => { 172 update_drawing_and_background_tools (self); 173 }); 174 draw_tools.add_tool (move_background); 175 176 move_canvas = new Tool ("move_canvas", t_("Move canvas")); 177 move_canvas.select_action.connect ((self) => { 178 update_drawing_and_background_tools (self); 179 }); 180 draw_tools.add_tool (move_canvas); 181 182 // Tools on android 183 // Delete key 184 delete_button = new Tool ("delete_button", t_("Delete")); 185 delete_button.select_action.connect ((self) => { 186 TabContent.key_press (Key.DEL); 187 }); 188 key_tools.add_tool (delete_button); 189 190 // Select all points or paths 191 select_all_button = new Tool ("select_all", t_("Select all points or paths")); 192 select_all_button.select_action.connect ((self) => { 193 Glyph g = MainWindow.get_current_glyph (); 194 195 if (point_tool.is_selected () 196 || pen_tool.is_selected () 197 || track_tool.is_selected ()) { 198 pen_tool.select_all_points (); 199 g.open_path (); 200 } else { 201 DrawingTools.move_tool.select_all_paths (); 202 } 203 }); 204 key_tools.add_tool (select_all_button); 205 206 // Undo 207 undo_tool = new Tool ("undo_tool", t_("Undo")); 208 undo_tool.select_action.connect ((self) => { 209 TabContent.undo (); 210 }); 211 key_tools.add_tool (undo_tool); 212 213 bool insert_points = false; 214 insert_point_on_path_tool = new Tool ("new_point_on_path", t_("Insert new points on path")); 215 insert_point_on_path_tool.select_action.connect ((self) => { 216 insert_points = !insert_points; 217 insert_point_on_path_tool.set_selected (insert_points); 218 }); 219 insert_point_on_path_tool.set_persistent (true); 220 key_tools.add_tool (insert_point_on_path_tool); 221 222 // quadratic Bézier points 223 quadratic_points = new Tool ("quadratic_points", t_("Create quadratic Bézier curves")); 224 quadratic_points.select_action.connect ((self) => { 225 point_type = PointType.QUADRATIC; 226 Preferences.set ("point_type", "quadratic_points"); 227 update_type_selection (); 228 }); 229 draw_tool_modifiers.add_tool (quadratic_points); 230 231 // cubic Bézier points 232 cubic_points = new Tool ("cubic_points", t_("Create cubic Bézier curves")); 233 cubic_points.select_action.connect ((self) => { 234 point_type = PointType.CUBIC; 235 Preferences.set ("point_type", "cubic_points"); 236 update_type_selection (); 237 }); 238 draw_tool_modifiers.add_tool (cubic_points); 239 240 // two quadratic points off curve points for each quadratic control point 241 double_points = new Tool ("double_points", t_("Quadratic path with two line handles")); 242 double_points.select_action.connect ((self) => { 243 point_type = PointType.DOUBLE_CURVE; 244 Preferences.set ("point_type", "double_points"); 245 update_type_selection (); 246 }); 247 draw_tool_modifiers.add_tool (double_points); 248 249 // convert point 250 convert_points = new Tool ("convert_point", t_("Convert selected points")); 251 convert_points.select_action.connect ((self) => { 252 PenTool.convert_point_types (); 253 GlyphCanvas.redraw (); 254 update_type_selection (); 255 }); 256 convert_points.set_persistent (false); 257 draw_tool_modifiers.add_tool (convert_points); 258 259 // x coordinate 260 x_coordinate = new SpinButton ("x_coordinate", t_("X coordinate")); 261 x_coordinate.set_big_number (true); 262 x_coordinate.set_int_value ("0.000"); 263 x_coordinate.set_int_step (0.1); 264 x_coordinate.set_min (-999.99); 265 x_coordinate.set_max (999.99); 266 x_coordinate.show_icon (true); 267 x_coordinate.set_persistent (false); 268 x_coordinate.new_value_action.connect((self) => { 269 Glyph glyph = MainWindow.get_current_glyph (); 270 double x, y, w, h; 271 double delta; 272 273 glyph.selection_boundaries (out x, out y, out w, out h); 274 delta = x_coordinate.get_value () - x + glyph.left_limit; 275 276 foreach (Path path in glyph.active_paths) { 277 path.move (delta, 0); 278 } 279 280 GlyphCanvas.redraw (); 281 }); 282 draw_tool_modifiers.add_tool (x_coordinate); 283 284 move_tool.objects_moved.connect (() => { 285 Glyph glyph = MainWindow.get_current_glyph (); 286 x_coordinate.set_value_round (MoveTool.selection_box_center_x 287 - (MoveTool.selection_box_width / 2) 288 - glyph.left_limit, true, false); 289 }); 290 291 move_tool.selection_changed.connect (() => { 292 Glyph glyph = MainWindow.get_current_glyph (); 293 x_coordinate.set_value_round (MoveTool.selection_box_center_x 294 - (MoveTool.selection_box_width / 2) 295 - glyph.left_limit, true, false); 296 }); 297 298 move_tool.objects_deselected.connect (() => { 299 x_coordinate.set_value_round (0, true, false); 300 x_coordinate.hide_value (); 301 }); 302 303 // y coordinate 304 y_coordinate = new SpinButton ("y_coordinate", t_("Y coordinate")); 305 y_coordinate.set_big_number (true); 306 y_coordinate.set_int_value ("0.000"); 307 y_coordinate.set_int_step (0.1); 308 y_coordinate.set_min (-999.99); 309 y_coordinate.set_max (999.99); 310 y_coordinate.show_icon (true); 311 y_coordinate.set_persistent (false); 312 y_coordinate.new_value_action.connect((self) => { 313 double x, y, w, h; 314 Glyph glyph = MainWindow.get_current_glyph (); 315 Font font = BirdFont.get_current_font (); 316 317 glyph.selection_boundaries (out x, out y, out w, out h); 318 319 foreach (Path path in glyph.active_paths) { 320 path.move (0, y_coordinate.get_value () - (y - h) - font.base_line); 321 } 322 323 GlyphCanvas.redraw (); 324 }); 325 draw_tool_modifiers.add_tool (y_coordinate); 326 327 move_tool.objects_moved.connect (() => { 328 Font font = BirdFont.get_current_font (); 329 y_coordinate.set_value_round (MoveTool.selection_box_center_y 330 - (MoveTool.selection_box_height / 2) 331 + font.base_line, true, false); 332 }); 333 334 move_tool.selection_changed.connect (() => { 335 Font font = BirdFont.get_current_font (); 336 y_coordinate.set_value_round (MoveTool.selection_box_center_y 337 - (MoveTool.selection_box_height / 2) 338 + font.base_line, true, false); 339 }); 340 341 move_tool.objects_deselected.connect (() => { 342 y_coordinate.set_value_round (0, true, false); 343 y_coordinate.hide_value (); 344 }); 345 346 // rotation 347 rotation = new SpinButton ("rotation", t_("Rotation")); 348 rotation.set_big_number (true); 349 rotation.set_int_value ("0.000"); 350 rotation.set_int_step (0.1); 351 rotation.set_min (-360); 352 rotation.set_max (360); 353 rotation.show_icon (true); 354 rotation.set_persistent (false); 355 rotation.new_value_action.connect ((self) => { 356 double x, y, w, h; 357 Glyph glyph = MainWindow.get_current_glyph (); 358 double angle = (self.get_value () / 360) * 2 * PI; 359 Path last_path; 360 glyph.selection_boundaries (out x, out y, out w, out h); 361 362 x += w / 2; 363 y -= h / 2; 364 365 if (glyph.active_paths.size > 0) { 366 last_path = glyph.active_paths.get (glyph.active_paths.size - 1); 367 resize_tool.rotate_selected_paths (angle - last_path.rotation, x, y); 368 } 369 370 GlyphCanvas.redraw (); 371 }); 372 373 resize_tool.objects_rotated.connect ((angle) => { 374 rotation.set_value_round (angle, true, false); 375 }); 376 377 move_tool.objects_deselected.connect (() => { 378 rotation.set_value_round (0, true, false); 379 rotation.hide_value (); 380 }); 381 382 draw_tool_modifiers.add_tool (rotation); 383 384 // skew 385 skew = new SpinButton ("skew", t_("Skew")); 386 skew.set_big_number (true); 387 skew.set_int_value ("0.000"); 388 skew.set_int_step (1); 389 skew.set_min (-100); 390 skew.set_max (100); 391 skew.show_icon (true); 392 skew.set_persistent (false); 393 skew.new_value_action.connect ((self) => { 394 resize_tool.skew (-skew.get_value ()); 395 GlyphCanvas.redraw (); 396 }); 397 398 move_tool.objects_moved.connect (() => { 399 Glyph glyph = MainWindow.get_current_glyph (); 400 double d; 401 402 if (glyph.active_paths.size > 0) { 403 d = glyph.active_paths.get (0).skew; 404 resize_tool.last_skew = d; 405 skew.set_value_round (-d, true, false); 406 } 407 }); 408 409 move_tool.objects_deselected.connect (() => { 410 skew.set_value_round (0, true, false); 411 skew.hide_value (); 412 }); 413 414 draw_tool_modifiers.add_tool (skew); 415 416 // width 417 width = new SpinButton ("width", t_("Width")); 418 width.set_big_number (true); 419 width.set_int_value ("0.0000"); 420 width.set_int_step (0.01); 421 width.show_icon (true); 422 width.set_persistent (false); 423 width.new_value_action.connect ((self) => { 424 double x, y, w, h; 425 Glyph glyph; 426 double new_size; 427 428 glyph = MainWindow.get_current_glyph (); 429 glyph.selection_boundaries (out x, out y, out w, out h); 430 431 new_size = self.get_value () / w; 432 433 if (self.get_value () > 0 && new_size != 1) { 434 resize_tool.resize_selected_paths (new_size); 435 } 436 437 GlyphCanvas.redraw (); 438 }); 439 draw_tool_modifiers.add_tool (width); 440 441 // height 442 height = new SpinButton ("height", t_("Height")); 443 height.set_big_number (true); 444 height.set_int_value ("0.0000"); 445 height.set_int_step (0.01); 446 height.show_icon (true); 447 height.set_persistent (false); 448 height.new_value_action.connect ((self) => { 449 double x, y, w, h; 450 Glyph glyph; 451 double new_size; 452 453 glyph = MainWindow.get_current_glyph (); 454 glyph.selection_boundaries (out x, out y, out w, out h); 455 456 new_size = self.get_value () / h; 457 458 if (self.get_value () > 0 && new_size != 1) { 459 resize_tool.resize_selected_paths (new_size); 460 } 461 462 GlyphCanvas.redraw (); 463 }); 464 draw_tool_modifiers.add_tool (height); 465 466 resize_tool.objects_resized.connect ((w, h) => { 467 height.set_value_round (h, true, false); 468 width.set_value_round (w, true, false); 469 }); 470 471 move_tool.objects_deselected.connect (() => { 472 width.set_value_round (0, true, false); 473 width.hide_value (); 474 475 height.set_value_round (0, true, false); 476 height.hide_value (); 477 }); 478 479 move_tool.objects_moved.connect (() => { 480 width.set_value_round (MoveTool.selection_box_width, true, false); 481 height.set_value_round (MoveTool.selection_box_height, true, false); 482 }); 483 484 // edit stroke width 485 object_stroke = new SpinButton ("object_stroke", t_("Stroke width")); 486 object_stroke.set_int_value ("2.000"); 487 object_stroke.set_int_step (0.015); 488 489 object_stroke.new_value_action.connect((self) => { 490 if (move_tool.is_selected () && object_stroke.is_selected ()) { 491 StrokeTool.set_stroke_for_selected_paths (object_stroke.get_value ()); 492 } 493 494 if (track_tool.is_selected ()) { 495 track_tool.set_stroke_width (object_stroke.get_value ()); 496 } 497 498 if (pen_tool.is_selected ()) { 499 pen_tool.set_stroke_width (object_stroke.get_value ()); 500 } 501 }); 502 503 if (BirdFont.has_argument ("--test") || BirdFont.android) { 504 draw_tool_modifiers.add_tool (object_stroke); 505 } 506 507 stroke_tool = new StrokeTool ("stroke"); // create outline from path 508 stroke_tool.select_action.connect ((self) => { 509 update_drawing_and_background_tools (self); 510 }); 511 512 if (BirdFont.has_argument ("--test") || BirdFont.android) { 513 draw_tools.add_tool (stroke_tool); 514 } 515 516 // tie edit point handles 517 tie_handles = new Tool ("tie_point", t_("Tie curve handles for the selected edit point")); 518 tie_handles.select_action.connect ((self) => { 519 bool tie; 520 EditPoint p; 521 522 if (PenTool.move_selected_handle) { 523 p = PenTool.active_handle.parent; 524 tie = !p.tie_handles; 525 526 if (tie) { 527 p.process_tied_handle (); 528 p.set_reflective_handles (false); 529 } 530 531 p.set_tie_handle (tie); 532 533 PenTool.handle_selection.path.update_region_boundaries (); 534 } else { 535 foreach (PointSelection ep in PenTool.selected_points) { 536 tie = !ep.point.tie_handles; 537 538 if (tie) { 539 ep.point.process_tied_handle (); 540 ep.point.set_reflective_handles (false); 541 } 542 543 ep.point.set_tie_handle (tie); 544 ep.path.update_region_boundaries (); 545 } 546 } 547 548 MainWindow.get_current_glyph ().update_view (); 549 }); 550 draw_tool_modifiers.add_tool (tie_handles); 551 552 // symmetrical handles 553 reflect_handle = new Tool ("symmetric", t_("Symmetrical handles")); 554 reflect_handle.select_action.connect ((self) => { 555 bool symmetrical; 556 PointSelection ep; 557 if (PenTool.selected_points.size > 0) { 558 ep = PenTool.selected_points.get (0); 559 symmetrical = ep.point.reflective_point; 560 foreach (PointSelection p in PenTool.selected_points) { 561 p.point.set_reflective_handles (!symmetrical); 562 p.point.process_symmetrical_handles (); 563 564 if (symmetrical) { 565 ep.point.set_tie_handle (false); 566 } 567 568 p.path.update_region_boundaries (); 569 } 570 MainWindow.get_current_glyph ().update_view (); 571 } 572 }); 573 draw_tool_modifiers.add_tool (reflect_handle); 574 575 create_line = new Tool ("create_line", t_("Convert segment to line.")); 576 create_line.select_action.connect ((self) => { 577 PenTool.convert_segment_to_line (); 578 MainWindow.get_current_glyph ().update_view (); 579 }); 580 draw_tool_modifiers.add_tool (create_line); 581 582 reverse_path_tool = new OrientationTool ("reverse_path", t_("Create counter from outline")); 583 draw_tool_modifiers.add_tool (reverse_path_tool); 584 585 move_layer = new Tool ("move_layer", t_("Move to path to the bottom layer")); 586 move_layer.select_action.connect ((self) => { 587 Glyph g = MainWindow.get_current_glyph (); 588 589 foreach (Path p in g.active_paths) { 590 g.path_list.remove (p); 591 g.path_list.insert (0, p); 592 } 593 }); 594 draw_tool_modifiers.add_tool (move_layer); 595 596 flip_vertical = new Tool ("flip_vertical", t_("Flip path vertically")); 597 flip_vertical.select_action.connect ((self) => { 598 MoveTool.flip_vertical (); 599 MainWindow.get_current_glyph ().update_view (); 600 }); 601 draw_tool_modifiers.add_tool (flip_vertical); 602 603 flip_horizontal = new Tool ("flip_horizontal", t_("Flip path horizontally")); 604 flip_horizontal.select_action.connect ((self) => { 605 MoveTool.flip_horizontal (); 606 MainWindow.get_current_glyph ().update_view (); 607 }); 608 draw_tool_modifiers.add_tool (flip_horizontal); 609 610 // background tools 611 background_scale = new SpinButton ("scale_background", t_("Set size for background image")); 612 background_scale.show_icon (true); 613 background_scale.set_int_value ("1.000"); 614 615 background_scale.new_value_action.connect((self) => { 616 background_scale.select_action (self); 617 }); 618 619 background_scale.select_action.connect((self) => { 620 SpinButton sb = (SpinButton) self; 621 Glyph g = MainWindow.get_current_glyph (); 622 BackgroundImage? img = g.get_background_image (); 623 double s = sb.get_value (); 624 BackgroundImage i; 625 double xc, yc; 626 627 if (img != null) { 628 i = (!) img; 629 xc = i.img_middle_x; 630 yc = i.img_middle_y; 631 632 i.set_img_scale (s, s); 633 634 i.img_middle_x = xc; 635 i.img_middle_y = yc; 636 } 637 638 GlyphCanvas.redraw (); 639 }); 640 641 draw_tool_modifiers.add_tool (background_scale); 642 643 cut_background = new CutBackgroundTool ("cut_background"); 644 cut_background.select_action.connect ((self) => { 645 update_drawing_and_background_tools (self); 646 }); 647 draw_tool_modifiers.add_tool (cut_background); 648 649 show_bg = new Tool ("show_background", t_("Show/hide background image")); 650 show_bg.select_action.connect ((self) => { 651 update_drawing_and_background_tools (self); 652 }); 653 show_bg.select_action.connect ((self) => { 654 Glyph g = MainWindow.get_current_glyph (); 655 g.set_background_visible (!g.get_background_visible ()); 656 GlyphCanvas.redraw (); 657 }); 658 draw_tool_modifiers.add_tool (show_bg); 659 660 bg_selection = new Tool ("insert_background", t_("Insert a new background image")); 661 bg_selection.select_action.connect ((self) => { 662 update_drawing_and_background_tools (self); 663 }); 664 665 bg_selection.select_action.connect((self) => { 666 if (MainWindow.get_current_display () is Glyph) { 667 BackgroundTool.import_background_image (); 668 } 669 }); 670 671 bg_selection.set_show_background (true); 672 draw_tool_modifiers.add_tool (bg_selection); 673 674 high_contrast_background = new Tool ("high_contrast_background", t_("High contrast")); 675 high_contrast_background.select_action.connect ((self) => { 676 Glyph g = MainWindow.get_current_glyph (); 677 BackgroundImage? bg = g.get_background_image (); 678 BackgroundImage b; 679 680 if (bg != null) { 681 b = (!) bg; 682 b.set_high_contrast (!b.high_contrast); 683 b.update_background (); 684 } 685 }); 686 draw_tool_modifiers.add_tool (high_contrast_background); 687 688 background_threshold = new SpinButton ("contrast_threshold", t_("Set background threshold")); 689 background_threshold.show_icon (true); 690 background_threshold.set_value_round (1); 691 692 background_threshold.new_value_action.connect ((self) => { 693 Glyph g = MainWindow.get_current_glyph (); 694 BackgroundImage? bg = g.get_background_image (); 695 BackgroundImage b; 696 697 if (bg != null) { 698 b = (!) bg; 699 b.set_threshold (background_threshold.get_value ()); 700 b.update_background (); 701 } 702 }); 703 704 draw_tool_modifiers.add_tool (background_threshold); 705 706 auto_trace_resolution = new SpinButton ("auto_trace_resolution", t_("Amount of autotrace details")); 707 auto_trace_resolution.set_value_round (1); 708 auto_trace_resolution.show_icon (true); 709 710 auto_trace_resolution.new_value_action.connect ((self) => { 711 Glyph g = MainWindow.get_current_glyph (); 712 BackgroundImage? bg = g.get_background_image (); 713 BackgroundImage b; 714 715 if (bg != null) { 716 b = (!) bg; 717 b.set_trace_resolution (auto_trace_resolution.get_value ()); 718 b.update_background (); 719 } 720 }); 721 722 draw_tool_modifiers.add_tool (auto_trace_resolution); 723 724 auto_trace_simplify = new SpinButton ("auto_trace_simplify", t_("Autotrace simplification")); 725 auto_trace_simplify.set_value_round (0.5); 726 auto_trace_simplify.show_icon (true); 727 728 auto_trace_simplify.new_value_action.connect ((self) => { 729 Glyph g = MainWindow.get_current_glyph (); 730 BackgroundImage? bg = g.get_background_image (); 731 BackgroundImage b; 732 733 if (bg != null) { 734 b = (!) bg; 735 b.set_trace_simplification (auto_trace_simplify.get_value ()); 736 } 737 }); 738 739 draw_tool_modifiers.add_tool (auto_trace_simplify); 740 741 auto_trace = new Tool ("autotrace", t_("Autotrace background image")); 742 auto_trace.select_action.connect ((self) => { 743 Task t = new Task (); 744 t.task.connect (auto_trace_background); 745 MainWindow.native_window.run_background_thread (t); 746 }); 747 748 draw_tool_modifiers.add_tool (auto_trace); 749 750 delete_background = new Tool ("delete_background", t_("Delete background image")); 751 delete_background.select_action.connect ((self) => { 752 MainWindow.get_current_glyph ().delete_background (); 753 }); 754 755 draw_tool_modifiers.add_tool (delete_background); 756 757 if (BirdFont.has_argument ("--test")) { 758 Tool test_case = new Tool ("test_case"); 759 test_case.select_action.connect((self) => { 760 if (self.is_selected ()) { 761 if (TestBirdFont.is_running ()) { 762 TestBirdFont.pause (); 763 } else { 764 TestBirdFont.continue (); 765 } 766 } 767 }); 768 test_tools.add_tool (test_case); 769 770 Tool slow_test = new Tool ("slow_test"); 771 slow_test.select_action.connect((self) => { 772 bool s = TestBirdFont.is_slow_test (); 773 TestBirdFont.set_slow_test (!s); 774 s = TestBirdFont.is_slow_test (); 775 self.set_selected (s); 776 }); 777 778 test_tools.add_tool (slow_test); 779 780 // Run from commad line 781 string? st = BirdFont.get_argument ("--test"); 782 if (st != null && ((!)st).char_count () > 0) { 783 IdleSource idle = new IdleSource (); 784 785 idle.set_callback (() => { 786 MainWindow.get_toolbox ().select_tool (test_case); 787 return false; 788 }); 789 790 idle.attach (null); 791 } 792 793 if (BirdFont.has_argument ("--slow")) { 794 MainWindow.get_toolbox ().select_tool (slow_test); 795 } 796 797 } 798 799 // guide lines, grid and other guidlines 800 help_lines = new Tool ("help_lines", t_("Show guidelines")); 801 help_lines.select_action.connect ((self) => { 802 bool h; 803 h = GlyphCanvas.get_current_glyph ().get_show_help_lines (); 804 GlyphCanvas.get_current_glyph ().set_show_help_lines (!h); 805 self.set_selected (!h); 806 GlyphCanvas.get_current_glyph ().redraw_help_lines (); 807 }); 808 selected_line = GlyphCanvas.get_current_glyph ().get_show_help_lines (); 809 help_lines.set_selected (selected_line); 810 guideline_tools.add_tool (help_lines); 811 812 xheight_help_lines = new Tool ("show_xheight_helplines", t_("Show more guidelines")); 813 xheight_help_lines.select_action.connect ((self) => { 814 Glyph g = MainWindow.get_current_glyph (); 815 bool v = !g.get_xheight_lines_visible (); 816 g.set_xheight_lines_visible (v); 817 self.set_selected (v); 818 GlyphCanvas.redraw (); 819 820 if (v && !help_lines.is_selected ()) { 821 MainWindow.get_toolbox ().select_tool (help_lines); 822 } 823 }); 824 selected_line = GlyphCanvas.get_current_glyph ().get_xheight_lines_visible (); 825 xheight_help_lines.set_selected (selected_line); 826 guideline_tools.add_tool (xheight_help_lines); 827 828 background_help_lines = new Tool ("background_help_lines", t_("Show guidelines at top and bottom margin")); 829 background_help_lines.select_action.connect ((self) => { 830 Glyph g = MainWindow.get_current_glyph (); 831 bool v = !g.get_margin_lines_visible (); 832 g.set_margin_lines_visible (v); 833 self.set_selected (v); 834 GlyphCanvas.redraw (); 835 836 if (v && !help_lines.is_selected ()) { 837 MainWindow.get_toolbox ().select_tool (help_lines); 838 } 839 }); 840 selected_line = GlyphCanvas.get_current_glyph ().get_margin_lines_visible (); 841 background_help_lines.set_selected (selected_line); 842 guideline_tools.add_tool (background_help_lines); 843 844 Tool new_grid = new GridTool ("show_grid"); 845 guideline_tools.add_tool (new_grid); 846 847 // Zoom tools 848 zoom_bar = new ZoomBar (); 849 zoom_bar.new_zoom.connect ((z) => { 850 Glyph g = MainWindow.get_current_glyph (); 851 double zoom = 20 * z + 1; 852 double xc, yc, nxc, nyc; 853 854 xc = Glyph.path_coordinate_x (Glyph.xc ()); 855 yc = Glyph.path_coordinate_y (Glyph.yc ()); 856 857 g.set_zoom (zoom); 858 859 nxc = Glyph.path_coordinate_x (Glyph.xc ()); 860 nyc = Glyph.path_coordinate_y (Glyph.yc ()); 861 862 g.view_offset_x -= nxc - xc; 863 g.view_offset_y += nyc - yc; 864 865 GlyphCanvas.redraw (); 866 }); 867 zoombar_tool.add_tool (zoom_bar); 868 869 Tool reset_zoom = new Tool ("zoom_1_1", t_("Zoom Out More")); 870 reset_zoom.select_action.connect ((self) => { 871 zoom_tool.store_current_view (); 872 glyph_canvas.get_current_display ().reset_zoom (); 873 glyph_canvas.redraw_area(0, 0, GlyphCanvas.allocation.width, GlyphCanvas.allocation.height); 874 }); 875 view_tools.add_tool (reset_zoom); 876 reset_zoom.set_tool_visibility (false); 877 878 Tool full_glyph = new Tool ("full_glyph", t_("Show full glyph")); 879 full_glyph.select_action.connect((self) => { 880 zoom_tool.store_current_view (); 881 zoom_tool.zoom_full_glyph (); 882 }); 883 view_tools.add_tool (full_glyph); 884 885 Tool zoom_boundaries = new Tool ("zoom_boundaries", t_("Fit in view")); 886 zoom_boundaries.select_action.connect((self) => { 887 zoom_tool.store_current_view (); 888 glyph_canvas.get_current_display ().zoom_max (); 889 }); 890 view_tools.add_tool (zoom_boundaries); 891 892 Tool zoom_bg = new Tool ("zoom_background_image", t_("Zoom in on background image")); 893 zoom_bg.select_action.connect((self) => { 894 if (MainWindow.get_current_glyph ().get_background_image () != null) { 895 zoom_tool.store_current_view (); 896 ZoomTool.zoom_full_background_image (); 897 glyph_canvas.redraw_area(0, 0, GlyphCanvas.allocation.width, GlyphCanvas.allocation.height); 898 } 899 }); 900 view_tools.add_tool (zoom_bg); 901 902 Tool zoom_prev = new Tool ("prev", t_("Previous view")); 903 zoom_prev.select_action.connect((self) => { 904 zoom_tool.previous_view (); 905 }); 906 view_tools.add_tool (zoom_prev); 907 908 Tool zoom_next = new Tool ("next", t_("Next view")); 909 zoom_next.select_action.connect((self) => { 910 zoom_tool.next_view (); 911 }); 912 view_tools.add_tool (zoom_next); 913 zoom_next.set_tool_visibility (false); 914 915 // shape tools 916 circle = new CircleTool ("circle"); 917 circle.select_action.connect ((self) => { 918 update_drawing_and_background_tools (self); 919 }); 920 shape_tools.add_tool (circle); 921 922 rectangle = new RectangleTool ("rectangle"); 923 rectangle.select_action.connect ((self) => { 924 update_drawing_and_background_tools (self); 925 }); 926 shape_tools.add_tool (rectangle); 927 928 add_expander (font_name); 929 add_expander (draw_tools); 930 931 if (BirdFont.android) { 932 add_expander (key_tools); 933 } 934 935 add_expander (draw_tool_modifiers); 936 add_expander (guideline_tools); 937 add_expander (grid); 938 add_expander (zoombar_tool); 939 add_expander (view_tools); 940 add_expander (shape_tools); 941 942 // Fixa: add_expander (trace); 943 if (BirdFont.has_argument ("--test")) { 944 add_expander (test_tools); 945 } 946 947 draw_tools.set_persistent (true); 948 draw_tools.set_unique (false); 949 950 key_tools.set_persistent (false); 951 key_tools.set_unique (false); 952 953 draw_tool_modifiers.set_persistent (true); 954 draw_tool_modifiers.set_unique (false); 955 956 test_tools.set_persistent (true); 957 958 guideline_tools.set_persistent (true); 959 guideline_tools.set_unique (false); 960 961 grid.set_persistent (true); 962 grid.set_unique (true); 963 964 shape_tools.set_persistent (true); 965 shape_tools.set_unique (true); 966 967 // let these tools progagate events even when other tools are selected 968 foreach (Tool t in draw_tools.tool) { 969 t.editor_events = true; 970 } 971 972 foreach (Tool t in shape_tools.tool) { 973 t.editor_events = true; 974 t.persistent = true; 975 } 976 977 move_background.editor_events = true; 978 cut_background.editor_events = true; 979 move_canvas.editor_events = true; 980 981 move_background.persistent = true; 982 cut_background.persistent = true; 983 move_canvas.persistent = true; 984 985 // Default selection 986 IdleSource idle = new IdleSource (); 987 idle.set_callback (() => { 988 MainWindow.get_toolbox ().reset_active_tool (); 989 990 foresight_tool.set_selected (true); 991 update_drawing_and_background_tools (foresight_tool); 992 993 select_draw_tool (); 994 set_point_type_from_preferences (); 995 996 if (GlyphCanvas.get_current_glyph ().get_show_help_lines ()) { 997 help_lines.set_selected (true); 998 help_lines.set_active (false); 999 } 1000 1001 add_new_grid (1); 1002 add_new_grid (2); 1003 add_new_grid (4); 1004 1005 MainWindow.get_toolbox ().move (0, 0); 1006 1007 return false; 1008 }); 1009 idle.attach (null); 1010 1011 // update selelction when the user switches tab 1012 MainWindow.get_tab_bar ().signal_tab_selected.connect((tab) => { 1013 Glyph glyph; 1014 1015 if (tab.get_display () is Glyph) { 1016 glyph = (Glyph) tab.get_display (); 1017 show_bg.set_selected (glyph.get_background_visible ()); 1018 update_line_selection (glyph); 1019 } 1020 }); 1021 } 1022 1023 void auto_trace_background () { 1024 Glyph g = MainWindow.get_current_glyph (); 1025 BackgroundImage? bg = g.get_background_image (); 1026 BackgroundImage b; 1027 PathList pl; 1028 1029 if (bg != null) { 1030 b = (!) bg; 1031 pl = b.autotrace (); 1032 foreach (Path p in pl.paths) { 1033 g.add_path (p); 1034 } 1035 } 1036 } 1037 1038 void update_line_selection (Glyph glyph) { 1039 help_lines.set_selected (glyph.get_show_help_lines ()); 1040 xheight_help_lines.set_selected (glyph.get_xheight_lines_visible ()); 1041 background_help_lines.set_selected (glyph.get_margin_lines_visible ()); 1042 } 1043 1044 public static void set_point_type_from_preferences () { 1045 string type = Preferences.get ("point_type"); 1046 if (type == "double_points") { 1047 Toolbox.select_tool_by_name ("double_points"); 1048 } else if (type == "quadratic_points") { 1049 Toolbox.select_tool_by_name ("quadratic_points"); 1050 } if (type == "cubic_points") { 1051 Toolbox.select_tool_by_name ("cubic_points"); 1052 } 1053 } 1054 1055 void hide_all_modifiers () { 1056 x_coordinate.set_tool_visibility (false); 1057 y_coordinate.set_tool_visibility (false); 1058 rotation.set_tool_visibility (false); 1059 width.set_tool_visibility (false); 1060 height.set_tool_visibility (false); 1061 skew.set_tool_visibility (false); 1062 reverse_path_tool.set_tool_visibility (false); 1063 move_layer.set_tool_visibility (false); 1064 flip_vertical.set_tool_visibility (false); 1065 flip_horizontal.set_tool_visibility (false); 1066 1067 tie_handles.set_tool_visibility (false); 1068 reflect_handle.set_tool_visibility (false); 1069 create_line.set_tool_visibility (false); 1070 1071 quadratic_points.set_tool_visibility (false); 1072 cubic_points.set_tool_visibility (false); 1073 double_points.set_tool_visibility (false); 1074 convert_points.set_tool_visibility (false); 1075 1076 cut_background.set_tool_visibility (false); 1077 show_bg.set_tool_visibility (false); 1078 bg_selection.set_tool_visibility (false); 1079 background_threshold.set_tool_visibility (false); 1080 background_scale.set_tool_visibility (false); 1081 high_contrast_background.set_tool_visibility (false); 1082 auto_trace_resolution.set_tool_visibility (false); 1083 auto_trace.set_tool_visibility (false); 1084 auto_trace_simplify.set_tool_visibility (false); 1085 delete_background.set_tool_visibility (false); 1086 } 1087 1088 void show_background_tool_modifiers () { 1089 draw_tool_modifiers.set_headline (t_("Background Tools")); 1090 1091 cut_background.set_tool_visibility (true); 1092 show_bg.set_tool_visibility (true); 1093 bg_selection.set_tool_visibility (true); 1094 background_threshold.set_tool_visibility (true); 1095 background_scale.set_tool_visibility (true); 1096 high_contrast_background.set_tool_visibility (true); 1097 auto_trace_resolution.set_tool_visibility (true); 1098 auto_trace.set_tool_visibility (true); 1099 auto_trace_simplify.set_tool_visibility (true); 1100 delete_background.set_tool_visibility (true); 1101 } 1102 1103 void show_point_tool_modifiers () { 1104 draw_tool_modifiers.set_headline (t_("Control Points")); 1105 1106 tie_handles.set_tool_visibility (true); 1107 reflect_handle.set_tool_visibility (true); 1108 create_line.set_tool_visibility (true); 1109 1110 quadratic_points.set_tool_visibility (true); 1111 cubic_points.set_tool_visibility (true); 1112 double_points.set_tool_visibility (true); 1113 convert_points.set_tool_visibility (true); 1114 1115 reverse_path_tool.set_tool_visibility (true); 1116 } 1117 1118 void show_object_tool_modifiers () { 1119 draw_tool_modifiers.set_headline (t_("Object Tools")); 1120 1121 x_coordinate.set_tool_visibility (true); 1122 y_coordinate.set_tool_visibility (true); 1123 rotation.set_tool_visibility (true); 1124 width.set_tool_visibility (true); 1125 height.set_tool_visibility (true); 1126 skew.set_tool_visibility (true); 1127 1128 reverse_path_tool.set_tool_visibility (true); 1129 move_layer.set_tool_visibility (true); 1130 flip_vertical.set_tool_visibility (true); 1131 flip_horizontal.set_tool_visibility (true); 1132 } 1133 1134 public void update_drawing_and_background_tools (Tool current_tool) { 1135 IdleSource idle = new IdleSource (); 1136 1137 idle.set_callback (() => { 1138 Glyph g = MainWindow.get_current_glyph (); 1139 1140 hide_all_modifiers (); 1141 1142 move_background.set_selected (false); 1143 cut_background.set_selected (false); 1144 1145 foresight_tool.set_selected (false); 1146 pen_tool.set_selected (false); 1147 point_tool.set_selected (false); 1148 zoom_tool.set_selected (false); 1149 move_tool.set_selected (false); 1150 resize_tool.set_selected (false); 1151 stroke_tool.set_selected (false); 1152 track_tool.set_selected (false); 1153 move_canvas.set_selected (false); 1154 delete_background.set_selected (false); 1155 1156 show_bg.set_selected (g.get_background_visible ()); 1157 show_bg.set_active (false); 1158 bg_selection.set_selected (false); 1159 background_scale.set_active (false); 1160 1161 rectangle.set_selected (false); 1162 circle.set_selected (false); 1163 1164 reverse_path_tool.set_selected (false); 1165 move_layer.set_selected (false); 1166 flip_vertical.set_selected (false); 1167 flip_horizontal.set_selected (false); 1168 1169 current_tool.set_selected (true); 1170 1171 if (resize_tool.is_selected () || move_tool.is_selected ()) { 1172 show_object_tool_modifiers (); 1173 } else if (foresight_tool.is_selected () 1174 || pen_tool.is_selected () 1175 || point_tool.is_selected () 1176 || track_tool.is_selected ()) { 1177 show_point_tool_modifiers (); 1178 } else if (move_background.is_selected () 1179 || cut_background.is_selected () 1180 || show_bg.is_selected () 1181 || high_contrast_background.is_selected () 1182 || auto_trace.is_selected ()) { 1183 show_background_tool_modifiers (); 1184 } 1185 1186 MainWindow.get_toolbox ().update_expanders (); 1187 Toolbox.redraw_tool_box (); 1188 1189 return false; 1190 }); 1191 1192 idle.attach (null); 1193 } 1194 1195 void update_type_selection () { 1196 IdleSource idle = new IdleSource (); 1197 1198 // Do this in idle, after the animation 1199 idle.set_callback (() => { 1200 quadratic_points.set_selected (false); 1201 cubic_points.set_selected (false); 1202 double_points.set_selected (false); 1203 1204 switch (point_type) { 1205 case PointType.QUADRATIC: 1206 quadratic_points.set_selected (true); 1207 break; 1208 case PointType.CUBIC: 1209 cubic_points.set_selected (true); 1210 break; 1211 case PointType.DOUBLE_CURVE: 1212 double_points.set_selected (true); 1213 break; 1214 } 1215 1216 convert_points.set_selected (false); 1217 1218 Toolbox.redraw_tool_box (); 1219 return false; 1220 }); 1221 1222 idle.attach (null); 1223 } 1224 1225 public override Gee.ArrayList<Expander> get_expanders () { 1226 return expanders; 1227 } 1228 1229 /** Insert new points of this type. */ 1230 public static PointType get_selected_point_type () { 1231 return point_type; 1232 } 1233 1234 private void select_draw_tool () { 1235 Toolbox.select_tool_by_name ("pen_tool"); 1236 } 1237 1238 public void remove_all_grid_buttons () { 1239 grid_expander.tool.clear (); 1240 1241 GridTool.sizes.clear (); 1242 1243 MainWindow.get_toolbox ().update_expanders (); 1244 MainWindow.get_toolbox ().redraw (0, 0, Toolbox.allocation_width, Toolbox.allocation_height); 1245 } 1246 1247 public void parse_grid (string spin_button_value) { 1248 SpinButton sb = add_new_grid (); 1249 sb.set_value (spin_button_value); 1250 MainWindow.get_toolbox ().select_tool (sb); 1251 } 1252 1253 public SpinButton add_new_grid (double size = 2) { 1254 SpinButton grid_width = new SpinButton ("grid_width", t_("Set size for grid")); 1255 Toolbox tb = MainWindow.get_toolbox (); 1256 1257 grid_width.new_value_action.connect((self) => { 1258 grid_width.select_action (grid_width); 1259 }); 1260 1261 grid_width.select_action.connect((self) => { 1262 SpinButton sb = (SpinButton) self; 1263 GridTool.set_grid_width (sb.get_value ()); 1264 GlyphCanvas.redraw (); 1265 }); 1266 1267 grid_expander.add_tool (grid_width); 1268 1269 GridTool.sizes.add (grid_width); 1270 1271 grid_width.set_value_round (size); 1272 1273 tb.update_expanders (); 1274 1275 tb.redraw (0, 0, Toolbox.allocation_width, Toolbox.allocation_height); 1276 1277 tb.select_tool (grid_width); 1278 grid_width.set_active (false); 1279 1280 return grid_width; 1281 } 1282 1283 public void remove_current_grid () { 1284 Tool grid_width; 1285 Toolbox tb = MainWindow.get_toolbox (); 1286 1287 foreach (Tool t in grid_expander.tool) { 1288 if (t.is_selected () && t is SpinButton) { 1289 GridTool.sizes.remove ((SpinButton)t); 1290 grid_expander.tool.remove (t); 1291 break; 1292 } 1293 } 1294 1295 if (grid_expander.tool.size > 0) { 1296 grid_width = grid_expander.tool.get (grid_expander.tool.size - 1); 1297 tb.select_tool (grid_width); 1298 grid_width.set_active (false); 1299 } 1300 1301 MainWindow.get_toolbox ().update_expanders (); 1302 tb.redraw (0, 0, Toolbox.allocation_width, Toolbox.allocation_height); 1303 } 1304 1305 private void add_expander (Expander e) { 1306 expanders.add (e); 1307 } 1308 1309 public override Gee.ArrayList<string> get_displays () { 1310 Gee.ArrayList<string> d = new Gee.ArrayList<string> (); 1311 d.add ("Glyph"); 1312 return d; 1313 } 1314 } 1315 1316 } 1317