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