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