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.
Store fallback font in database
1 /* 2 Copyright (C) 2012 2014 Johan Mattsson 3 4 This library is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 3 of the 7 License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 */ 14 15 using Cairo; 16 17 namespace BirdFont { 18 19 /** A display with all glyphs present in this font. */ 20 public class OverView : FontDisplay { 21 public WidgetAllocation allocation = new WidgetAllocation (); 22 23 OverViewItem selected_item = new OverViewItem (null, '\0', 0, 0); 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 string search_query = ""; 41 42 Gee.ArrayList<OverViewItem> visible_items = new Gee.ArrayList<OverViewItem> (); 43 44 /** List of undo commands. */ 45 Gee.ArrayList<OverViewUndoItem> undo_items = new Gee.ArrayList<OverViewUndoItem> (); 46 Gee.ArrayList<OverViewUndoItem> redo_items = new Gee.ArrayList<OverViewUndoItem> (); 47 48 /** Show all characters that has been drawn. */ 49 public bool all_available = true; 50 51 /** Show unicode database info. */ 52 CharacterInfo? character_info = null; 53 54 double scroll_size = 1; 55 56 public OverView (GlyphRange? range = null, bool open_selected = true) { 57 GlyphRange gr; 58 59 if (range == null) { 60 gr = new GlyphRange (); 61 set_glyph_range (gr); 62 } 63 64 if (open_selected) { 65 this.open_glyph_signal.connect ((glyph_collection) => { 66 TabBar tabs = MainWindow.get_tab_bar (); 67 string n = glyph_collection.get_current ().name; 68 bool selected = tabs.select_char (n); 69 GlyphCanvas canvas; 70 Glyph g = glyph_collection.get_current (); 71 72 if (!selected) { 73 canvas = MainWindow.get_glyph_canvas (); 74 tabs.add_tab (g, true, glyph_collection); 75 canvas.set_current_glyph_collection (glyph_collection); 76 set_initial_zoom (); 77 } 78 }); 79 80 this.open_new_glyph_signal.connect ((character) => { 81 create_new_glyph (character); 82 }); 83 } 84 85 IdleSource idle = new IdleSource (); 86 87 idle.set_callback (() => { 88 selected_canvas (); 89 return false; 90 }); 91 92 idle.attach (null); 93 94 update_scrollbar (); 95 reset_zoom (); 96 } 97 98 public GlyphCollection create_new_glyph (unichar character) { 99 StringBuilder name = new StringBuilder (); 100 TabBar tabs = MainWindow.get_tab_bar (); 101 bool selected; 102 Glyph glyph; 103 GlyphCollection glyph_collection = MainWindow.get_current_glyph_collection (); 104 GlyphCanvas canvas; 105 106 name.append_unichar (character); 107 selected = tabs.select_char (name.str); 108 109 if (!selected) { 110 glyph_collection = add_character_to_font (character); 111 112 glyph = glyph_collection.get_current (); 113 tabs.add_tab (glyph, true, glyph_collection); 114 115 selected_items.add (glyph_collection); 116 117 canvas = MainWindow.get_glyph_canvas (); 118 canvas.set_current_glyph_collection (glyph_collection); 119 120 set_initial_zoom (); 121 } else { 122 warning ("Glyph is already open"); 123 } 124 125 OverviewTools.update_overview_characterset (); 126 return glyph_collection; 127 } 128 129 public GlyphCollection add_empty_character_to_font (unichar character, bool unassigned, string name) { 130 return add_character_to_font (character, true, unassigned); 131 } 132 133 public GlyphCollection add_character_to_font (unichar character, bool empty = false, 134 bool unassigned = false, string glyph_name = "") { 135 StringBuilder name = new StringBuilder (); 136 Font font = BirdFont.get_current_font (); 137 GlyphCollection? fg; 138 Glyph glyph; 139 GlyphCollection glyph_collection; 140 141 if (glyph_name == "") { 142 name.append_unichar (character); 143 } else { 144 name.append (glyph_name); 145 } 146 147 if (all_available) { 148 fg = font.get_glyph_collection_by_name (name.str); 149 } else { 150 fg = font.get_glyph_collection (name.str); 151 } 152 153 if (fg != null) { 154 glyph_collection = (!) fg; 155 } else { 156 glyph_collection = new GlyphCollection (character, name.str); 157 158 if (!empty) { 159 glyph = new Glyph (name.str, (!unassigned) ? character : '\0'); 160 glyph_collection.insert_glyph (glyph, true); 161 } 162 163 font.add_glyph_collection (glyph_collection); 164 } 165 166 glyph_collection.set_unassigned (unassigned); 167 168 return glyph_collection; 169 } 170 171 public static void search () { 172 OverView ow = MainWindow.get_overview (); 173 TextListener listener = new TextListener (t_("Search"), ow.search_query, t_("Filter")); 174 175 listener.signal_text_input.connect ((text) => { 176 OverView o = MainWindow.get_overview (); 177 o.search_query = text; 178 }); 179 180 listener.signal_submit.connect (() => { 181 OverView o = MainWindow.get_overview (); 182 GlyphRange r = CharDatabase.search (o.search_query); 183 o.set_glyph_range (r); 184 TabContent.hide_text_input (); 185 MainWindow.get_tab_bar ().select_tab_name ("Overview"); 186 }); 187 188 TabContent.show_text_input (listener); 189 } 190 191 public Glyph? get_current_glyph () { 192 OverViewItem oi = selected_item; 193 if (oi.glyphs != null) { 194 return ((!) oi.glyphs).get_current (); 195 } 196 return null; 197 } 198 199 private void set_initial_zoom () { 200 Toolbox tools = MainWindow.get_toolbox (); 201 ZoomTool z = (ZoomTool) tools.get_tool ("zoom_tool"); 202 z.store_current_view (); 203 MainWindow.get_current_glyph ().default_zoom (); 204 z.store_current_view (); 205 } 206 207 public double get_height () { 208 double l; 209 Font f; 210 211 if (rows == 0) { 212 return 0; 213 } 214 215 if (all_available) { 216 f = BirdFont.get_current_font (); 217 l = f.length (); 218 } else { 219 l = glyph_range.length (); 220 } 221 222 return 2.0 * OverViewItem.height * (l / rows); 223 } 224 225 public bool selected_char_is_visible () { 226 return first_visible <= selected <= first_visible + items_per_row * rows; 227 } 228 229 public override bool has_scrollbar () { 230 return true; 231 } 232 233 public override void scroll_wheel_up (double x, double y) { 234 key_up (); 235 update_scrollbar (); 236 GlyphCanvas.redraw (); 237 hide_menu (); 238 239 selected_item = get_selected_item (); 240 selected_items.clear (); 241 if (selected_item.glyphs != null) { 242 selected_items.add ((!) selected_item.glyphs); 243 } 244 } 245 246 public override void scroll_wheel_down (double x, double y) { 247 key_down (); 248 update_scrollbar (); 249 GlyphCanvas.redraw (); 250 hide_menu (); 251 252 selected_item = get_selected_item (); 253 selected_items.clear (); 254 if (selected_item.glyphs != null) { 255 selected_items.add ((!) selected_item.glyphs); 256 } 257 } 258 259 public override void selected_canvas () { 260 OverviewTools.update_overview_characterset (); 261 KeyBindings.set_require_modifier (true); 262 update_scrollbar (); 263 update_zoom_bar (); 264 OverViewItem.glyph_scale = 1; 265 update_item_list (); 266 selected_item = get_selected_item (); 267 GlyphCanvas.redraw (); 268 } 269 270 public void update_zoom_bar () { 271 double z = OverViewItem.width / OverViewItem.DEFAULT_WIDTH - 0.5; 272 Toolbox.overview_tools.zoom_bar.set_zoom (z); 273 Toolbox.redraw_tool_box (); 274 update_item_list (); 275 } 276 277 public void set_zoom (double zoom) { 278 double z = zoom + 0.5; 279 OverViewItem.glyph_scale = 1; 280 OverViewItem.width = OverViewItem.DEFAULT_WIDTH * z; 281 OverViewItem.height = OverViewItem.DEFAULT_HEIGHT * z; 282 OverViewItem.margin = OverViewItem.DEFAULT_MARGIN * z; 283 GlyphCanvas.redraw (); 284 } 285 286 public override void zoom_min () { 287 OverViewItem.width = OverViewItem.DEFAULT_WIDTH * 0.5; 288 OverViewItem.height = OverViewItem.DEFAULT_HEIGHT * 0.5; 289 OverViewItem.margin = OverViewItem.DEFAULT_MARGIN * 0.5; 290 GlyphCanvas.redraw (); 291 update_zoom_bar (); 292 } 293 294 public override void reset_zoom () { 295 OverViewItem.width = OverViewItem.DEFAULT_WIDTH; 296 OverViewItem.height = OverViewItem.DEFAULT_HEIGHT; 297 OverViewItem.margin = OverViewItem.DEFAULT_MARGIN; 298 GlyphCanvas.redraw (); 299 update_zoom_bar (); 300 } 301 302 public override void zoom_max () { 303 OverViewItem.width = allocation.width; 304 OverViewItem.height = allocation.height; 305 GlyphCanvas.redraw (); 306 } 307 308 public override void zoom_in () { 309 OverViewItem.width *= 1.1; 310 OverViewItem.height *= 1.1; 311 OverViewItem.margin *= 1.1; 312 GlyphCanvas.redraw (); 313 update_zoom_bar (); 314 } 315 316 public override void zoom_out () { 317 OverViewItem.width *= 0.9; 318 OverViewItem.height *= 0.9; 319 OverViewItem.margin *= 0.9; 320 GlyphCanvas.redraw (); 321 update_zoom_bar (); 322 } 323 324 public override void store_current_view () { 325 } 326 327 public override void restore_last_view () { 328 } 329 330 public override void next_view () { 331 } 332 333 public override string get_label () { 334 return t_("Overview"); 335 } 336 337 public override string get_name () { 338 return "Overview"; 339 } 340 341 public void display_all_available_glyphs () { 342 all_available = true; 343 344 first_visible = 0; 345 selected = 0; 346 347 selected_item = get_selected_item (); 348 GlyphCanvas.redraw (); 349 } 350 351 OverViewItem get_selected_item () { 352 if (visible_items.size == 0) { 353 return new OverViewItem (null, '\0', 0, 0); 354 } 355 356 if (unlikely (!(0 <= selected < visible_items.size))) { 357 warning (@"0 <= $selected < $(visible_items.size)"); 358 return new OverViewItem (null, '\0', 0, 0); 359 } 360 361 return visible_items.get (selected); 362 } 363 364 int get_items_per_row () { 365 int i = 1; 366 OverViewItem.margin = OverViewItem.width * 0.1; 367 double l = OverViewItem.margin + OverViewItem.full_width (); 368 while (l <= allocation.width) { 369 l += OverViewItem.full_width (); 370 i++; 371 } 372 return i - 1; 373 } 374 375 public void update_item_list (int item_list_length = -1) { 376 string character_string; 377 Font f = BirdFont.get_current_font (); 378 GlyphCollection? glyphs = null; 379 uint32 index; 380 OverViewItem item; 381 double x, y; 382 unichar character; 383 Glyph glyph; 384 385 items_per_row = get_items_per_row (); 386 rows = (int) (allocation.height / OverViewItem.full_height ()) + 2; 387 388 if (item_list_length == -1) { 389 item_list_length = items_per_row * rows; 390 } 391 392 visible_items.clear (); 393 visible_items = new Gee.ArrayList<OverViewItem> (); 394 395 // update item list 396 index = (uint32) first_visible; 397 x = OverViewItem.margin; 398 y = OverViewItem.margin; 399 for (int i = 0; i < item_list_length; i++) { 400 if (all_available) { 401 if (! (0 <= index < f.length ())) { 402 break; 403 } 404 405 glyphs = f.get_glyph_collection_indice ((uint32) index); 406 return_if_fail (glyphs != null); 407 408 glyph = ((!) glyphs).get_current (); 409 character_string = glyph.name; 410 character = glyph.unichar_code; 411 } else { 412 if (!(0 <= index < glyph_range.get_length ())) { 413 break; 414 } 415 416 character_string = glyph_range.get_char ((uint32) index); 417 glyphs = f.get_glyph_collection_by_name (character_string); 418 character = character_string.get_char (0); 419 } 420 421 item = new OverViewItem (glyphs, character, x, y); 422 item.adjust_scale (); 423 424 x += OverViewItem.full_width (); 425 426 if (x + OverViewItem.full_width () >= allocation.width) { 427 x = OverViewItem.margin; 428 y += OverViewItem.full_height (); 429 } 430 431 item.selected = (i == selected); 432 433 if (glyphs != null) { 434 item.selected |= selected_items.index_of ((!) glyphs) != -1; 435 } 436 437 visible_items.add (item); 438 index++; 439 } 440 441 // offset 442 item = get_selected_item (); 443 if (item.y + OverViewItem.height > allocation.height) { 444 view_offset_y = allocation.height - (item.y + OverViewItem.height); 445 } 446 447 if (item.y + view_offset_y < 0) { 448 view_offset_y = 0; 449 } 450 451 foreach (OverViewItem i in visible_items) { 452 i.y += view_offset_y; 453 i.x += view_offset_x; 454 } 455 } 456 457 public override void draw (WidgetAllocation allocation, Context cr) { 458 this.allocation = allocation; 459 460 // clear canvas 461 cr.save (); 462 Theme.color (cr, "Background 1"); 463 cr.rectangle (0, 0, allocation.width, allocation.height); 464 cr.fill (); 465 cr.restore (); 466 467 foreach (OverViewItem i in visible_items) { 468 i.draw (allocation, cr); 469 } 470 471 if (visible_items.size == 0) { 472 draw_empty_canvas (allocation, cr); 473 } 474 475 if (character_info != null) { 476 draw_character_info (cr); 477 } 478 } 479 480 void draw_empty_canvas (WidgetAllocation allocation, Context cr) { 481 Text t; 482 483 cr.save (); 484 t = new Text (t_("No glyphs in this view."), 24); 485 Theme.text_color (t, "Text Foreground"); 486 t.widget_x = 40; 487 t.widget_y = 30; 488 t.draw (cr); 489 cr.restore (); 490 } 491 492 public void scroll_rows (int row_adjustment) { 493 for (int i = 0; i < row_adjustment; i++) { 494 scroll (-OverViewItem.height); 495 } 496 497 for (int i = 0; i > row_adjustment; i--) { 498 scroll (OverViewItem.height); 499 } 500 } 501 502 public void scroll_adjustment (double pixel_adjustment) { 503 double l; 504 Font f; 505 506 if (all_available) { 507 f = BirdFont.get_current_font (); 508 l = f.length (); 509 } else { 510 l = glyph_range.length (); 511 } 512 513 if (first_visible <= 0) { 514 return; 515 } 516 517 if (first_visible + rows * items_per_row >= l) { 518 return; 519 } 520 521 scroll ((int64) pixel_adjustment); 522 } 523 524 void default_position () { 525 scroll_top (); 526 scroll_rows (1); 527 } 528 529 void scroll_to_position (int64 r) { 530 if (r < 0) { 531 scroll_top (); 532 return; 533 } 534 535 default_position (); 536 537 first_visible = (int) r; 538 update_item_list (); 539 } 540 541 public override void scroll_to (double position) requires (items_per_row > 0) { 542 double r; 543 int nrows; 544 Font f; 545 546 if (all_available) { 547 f = BirdFont.get_current_font (); 548 nrows = (int) (f.length () / items_per_row); 549 } else { 550 nrows = (int) (glyph_range.length () / items_per_row); 551 } 552 553 view_offset_y = 0; 554 r = (int64) (position * (nrows - rows + 3)); // 3 invisible rows 555 r *= items_per_row; 556 557 scroll_to_position ((int64) r); 558 update_item_list (); 559 GlyphCanvas.redraw (); 560 } 561 562 private void scroll (double pixel_adjustment) { 563 if (first_visible < 0 && pixel_adjustment < 0) { 564 scroll_top (); 565 return; 566 } 567 568 view_offset_y += pixel_adjustment; 569 570 if (view_offset_y >= 0) { 571 while (view_offset_y > OverViewItem.height) { 572 view_offset_y -= OverViewItem.height; 573 first_visible -= items_per_row; 574 } 575 576 first_visible -= items_per_row; 577 view_offset_y -= OverViewItem.height; 578 } else if (view_offset_y < -OverViewItem.height) { 579 view_offset_y = 0; 580 first_visible += items_per_row; 581 } 582 583 update_item_list (); 584 } 585 586 public void scroll_top () { 587 selected = 0; 588 first_visible = 0; 589 590 update_item_list (); 591 592 if (visible_items.size != 0) { 593 selected_item = get_selected_item (); 594 } 595 } 596 597 /** Returns true if the selected glyph is at the last row. */ 598 private bool last_row () { 599 return visible_items.size - selected <= items_per_row; 600 } 601 602 public void key_down () { 603 Font f = BirdFont.get_current_font (); 604 int64 len = (all_available) ? f.length () : glyph_range.length (); 605 606 if (at_bottom () && last_row ()) { 607 return; 608 } 609 610 selected += items_per_row; 611 612 if (selected >= items_per_row * rows) { 613 first_visible += items_per_row; 614 selected -= items_per_row; 615 } 616 617 if (first_visible + selected >= len) { 618 selected = (int) (len - first_visible - 1); 619 620 if (selected < items_per_row * (rows - 1)) { 621 first_visible -= items_per_row; 622 selected += items_per_row; 623 } 624 } 625 626 if (selected >= visible_items.size) { 627 selected = (int) (visible_items.size - 1); 628 } 629 630 selected_item = get_selected_item (); 631 update_item_list (); 632 } 633 634 public void key_right () { 635 Font f = BirdFont.get_current_font (); 636 int64 len = (all_available) ? f.length () : glyph_range.length (); 637 638 if (at_bottom () && first_visible + selected + 1 >= len) { 639 selected = (int) (visible_items.size - 1); 640 selected_item = get_selected_item (); 641 return; 642 } 643 644 selected += 1; 645 646 if (selected >= items_per_row * rows) { 647 first_visible += items_per_row; 648 selected -= items_per_row; 649 selected -= 1; 650 } 651 652 if (first_visible + selected > len) { 653 first_visible -= items_per_row; 654 selected = (int) (len - first_visible - 1); 655 selected_item = get_selected_item (); 656 } 657 update_item_list (); 658 } 659 660 public void key_up () { 661 selected -= items_per_row; 662 663 if (selected < 0) { 664 first_visible -= items_per_row; 665 selected += items_per_row; 666 } 667 668 if (first_visible < 0) { 669 first_visible = 0; 670 } 671 update_item_list (); 672 } 673 674 public void key_left () { 675 selected -= 1; 676 677 if (selected < 0) { 678 first_visible -= items_per_row; 679 selected += items_per_row; 680 selected += 1; 681 } 682 683 if (first_visible < 0) { 684 scroll_top (); 685 } 686 update_item_list (); 687 } 688 689 public string get_selected_char () { 690 Font f; 691 Glyph? g; 692 693 if (all_available) { 694 f = BirdFont.get_current_font (); 695 g = f.get_glyph_indice (selected); 696 return_val_if_fail (g != null, "".dup ()); 697 return ((!) g).get_name (); 698 } 699 700 return glyph_range.get_char (selected); 701 } 702 703 public override void key_press (uint keyval) { 704 hide_menu (); 705 update_item_list (); 706 GlyphCanvas.redraw (); 707 708 if (KeyBindings.modifier == CTRL) { 709 return; 710 } 711 712 switch (keyval) { 713 case Key.ENTER: 714 open_current_glyph (); 715 return; 716 717 case Key.UP: 718 key_up (); 719 selected_item = get_selected_item (); 720 721 selected_items.clear (); 722 if (selected_item.glyphs != null) { 723 selected_items.add ((!) selected_item.glyphs); 724 } 725 return; 726 727 case Key.RIGHT: 728 key_right (); 729 selected_item = get_selected_item (); 730 731 selected_items.clear (); 732 if (selected_item.glyphs != null) { 733 selected_items.add ((!) selected_item.glyphs); 734 } 735 return; 736 737 case Key.LEFT: 738 key_left (); 739 selected_item = get_selected_item (); 740 741 selected_items.clear (); 742 if (selected_item.glyphs != null) { 743 selected_items.add ((!) selected_item.glyphs); 744 } 745 return; 746 747 case Key.DOWN: 748 key_down (); 749 selected_item = get_selected_item (); 750 751 selected_items.clear (); 752 if (selected_item.glyphs != null) { 753 selected_items.add ((!) selected_item.glyphs); 754 } 755 return; 756 757 case Key.PG_UP: 758 for (int i = 0; i < rows; i++) { 759 key_up (); 760 } 761 selected_item = get_selected_item (); 762 763 selected_items.clear (); 764 if (selected_item.glyphs != null) { 765 selected_items.add ((!) selected_item.glyphs); 766 } 767 return; 768 769 case Key.PG_DOWN: 770 for (int i = 0; i < rows; i++) { 771 key_down (); 772 } 773 selected_item = get_selected_item (); 774 775 selected_items.clear (); 776 if (selected_item.glyphs != null) { 777 selected_items.add ((!) selected_item.glyphs); 778 } 779 return; 780 781 case Key.DEL: 782 delete_selected_glyph (); 783 selected_item = get_selected_item (); 784 return; 785 786 case Key.BACK_SPACE: 787 delete_selected_glyph (); 788 selected_item = get_selected_item (); 789 return; 790 } 791 792 scroll_to_char (keyval); 793 selected_item = get_selected_item (); 794 795 selected_items.clear (); 796 if (selected_item.glyphs != null) { 797 selected_items.add ((!) selected_item.glyphs); 798 } 799 800 update_item_list (); 801 } 802 803 public void delete_selected_glyph () { 804 Font font = BirdFont.get_current_font (); 805 OverViewUndoItem undo_item = new OverViewUndoItem (); 806 807 foreach (GlyphCollection g in selected_items) { 808 undo_item.glyphs.add (g.copy ()); 809 } 810 store_undo_items (undo_item); 811 812 foreach (GlyphCollection gc in selected_items) { 813 font.delete_glyph (gc); 814 } 815 } 816 817 public override void undo () { 818 Font font = BirdFont.get_current_font (); 819 OverViewUndoItem previous_collection; 820 821 if (undo_items.size == 0) { 822 return; 823 } 824 825 previous_collection = undo_items.get (undo_items.size - 1); 826 redo_items.add (get_current_state (previous_collection)); 827 828 // remove the old glyph and add the new one 829 foreach (GlyphCollection g in previous_collection.glyphs) { 830 font.delete_glyph (g); 831 832 if (g.length () > 0) { 833 font.add_glyph_collection (g); 834 } 835 } 836 837 undo_items.remove_at (undo_items.size - 1); 838 GlyphCanvas.redraw (); 839 } 840 841 public override void redo () { 842 Font font = BirdFont.get_current_font (); 843 OverViewUndoItem previous_collection; 844 845 if (redo_items.size == 0) { 846 return; 847 } 848 849 previous_collection = redo_items.get (redo_items.size - 1); 850 undo_items.add (get_current_state (previous_collection)); 851 852 // remove the old glyph and add the new one 853 foreach (GlyphCollection g in previous_collection.glyphs) { 854 font.delete_glyph (g); 855 font.add_glyph_collection (g); 856 } 857 858 redo_items.remove_at (redo_items.size - 1); 859 GlyphCanvas.redraw (); 860 } 861 862 public OverViewUndoItem get_current_state (OverViewUndoItem previous_collection) { 863 GlyphCollection? gc; 864 OverViewUndoItem ui = new OverViewUndoItem (); 865 Font font = BirdFont.get_current_font (); 866 867 foreach (GlyphCollection g in previous_collection.glyphs) { 868 gc = font.get_glyph_collection (g.get_name ()); 869 870 if (gc != null) { 871 ui.glyphs.add (((!) gc).copy ()); 872 } else { 873 ui.glyphs.add (new GlyphCollection (g.get_unicode_character (), g.get_name ())); 874 } 875 } 876 877 return ui; 878 } 879 880 public void store_undo_state (GlyphCollection gc) { 881 OverViewUndoItem i = new OverViewUndoItem (); 882 i.glyphs.add (gc); 883 store_undo_items (i); 884 } 885 886 public void store_undo_items (OverViewUndoItem i) { 887 undo_items.add (i); 888 redo_items.clear (); 889 } 890 891 bool select_visible_glyph (string name) { 892 int i = 0; 893 894 foreach (OverViewItem o in visible_items) { 895 if (o.get_name () == name) { 896 selected = i; 897 selected_item = get_selected_item (); 898 return true; 899 } 900 901 if (i > 1000) { 902 warning ("selected character not found"); 903 return true; 904 } 905 906 i++; 907 } 908 909 return false; 910 } 911 912 public void scroll_to_char (unichar c) { 913 StringBuilder s = new StringBuilder (); 914 915 if (is_modifier_key (c)) { 916 return; 917 } 918 919 s.append_unichar (c); 920 scroll_to_glyph (s.str); 921 } 922 923 public void scroll_to_glyph (string name) { 924 GlyphRange gr = glyph_range; 925 int i, r, index; 926 string ch; 927 Font font = BirdFont.get_current_font (); 928 GlyphCollection? glyphs = null; 929 Glyph glyph; 930 931 index = -1; 932 933 if (items_per_row <= 0) { 934 return; 935 } 936 937 ch = name; 938 939 // selected char is visible 940 if (select_visible_glyph (ch)) { 941 return; 942 } 943 944 // scroll to char 945 if (all_available) { 946 947 // don't search for glyphs in huge CJK fonts 948 if (font.length () > 300) { 949 r = 0; 950 } else { 951 // FIXME: too slow 952 for (r = 0; r < font.length (); r += items_per_row) { 953 for (i = 0; i < items_per_row; i++) { 954 glyphs = font.get_glyph_collection_indice ((uint32) r + i); 955 return_if_fail (glyphs != null); 956 glyph = ((!) glyphs).get_current (); 957 958 if (glyph.name == ch) { 959 index = i; 960 } 961 } 962 963 if (index > -1) { 964 break; 965 } 966 } 967 } 968 } else { 969 970 if (ch.char_count () > 1) { 971 warning ("Can't scroll to ligature in this view"); 972 return; 973 } 974 975 for (r = 0; r < gr.length (); r += items_per_row) { 976 for (i = 0; i < items_per_row; i++) { 977 if (gr.get_char (r + i) == ch) { 978 index = i; 979 } 980 } 981 982 if (index > -1) { 983 break; 984 } 985 } 986 } 987 988 if (index > -1) { 989 first_visible = r; 990 update_item_list (); 991 select_visible_glyph (ch); 992 } 993 } 994 995 public override void double_click (uint button, double ex, double ey) 996 requires (!is_null (visible_items) && !is_null (allocation)) { 997 998 return_if_fail (!is_null (this)); 999 1000 foreach (OverViewItem i in visible_items) { 1001 if (i.double_click (button, ex, ey)) { 1002 open_overview_item (i); 1003 } 1004 } 1005 1006 GlyphCanvas.redraw (); 1007 } 1008 1009 public void open_overview_item (OverViewItem i) { 1010 if (i.glyphs != null) { 1011 open_glyph_signal ((!) i.glyphs); 1012 ((!) i.glyphs).get_current ().close_path (); 1013 } else { 1014 open_new_glyph_signal (i.character); 1015 } 1016 } 1017 1018 public void set_character_info (CharacterInfo i) { 1019 character_info = i; 1020 } 1021 1022 public int get_selected_index () { 1023 GlyphCollection gc; 1024 int index = 0; 1025 1026 if (selected_items.size == 0) { 1027 return 0; 1028 } 1029 1030 gc = selected_items.get (0); 1031 1032 foreach (OverViewItem i in visible_items) { 1033 1034 if (i.glyphs != null && gc == ((!) i.glyphs)) { 1035 break; 1036 } 1037 1038 index++; 1039 } 1040 1041 return index; 1042 } 1043 1044 public void hide_menu () { 1045 foreach (OverViewItem i in visible_items) { 1046 i.hide_menu (); 1047 } 1048 } 1049 1050 public override void button_press (uint button, double x, double y) { 1051 int index = 0; 1052 int selected_index = -1; 1053 bool update = false; 1054 1055 if (character_info != null) { 1056 character_info = null; 1057 GlyphCanvas.redraw (); 1058 return; 1059 } 1060 1061 foreach (OverViewItem i in visible_items) { 1062 if (i.click (button, x, y)) { 1063 selected = index; 1064 selected_item = get_selected_item (); 1065 1066 if (KeyBindings.has_shift ()) { 1067 if (selected_item.glyphs != null) { 1068 1069 selected_index = selected_items.index_of ((!) selected_item.glyphs); 1070 if (selected_index == -1) { 1071 selected_items.add ((!) selected_item.glyphs); 1072 } else { 1073 return_if_fail (0 <= selected_index < selected_items.size); 1074 selected_items.remove_at (selected_index); 1075 selected = get_selected_index (); 1076 selected_item = get_selected_item (); 1077 } 1078 } 1079 } else { 1080 selected_items.clear (); 1081 if (selected_item.glyphs != null) { 1082 selected_items.add ((!) selected_item.glyphs); 1083 } 1084 } 1085 1086 update = !i.version_menu.menu_visible; 1087 } 1088 index++; 1089 } 1090 1091 if (update) { 1092 update_item_list (); 1093 } 1094 1095 // FIXME: update_item_list (); 1096 GlyphCanvas.redraw (); 1097 } 1098 1099 /** Returns true if overview shows the last character. */ 1100 private bool at_bottom () { 1101 Font f; 1102 double t = rows * items_per_row + first_visible; 1103 1104 if (all_available) { 1105 f = BirdFont.get_current_font (); 1106 return t >= f.length (); 1107 } 1108 1109 return t >= glyph_range.length (); 1110 } 1111 1112 public void set_glyph_range (GlyphRange range) { 1113 GlyphRange? current = glyph_range; 1114 string c; 1115 1116 if (current != null) { 1117 c = glyph_range.get_char (selected); 1118 } 1119 1120 all_available = false; 1121 1122 glyph_range = range; 1123 scroll_top (); 1124 1125 // TODO: scroll down to c 1126 update_item_list (); 1127 selected_item = get_selected_item (); 1128 1129 GlyphCanvas.redraw (); 1130 } 1131 1132 public void select_next_glyph () { 1133 key_right (); 1134 } 1135 1136 public void open_current_glyph () { 1137 open_overview_item (selected_item); 1138 } 1139 1140 public override void update_scrollbar () { 1141 Font f; 1142 double nrows = 0; 1143 double pos = 0; 1144 double size; 1145 double visible_rows; 1146 1147 if (rows == 0) { 1148 MainWindow.set_scrollbar_size (0); 1149 MainWindow.set_scrollbar_position (0); 1150 } else { 1151 if (all_available) { 1152 f = BirdFont.get_current_font (); 1153 nrows = Math.floor ((f.length ()) / rows); 1154 size = f.length (); 1155 } else { 1156 nrows = Math.floor ((glyph_range.length ()) / rows); 1157 size = glyph_range.length (); 1158 } 1159 1160 if (nrows <= 0) { 1161 nrows = 1; 1162 } 1163 1164 // FIXME: this is not correct 1165 visible_rows = allocation.height / OverViewItem.height; 1166 scroll_size = visible_rows / nrows; 1167 MainWindow.set_scrollbar_size (scroll_size); 1168 pos = first_visible / (nrows * items_per_row - visible_rows * items_per_row); 1169 MainWindow.set_scrollbar_position (pos); 1170 } 1171 } 1172 1173 /** Display one entry from the Unicode Character Database. */ 1174 void draw_character_info (Context cr) 1175 requires (character_info != null) { 1176 double x, y, w, h; 1177 int i; 1178 string unicode_value, unicode_description; 1179 string[] column; 1180 string entry; 1181 int len = 0; 1182 int length = 0; 1183 bool see_also = false; 1184 WidgetAllocation allocation = MainWindow.get_overview ().allocation; 1185 string name; 1186 1187 entry = ((!)character_info).get_entry (); 1188 1189 foreach (string line in entry.split ("\n")) { 1190 len = line.char_count (); 1191 if (len > length) { 1192 length = len; 1193 } 1194 } 1195 1196 x = allocation.width * 0.1; 1197 y = allocation.height * 0.1; 1198 w = allocation.width * 0.9 - x; 1199 h = allocation.height * 0.9 - y; 1200 1201 if (w < 8 * length) { 1202 w = 8 * length; 1203 x = (allocation.width - w) / 2.0; 1204 } 1205 1206 if (x < 0) { 1207 x = 2; 1208 } 1209 1210 // background 1211 cr.save (); 1212 Theme.color_opacity (cr, "Background 1", 0.98); 1213 cr.rectangle (x, y, w, h); 1214 cr.fill (); 1215 cr.restore (); 1216 1217 cr.save (); 1218 Theme.color_opacity (cr, "Foreground 1", 0.98); 1219 cr.set_line_width (2); 1220 cr.rectangle (x, y, w, h); 1221 cr.stroke (); 1222 cr.restore (); 1223 1224 // database entry 1225 1226 if (((!)character_info).is_ligature ()) { 1227 name = ((!)character_info).get_name (); 1228 draw_info_line (t_("Ligature") + ": " + name, cr, x, y, 0); 1229 } else { 1230 i = 0; 1231 foreach (string line in entry.split ("\n")) { 1232 if (i == 0) { 1233 column = line.split ("\t"); 1234 return_if_fail (column.length == 2); 1235 unicode_value = "U+" + column[0]; 1236 unicode_description = column[1]; 1237 1238 draw_info_line (unicode_description, cr, x, y, i); 1239 i++; 1240 1241 draw_info_line (unicode_value, cr, x, y, i); 1242 i++; 1243 } else { 1244 1245 if (line.has_prefix ("\t*")) { 1246 draw_info_line (line.replace ("\t*", "•"), cr, x, y, i); 1247 i++; 1248 } else if (line.has_prefix ("\tx (")) { 1249 if (!see_also) { 1250 i++; 1251 draw_info_line (t_("See also:"), cr, x, y, i); 1252 i++; 1253 see_also = true; 1254 } 1255 1256 draw_info_line (line.replace ("\tx (", "•").replace (")", ""), cr, x, y, i); 1257 i++; 1258 } else { 1259 1260 i++; 1261 } 1262 } 1263 } 1264 } 1265 } 1266 1267 void draw_info_line (string line, Context cr, double x, double y, int row) { 1268 cr.save (); 1269 cr.set_font_size (12); 1270 Theme.color (cr, "Foreground 1"); 1271 cr.move_to (x + 10, y + 28 + row * 18 * 1.2); 1272 cr.show_text (line); 1273 cr.restore (); 1274 } 1275 1276 public void paste () { 1277 GlyphCollection gc = new GlyphCollection ('\0', ""); 1278 GlyphCollection? c; 1279 Glyph glyph; 1280 uint32 index; 1281 int i; 1282 int skip = 0; 1283 int s; 1284 string character_string; 1285 Gee.ArrayList<GlyphCollection> glyps = new Gee.ArrayList<GlyphCollection> (); 1286 Font f = BirdFont.get_current_font (); 1287 OverViewUndoItem undo_item; 1288 1289 copied_glyphs.sort ((a, b) => { 1290 return (int) ((GlyphCollection) a).get_unicode_character () 1291 - (int) ((GlyphCollection) b).get_unicode_character (); 1292 }); 1293 1294 index = (uint32) first_visible + selected; 1295 for (i = 0; i < copied_glyphs.size; i++) { 1296 if (all_available) { 1297 if (f.length () == 0) { 1298 c = add_empty_character_to_font (copied_glyphs.get (i).get_unicode_character (), 1299 copied_glyphs.get (i).is_unassigned (), 1300 copied_glyphs.get (i).get_name ()); 1301 } else if (index >= f.length ()) { 1302 // FIXME: duplicated unicodes? 1303 c = add_empty_character_to_font (copied_glyphs.get (i).get_unicode_character (), 1304 copied_glyphs.get (i).is_unassigned (), 1305 copied_glyphs.get (i).get_name ()); 1306 } else { 1307 c = f.get_glyph_collection_indice ((uint32) index); 1308 } 1309 1310 if (c == null) { 1311 c = add_empty_character_to_font (copied_glyphs.get (i).get_unicode_character (), 1312 copied_glyphs.get (i).is_unassigned (), 1313 copied_glyphs.get (i).get_name ()); 1314 } 1315 1316 return_if_fail (c != null); 1317 gc = (!) c; 1318 } else { 1319 if (i != 0) { 1320 s = (int) copied_glyphs.get (i).get_unicode_character (); 1321 s -= (int) copied_glyphs.get (i - 1).get_unicode_character (); 1322 s -= 1; 1323 skip += s; 1324 } 1325 1326 character_string = glyph_range.get_char ((uint32) (index + skip)); 1327 c = f.get_glyph_collection_by_name (character_string); 1328 1329 if (c == null) { 1330 gc = add_empty_character_to_font (character_string.get_char (), 1331 copied_glyphs.get (i).is_unassigned (), 1332 copied_glyphs.get (i).get_name ()); 1333 } else { 1334 gc = (!) c; 1335 } 1336 } 1337 1338 glyps.add (gc); 1339 index++; 1340 } 1341 1342 undo_item = new OverViewUndoItem (); 1343 foreach (GlyphCollection g in glyps) { 1344 undo_item.glyphs.add (g.copy ()); 1345 } 1346 store_undo_items (undo_item); 1347 1348 if (glyps.size != copied_glyphs.size) { 1349 warning ("glyps.size != copied_glyphs.size"); 1350 return; 1351 } 1352 1353 i = 0; 1354 foreach (GlyphCollection g in glyps) { 1355 glyph = copied_glyphs.get (i).get_current ().copy (); 1356 glyph.version_id = (glyph.version_id == -1 || g.length () == 0) ? 1 : g.get_last_id () + 1; 1357 glyph.unichar_code = g.get_unicode_character (); 1358 1359 if (!g.is_unassigned ()) { 1360 glyph.name = (!) glyph.unichar_code.to_string (); 1361 } else { 1362 glyph.name = g.get_name (); 1363 } 1364 1365 g.insert_glyph (glyph, true); 1366 i++; 1367 } 1368 1369 f.touch (); 1370 } 1371 1372 public class OverViewUndoItem { 1373 public Gee.ArrayList<GlyphCollection> glyphs = new Gee.ArrayList<GlyphCollection> (); 1374 } 1375 } 1376 1377 } 1378