The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

OverView.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
Add *nix as OS tag heads/master
1 /* 2 Copyright (C) 2012 - 2016 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 /** A display with all glyphs present in this font. */ 20 public class OverView : FontDisplay { 21 public WidgetAllocation allocation = new WidgetAllocation (); 22 23 public OverViewItem selected_item = new OverViewItem (); 24 25 public Gee.ArrayList<GlyphCollection> copied_glyphs = new Gee.ArrayList<GlyphCollection> (); 26 public Gee.ArrayList<GlyphCollection> selected_items = new Gee.ArrayList<GlyphCollection> (); 27 28 int selected = 0; 29 int first_visible = 0; 30 int rows = 0; 31 int items_per_row = 0; 32 33 double view_offset_y = 0; 34 double view_offset_x = 0; 35 36 public signal void open_new_glyph_signal (unichar c); 37 public signal void open_glyph_signal (GlyphCollection c); 38 39 public GlyphRange glyph_range { 40 get { 41 return _glyph_range; 42 } 43 44 set { 45 _glyph_range = value; 46 } 47 } 48 49 GlyphRange _glyph_range; 50 51 string search_query = ""; 52 53 public Gee.ArrayList<OverViewItem> visible_items = new Gee.ArrayList<OverViewItem> (); 54 55 /** List of undo commands. */ 56 public Gee.ArrayList<OverViewUndoItem> undo_items = new Gee.ArrayList<OverViewUndoItem> (); 57 public Gee.ArrayList<OverViewUndoItem> redo_items = new Gee.ArrayList<OverViewUndoItem> (); 58 59 /** Show all characters that has been drawn. */ 60 public bool all_available { 61 set { 62 _all_available = value; 63 update_item_list (); 64 } 65 66 get { 67 return _all_available; 68 } 69 } 70 71 bool _all_available = false; 72 73 /** Show unicode database info. */ 74 CharacterInfo? character_info = null; 75 76 double scroll_size = 1; 77 const double UCD_LINE_HEIGHT = 17 * 1.3; 78 79 private bool update_scheduled = true; 80 81 public OverView (GlyphRange? range = null, 82 bool open_selected = true, bool default_character_set = true) { 83 84 GlyphRange gr; 85 86 if (range == null) { 87 gr = new GlyphRange (); 88 set_current_glyph_range (gr); 89 } 90 91 if (open_selected) { 92 this.open_glyph_signal.connect ((glyph_collection) => { 93 TabBar tabs = MainWindow.get_tab_bar (); 94 string n = glyph_collection.get_current ().name; 95 bool selected = tabs.select_char (n); 96 GlyphTab glyph_tab; 97 98 if (!selected) { 99 glyph_tab = new GlyphTab (glyph_collection); 100 tabs.add_tab (glyph_tab, true, glyph_collection); 101 set_glyph_zoom (glyph_collection); 102 PenTool.update_orientation (); 103 } 104 }); 105 106 this.open_new_glyph_signal.connect ((character) => { 107 // ignore control characters 108 if (character <= 0x1F || character == 0xFFFF) { 109 return; 110 } 111 112 create_new_glyph (character); 113 }); 114 } 115 116 if (default_character_set) { 117 IdleSource idle = new IdleSource (); 118 119 idle.set_callback (() => { 120 use_default_character_set (); 121 selected_canvas (); 122 return false; 123 }); 124 125 idle.attach (null); 126 } 127 128 update_item_list (); 129 update_scrollbar (); 130 reset_zoom (); 131 132 string? zoom = Preferences.get ("overview_zoom"); 133 134 if (zoom != null) { 135 string z = (!) zoom; 136 137 if (z != "") { 138 set_zoom (double.parse (z)); 139 } 140 } 141 } 142 143 public void reset_cache () { 144 foreach (OverViewItem i in visible_items) { 145 i.clear_cache (); 146 } 147 } 148 149 public Glyph? get_selected_glyph () { 150 if (selected_items.size == 0) { 151 return null; 152 } 153 154 return selected_items.get (0).get_current (); 155 } 156 157 public void select_all_glyphs () { 158 Font f; 159 GlyphCollection? glyphs; 160 161 f = BirdFont.get_current_font (); 162 163 for (int index = 0; index < f.length (); index++) { 164 glyphs = f.get_glyph_collection_index ((uint32) index); 165 return_if_fail (glyphs != null); 166 167 selected_items.add ((!) glyphs); 168 } 169 170 foreach (OverViewItem item in visible_items) { 171 item.selected = item.glyphs != null; 172 } 173 174 GlyphCanvas.redraw (); 175 } 176 177 public void use_default_character_set () { 178 GlyphRange gr = new GlyphRange (); 179 all_available = false; 180 DefaultCharacterSet.use_default_range (gr); 181 set_current_glyph_range (gr); 182 OverviewTools.update_overview_characterset (); 183 FontDisplay.dirty_scrollbar = true; 184 } 185 186 public GlyphCollection create_new_glyph (unichar character) { 187 StringBuilder name = new StringBuilder (); 188 TabBar tabs = MainWindow.get_tab_bar (); 189 bool selected; 190 Glyph glyph; 191 GlyphCollection glyph_collection; 192 GlyphCanvas canvas; 193 GlyphTab glyph_tab; 194 195 glyph_collection = MainWindow.get_current_glyph_collection (); 196 name.append_unichar (character); 197 selected = tabs.select_char (name.str); 198 199 if (!selected) { 200 glyph_collection = add_character_to_font (character); 201 glyph_tab = new GlyphTab (glyph_collection); 202 glyph = glyph_collection.get_current (); 203 glyph.layers.add_layer (new Layer ()); 204 tabs.add_tab (glyph_tab, true, glyph_collection); 205 206 selected_items.add (glyph_collection); 207 208 canvas = MainWindow.get_glyph_canvas (); 209 canvas.set_current_glyph_collection (glyph_collection); 210 211 set_glyph_zoom (glyph_collection); 212 } else { 213 warning ("Glyph is already open"); 214 } 215 216 OverviewTools.update_overview_characterset (); 217 return glyph_collection; 218 } 219 220 public GlyphCollection add_empty_character_to_font (unichar character, bool unassigned, string name) { 221 return add_character_to_font (character, true, unassigned); 222 } 223 224 public GlyphCollection add_character_to_font (unichar character, bool empty = false, 225 bool unassigned = false, string glyph_name = "") { 226 StringBuilder name = new StringBuilder (); 227 Font font = BirdFont.get_current_font (); 228 GlyphCollection? fg; 229 Glyph glyph; 230 GlyphCollection glyph_collection; 231 232 if (glyph_name == "") { 233 name.append_unichar (character); 234 } else { 235 name.append (glyph_name); 236 } 237 238 if (all_available) { 239 fg = font.get_glyph_collection_by_name (name.str); 240 } else { 241 fg = font.get_glyph_collection (name.str); 242 } 243 244 if (fg != null) { 245 glyph_collection = (!) fg; 246 } else { 247 glyph_collection = new GlyphCollection (character, name.str); 248 249 if (!empty) { 250 glyph = new Glyph (name.str, !unassigned ? character : '\0'); 251 glyph_collection.add_master (new GlyphMaster ()); 252 glyph_collection.insert_glyph (glyph, true); 253 } 254 255 font.add_glyph_collection (glyph_collection); 256 } 257 258 glyph_collection.set_unassigned (unassigned); 259 260 return glyph_collection; 261 } 262 263 public static void search () { 264 OverView ow = MainWindow.get_overview (); 265 TextListener listener = new TextListener (t_("Search"), ow.search_query, t_("Filter")); 266 267 listener.signal_text_input.connect ((text) => { 268 OverView o = MainWindow.get_overview (); 269 o.search_query = text; 270 }); 271 272 listener.signal_submit.connect (() => { 273 OverView o = MainWindow.get_overview (); 274 string q = o.search_query; 275 276 if (q == "*") { 277 q = "star"; 278 } 279 280 if (q == "?") { 281 q = "question"; 282 } 283 284 if (q.char_count () > 1) { 285 q = q.down (); 286 } 287 288 GlyphRange r = CharDatabase.search (q); 289 o.set_current_glyph_range (r); 290 MainWindow.get_tab_bar ().select_tab_name ("Overview"); 291 292 TextListener tl = new TextListener (t_("Search"), o.search_query, t_("Filter")); 293 TabContent.show_text_input (tl); 294 }); 295 296 TabContent.show_text_input (listener); 297 } 298 299 public Glyph? get_current_glyph () { 300 OverViewItem oi = selected_item; 301 if (oi.glyphs != null) { 302 return ((!) oi.glyphs).get_current (); 303 } 304 return null; 305 } 306 307 public void set_glyph_zoom (GlyphCollection glyphs) { 308 GlyphCanvas canvas; 309 canvas = MainWindow.get_glyph_canvas (); 310 canvas.set_current_glyph_collection (glyphs); 311 Toolbox tools = MainWindow.get_toolbox (); 312 ZoomTool z = (ZoomTool) tools.get_tool ("zoom_tool"); 313 z.store_current_view (); 314 glyphs.get_current ().default_zoom (); 315 z.store_current_view (); 316 OverViewItem.reset_label (); 317 } 318 319 public double get_height () { 320 double l; 321 Font f; 322 323 if (rows == 0) { 324 return 0; 325 } 326 327 if (all_available) { 328 f = BirdFont.get_current_font (); 329 l = f.length (); 330 } else { 331 l = glyph_range.length (); 332 } 333 334 return 2.0 * OverViewItem.height * (l / rows); 335 } 336 337 public bool selected_char_is_visible () { 338 return first_visible <= selected <= first_visible + items_per_row * rows; 339 } 340 341 public override bool has_scrollbar () { 342 return true; 343 } 344 345 public bool all_characters_in_view () { 346 double length; 347 Font f; 348 349 if (all_available) { 350 f = BirdFont.get_current_font (); 351 length = f.length (); 352 } else { 353 length = glyph_range.length (); 354 } 355 356 return length < items_per_row * rows; 357 } 358 359 public void move_up () { 360 first_visible -= items_per_row; 361 selected += items_per_row; 362 363 if (first_visible < 0) { 364 selected -= items_per_row; 365 first_visible = 0; 366 view_offset_y = 0; 367 } 368 } 369 370 public void move_down () { 371 if (!at_bottom ()) { 372 first_visible += items_per_row; 373 selected -= items_per_row; 374 } 375 } 376 377 public override void scroll_wheel (double x, double y, double dx, double dy) { 378 double pixel_delta = 3 * dy; 379 380 if (dy > 0) { 381 view_offset_y += pixel_delta; 382 383 while (view_offset_y > 0) { 384 view_offset_y -= OverViewItem.height; 385 move_up (); 386 } 387 } else { 388 if (at_bottom ()) { 389 if (view_offset_y > -2 * OverViewItem.height && !all_characters_in_view ()) { 390 view_offset_y += pixel_delta; 391 } 392 } else { 393 view_offset_y += pixel_delta; 394 while (view_offset_y < -OverViewItem.height) { 395 view_offset_y += OverViewItem.height; 396 move_down (); 397 } 398 } 399 } 400 401 if (view_offset_y < -2 * OverViewItem.height) { 402 view_offset_y = -2 * OverViewItem.height; 403 } 404 405 update_item_list (); 406 update_scrollbar (); 407 hide_menu (); 408 GlyphCanvas.redraw (); 409 } 410 411 public override void selected_canvas () { 412 OverviewTools.update_overview_characterset (); 413 KeyBindings.set_require_modifier (true); 414 update_scrollbar (); 415 update_zoom_bar (); 416 OverViewItem.glyph_scale = 1; 417 update_item_list (); 418 selected_item = get_selected_item (); 419 GlyphCanvas.redraw (); 420 } 421 422 public void update_zoom_bar () { 423 double z = OverViewItem.width / OverViewItem.DEFAULT_WIDTH - 0.5; 424 Toolbox.overview_tools.zoom_bar.set_zoom (z); 425 Toolbox.redraw_tool_box (); 426 update_item_list (); 427 } 428 429 public void set_zoom (double zoom) { 430 double z = zoom + 0.5; 431 OverViewItem.glyph_scale = 1; 432 OverViewItem.width = OverViewItem.DEFAULT_WIDTH * z; 433 OverViewItem.height = OverViewItem.DEFAULT_HEIGHT * z; 434 OverViewItem.margin = OverViewItem.DEFAULT_MARGIN * z; 435 update_item_list (); 436 OverViewItem.reset_label (); 437 Preferences.set ("overview_zoom", @"$zoom"); 438 439 Font font = BirdFont.get_current_font (); 440 for (int index = 0; index < font.length (); index++) { 441 GlyphCollection? glyphs = font.get_glyph_collection_index ((uint32) index); 442 return_if_fail (glyphs != null); 443 GlyphCollection g = (!) glyphs; 444 g.get_current ().overview_thumbnail = null; 445 } 446 447 GlyphCanvas.redraw (); 448 } 449 450 public override void zoom_min () { 451 OverViewItem.width = OverViewItem.DEFAULT_WIDTH * 0.5; 452 OverViewItem.height = OverViewItem.DEFAULT_HEIGHT * 0.5; 453 OverViewItem.margin = OverViewItem.DEFAULT_MARGIN * 0.5; 454 update_item_list (); 455 OverViewItem.reset_label (); 456 GlyphCanvas.redraw (); 457 update_zoom_bar (); 458 } 459 460 public override void reset_zoom () { 461 OverViewItem.width = OverViewItem.DEFAULT_WIDTH; 462 OverViewItem.height = OverViewItem.DEFAULT_HEIGHT; 463 OverViewItem.margin = OverViewItem.DEFAULT_MARGIN; 464 update_item_list (); 465 OverViewItem.reset_label (); 466 GlyphCanvas.redraw (); 467 update_zoom_bar (); 468 } 469 470 public override void zoom_max () { 471 OverViewItem.width = allocation.width; 472 OverViewItem.height = allocation.height; 473 update_item_list (); 474 OverViewItem.reset_label (); 475 GlyphCanvas.redraw (); 476 } 477 478 public override void zoom_in () { 479 OverViewItem.width *= 1.1; 480 OverViewItem.height *= 1.1; 481 OverViewItem.margin *= 1.1; 482 update_item_list (); 483 OverViewItem.reset_label (); 484 GlyphCanvas.redraw (); 485 update_zoom_bar (); 486 } 487 488 public override void zoom_out () { 489 OverViewItem.width *= 0.9; 490 OverViewItem.height *= 0.9; 491 OverViewItem.margin *= 0.9; 492 update_item_list (); 493 OverViewItem.reset_label (); 494 GlyphCanvas.redraw (); 495 update_zoom_bar (); 496 } 497 498 public override void store_current_view () { 499 } 500 501 public override void restore_last_view () { 502 } 503 504 public override void next_view () { 505 } 506 507 public override string get_label () { 508 return t_("Overview"); 509 } 510 511 public override string get_name () { 512 return "Overview"; 513 } 514 515 public void display_all_available_glyphs () { 516 all_available = true; 517 518 view_offset_y = 0; 519 first_visible = 0; 520 selected = 0; 521 522 update_item_list (); 523 selected_item = get_selected_item (); 524 GlyphCanvas.redraw (); 525 } 526 527 OverViewItem get_selected_item () { 528 if (visible_items.size == 0) { 529 return new OverViewItem (); 530 } 531 532 if (!(0 <= selected < visible_items.size)) { 533 return selected_item; 534 } 535 536 OverViewItem item = visible_items.get (selected); 537 item.selected = true; 538 return item; 539 } 540 541 int get_items_per_row () { 542 int i = 1; 543 double tab_with = allocation.width - 30; // 30 px for the scroll bar 544 OverViewItem.margin = OverViewItem.width * 0.1; 545 double l = OverViewItem.margin + OverViewItem.full_width (); 546 547 while (l <= tab_with) { 548 l += OverViewItem.full_width (); 549 i++; 550 } 551 552 return i - 1; 553 } 554 555 public void update_item_list () { 556 update_scheduled = true; 557 } 558 559 public void process_item_list_update () { 560 string character_string; 561 Font f = BirdFont.get_current_font (); 562 GlyphCollection? glyphs = null; 563 uint32 index; 564 OverViewItem item; 565 double x, y; 566 unichar character; 567 Glyph glyph; 568 double tab_with; 569 int item_list_length; 570 int visible_size; 571 572 tab_with = allocation.width - 30; // scrollbar 573 574 items_per_row = get_items_per_row (); 575 rows = (int) (allocation.height / OverViewItem.full_height ()) + 2; 576 577 item_list_length = items_per_row * rows; 578 visible_items.clear (); 579 580 index = (uint32) first_visible; 581 x = OverViewItem.margin; 582 y = OverViewItem.margin; 583 584 if (all_available) { 585 uint font_length = f.length (); 586 587 for (int i = 0; i < item_list_length && index < font_length; i++) { 588 glyphs = f.get_glyph_collection_index ((uint32) index); 589 return_if_fail (glyphs != null); 590 591 glyph = ((!) glyphs).get_current (); 592 character_string = glyph.name; 593 character = glyph.unichar_code; 594 595 item = new OverViewItem (); 596 item.set_character (character); 597 item.set_glyphs (glyphs); 598 item.x = x; 599 item.y = y; 600 visible_items.add (item); 601 index++; 602 } 603 } else { 604 uint32 glyph_range_size = glyph_range.get_length (); 605 606 uint max_length = glyph_range.length () - first_visible; 607 608 if (item_list_length > max_length) { 609 item_list_length = (int) max_length; 610 } 611 612 for (int i = 0; i < item_list_length && index < glyph_range_size; i++) { 613 item = new OverViewItem (); 614 visible_items.add (item); 615 } 616 617 index = (uint32) first_visible; 618 visible_size = visible_items.size; 619 for (int i = 0; i < visible_size; i++) { 620 item = visible_items.get (i); 621 character = glyph_range.get_character ((uint32) index); 622 item.set_character (character); 623 index++; 624 } 625 626 visible_size = visible_items.size; 627 for (int i = 0; i < visible_size; i++) { 628 item = visible_items.get (i); 629 glyphs = f.get_glyph_collection_by_name ((!) item.character.to_string ()); 630 item.set_glyphs (glyphs); 631 } 632 } 633 634 x = OverViewItem.margin; 635 y = OverViewItem.margin; 636 637 visible_size = visible_items.size; 638 int selected_index; 639 bool selected_item; 640 double full_width = OverViewItem.full_width (); 641 642 for (int i = 0; i < visible_size; i++) { 643 item = visible_items.get (i); 644 645 selected_item = false; 646 647 if (all_available) { 648 glyphs = f.get_glyph_collection_index ((uint32) i); 649 } else { 650 glyphs = f.get_glyph_collection_by_name ((!) item.character.to_string ()); 651 } 652 653 if (glyphs != null) { 654 selected_index = selected_items.index_of ((!) glyphs); 655 selected_item = (selected_index != -1); 656 } 657 658 selected_item |= (i == selected); 659 item.selected = selected_item; 660 661 item.x = x + view_offset_x; 662 item.y = y + view_offset_y; 663 664 x += full_width; 665 666 if (x + full_width >= tab_with) { 667 x = OverViewItem.margin; 668 y += OverViewItem.full_height (); 669 } 670 } 671 672 update_scheduled = false; 673 } 674 675 public override void draw (WidgetAllocation allocation, Context cr) { 676 if (update_scheduled 677 || this.allocation.width != allocation.width 678 || this.allocation.height != allocation.height 679 || this.allocation.width == 0) { 680 this.allocation = allocation; 681 process_item_list_update (); 682 } 683 684 this.allocation = allocation; 685 686 // clear canvas 687 cr.save (); 688 Theme.color (cr, "Background 1"); 689 cr.rectangle (0, 0, allocation.width, allocation.height); 690 cr.fill (); 691 cr.restore (); 692 693 foreach (OverViewItem i in visible_items) { 694 i.draw (allocation, cr); 695 } 696 697 if (unlikely (visible_items.size == 0)) { 698 draw_empty_canvas (allocation, cr); 699 } 700 701 if (unlikely (character_info != null)) { 702 draw_character_info (cr); 703 } 704 } 705 706 void draw_empty_canvas (WidgetAllocation allocation, Context cr) { 707 Text t; 708 709 cr.save (); 710 t = new Text (t_("No glyphs in this view."), 24); 711 Theme.text_color (t, "Text Foreground"); 712 t.widget_x = 40; 713 t.widget_y = 30; 714 t.draw (cr); 715 cr.restore (); 716 } 717 718 public void scroll_rows (int row_adjustment) { 719 for (int i = 0; i < row_adjustment; i++) { 720 scroll (-OverViewItem.height); 721 } 722 723 for (int i = 0; i > row_adjustment; i--) { 724 scroll (OverViewItem.height); 725 } 726 } 727 728 public void scroll_adjustment (double pixel_adjustment) { 729 double l; 730 Font f; 731 732 if (all_available) { 733 f = BirdFont.get_current_font (); 734 l = f.length (); 735 } else { 736 l = glyph_range.length (); 737 } 738 739 if (first_visible <= 0) { 740 return; 741 } 742 743 if (first_visible + rows * items_per_row >= l) { 744 return; 745 } 746 747 scroll ((int64) pixel_adjustment); 748 } 749 750 void scroll_to_position (int64 r) { 751 if (r < 0) { 752 scroll_top (); 753 return; 754 } 755 756 selected -= (int) (r - first_visible); 757 first_visible = (int) r; 758 update_item_list (); 759 } 760 761 public override void scroll_to (double position) requires (items_per_row > 0) { 762 double r; 763 int nrows; 764 Font f; 765 766 if (all_available) { 767 f = BirdFont.get_current_font (); 768 nrows = (int) (f.length () / items_per_row); 769 } else { 770 nrows = (int) (glyph_range.length () / items_per_row); 771 } 772 773 view_offset_y = 0; 774 r = (int64) (position * (nrows - rows + 3)); // 3 invisible rows 775 r *= items_per_row; 776 777 scroll_to_position ((int64) r); 778 update_item_list (); 779 GlyphCanvas.redraw (); 780 } 781 782 private void scroll (double pixel_adjustment) { 783 if (first_visible < 0 && pixel_adjustment < 0) { 784 scroll_top (); 785 return; 786 } 787 788 view_offset_y += pixel_adjustment; 789 790 if (view_offset_y >= 0) { 791 while (view_offset_y > OverViewItem.height) { 792 view_offset_y -= OverViewItem.height; 793 first_visible -= items_per_row; 794 } 795 796 first_visible -= items_per_row; 797 view_offset_y -= OverViewItem.height; 798 } else if (view_offset_y < -OverViewItem.height) { 799 view_offset_y = 0; 800 first_visible += items_per_row; 801 } 802 803 update_item_list (); 804 } 805 806 public void scroll_top () { 807 first_visible = 0; 808 view_offset_y = 0; 809 810 update_item_list (); 811 812 if (visible_items.size != 0) { 813 selected_item = get_selected_item (); 814 } 815 } 816 817 /** Returns true if the selected glyph is at the last row. */ 818 private bool last_row () { 819 return visible_items.size - selected <= items_per_row; 820 } 821 822 public void key_down () { 823 Font f = BirdFont.get_current_font (); 824 int64 len = (all_available) ? f.length () : glyph_range.length (); 825 826 if (at_bottom () && last_row ()) { 827 return; 828 } 829 830 selected += items_per_row; 831 832 if (selected >= items_per_row * rows) { 833 first_visible += items_per_row; 834 selected -= items_per_row; 835 } 836 837 if (first_visible + selected >= len) { 838 selected = (int) (len - first_visible - 1); 839 840 if (selected < items_per_row * (rows - 1)) { 841 first_visible -= items_per_row; 842 selected += items_per_row; 843 } 844 } 845 846 if (selected >= visible_items.size) { 847 selected = (int) (visible_items.size - 1); 848 } 849 850 selected_item = get_selected_item (); 851 update_item_list (); 852 } 853 854 public void key_right () { 855 Font f = BirdFont.get_current_font (); 856 int64 len = (all_available) ? f.length () : glyph_range.length (); 857 858 if (at_bottom () && first_visible + selected + 1 >= len) { 859 selected = (int) (visible_items.size - 1); 860 selected_item = get_selected_item (); 861 return; 862 } 863 864 selected += 1; 865 866 if (selected >= items_per_row * rows) { 867 first_visible += items_per_row; 868 selected -= items_per_row; 869 selected -= 1; 870 } 871 872 if (first_visible + selected > len) { 873 first_visible -= items_per_row; 874 selected = (int) (len - first_visible - 1); 875 selected_item = get_selected_item (); 876 } 877 update_item_list (); 878 } 879 880 public void key_up () { 881 selected -= items_per_row; 882 883 if (selected < 0) { 884 first_visible -= items_per_row; 885 selected += items_per_row; 886 } 887 888 if (first_visible < 0) { 889 first_visible = 0; 890 } 891 update_item_list (); 892 } 893 894 public void key_left () { 895 selected -= 1; 896 897 if (selected < 0) { 898 first_visible -= items_per_row; 899 selected += items_per_row; 900 selected += 1; 901 } 902 903 if (first_visible < 0) { 904 scroll_top (); 905 } 906 update_item_list (); 907 } 908 909 public string get_selected_char () { 910 Font f; 911 Glyph? g; 912 913 if (all_available) { 914 f = BirdFont.get_current_font (); 915 g = f.get_glyph_index (selected); 916 return_val_if_fail (g != null, "".dup ()); 917 return ((!) g).get_name (); 918 } 919 920 return glyph_range.get_char (selected); 921 } 922 923 public override void key_press (uint keyval) { 924 hide_menu (); 925 update_item_list (); 926 GlyphCanvas.redraw (); 927 928 if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { 929 return; 930 } 931 932 switch (keyval) { 933 case Key.ENTER: 934 open_current_glyph (); 935 return; 936 937 case Key.UP: 938 get_selected_item ().selected = false; 939 940 key_up (); 941 selected_item = get_selected_item (); 942 943 selected_items.clear (); 944 if (selected_item.glyphs != null) { 945 selected_items.add ((!) selected_item.glyphs); 946 } 947 948 update_scrollbar (); 949 return; 950 951 case Key.RIGHT: 952 get_selected_item ().selected = false; 953 954 key_right (); 955 selected_item = get_selected_item (); 956 957 selected_items.clear (); 958 if (selected_item.glyphs != null) { 959 selected_items.add ((!) selected_item.glyphs); 960 } 961 962 update_scrollbar (); 963 return; 964 965 case Key.LEFT: 966 get_selected_item ().selected = false; 967 968 key_left (); 969 selected_item = get_selected_item (); 970 971 selected_items.clear (); 972 if (selected_item.glyphs != null) { 973 selected_items.add ((!) selected_item.glyphs); 974 } 975 976 update_scrollbar(); 977 return; 978 979 case Key.DOWN: 980 get_selected_item ().selected = false; 981 982 key_down (); 983 selected_item = get_selected_item (); 984 985 selected_items.clear (); 986 if (selected_item.glyphs != null) { 987 selected_items.add ((!) selected_item.glyphs); 988 } 989 990 update_scrollbar (); 991 return; 992 993 case Key.PG_UP: 994 get_selected_item ().selected = false; 995 996 for (int i = 0; i < rows; i++) { 997 key_up (); 998 } 999 selected_item = get_selected_item (); 1000 1001 selected_items.clear (); 1002 if (selected_item.glyphs != null) { 1003 selected_items.add ((!) selected_item.glyphs); 1004 } 1005 1006 update_scrollbar (); 1007 return; 1008 1009 case Key.PG_DOWN: 1010 get_selected_item ().selected = false; 1011 1012 for (int i = 0; i < rows; i++) { 1013 key_down (); 1014 } 1015 selected_item = get_selected_item (); 1016 1017 selected_items.clear (); 1018 if (selected_item.glyphs != null) { 1019 selected_items.add ((!) selected_item.glyphs); 1020 } 1021 1022 update_scrollbar (); 1023 return; 1024 1025 case Key.DEL: 1026 delete_selected_glyph (); 1027 selected_item = get_selected_item (); 1028 return; 1029 1030 case Key.BACK_SPACE: 1031 delete_selected_glyph (); 1032 selected_item = get_selected_item (); 1033 return; 1034 } 1035 1036 if (!KeyBindings.has_ctrl () && !KeyBindings.has_logo ()) { 1037 scroll_to_char (keyval); 1038 } 1039 1040 selected_item = get_selected_item (); 1041 1042 selected_items.clear (); 1043 if (selected_item.glyphs != null) { 1044 selected_items.add ((!) selected_item.glyphs); 1045 } 1046 1047 update_item_list (); 1048 GlyphCanvas.redraw (); 1049 } 1050 1051 public void delete_selected_glyph () { 1052 Font font = BirdFont.get_current_font (); 1053 OverViewUndoItem undo_item = new OverViewUndoItem (); 1054 1055 undo_item.alternate_sets = font.alternates.copy (); 1056 1057 foreach (GlyphCollection g in selected_items) { 1058 undo_item.glyphs.add (g.copy ()); 1059 } 1060 store_undo_items (undo_item); 1061 1062 foreach (GlyphCollection glyph_collection in selected_items) { 1063 font.delete_glyph (glyph_collection); 1064 string name = glyph_collection.get_name (); 1065 MainWindow.get_tab_bar ().close_background_tab_by_name (name); 1066 } 1067 1068 update_item_list (); 1069 GlyphCanvas.redraw (); 1070 } 1071 1072 public override void undo () { 1073 Font font = BirdFont.get_current_font (); 1074 OverViewUndoItem previous_collection; 1075 1076 if (undo_items.size == 0) { 1077 return; 1078 } 1079 1080 previous_collection = undo_items.get (undo_items.size - 1); 1081 redo_items.add (get_current_state (previous_collection)); 1082 1083 // remove the old glyph and add the new one 1084 foreach (GlyphCollection g in previous_collection.glyphs) { 1085 font.delete_glyph (g); 1086 1087 if (g.length () > 0) { 1088 font.add_glyph_collection (g); 1089 } 1090 1091 TabBar tabs = MainWindow.get_tab_bar (); 1092 Tab? tab = tabs.get_tab (g.get_name ()); 1093 1094 if (tab != null) { 1095 Tab t = (!) tab; 1096 set_glyph_zoom (g); 1097 t.set_glyph_collection (g); 1098 t.set_display (new GlyphTab (g)); 1099 } 1100 } 1101 1102 Font f = BirdFont.get_current_font (); 1103 f.alternates = previous_collection.alternate_sets.copy (); 1104 1105 undo_items.remove_at (undo_items.size - 1); 1106 update_item_list (); 1107 GlyphCanvas.redraw (); 1108 } 1109 1110 public override void redo () { 1111 Font font = BirdFont.get_current_font (); 1112 OverViewUndoItem previous_collection; 1113 1114 if (redo_items.size == 0) { 1115 return; 1116 } 1117 1118 previous_collection = redo_items.get (redo_items.size - 1); 1119 undo_items.add (get_current_state (previous_collection)); 1120 1121 // remove the old glyph and add the new one 1122 foreach (GlyphCollection g in previous_collection.glyphs) { 1123 font.delete_glyph (g); 1124 font.add_glyph_collection (g); 1125 1126 TabBar tabs = MainWindow.get_tab_bar (); 1127 Tab? tab = tabs.get_tab (g.get_name ()); 1128 1129 if (tab != null) { 1130 Tab t = (!) tab; 1131 set_glyph_zoom (g); 1132 t.set_glyph_collection (g); 1133 t.set_display (new GlyphTab (g)); 1134 } 1135 } 1136 font.alternates = previous_collection.alternate_sets.copy (); 1137 1138 redo_items.remove_at (redo_items.size - 1); 1139 GlyphCanvas.redraw (); 1140 } 1141 1142 public OverViewUndoItem get_current_state (OverViewUndoItem previous_collection) { 1143 GlyphCollection? gc; 1144 OverViewUndoItem ui = new OverViewUndoItem (); 1145 Font font = BirdFont.get_current_font (); 1146 1147 ui.alternate_sets = font.alternates.copy (); 1148 1149 foreach (GlyphCollection g in previous_collection.glyphs) { 1150 gc = font.get_glyph_collection (g.get_name ()); 1151 1152 if (gc != null) { 1153 ui.glyphs.add (((!) gc).copy ()); 1154 } else { 1155 ui.glyphs.add (new GlyphCollection (g.get_unicode_character (), g.get_name ())); 1156 } 1157 } 1158 1159 return ui; 1160 } 1161 1162 public void store_undo_state (GlyphCollection gc) { 1163 OverViewUndoItem i = new OverViewUndoItem (); 1164 Font f = BirdFont.get_current_font (); 1165 i.alternate_sets = f.alternates.copy (); 1166 i.glyphs.add (gc); 1167 store_undo_items (i); 1168 } 1169 1170 public void store_undo_items (OverViewUndoItem i) { 1171 undo_items.add (i); 1172 redo_items.clear (); 1173 } 1174 1175 bool select_visible_glyph (string name) { 1176 int i = 0; 1177 1178 foreach (OverViewItem o in visible_items) { 1179 if (o.get_name () == name) { 1180 selected = i; 1181 selected_item = get_selected_item (); 1182 1183 if (selected_item.y > allocation.height - OverViewItem.height) { 1184 view_offset_y -= (selected_item.y + OverViewItem.height) - allocation.height; 1185 } 1186 1187 if (selected_item.y < 0) { 1188 view_offset_y = 0; 1189 } 1190 1191 return true; 1192 } 1193 1194 if (i > 1000) { 1195 warning ("selected character not found"); 1196 return true; 1197 } 1198 1199 i++; 1200 } 1201 1202 return false; 1203 } 1204 1205 public void scroll_to_char (unichar c) { 1206 StringBuilder s = new StringBuilder (); 1207 1208 if (is_modifier_key (c)) { 1209 return; 1210 } 1211 1212 s.append_unichar (c); 1213 scroll_to_glyph (s.str); 1214 } 1215 1216 public void scroll_to_glyph (string name) { 1217 GlyphRange gr = glyph_range; 1218 int i, r, index; 1219 string ch; 1220 Font font = BirdFont.get_current_font (); 1221 GlyphCollection? glyphs = null; 1222 Glyph glyph; 1223 1224 index = -1; 1225 1226 if (items_per_row <= 0) { 1227 return; 1228 } 1229 1230 ch = name; 1231 1232 // selected char is visible 1233 if (select_visible_glyph (ch)) { 1234 return; 1235 } 1236 1237 // scroll to char 1238 if (all_available) { 1239 1240 // don't search for glyphs in huge CJK fonts 1241 if (font.length () > 500) { 1242 r = 0; 1243 } else { 1244 // FIXME: too slow 1245 for (r = 0; r < font.length (); r += items_per_row) { 1246 for (i = 0; i < items_per_row && i < font.length (); i++) { 1247 glyphs = font.get_glyph_collection_index ((uint32) r + i); 1248 return_if_fail (glyphs != null); 1249 glyph = ((!) glyphs).get_current (); 1250 1251 if (glyph.name == ch) { 1252 index = i; 1253 } 1254 } 1255 1256 if (index > -1) { 1257 break; 1258 } 1259 } 1260 } 1261 } else { 1262 1263 if (ch.char_count () > 1) { 1264 warning ("Can't scroll to ligature in this view"); 1265 return; 1266 } 1267 1268 for (r = 0; r < gr.length (); r += items_per_row) { 1269 for (i = 0; i < items_per_row; i++) { 1270 if (gr.get_char (r + i) == ch) { 1271 index = i; 1272 } 1273 } 1274 1275 if (index > -1) { 1276 break; 1277 } 1278 } 1279 } 1280 1281 if (index > -1) { 1282 first_visible = r; 1283 process_item_list_update (); 1284 update_item_list (); 1285 select_visible_glyph (ch); 1286 } 1287 } 1288 1289 public override void double_click (uint button, double ex, double ey) 1290 requires (!is_null (visible_items) && !is_null (allocation)) { 1291 1292 return_if_fail (!is_null (this)); 1293 1294 foreach (OverViewItem i in visible_items) { 1295 if (i.double_click (button, ex, ey)) { 1296 open_overview_item (i); 1297 } 1298 } 1299 1300 GlyphCanvas.redraw (); 1301 } 1302 1303 public void open_overview_item (OverViewItem i) { 1304 return_if_fail (!is_null (i)); 1305 1306 if (i.glyphs != null) { 1307 open_glyph_signal ((!) i.glyphs); 1308 GlyphCollection gc = (!) i.glyphs; 1309 gc.get_current ().close_path (); 1310 } else { 1311 open_new_glyph_signal (i.character); 1312 } 1313 } 1314 1315 public void set_character_info (CharacterInfo i) { 1316 character_info = i; 1317 } 1318 1319 public int get_selected_index () { 1320 GlyphCollection gc; 1321 int index = 0; 1322 1323 if (selected_items.size == 0) { 1324 return 0; 1325 } 1326 1327 gc = selected_items.get (0); 1328 1329 foreach (OverViewItem i in visible_items) { 1330 if (i.glyphs != null && gc == ((!) i.glyphs)) { 1331 break; 1332 } 1333 1334 index++; 1335 } 1336 1337 return index; 1338 } 1339 1340 public void hide_menu () { 1341 foreach (OverViewItem i in visible_items) { 1342 i.hide_menu (); 1343 } 1344 } 1345 1346 public override void button_press (uint button, double x, double y) { 1347 OverViewItem i; 1348 int index = 0; 1349 int selected_index = -1; 1350 bool update = false; 1351 1352 if (character_info != null) { 1353 character_info = null; 1354 GlyphCanvas.redraw (); 1355 return; 1356 } 1357 1358 if (!KeyBindings.has_shift ()) { 1359 selected_items.clear (); 1360 } 1361 1362 for (int j = 0; j < visible_items.size; j++) { 1363 i = visible_items.get (j); 1364 1365 if (i.click (button, x, y)) { 1366 selected = index; 1367 selected_item = get_selected_item (); 1368 1369 if (KeyBindings.has_shift ()) { 1370 if (selected_item.glyphs != null) { 1371 1372 selected_index = selected_items.index_of ((!) selected_item.glyphs); 1373 if (selected_index == -1) { 1374 selected_items.add ((!) selected_item.glyphs); 1375 } else { 1376 return_if_fail (0 <= selected_index < selected_items.size); 1377 selected_items.remove_at (selected_index); 1378 selected = get_selected_index (); 1379 selected_item = get_selected_item (); 1380 } 1381 } 1382 } else { 1383 selected_items.clear (); 1384 if (selected_item.glyphs != null) { 1385 selected_items.add ((!) selected_item.glyphs); 1386 } 1387 } 1388 1389 if (!is_null (i.version_menu)) { 1390 update = !((!)i).version_menu.menu_visible; 1391 } else { 1392 update = true; 1393 } 1394 } 1395 index++; 1396 } 1397 1398 if (update) { 1399 update_item_list (); 1400 } 1401 1402 GlyphCanvas.redraw (); 1403 } 1404 1405 /** Returns true if overview shows the last character. */ 1406 private bool at_bottom () { 1407 Font f; 1408 double t = rows * items_per_row + first_visible; 1409 1410 if (all_available) { 1411 f = BirdFont.get_current_font (); 1412 return t >= f.length (); 1413 } 1414 1415 return t >= glyph_range.length (); 1416 } 1417 1418 public void set_current_glyph_range (GlyphRange range) { 1419 GlyphRange? current = glyph_range; 1420 string c; 1421 1422 if (current != null) { 1423 c = glyph_range.get_char (selected); 1424 } 1425 1426 all_available = false; 1427 1428 glyph_range = range; 1429 scroll_top (); 1430 1431 // TODO: scroll down to c 1432 update_item_list (); 1433 selected_item = get_selected_item (); 1434 1435 GlyphCanvas.redraw (); 1436 } 1437 1438 public void select_next_glyph () { 1439 key_right (); 1440 } 1441 1442 public void open_current_glyph () { 1443 // keep this object even if open_glyph_signal closes the display 1444 this.ref (); 1445 1446 selected_item = get_selected_item (); 1447 if (selected_item.glyphs != null) { 1448 open_glyph_signal ((!) selected_item.glyphs); 1449 GlyphCollection gc = (!) selected_item.glyphs; 1450 gc.get_current ().close_path (); 1451 } else { 1452 open_new_glyph_signal (selected_item.character); 1453 } 1454 1455 this.unref (); 1456 } 1457 1458 public override void update_scrollbar () { 1459 Font f; 1460 double nrows = 0; 1461 double pos = 0; 1462 double size; 1463 double visible_rows; 1464 1465 if (rows == 0) { 1466 MainWindow.set_scrollbar_size (0); 1467 MainWindow.set_scrollbar_position (0); 1468 } else { 1469 if (all_available) { 1470 f = BirdFont.get_current_font (); 1471 nrows = Math.floor ((f.length ()) / rows); 1472 size = f.length (); 1473 } else { 1474 nrows = Math.floor ((glyph_range.length ()) / rows); 1475 size = glyph_range.length (); 1476 } 1477 1478 if (nrows <= 0) { 1479 nrows = 1; 1480 } 1481 1482 visible_rows = allocation.height / OverViewItem.height; 1483 scroll_size = visible_rows / nrows; 1484 MainWindow.set_scrollbar_size (scroll_size); 1485 pos = first_visible / (nrows * items_per_row - visible_rows * items_per_row); 1486 MainWindow.set_scrollbar_position (pos); 1487 } 1488 } 1489 1490 /** Display one entry from the Unicode Character Database. */ 1491 void draw_character_info (Context cr) 1492 requires (character_info != null) { 1493 double x, y, w, h; 1494 int i; 1495 string unicode_value, unicode_description; 1496 string[] column; 1497 string entry; 1498 int len = 0; 1499 int length = 0; 1500 bool see_also = false; 1501 WidgetAllocation allocation = MainWindow.get_overview ().allocation; 1502 string name; 1503 string[] lines; 1504 double character_start; 1505 double character_height; 1506 1507 entry = ((!)character_info).get_entry (); 1508 1509 lines = entry.split ("\n"); 1510 1511 foreach (string line in entry.split ("\n")) { 1512 len = line.char_count (); 1513 if (len > length) { 1514 length = len; 1515 } 1516 } 1517 1518 x = allocation.width * 0.1; 1519 y = allocation.height * 0.1; 1520 w = allocation.width * 0.9 - x; 1521 h = allocation.height * 0.9 - y; 1522 1523 if (w < 8 * length) { 1524 w = 8 * length; 1525 x = (allocation.width - w) / 2.0; 1526 } 1527 1528 if (x < 0) { 1529 x = 2; 1530 } 1531 1532 // background 1533 cr.save (); 1534 Theme.color_opacity (cr, "Background 1", 0.98); 1535 cr.rectangle (x, y, w, h); 1536 cr.fill (); 1537 cr.restore (); 1538 1539 cr.save (); 1540 Theme.color_opacity (cr, "Foreground 1", 0.98); 1541 cr.set_line_width (2); 1542 cr.rectangle (x, y, w, h); 1543 cr.stroke (); 1544 cr.restore (); 1545 1546 // database entry 1547 1548 if (((!)character_info).is_ligature ()) { 1549 name = ((!)character_info).get_name (); 1550 draw_info_line (name, cr, x, y, 0); 1551 } else { 1552 i = 0; 1553 foreach (string line in lines) { 1554 if (i == 0) { 1555 column = line.split ("\t"); 1556 return_if_fail (column.length == 2); 1557 unicode_value = "U+" + column[0]; 1558 unicode_description = column[1]; 1559 1560 draw_info_line (unicode_description, cr, x, y, i); 1561 i++; 1562 1563 draw_info_line (unicode_value, cr, x, y, i); 1564 i++; 1565 } else { 1566 1567 if (line.has_prefix ("\t*")) { 1568 draw_info_line (line.replace ("\t*", "•"), cr, x, y, i); 1569 i++; 1570 } else if (line.has_prefix ("\tx (")) { 1571 if (!see_also) { 1572 i++; 1573 draw_info_line (t_("See also:"), cr, x, y, i); 1574 i++; 1575 see_also = true; 1576 } 1577 1578 draw_info_line (line.replace ("\tx (", "•").replace (")", ""), cr, x, y, i); 1579 i++; 1580 } else { 1581 i++; 1582 } 1583 } 1584 } 1585 1586 character_start = y + 10 + i * UCD_LINE_HEIGHT; 1587 character_height = h - character_start; 1588 1589 draw_fallback_character (cr, x, character_start, character_height); 1590 } 1591 } 1592 1593 /** Fallback character in UCD info. */ 1594 void draw_fallback_character (Context cr, double x, double y, double height) 1595 requires (character_info != null) { 1596 unichar c = ((!)character_info).unicode; 1597 1598 if (height < 0) { 1599 return; 1600 } 1601 1602 cr.save (); 1603 Text character = new Text (); 1604 character.set_use_cache (false); 1605 Theme.text_color (character, "Foreground 1"); 1606 character.set_text ((!) c.to_string ()); 1607 character.set_font_size (height); 1608 character.draw_at_top (cr, x + 10, y); 1609 cr.restore (); 1610 } 1611 1612 void draw_info_line (string line, Context cr, double x, double y, int row) { 1613 Text ucd_entry = new Text (line); 1614 cr.save (); 1615 Theme.text_color (ucd_entry, "Foreground 1"); 1616 ucd_entry.widget_x = 10 + x; 1617 ucd_entry.widget_y = 10 + y + row * UCD_LINE_HEIGHT; 1618 ucd_entry.draw (cr); 1619 cr.restore (); 1620 } 1621 1622 public void paste () { 1623 GlyphCollection gc; 1624 GlyphCollection? c; 1625 Glyph glyph; 1626 uint32 index; 1627 int i; 1628 int skip = 0; 1629 int s; 1630 string character_string; 1631 Gee.ArrayList<GlyphCollection> glyps; 1632 Font f; 1633 OverViewUndoItem undo_item; 1634 1635 f = BirdFont.get_current_font (); 1636 gc = new GlyphCollection ('\0', ""); 1637 glyps = new Gee.ArrayList<GlyphCollection> (); 1638 1639 copied_glyphs.sort ((a, b) => { 1640 return (int) ((GlyphCollection) a).get_unicode_character () 1641 - (int) ((GlyphCollection) b).get_unicode_character (); 1642 }); 1643 1644 index = (uint32) first_visible + selected; 1645 for (i = 0; i < copied_glyphs.size; i++) { 1646 if (all_available) { 1647 if (f.length () == 0) { 1648 c = add_empty_character_to_font (copied_glyphs.get (i).get_unicode_character (), 1649 copied_glyphs.get (i).is_unassigned (), 1650 copied_glyphs.get (i).get_name ()); 1651 } else if (index >= f.length ()) { 1652 // FIXME: duplicated unicodes? 1653 c = add_empty_character_to_font (copied_glyphs.get (i).get_unicode_character (), 1654 copied_glyphs.get (i).is_unassigned (), 1655 copied_glyphs.get (i).get_name ()); 1656 } else { 1657 c = f.get_glyph_collection_index ((uint32) index); 1658 } 1659 1660 if (c == null) { 1661 c = add_empty_character_to_font (copied_glyphs.get (i).get_unicode_character (), 1662 copied_glyphs.get (i).is_unassigned (), 1663 copied_glyphs.get (i).get_name ()); 1664 } 1665 1666 return_if_fail (c != null); 1667 gc = (!) c; 1668 } else { 1669 if (i != 0) { 1670 s = (int) copied_glyphs.get (i).get_unicode_character (); 1671 s -= (int) copied_glyphs.get (i - 1).get_unicode_character (); 1672 s -= 1; 1673 skip += s; 1674 } 1675 1676 character_string = glyph_range.get_char ((uint32) (index + skip)); 1677 c = f.get_glyph_collection_by_name (character_string); 1678 1679 if (c == null) { 1680 gc = add_empty_character_to_font (character_string.get_char (), 1681 copied_glyphs.get (i).is_unassigned (), 1682 copied_glyphs.get (i).get_name ()); 1683 } else { 1684 gc = (!) c; 1685 } 1686 } 1687 1688 glyps.add (gc); 1689 index++; 1690 } 1691 1692 undo_item = new OverViewUndoItem (); 1693 undo_item.alternate_sets = f.alternates.copy (); 1694 foreach (GlyphCollection g in glyps) { 1695 undo_item.glyphs.add (g.copy ()); 1696 } 1697 store_undo_items (undo_item); 1698 1699 if (glyps.size != copied_glyphs.size) { 1700 warning ("glyps.size != copied_glyphs.size"); 1701 return; 1702 } 1703 1704 if (copied_glyphs.size < i) { 1705 warning ("Array index out of bounds."); 1706 return; 1707 } 1708 1709 i = 0; 1710 foreach (GlyphCollection g in glyps) { 1711 glyph = copied_glyphs.get (i).get_current ().copy (); 1712 glyph.version_id = (glyph.version_id == -1 || g.length () == 0) ? 1 : g.get_last_id () + 1; 1713 glyph.unichar_code = g.get_unicode_character (); 1714 1715 if (!g.is_unassigned ()) { 1716 glyph.name = (!) glyph.unichar_code.to_string (); 1717 } else { 1718 glyph.name = g.get_name (); 1719 } 1720 1721 g.insert_glyph (glyph, true); 1722 i++; 1723 } 1724 1725 f.touch (); 1726 1727 update_item_list (); 1728 GlyphCanvas.redraw (); 1729 } 1730 1731 public override bool needs_modifier () { 1732 return true; 1733 } 1734 1735 public class OverViewUndoItem { 1736 public AlternateSets alternate_sets = new AlternateSets (); 1737 public Gee.ArrayList<GlyphCollection> glyphs = new Gee.ArrayList<GlyphCollection> (); 1738 } 1739 } 1740 1741 } 1742