The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

KerningDisplay.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/KerningDisplay.vala.
Thread safety in text rendering
1 /* 2 Copyright (C) 2012, 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 17 namespace BirdFont { 18 19 /** Kerning context. */ 20 public class KerningDisplay : FontDisplay { 21 22 public bool suppress_input = false; 23 24 Gee.ArrayList <GlyphSequence> row; 25 int active_handle = -1; 26 int selected_handle = -1; 27 bool moving = false; 28 Glyph left_active_glyph = new Glyph ("null", '\0'); 29 Glyph right_active_glyph = new Glyph ("null", '\0'); 30 31 double begin_handle_x = 0; 32 double begin_handle_y = 0; 33 34 double last_handle_x = 0; 35 36 public bool text_input = false; 37 38 Gee.ArrayList<UndoItem> undo_items; 39 Gee.ArrayList<UndoItem> redo_items; 40 bool first_update = true; 41 42 Font current_font = new Font (); 43 Text kerning_label = new Text (); 44 45 public bool adjust_side_bearings = false; 46 bool right_side_bearing = true; 47 48 public KerningDisplay () { 49 GlyphSequence w = new GlyphSequence (); 50 row = new Gee.ArrayList <GlyphSequence> (); 51 undo_items = new Gee.ArrayList <UndoItem> (); 52 redo_items = new Gee.ArrayList <UndoItem> (); 53 row.add (w); 54 } 55 56 public GlyphSequence get_first_row () { 57 return row.size > 0 ? row.get (0) : new GlyphSequence (); 58 } 59 60 public override string get_label () { 61 return t_("Kerning"); 62 } 63 64 public override string get_name () { 65 return "Kerning"; 66 } 67 68 public void show_parse_error () { 69 string line1 = t_("The current kerning class is malformed."); 70 string line2 = t_("Add single characters separated by space and ranges on the form A-Z."); 71 string line3 = t_("Type “space” to kern the space character and “divis” to kern -."); 72 73 MainWindow.show_dialog (new MessageDialog (line1 + " " + line2 + " " + line3)); 74 } 75 76 public override void draw (WidgetAllocation allocation, Context cr) { 77 draw_kerning_pairs (allocation, cr); 78 } 79 80 public double get_row_height () { 81 return current_font.top_limit - current_font.bottom_limit; 82 } 83 84 public void draw_kerning_pairs (WidgetAllocation allocation, Context cr) { 85 Glyph glyph; 86 double x, y, w, kern, alpha; 87 double x2; 88 double caret_y; 89 int i, wi; 90 Glyph? prev; 91 GlyphSequence word_with_ligatures; 92 GlyphRange? gr_left, gr_right; 93 bool first_row = true; 94 double row_height; 95 Font font; 96 double item_size = 1.0 / KerningTools.font_size; 97 double item_size2 = 2.0 / KerningTools.font_size; 98 99 font = current_font; 100 i = 0; 101 102 // bg color 103 cr.save (); 104 cr.set_source_rgba (1, 1, 1, 1); 105 cr.rectangle (0, 0, allocation.width, allocation.height); 106 cr.fill (); 107 cr.restore (); 108 109 cr.save (); 110 cr.scale (KerningTools.font_size, KerningTools.font_size); 111 112 glyph = MainWindow.get_current_glyph (); 113 114 row_height = get_row_height (); 115 116 alpha = 1; 117 y = get_row_height () + font.base_line + 20; 118 x = 20; 119 w = 0; 120 prev = null; 121 kern = 0; 122 123 foreach (GlyphSequence word in row) { 124 wi = 0; 125 word_with_ligatures = word.process_ligatures (font); 126 gr_left = null; 127 gr_right = null; 128 foreach (Glyph? g in word_with_ligatures.glyph) { 129 if (g == null) { 130 continue; 131 } 132 133 if (prev == null || wi == 0) { 134 kern = 0; 135 } else { 136 return_if_fail (wi < word_with_ligatures.ranges.size); 137 return_if_fail (wi - 1 >= 0); 138 139 gr_left = word_with_ligatures.ranges.get (wi - 1); 140 gr_right = word_with_ligatures.ranges.get (wi); 141 142 kern = get_kerning_for_pair (((!)prev).get_name (), ((!)g).get_name (), gr_left, gr_right); 143 } 144 145 // draw glyph 146 if (g == null) { 147 w = 50; 148 alpha = 1; 149 } else { 150 alpha = 0; 151 glyph = (!) g; 152 153 cr.save (); 154 glyph.add_help_lines (); 155 cr.translate (kern + x - glyph.get_lsb () - Glyph.xc (), glyph.get_baseline () + y - Glyph.yc ()); 156 glyph.draw_paths (cr); 157 cr.restore (); 158 159 w = glyph.get_width (); 160 } 161 162 // handle 163 if (first_row && (active_handle == i || selected_handle == i)) { 164 x2 = x + kern / 2.0; 165 166 cr.save (); 167 168 if (selected_handle == i) { 169 cr.set_source_rgba (0, 0, 0, 1); 170 } else { 171 cr.set_source_rgba (123/255.0, 123/255.0, 123/255.0, 1); 172 } 173 174 if (!adjust_side_bearings) { 175 cr.move_to (x2 - 5 * item_size, y + 20 * item_size); 176 cr.line_to (x2, y + 20 * item_size - 5 * item_size); 177 cr.line_to (x2 + 5 * item_size, y + 20* item_size); 178 cr.fill (); 179 180 if (gr_left != null || gr_right != null) { 181 cr.move_to (x2 - 5 * item_size, y + 20 * item_size); 182 cr.line_to (x2 + 5 * item_size, y + 20 * item_size); 183 cr.line_to (x2 + 5 * item_size, y + 24 * item_size); 184 cr.line_to (x2 - 5 * item_size, y + 24 * item_size); 185 cr.fill (); 186 } 187 } else { 188 if (right_side_bearing) { 189 cr.move_to (x2 - 5 * item_size2, y + 20 * item_size2); 190 cr.line_to (x2, y + 20 * item_size2 - 5 * item_size2); 191 cr.line_to (x2, y + 20* item_size2); 192 cr.fill (); 193 } else { 194 cr.move_to (x2, y + 20 * item_size2); 195 cr.line_to (x2, y + 20 * item_size2 - 5 * item_size2); 196 cr.line_to (x2 + 5 * item_size2, y + 20* item_size2); 197 cr.fill (); 198 } 199 } 200 201 if (active_handle == i && !adjust_side_bearings) { 202 cr.save (); 203 cr.scale (1 / KerningTools.font_size, 1 / KerningTools.font_size); 204 kerning_label.widget_x = x2 * KerningTools.font_size; 205 kerning_label.widget_y = y * KerningTools.font_size + 40; 206 kerning_label.draw (cr); 207 cr.fill (); 208 cr.restore (); 209 } 210 } 211 212 x += w + kern; 213 214 // caption 215 if (g == null || ((!)g).is_empty ()) { 216 cr.save (); 217 cr.set_source_rgba (153/255.0, 153/255.0, 153/255.0, alpha); 218 cr.move_to (x - w / 2.0 - 5, y + 20); 219 cr.set_font_size (10 * item_size); 220 cr.show_text ("?"); 221 cr.restore (); 222 } 223 224 prev = g; 225 226 wi++; 227 i++; 228 } 229 230 // draw caret 231 if (first_row) { 232 x2 = x; 233 caret_y = get_row_height () + font.base_line + 20; 234 cr.save (); 235 cr.set_line_width (1.0 / KerningTools.font_size); 236 cr.set_source_rgba (0, 0, 0, 0.5); 237 cr.move_to (x2, caret_y + 20); 238 cr.line_to (x2, 20); 239 cr.stroke (); 240 cr.restore (); 241 } 242 243 y += row_height + 20; 244 x = 20; 245 first_row = false; 246 247 if (y > allocation.height) { 248 break; 249 } 250 } 251 252 for (int j = row.size - 1; j > 30; j--) { 253 row.remove_at (j); 254 } 255 256 cr.restore (); 257 } 258 259 private void display_kerning_value (double k) { 260 string kerning = round (k); 261 kerning_label = new Text (@"$(kerning)", 17 * MainWindow.units); 262 } 263 264 private void set_active_handle_index (int h) { 265 double kern = get_kerning_for_handle (h); 266 active_handle = h; 267 268 if (1 <= active_handle < row.get (0).glyph.size) { 269 display_kerning_value (kern); 270 } 271 } 272 273 private double get_kerning_for_handle (int handle) { 274 string a, b; 275 GlyphRange? gr_left, gr_right; 276 bool got_pair; 277 278 got_pair = get_kerning_pair (handle, out a, out b, out gr_left, out gr_right); 279 280 if (got_pair) { 281 return get_kerning_for_pair (a, b, gr_left, gr_right); 282 } 283 284 return 0; 285 } 286 287 private bool get_kerning_pair (int handle, out string left, out string right, 288 out GlyphRange? range_left, out GlyphRange? range_right) { 289 string a, b; 290 Font font; 291 int wi = 0; 292 GlyphSequence word_with_ligatures; 293 int ranges_index = 0; 294 GlyphRange? gr_left, gr_right; 295 int row_index = 0; 296 297 font = current_font; 298 299 font.touch (); 300 301 a = ""; 302 b = ""; 303 304 left = ""; 305 right = ""; 306 range_left = null; 307 range_right = null; 308 309 if (handle <= 0) { 310 return false; 311 } 312 313 foreach (GlyphSequence word in row) { 314 word_with_ligatures = word.process_ligatures (font); 315 ranges_index = 0; 316 foreach (Glyph? g in word_with_ligatures.glyph) { 317 318 if (g == null) { 319 continue; 320 } 321 322 b = ((!) g).get_name (); 323 324 if (handle == wi && row_index == 0) { 325 if (wi >= word_with_ligatures.ranges.size) { 326 return false; 327 } 328 return_val_if_fail (wi - 1 >= 0, false); 329 330 if (word_with_ligatures.ranges.size != word_with_ligatures.glyph.size) { 331 return false; 332 } 333 334 gr_left = word_with_ligatures.ranges.get (wi - 1); 335 gr_right = word_with_ligatures.ranges.get (wi); 336 337 left = a; 338 right = b; 339 range_left = gr_left; 340 range_right = gr_right; 341 342 return true; 343 } 344 345 wi++; 346 347 a = b; 348 } 349 350 row_index++; 351 } 352 353 return false; 354 } 355 356 public void set_absolute_kerning (int handle, double val) { 357 double kern; 358 359 if (MenuTab.suppress_event) { 360 return; 361 } 362 363 if (!adjust_side_bearings) { 364 kern = get_kerning_for_handle (handle); 365 set_space (handle, val - kern); 366 } 367 } 368 369 370 /** Adjust kerning or right side bearing. */ 371 private void set_space (int handle, double val) { 372 string a, b; 373 Font font; 374 GlyphRange? gr_left, gr_right; 375 376 font = current_font; 377 font.touch (); 378 379 if (!adjust_side_bearings) { 380 get_kerning_pair (handle, out a, out b, out gr_left, out gr_right); 381 set_kerning_pair (a, b, ref gr_left, ref gr_right, val); 382 } else { 383 if (right_side_bearing) { 384 left_active_glyph.right_limit += val; 385 left_active_glyph.remove_lines (); 386 left_active_glyph.add_help_lines (); 387 left_active_glyph.update_other_spacing_classes (); 388 } else { 389 right_active_glyph.left_limit -= val; 390 right_active_glyph.remove_lines (); 391 right_active_glyph.add_help_lines (); 392 right_active_glyph.update_other_spacing_classes (); 393 } 394 } 395 } 396 397 /** Class based gpos kerning. */ 398 public void set_kerning_pair (string a, string b, 399 ref GlyphRange? gr_left, ref GlyphRange? gr_right, 400 double val) { 401 double kern; 402 GlyphRange grl, grr; 403 KerningClasses classes; 404 string n, f; 405 bool has_kerning; 406 Font font; 407 408 font = current_font; 409 font.touch (); 410 classes = font.get_kerning_classes (); 411 412 kern = get_kerning_for_pair (a, b, gr_left, gr_right); 413 414 try { 415 if (gr_left == null) { 416 grl = new GlyphRange (); 417 grl.parse_ranges (a); 418 gr_left = grl; // update the range list 419 } else { 420 grl = (!) gr_left; 421 } 422 423 if (gr_right == null) { 424 grr = new GlyphRange (); 425 grr.parse_ranges (b); 426 gr_right = grr; 427 } else { 428 grr = (!) gr_right; 429 } 430 431 if (first_update) { 432 f = grl.get_all_ranges (); 433 n = grr.get_all_ranges (); 434 has_kerning = classes.has_kerning (f, n); 435 undo_items.add (new UndoItem (f, n, kern, has_kerning)); 436 redo_items.clear (); 437 first_update = false; 438 } 439 440 classes.set_kerning (grl, grr, kern + val); 441 display_kerning_value (kern + val); 442 } catch (MarkupError e) { 443 // FIXME: unassigned glyphs and ligatures 444 warning (e.message); 445 } 446 } 447 448 public static double get_kerning_for_pair (string a, string b, GlyphRange? gr_left, GlyphRange? gr_right) { 449 KerningClasses k = BirdFont.get_current_font ().get_kerning_classes (); 450 return k.get_kerning_for_pair (a, b, gr_left, gr_right); 451 } 452 453 public void set_current_font (Font f) { 454 current_font = f; 455 } 456 457 public override void selected_canvas () { 458 Glyph g; 459 GlyphSequence w; 460 StringBuilder s = new StringBuilder (); 461 bool append_char = false; 462 463 current_font = BirdFont.get_current_font (); 464 465 KeyBindings.set_require_modifier (true); 466 467 g = MainWindow.get_current_glyph (); 468 s.append_unichar (g.get_unichar ()); 469 470 if (row.size == 0) { 471 append_char = true; 472 } 473 474 if (append_char) { 475 w = new GlyphSequence (); 476 row.add (w); 477 w.glyph.insert (0, current_font.get_glyph (s.str)); 478 } 479 } 480 481 public void add_kerning_class (int index) { 482 add_range (KerningTools.get_kerning_class (index)); 483 } 484 485 public void add_range (GlyphRange range) { 486 Font font = current_font; 487 Glyph? glyph; 488 489 glyph = font.get_glyph_by_name (range.get_char (0)); 490 491 if (glyph == null) { 492 warning ("Kerning range is not represented by a valid glyph."); 493 return; 494 } 495 496 row.get (0).glyph.add ((!) glyph); 497 row.get (0).ranges.add (range); 498 499 GlyphCanvas.redraw (); 500 } 501 502 void set_selected_handle (int handle) { 503 Glyph? g; 504 selected_handle = handle; 505 GlyphSequence sequence_with_ligatures; 506 Font font = BirdFont.get_current_font (); 507 508 sequence_with_ligatures = row.get (0).process_ligatures (font); 509 510 if (selected_handle <= 0) { 511 selected_handle = 1; 512 } 513 514 if (selected_handle >= sequence_with_ligatures.glyph.size) { 515 selected_handle = (int) sequence_with_ligatures.glyph.size - 1; 516 } 517 518 set_active_handle_index (handle); 519 520 if (0 <= selected_handle - 1 < sequence_with_ligatures.glyph.size) { 521 g = sequence_with_ligatures.glyph.get (selected_handle - 1); 522 if (g != null) { 523 left_active_glyph = (!) g; 524 } 525 } 526 527 if (0 <= selected_handle < sequence_with_ligatures.glyph.size) { 528 g = sequence_with_ligatures.glyph.get (selected_handle); 529 if (g != null) { 530 right_active_glyph = (!) g; 531 } 532 } 533 534 GlyphCanvas.redraw (); 535 } 536 537 public static void previous_pair () { 538 KerningDisplay d = MainWindow.get_kerning_display (); 539 d.set_selected_handle (d.selected_handle - 1); 540 } 541 542 public static void next_pair () { 543 KerningDisplay d = MainWindow.get_kerning_display (); 544 d.set_selected_handle (d.selected_handle + 1); 545 } 546 547 private static string round (double d) { 548 char[] b = new char [22]; 549 unowned string s = d.format (b, "%.2f"); 550 string n = s.dup (); 551 552 n = n.replace (",", "."); 553 554 if (n == "-0.00") { 555 n = "0.00"; 556 } 557 558 return n; 559 } 560 561 public override void key_press (uint keyval) { 562 unichar c; 563 564 if (MenuTab.suppress_event) { // don't update kerning while saving font 565 warning ("A background thread uses the current font."); 566 return; 567 } 568 569 c = (unichar) keyval; 570 571 if (suppress_input) { 572 return; 573 } 574 575 if ((keyval == 'u' || keyval == 'U') && KeyBindings.has_ctrl ()) { 576 insert_unichar (); 577 } else { 578 if (keyval == Key.LEFT && KeyBindings.modifier == NONE) { 579 first_update = true; 580 set_space (selected_handle, -1 / KerningTools.font_size); 581 } 582 583 if (keyval == Key.RIGHT && KeyBindings.modifier == NONE) { 584 first_update = true; 585 set_space (selected_handle, 1 / KerningTools.font_size); 586 } 587 588 if (KeyBindings.modifier == CTRL && (keyval == Key.LEFT || keyval == Key.RIGHT)) { 589 if (keyval == Key.LEFT) { 590 selected_handle--; 591 } 592 593 if (keyval == Key.RIGHT) { 594 selected_handle++; 595 } 596 597 set_selected_handle (selected_handle); 598 } 599 600 if (KeyBindings.modifier == NONE 601 || KeyBindings.modifier == SHIFT 602 || KeyBindings.modifier == ALT) { 603 604 if (keyval == Key.BACK_SPACE && row.size > 0 && row.get (0).glyph.size > 0) { 605 row.get (0).glyph.remove_at (row.get (0).glyph.size - 1); 606 row.get (0).ranges.remove_at (row.get (0).ranges.size - 1); 607 } 608 609 if (row.size == 0 || c == Key.ENTER) { 610 new_line (); 611 } 612 613 add_character (c); 614 } 615 } 616 617 GlyphCanvas.redraw (); 618 } 619 620 public void insert_unichar () { 621 TextListener listener; 622 string submitted_value = ""; 623 string unicodestart; 624 625 unicodestart = (KeyBindings.has_shift ()) ? "" : "U+"; 626 627 listener = new TextListener (t_("Unicode"), unicodestart, t_("Insert")); 628 629 listener.signal_text_input.connect ((text) => { 630 submitted_value = text; 631 632 if (MenuTab.suppress_event) { 633 return; 634 } 635 636 GlyphCanvas.redraw (); 637 }); 638 639 listener.signal_submit.connect (() => { 640 unichar c; 641 MainWindow.native_window.hide_text_input (); 642 643 text_input = false; 644 suppress_input = false; 645 646 if (submitted_value.has_prefix ("u+") || submitted_value.has_prefix ("U+")) { 647 c = Font.to_unichar (submitted_value); 648 add_character (c); 649 } else { 650 add_text (submitted_value); 651 } 652 }); 653 654 suppress_input = true; 655 text_input = true; 656 MainWindow.native_window.set_text_listener (listener); 657 } 658 659 public void new_line () { 660 row.insert (0, new GlyphSequence ()); 661 } 662 663 void add_character (unichar c) { 664 Glyph? g; 665 string name; 666 Font f; 667 668 if (MenuTab.suppress_event) { 669 return; 670 } 671 672 f = current_font; 673 674 if (!is_modifier_key (c) && c.validate ()) { 675 name = f.get_name_for_character (c); 676 g = f.get_glyph_by_name (name); 677 inser_glyph (g); 678 } 679 } 680 681 public void inser_glyph (Glyph? g) { 682 if (g != null) { 683 row.get (0).glyph.add (g); 684 row.get (0).ranges.add (null); 685 686 set_selected_handle ((int) row.get (0).glyph.size - 1); 687 set_active_handle_index (selected_handle); 688 } 689 } 690 691 public override void motion_notify (double ex, double ey) { 692 double k, y; 693 694 if (MenuTab.suppress_event) { 695 return; 696 } 697 698 if (!moving) { 699 set_active_handle (ex, ey); 700 } else { 701 y = 1; 702 703 if (Math.fabs (ey - begin_handle_y) > 20) { 704 y = ((Math.fabs (ey - begin_handle_y) / 100) + 1); 705 } 706 707 k = (ex - last_handle_x) / y; // y-axis is for variable precision 708 k /= KerningTools.font_size; 709 set_space (selected_handle, k); 710 GlyphCanvas.redraw (); 711 } 712 713 last_handle_x = ex; 714 } 715 716 public void set_active_handle (double ex, double ey) { 717 double item_size = 1.0 / KerningTools.font_size; 718 double y = 100 * item_size; 719 double x = 20; 720 double w = 0; 721 double d, kern; 722 double min = double.MAX; 723 int i = 0; 724 int row_index = 0; 725 int col_index = 0; 726 double fs = KerningTools.font_size; 727 Glyph glyph = new Glyph.no_lines (""); 728 Font font = BirdFont.get_current_font (); 729 730 GlyphRange? gr_left, gr_right; 731 732 Glyph? prev = null; 733 string gl_name = ""; 734 GlyphSequence word_with_ligatures; 735 736 foreach (GlyphSequence word in row) { 737 col_index = 0; 738 739 word_with_ligatures = word.process_ligatures (font); 740 foreach (Glyph? g in word_with_ligatures.glyph) { 741 if (g == null) { 742 w = 50; 743 warning ("glyph does not exist"); 744 } else { 745 glyph = (!) g; 746 w = glyph.get_width (); 747 } 748 749 gl_name = glyph.get_name (); 750 751 if (prev == null && col_index != 0) { 752 warning (@"previous glyph does not exist row: $row_index column: $col_index"); 753 } 754 755 if (prev == null || col_index == 0) { 756 kern = 0; 757 } else { 758 return_if_fail (col_index < word_with_ligatures.ranges.size); 759 return_if_fail (col_index - 1 >= 0); 760 761 gr_left = word_with_ligatures.ranges.get (col_index - 1); 762 gr_right = word_with_ligatures.ranges.get (col_index); 763 764 kern = get_kerning_for_pair (((!)prev).get_name (), ((!)g).get_name (), gr_left, gr_right); 765 } 766 767 d = Math.pow (fs * (x + kern) - ex, 2) + Math.pow (fs * (y - ey), 2); 768 769 if (d < min) { 770 min = d; 771 772 if (ex != fs * (x + kern)) { // don't swap direction after button release 773 right_side_bearing = ex < fs * (x + kern); // right or left side bearing handle 774 } 775 776 if (active_handle != i - row_index) { 777 set_active_handle_index (i - row_index); 778 GlyphCanvas.redraw (); 779 } 780 781 if (col_index == word.glyph.size || col_index == 0) { 782 set_active_handle_index (-1); 783 } else { 784 set_active_handle_index (active_handle + row_index); 785 } 786 } 787 788 prev = g; 789 x += w + kern; 790 i++; 791 col_index++; 792 } 793 794 row_index++; 795 y += MainWindow.get_current_glyph ().get_height () + 20; 796 x = 20; 797 } 798 } 799 800 public override void button_release (int button, double ex, double ey) { 801 set_active_handle (ex, ey); 802 moving = false; 803 first_update = true; 804 805 if (button == 3 || text_input) { 806 set_kerning_by_text (); 807 } 808 } 809 810 public void set_kerning_by_text () { 811 TextListener listener; 812 string kerning = @"$(get_kerning_for_handle (selected_handle))"; 813 814 if (MenuTab.suppress_event) { 815 return; 816 } 817 818 if (selected_handle == -1) { 819 set_selected_handle (0); 820 } 821 822 listener = new TextListener (t_("Kerning"), kerning, t_("Close")); 823 824 listener.signal_text_input.connect ((text) => { 825 string submitted_value; 826 double parsed_value; 827 828 if (MenuTab.suppress_event) { 829 return; 830 } 831 832 submitted_value = text.replace (",", "."); 833 parsed_value = double.parse (submitted_value); 834 set_absolute_kerning (selected_handle, parsed_value); 835 GlyphCanvas.redraw (); 836 }); 837 838 listener.signal_submit.connect (() => { 839 MainWindow.native_window.hide_text_input (); 840 text_input = false; 841 suppress_input = false; 842 }); 843 844 suppress_input = true; 845 text_input = true; 846 MainWindow.native_window.set_text_listener (listener); 847 848 GlyphCanvas.redraw (); 849 } 850 851 public override void button_press (uint button, double ex, double ey) { 852 set_active_handle (ex, ey); 853 set_selected_handle (active_handle); 854 begin_handle_x = ex; 855 begin_handle_y = ey; 856 last_handle_x = ex; 857 moving = true; 858 } 859 860 /** Insert text form clipboard. */ 861 public void add_text (string t) { 862 int c; 863 864 if (MenuTab.suppress_event) { 865 return; 866 } 867 868 c = t.char_count (); 869 for (int i = 0; i <= c; i++) { 870 add_character (t.get_char (t.index_of_nth_char (i))); 871 } 872 873 GlyphCanvas.redraw (); 874 } 875 876 public override void undo () { 877 UndoItem ui; 878 UndoItem redo_state; 879 880 if (MenuTab.suppress_event) { 881 return; 882 } 883 884 if (undo_items.size == 0) { 885 return; 886 } 887 888 ui = undo_items.get (undo_items.size - 1); 889 890 redo_state = apply_undo (ui); 891 redo_items.add (redo_state); 892 893 undo_items.remove_at (undo_items.size - 1); 894 } 895 896 public override void redo () { 897 UndoItem ui; 898 899 if (MenuTab.suppress_event) { 900 return; 901 } 902 903 if (redo_items.size == 0) { 904 return; 905 } 906 907 ui = redo_items.get (redo_items.size - 1); 908 apply_undo (ui); 909 redo_items.remove_at (redo_items.size - 1); 910 } 911 912 /** @return redo state. */ 913 public UndoItem apply_undo (UndoItem ui) { 914 KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes (); 915 GlyphRange glyph_range_first, glyph_range_next; 916 Font font = current_font; 917 string l, r; 918 UndoItem redo_state = new UndoItem ("", "", 0, false); 919 double? k; 920 921 l = GlyphRange.unserialize (ui.first); 922 r = GlyphRange.unserialize (ui.next); 923 924 try { 925 glyph_range_first = new GlyphRange (); 926 glyph_range_next = new GlyphRange (); 927 928 glyph_range_first.parse_ranges (ui.first); 929 glyph_range_next.parse_ranges (ui.next); 930 931 if (!ui.has_kerning) { 932 if (glyph_range_first.is_class () || glyph_range_next.is_class ()) { 933 redo_state.first = glyph_range_first.get_all_ranges (); 934 redo_state.next = glyph_range_next.get_all_ranges (); 935 redo_state.has_kerning = true; 936 redo_state.kerning = classes.get_kerning_for_range (glyph_range_first, glyph_range_next); 937 938 classes.delete_kerning_for_class (ui.first, ui.next); 939 } else { 940 941 redo_state.first = ui.first; 942 redo_state.next = ui.next; 943 redo_state.has_kerning = true; 944 k = classes.get_kerning_for_single_glyphs (ui.first, ui.next); 945 946 if (k != null) { 947 redo_state.kerning = (!) k; 948 } else { 949 warning ("No kerning"); 950 } 951 952 classes.delete_kerning_for_pair (ui.first, ui.next); 953 } 954 } else if (glyph_range_first.is_class () || glyph_range_next.is_class ()) { 955 glyph_range_first = new GlyphRange (); 956 glyph_range_next = new GlyphRange (); 957 958 glyph_range_first.parse_ranges (ui.first); 959 glyph_range_next.parse_ranges (ui.next); 960 961 redo_state.first = glyph_range_first.get_all_ranges (); 962 redo_state.next = glyph_range_next.get_all_ranges (); 963 k = classes.get_kerning_for_range (glyph_range_first, glyph_range_next); 964 965 if (k != null) { 966 redo_state.kerning = (!) k; 967 redo_state.has_kerning = true; 968 } else { 969 redo_state.has_kerning = false; 970 } 971 972 classes.set_kerning (glyph_range_first, glyph_range_next, ui.kerning); 973 } else { 974 redo_state.first = ui.first; 975 redo_state.next = ui.next; 976 redo_state.has_kerning = true; 977 k = classes.get_kerning_for_single_glyphs (ui.first, ui.next); 978 979 if (k != null) { 980 redo_state.kerning = (!) k; 981 redo_state.has_kerning = true; 982 } else { 983 redo_state.has_kerning = false; 984 } 985 986 classes.set_kerning_for_single_glyphs (ui.first, ui.next, ui.kerning); 987 } 988 } catch (MarkupError e) { 989 warning (e.message); 990 } 991 992 font.touch (); 993 GlyphCanvas.redraw (); 994 995 return redo_state; 996 } 997 998 public override void zoom_in () { 999 KerningTools.font_size += 0.1; 1000 1001 if (KerningTools.font_size > 3) { 1002 KerningTools.font_size = 3; 1003 } 1004 1005 KerningTools.zoom_bar.set_zoom (KerningTools.font_size / 3); 1006 GlyphCanvas.redraw (); 1007 } 1008 1009 public override void zoom_out () { 1010 KerningTools.font_size -= 0.1; 1011 1012 if (KerningTools.font_size < 0.3) { 1013 KerningTools.font_size = 0.3; 1014 } 1015 1016 KerningTools.zoom_bar.set_zoom (KerningTools.font_size / 3); 1017 GlyphCanvas.redraw (); 1018 } 1019 1020 public class UndoItem : GLib.Object { 1021 public string first; 1022 public string next; 1023 public double kerning; 1024 public bool has_kerning; 1025 1026 public UndoItem (string first, string next, double kerning, bool has_kerning) { 1027 this.first = first; 1028 this.next = next; 1029 this.kerning = kerning; 1030 this.has_kerning = has_kerning; 1031 } 1032 } 1033 } 1034 1035 } 1036