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