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