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

Revisions

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