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