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.
Suppress zoom when canvas is moving
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 Tool 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 MainWindow.get_current_glyph ().update_view (); 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 Tool ("reverse_path", t_("Create counter from outline")); 583 reverse_path_tool.select_action.connect ((self) => { 584 Glyph g = MainWindow.get_current_glyph (); 585 586 foreach (Path p in g.active_paths) { 587 p.reverse (); 588 } 589 590 g.redraw_area (0, 0, g.allocation.width, g.allocation.height); 591 }); 592 draw_tool_modifiers.add_tool (reverse_path_tool); 593 594 move_layer = new Tool ("move_layer", t_("Move to path to the bottom layer")); 595 move_layer.select_action.connect ((self) => { 596 Glyph g = MainWindow.get_current_glyph (); 597 598 foreach (Path p in g.active_paths) { 599 g.path_list.remove (p); 600 g.path_list.insert (0, p); 601 } 602 }); 603 draw_tool_modifiers.add_tool (move_layer); 604 605 flip_vertical = new Tool ("flip_vertical", t_("Flip path vertically")); 606 flip_vertical.select_action.connect ((self) => { 607 MoveTool.flip_vertical (); 608 MainWindow.get_current_glyph ().update_view (); 609 }); 610 draw_tool_modifiers.add_tool (flip_vertical); 611 612 flip_horizontal = new Tool ("flip_horizontal", t_("Flip path horizontally")); 613 flip_horizontal.select_action.connect ((self) => { 614 MoveTool.flip_horizontal (); 615 MainWindow.get_current_glyph ().update_view (); 616 }); 617 draw_tool_modifiers.add_tool (flip_horizontal); 618 619 // background tools 620 background_scale = new SpinButton ("scale_background", t_("Set size for background image")); 621 background_scale.show_icon (true); 622 background_scale.set_int_value ("1.000"); 623 624 background_scale.new_value_action.connect((self) => { 625 background_scale.select_action (self); 626 }); 627 628 background_scale.select_action.connect((self) => { 629 SpinButton sb = (SpinButton) self; 630 Glyph g = MainWindow.get_current_glyph (); 631 BackgroundImage? img = g.get_background_image (); 632 double s = sb.get_value (); 633 BackgroundImage i; 634 double xc, yc; 635 636 if (img != null) { 637 i = (!) img; 638 xc = i.img_middle_x; 639 yc = i.img_middle_y; 640 641 i.set_img_scale (s, s); 642 643 i.img_middle_x = xc; 644 i.img_middle_y = yc; 645 } 646 647 GlyphCanvas.redraw (); 648 }); 649 650 draw_tool_modifiers.add_tool (background_scale); 651 652 cut_background = new CutBackgroundTool ("cut_background"); 653 cut_background.select_action.connect ((self) => { 654 update_drawing_and_background_tools (self); 655 }); 656 draw_tool_modifiers.add_tool (cut_background); 657 658 show_bg = new Tool ("show_background", t_("Show/hide background image")); 659 show_bg.select_action.connect ((self) => { 660 update_drawing_and_background_tools (self); 661 }); 662 show_bg.select_action.connect ((self) => { 663 Glyph g = MainWindow.get_current_glyph (); 664 g.set_background_visible (!g.get_background_visible ()); 665 GlyphCanvas.redraw (); 666 }); 667 draw_tool_modifiers.add_tool (show_bg); 668 669 bg_selection = new Tool ("insert_background", t_("Insert a new background image")); 670 bg_selection.select_action.connect ((self) => { 671 update_drawing_and_background_tools (self); 672 }); 673 674 bg_selection.select_action.connect((self) => { 675 if (MainWindow.get_current_display () is Glyph) { 676 BackgroundTool.import_background_image (); 677 } 678 }); 679 680 bg_selection.set_show_background (true); 681 draw_tool_modifiers.add_tool (bg_selection); 682 683 high_contrast_background = new Tool ("high_contrast_background", t_("High contrast")); 684 high_contrast_background.select_action.connect ((self) => { 685 Glyph g = MainWindow.get_current_glyph (); 686 BackgroundImage? bg = g.get_background_image (); 687 BackgroundImage b; 688 689 if (bg != null) { 690 b = (!) bg; 691 b.set_high_contrast (!b.high_contrast); 692 b.update_background (); 693 } 694 }); 695 draw_tool_modifiers.add_tool (high_contrast_background); 696 697 background_threshold = new SpinButton ("contrast_threshold", t_("Set background threshold")); 698 background_threshold.show_icon (true); 699 background_threshold.set_value_round (1); 700 701 background_threshold.new_value_action.connect ((self) => { 702 Glyph g = MainWindow.get_current_glyph (); 703 BackgroundImage? bg = g.get_background_image (); 704 BackgroundImage b; 705 706 if (bg != null) { 707 b = (!) bg; 708 b.set_threshold (background_threshold.get_value ()); 709 b.update_background (); 710 } 711 }); 712 713 draw_tool_modifiers.add_tool (background_threshold); 714 715 auto_trace_resolution = new SpinButton ("auto_trace_resolution", t_("Amount of autotrace details")); 716 auto_trace_resolution.set_value_round (1); 717 auto_trace_resolution.show_icon (true); 718 719 auto_trace_resolution.new_value_action.connect ((self) => { 720 Glyph g = MainWindow.get_current_glyph (); 721 BackgroundImage? bg = g.get_background_image (); 722 BackgroundImage b; 723 724 if (bg != null) { 725 b = (!) bg; 726 b.set_trace_resolution (auto_trace_resolution.get_value ()); 727 b.update_background (); 728 } 729 }); 730 731 draw_tool_modifiers.add_tool (auto_trace_resolution); 732 733 auto_trace_simplify = new SpinButton ("auto_trace_simplify", t_("Autotrace simplification")); 734 auto_trace_simplify.set_value_round (0.5); 735 auto_trace_simplify.show_icon (true); 736 737 auto_trace_simplify.new_value_action.connect ((self) => { 738 Glyph g = MainWindow.get_current_glyph (); 739 BackgroundImage? bg = g.get_background_image (); 740 BackgroundImage b; 741 742 if (bg != null) { 743 b = (!) bg; 744 b.set_trace_simplification (auto_trace_simplify.get_value ()); 745 } 746 }); 747 748 draw_tool_modifiers.add_tool (auto_trace_simplify); 749 750 auto_trace = new Tool ("autotrace", t_("Autotrace background image")); 751 auto_trace.select_action.connect ((self) => { 752 Task t = new Task (); 753 t.task.connect (auto_trace_background); 754 MainWindow.native_window.run_background_thread (t); 755 }); 756 757 draw_tool_modifiers.add_tool (auto_trace); 758 759 delete_background = new Tool ("delete_background", t_("Delete background image")); 760 delete_background.select_action.connect ((self) => { 761 MainWindow.get_current_glyph ().delete_background (); 762 }); 763 764 draw_tool_modifiers.add_tool (delete_background); 765 766 if (BirdFont.has_argument ("--test")) { 767 Tool test_case = new Tool ("test_case"); 768 test_case.select_action.connect((self) => { 769 if (self.is_selected ()) { 770 if (TestBirdFont.is_running ()) { 771 TestBirdFont.pause (); 772 } else { 773 TestBirdFont.continue (); 774 } 775 } 776 }); 777 test_tools.add_tool (test_case); 778 779 Tool slow_test = new Tool ("slow_test"); 780 slow_test.select_action.connect((self) => { 781 bool s = TestBirdFont.is_slow_test (); 782 TestBirdFont.set_slow_test (!s); 783 s = TestBirdFont.is_slow_test (); 784 self.set_selected (s); 785 }); 786 787 test_tools.add_tool (slow_test); 788 789 // Run from commad line 790 string? st = BirdFont.get_argument ("--test"); 791 if (st != null && ((!)st).char_count () > 0) { 792 IdleSource idle = new IdleSource (); 793 794 idle.set_callback (() => { 795 MainWindow.get_toolbox ().select_tool (test_case); 796 return false; 797 }); 798 799 idle.attach (null); 800 } 801 802 if (BirdFont.has_argument ("--slow")) { 803 MainWindow.get_toolbox ().select_tool (slow_test); 804 } 805 806 } 807 808 // guide lines, grid and other guidlines 809 help_lines = new Tool ("help_lines", t_("Show guidelines")); 810 help_lines.select_action.connect ((self) => { 811 bool h; 812 h = GlyphCanvas.get_current_glyph ().get_show_help_lines (); 813 GlyphCanvas.get_current_glyph ().set_show_help_lines (!h); 814 self.set_selected (!h); 815 GlyphCanvas.get_current_glyph ().redraw_help_lines (); 816 }); 817 selected_line = GlyphCanvas.get_current_glyph ().get_show_help_lines (); 818 help_lines.set_selected (selected_line); 819 guideline_tools.add_tool (help_lines); 820 821 xheight_help_lines = new Tool ("show_xheight_helplines", t_("Show more guidelines")); 822 xheight_help_lines.select_action.connect ((self) => { 823 Glyph g = MainWindow.get_current_glyph (); 824 bool v = !g.get_xheight_lines_visible (); 825 g.set_xheight_lines_visible (v); 826 self.set_selected (v); 827 GlyphCanvas.redraw (); 828 829 if (v && !help_lines.is_selected ()) { 830 MainWindow.get_toolbox ().select_tool (help_lines); 831 } 832 }); 833 selected_line = GlyphCanvas.get_current_glyph ().get_xheight_lines_visible (); 834 xheight_help_lines.set_selected (selected_line); 835 guideline_tools.add_tool (xheight_help_lines); 836 837 background_help_lines = new Tool ("background_help_lines", t_("Show guidelines at top and bottom margin")); 838 background_help_lines.select_action.connect ((self) => { 839 Glyph g = MainWindow.get_current_glyph (); 840 bool v = !g.get_margin_lines_visible (); 841 g.set_margin_lines_visible (v); 842 self.set_selected (v); 843 GlyphCanvas.redraw (); 844 845 if (v && !help_lines.is_selected ()) { 846 MainWindow.get_toolbox ().select_tool (help_lines); 847 } 848 }); 849 selected_line = GlyphCanvas.get_current_glyph ().get_margin_lines_visible (); 850 background_help_lines.set_selected (selected_line); 851 guideline_tools.add_tool (background_help_lines); 852 853 Tool new_grid = new GridTool ("show_grid"); 854 guideline_tools.add_tool (new_grid); 855 856 // Zoom tools 857 zoom_bar = new ZoomBar (); 858 zoom_bar.new_zoom.connect ((z) => { 859 Glyph g = MainWindow.get_current_glyph (); 860 double zoom = 20 * z + 1; 861 double xc, yc, nxc, nyc; 862 863 xc = Glyph.path_coordinate_x (Glyph.xc ()); 864 yc = Glyph.path_coordinate_y (Glyph.yc ()); 865 866 g.set_zoom (zoom); 867 868 nxc = Glyph.path_coordinate_x (Glyph.xc ()); 869 nyc = Glyph.path_coordinate_y (Glyph.yc ()); 870 871 g.view_offset_x -= nxc - xc; 872 g.view_offset_y += nyc - yc; 873 874 GlyphCanvas.redraw (); 875 }); 876 zoombar_tool.add_tool (zoom_bar); 877 878 Tool reset_zoom = new Tool ("zoom_1_1", t_("Zoom Out More")); 879 reset_zoom.select_action.connect ((self) => { 880 zoom_tool.store_current_view (); 881 glyph_canvas.get_current_display ().reset_zoom (); 882 glyph_canvas.redraw_area(0, 0, GlyphCanvas.allocation.width, GlyphCanvas.allocation.height); 883 }); 884 view_tools.add_tool (reset_zoom); 885 reset_zoom.set_tool_visibility (false); 886 887 Tool full_glyph = new Tool ("full_glyph", t_("Show full glyph")); 888 full_glyph.select_action.connect((self) => { 889 zoom_tool.store_current_view (); 890 zoom_tool.zoom_full_glyph (); 891 }); 892 view_tools.add_tool (full_glyph); 893 894 Tool zoom_boundaries = new Tool ("zoom_boundaries", t_("Fit in view")); 895 zoom_boundaries.select_action.connect((self) => { 896 zoom_tool.store_current_view (); 897 glyph_canvas.get_current_display ().zoom_max (); 898 }); 899 view_tools.add_tool (zoom_boundaries); 900 901 Tool zoom_bg = new Tool ("zoom_background_image", t_("Zoom in on background image")); 902 zoom_bg.select_action.connect((self) => { 903 if (MainWindow.get_current_glyph ().get_background_image () != null) { 904 zoom_tool.store_current_view (); 905 ZoomTool.zoom_full_background_image (); 906 glyph_canvas.redraw_area(0, 0, GlyphCanvas.allocation.width, GlyphCanvas.allocation.height); 907 } 908 }); 909 view_tools.add_tool (zoom_bg); 910 911 Tool zoom_prev = new Tool ("prev", t_("Previous view")); 912 zoom_prev.select_action.connect((self) => { 913 zoom_tool.previous_view (); 914 }); 915 view_tools.add_tool (zoom_prev); 916 917 Tool zoom_next = new Tool ("next", t_("Next view")); 918 zoom_next.select_action.connect((self) => { 919 zoom_tool.next_view (); 920 }); 921 view_tools.add_tool (zoom_next); 922 zoom_next.set_tool_visibility (false); 923 924 // shape tools 925 circle = new CircleTool ("circle"); 926 circle.select_action.connect ((self) => { 927 update_drawing_and_background_tools (self); 928 }); 929 shape_tools.add_tool (circle); 930 931 rectangle = new RectangleTool ("rectangle"); 932 rectangle.select_action.connect ((self) => { 933 update_drawing_and_background_tools (self); 934 }); 935 shape_tools.add_tool (rectangle); 936 937 add_expander (font_name); 938 add_expander (draw_tools); 939 940 if (BirdFont.android) { 941 add_expander (key_tools); 942 } 943 944 add_expander (draw_tool_modifiers); 945 add_expander (guideline_tools); 946 add_expander (grid); 947 add_expander (zoombar_tool); 948 add_expander (view_tools); 949 add_expander (shape_tools); 950 951 // Fixa: add_expander (trace); 952 if (BirdFont.has_argument ("--test")) { 953 add_expander (test_tools); 954 } 955 956 draw_tools.set_persistent (true); 957 draw_tools.set_unique (false); 958 959 key_tools.set_persistent (false); 960 key_tools.set_unique (false); 961 962 draw_tool_modifiers.set_persistent (true); 963 draw_tool_modifiers.set_unique (false); 964 965 test_tools.set_persistent (true); 966 967 guideline_tools.set_persistent (true); 968 guideline_tools.set_unique (false); 969 970 grid.set_persistent (true); 971 grid.set_unique (true); 972 973 shape_tools.set_persistent (true); 974 shape_tools.set_unique (true); 975 976 // let these tools progagate events even when other tools are selected 977 foreach (Tool t in draw_tools.tool) { 978 t.editor_events = true; 979 } 980 981 foreach (Tool t in shape_tools.tool) { 982 t.editor_events = true; 983 t.persistent = true; 984 } 985 986 move_background.editor_events = true; 987 cut_background.editor_events = true; 988 move_canvas.editor_events = true; 989 990 move_background.persistent = true; 991 cut_background.persistent = true; 992 move_canvas.persistent = true; 993 994 // Default selection 995 IdleSource idle = new IdleSource (); 996 idle.set_callback (() => { 997 MainWindow.get_toolbox ().reset_active_tool (); 998 999 foresight_tool.set_selected (true); 1000 update_drawing_and_background_tools (foresight_tool); 1001 1002 select_draw_tool (); 1003 set_point_type_from_preferences (); 1004 1005 if (GlyphCanvas.get_current_glyph ().get_show_help_lines ()) { 1006 help_lines.set_selected (true); 1007 help_lines.set_active (false); 1008 } 1009 1010 add_new_grid (1); 1011 add_new_grid (2); 1012 add_new_grid (4); 1013 1014 MainWindow.get_toolbox ().move (0, 0); 1015 1016 return false; 1017 }); 1018 idle.attach (null); 1019 1020 // update selelction when the user switches tab 1021 MainWindow.get_tab_bar ().signal_tab_selected.connect((tab) => { 1022 Glyph glyph; 1023 1024 if (tab.get_display () is Glyph) { 1025 glyph = (Glyph) tab.get_display (); 1026 show_bg.set_selected (glyph.get_background_visible ()); 1027 update_line_selection (glyph); 1028 } 1029 }); 1030 } 1031 1032 void auto_trace_background () { 1033 Glyph g = MainWindow.get_current_glyph (); 1034 BackgroundImage? bg = g.get_background_image (); 1035 BackgroundImage b; 1036 PathList pl; 1037 1038 if (bg != null) { 1039 b = (!) bg; 1040 pl = b.autotrace (); 1041 foreach (Path p in pl.paths) { 1042 g.add_path (p); 1043 } 1044 } 1045 } 1046 1047 void update_line_selection (Glyph glyph) { 1048 help_lines.set_selected (glyph.get_show_help_lines ()); 1049 xheight_help_lines.set_selected (glyph.get_xheight_lines_visible ()); 1050 background_help_lines.set_selected (glyph.get_margin_lines_visible ()); 1051 } 1052 1053 public static void set_point_type_from_preferences () { 1054 string type = Preferences.get ("point_type"); 1055 if (type == "double_points") { 1056 Toolbox.select_tool_by_name ("double_points"); 1057 } else if (type == "quadratic_points") { 1058 Toolbox.select_tool_by_name ("quadratic_points"); 1059 } if (type == "cubic_points") { 1060 Toolbox.select_tool_by_name ("cubic_points"); 1061 } 1062 } 1063 1064 void hide_all_modifiers () { 1065 x_coordinate.set_tool_visibility (false); 1066 y_coordinate.set_tool_visibility (false); 1067 rotation.set_tool_visibility (false); 1068 width.set_tool_visibility (false); 1069 height.set_tool_visibility (false); 1070 skew.set_tool_visibility (false); 1071 reverse_path_tool.set_tool_visibility (false); 1072 move_layer.set_tool_visibility (false); 1073 flip_vertical.set_tool_visibility (false); 1074 flip_horizontal.set_tool_visibility (false); 1075 1076 tie_handles.set_tool_visibility (false); 1077 reflect_handle.set_tool_visibility (false); 1078 create_line.set_tool_visibility (false); 1079 1080 quadratic_points.set_tool_visibility (false); 1081 cubic_points.set_tool_visibility (false); 1082 double_points.set_tool_visibility (false); 1083 convert_points.set_tool_visibility (false); 1084 1085 cut_background.set_tool_visibility (false); 1086 show_bg.set_tool_visibility (false); 1087 bg_selection.set_tool_visibility (false); 1088 background_threshold.set_tool_visibility (false); 1089 background_scale.set_tool_visibility (false); 1090 high_contrast_background.set_tool_visibility (false); 1091 auto_trace_resolution.set_tool_visibility (false); 1092 auto_trace.set_tool_visibility (false); 1093 auto_trace_simplify.set_tool_visibility (false); 1094 delete_background.set_tool_visibility (false); 1095 } 1096 1097 void show_background_tool_modifiers () { 1098 draw_tool_modifiers.set_headline (t_("Background Tools")); 1099 1100 cut_background.set_tool_visibility (true); 1101 show_bg.set_tool_visibility (true); 1102 bg_selection.set_tool_visibility (true); 1103 background_threshold.set_tool_visibility (true); 1104 background_scale.set_tool_visibility (true); 1105 high_contrast_background.set_tool_visibility (true); 1106 auto_trace_resolution.set_tool_visibility (true); 1107 auto_trace.set_tool_visibility (true); 1108 auto_trace_simplify.set_tool_visibility (true); 1109 delete_background.set_tool_visibility (true); 1110 } 1111 1112 void show_point_tool_modifiers () { 1113 draw_tool_modifiers.set_headline (t_("Control Points")); 1114 1115 tie_handles.set_tool_visibility (true); 1116 reflect_handle.set_tool_visibility (true); 1117 create_line.set_tool_visibility (true); 1118 1119 quadratic_points.set_tool_visibility (true); 1120 cubic_points.set_tool_visibility (true); 1121 double_points.set_tool_visibility (true); 1122 convert_points.set_tool_visibility (true); 1123 1124 reverse_path_tool.set_tool_visibility (true); 1125 } 1126 1127 void show_object_tool_modifiers () { 1128 draw_tool_modifiers.set_headline (t_("Object Tools")); 1129 1130 x_coordinate.set_tool_visibility (true); 1131 y_coordinate.set_tool_visibility (true); 1132 rotation.set_tool_visibility (true); 1133 width.set_tool_visibility (true); 1134 height.set_tool_visibility (true); 1135 skew.set_tool_visibility (true); 1136 1137 reverse_path_tool.set_tool_visibility (true); 1138 move_layer.set_tool_visibility (true); 1139 flip_vertical.set_tool_visibility (true); 1140 flip_horizontal.set_tool_visibility (true); 1141 } 1142 1143 public void update_drawing_and_background_tools (Tool current_tool) { 1144 IdleSource idle = new IdleSource (); 1145 1146 idle.set_callback (() => { 1147 Glyph g = MainWindow.get_current_glyph (); 1148 1149 hide_all_modifiers (); 1150 1151 move_background.set_selected (false); 1152 cut_background.set_selected (false); 1153 1154 foresight_tool.set_selected (false); 1155 pen_tool.set_selected (false); 1156 point_tool.set_selected (false); 1157 zoom_tool.set_selected (false); 1158 move_tool.set_selected (false); 1159 resize_tool.set_selected (false); 1160 stroke_tool.set_selected (false); 1161 track_tool.set_selected (false); 1162 move_canvas.set_selected (false); 1163 delete_background.set_selected (false); 1164 1165 show_bg.set_selected (g.get_background_visible ()); 1166 show_bg.set_active (false); 1167 bg_selection.set_selected (false); 1168 background_scale.set_active (false); 1169 1170 rectangle.set_selected (false); 1171 circle.set_selected (false); 1172 1173 reverse_path_tool.set_selected (false); 1174 move_layer.set_selected (false); 1175 flip_vertical.set_selected (false); 1176 flip_horizontal.set_selected (false); 1177 1178 current_tool.set_selected (true); 1179 1180 if (resize_tool.is_selected () || move_tool.is_selected ()) { 1181 show_object_tool_modifiers (); 1182 } else if (foresight_tool.is_selected () 1183 || pen_tool.is_selected () 1184 || point_tool.is_selected () 1185 || track_tool.is_selected ()) { 1186 show_point_tool_modifiers (); 1187 } else if (move_background.is_selected () 1188 || cut_background.is_selected () 1189 || show_bg.is_selected () 1190 || high_contrast_background.is_selected () 1191 || auto_trace.is_selected ()) { 1192 show_background_tool_modifiers (); 1193 } 1194 1195 MainWindow.get_toolbox ().update_expanders (); 1196 Toolbox.redraw_tool_box (); 1197 1198 return false; 1199 }); 1200 1201 idle.attach (null); 1202 } 1203 1204 void update_type_selection () { 1205 IdleSource idle = new IdleSource (); 1206 1207 // Do this in idle, after the animation 1208 idle.set_callback (() => { 1209 quadratic_points.set_selected (false); 1210 cubic_points.set_selected (false); 1211 double_points.set_selected (false); 1212 1213 switch (point_type) { 1214 case PointType.QUADRATIC: 1215 quadratic_points.set_selected (true); 1216 break; 1217 case PointType.CUBIC: 1218 cubic_points.set_selected (true); 1219 break; 1220 case PointType.DOUBLE_CURVE: 1221 double_points.set_selected (true); 1222 break; 1223 } 1224 1225 convert_points.set_selected (false); 1226 1227 Toolbox.redraw_tool_box (); 1228 return false; 1229 }); 1230 1231 idle.attach (null); 1232 } 1233 1234 public override Gee.ArrayList<Expander> get_expanders () { 1235 return expanders; 1236 } 1237 1238 /** Insert new points of this type. */ 1239 public static PointType get_selected_point_type () { 1240 return point_type; 1241 } 1242 1243 private void select_draw_tool () { 1244 Toolbox.select_tool_by_name ("pen_tool"); 1245 } 1246 1247 public void remove_all_grid_buttons () { 1248 grid_expander.tool.clear (); 1249 1250 GridTool.sizes.clear (); 1251 1252 MainWindow.get_toolbox ().update_expanders (); 1253 MainWindow.get_toolbox ().redraw (0, 0, Toolbox.allocation_width, Toolbox.allocation_height); 1254 } 1255 1256 public void parse_grid (string spin_button_value) { 1257 SpinButton sb = add_new_grid (); 1258 sb.set_value (spin_button_value); 1259 MainWindow.get_toolbox ().select_tool (sb); 1260 } 1261 1262 public SpinButton add_new_grid (double size = 2) { 1263 SpinButton grid_width = new SpinButton ("grid_width", t_("Set size for grid")); 1264 Toolbox tb = MainWindow.get_toolbox (); 1265 1266 grid_width.new_value_action.connect((self) => { 1267 grid_width.select_action (grid_width); 1268 }); 1269 1270 grid_width.select_action.connect((self) => { 1271 SpinButton sb = (SpinButton) self; 1272 GridTool.set_grid_width (sb.get_value ()); 1273 GlyphCanvas.redraw (); 1274 }); 1275 1276 grid_expander.add_tool (grid_width); 1277 1278 GridTool.sizes.add (grid_width); 1279 1280 grid_width.set_value_round (size); 1281 1282 tb.update_expanders (); 1283 1284 tb.redraw (0, 0, Toolbox.allocation_width, Toolbox.allocation_height); 1285 1286 tb.select_tool (grid_width); 1287 grid_width.set_active (false); 1288 1289 return grid_width; 1290 } 1291 1292 public void remove_current_grid () { 1293 Tool grid_width; 1294 Toolbox tb = MainWindow.get_toolbox (); 1295 1296 foreach (Tool t in grid_expander.tool) { 1297 if (t.is_selected () && t is SpinButton) { 1298 GridTool.sizes.remove ((SpinButton)t); 1299 grid_expander.tool.remove (t); 1300 break; 1301 } 1302 } 1303 1304 if (grid_expander.tool.size > 0) { 1305 grid_width = grid_expander.tool.get (grid_expander.tool.size - 1); 1306 tb.select_tool (grid_width); 1307 grid_width.set_active (false); 1308 } 1309 1310 MainWindow.get_toolbox ().update_expanders (); 1311 tb.redraw (0, 0, Toolbox.allocation_width, Toolbox.allocation_height); 1312 } 1313 1314 private void add_expander (Expander e) { 1315 expanders.add (e); 1316 } 1317 1318 public override Gee.ArrayList<string> get_displays () { 1319 Gee.ArrayList<string> d = new Gee.ArrayList<string> (); 1320 d.add ("Glyph"); 1321 return d; 1322 } 1323 } 1324 1325 } 1326