The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

Menu.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/Menu.vala.
User defined guides
1 /* 2 Copyright (C) 2014 2015 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 Bird; 17 18 namespace BirdFont { 19 20 /** Interface for events from native window to the current tab. */ 21 public class Menu : GLib.Object { 22 23 public bool show_menu { 24 get { 25 return menu_visibility; 26 } 27 28 set { 29 string tab_name; 30 31 menu_visibility = value; 32 current_menu = top_menu; 33 34 if (menu_visibility) { 35 tab_name = MainWindow.get_tab_bar ().get_selected_tab ().get_display ().get_name (); 36 if (tab_name == "Preview") { 37 MenuTab.select_overview (); 38 } 39 } 40 } 41 } 42 43 public bool menu_visibility = false; 44 public SubMenu top_menu; 45 46 SubMenu current_menu; 47 WidgetAllocation allocation; 48 49 double width = 250 * MainWindow.units; 50 double height = 25 * MainWindow.units; 51 52 public Gee.HashMap<string, MenuItem> menu_items = new Gee.HashMap<string, MenuItem> (); 53 public Gee.ArrayList<MenuItem> sorted_menu_items = new Gee.ArrayList<MenuItem> (); 54 55 public Menu () { 56 SubMenu menu = new SubMenu (); 57 SubMenu file_menu = new SubMenu (); 58 SubMenu edit_menu = new SubMenu (); 59 SubMenu tab_menu = new SubMenu (); 60 SubMenu kerning_menu = new SubMenu (); 61 SubMenu ligature_menu = new SubMenu (); 62 SubMenu git_menu = new SubMenu (); 63 64 // file menu 65 MenuItem file = add_menu_item (t_("File")); 66 file.action.connect (() => { 67 set_menu (file_menu); 68 }); 69 menu.items.add (file); 70 71 MenuItem new_file = add_menu_item (t_("New"), "new"); 72 new_file.action.connect (() => { 73 MenuTab.new_file (); 74 show_menu = false; 75 }); 76 file_menu.items.add (new_file); 77 78 MenuItem open = add_menu_item (t_("Open"), "open"); 79 open.action.connect (() => { 80 show_menu = false; 81 MenuTab.load (); 82 }); 83 file_menu.items.add (open); 84 85 MenuItem recent_files = add_menu_item (t_("Recent Files"), "recent files"); 86 recent_files.action.connect (() => { 87 show_menu = false; 88 MainWindow.open_recent_files_tab (); 89 }); 90 file_menu.items.add (recent_files); 91 92 MenuItem save = add_menu_item (t_("Save"), "save"); 93 save.action.connect (() => { 94 MenuTab.save (); 95 show_menu = false; 96 }); 97 file_menu.items.add (save); 98 99 MenuItem save_as = add_menu_item (t_("Save As"), "save as"); 100 save_as.action.connect (() => { 101 MenuTab.save_as (); 102 show_menu = false; 103 }); 104 file_menu.items.add (save_as); 105 106 MenuItem export = add_menu_item (t_("Export"), "export"); 107 export.action.connect (() => { 108 MenuTab.export_fonts_in_background (); 109 show_menu = false; 110 }); 111 file_menu.items.add (export); 112 113 MenuItem preview = add_menu_item (t_("Preview"), "preview"); 114 preview.action.connect (() => { 115 MenuTab.preview (); 116 show_menu = false; 117 }); 118 file_menu.items.add (preview); 119 120 MenuItem select_character_set = add_menu_item (t_("Select Character Set"), "select character set"); 121 select_character_set.action.connect (() => { 122 MenuTab.select_language (); 123 show_menu = false; 124 }); 125 file_menu.items.add (select_character_set); 126 127 MenuItem quit = add_menu_item (t_("Quit"), "quit"); 128 quit.action.connect (() => { 129 MenuTab.quit (); 130 show_menu = false; 131 }); 132 file_menu.items.add (quit); 133 134 // edit menu 135 MenuItem edit = add_menu_item (t_("Edit")); 136 edit.action.connect (() => { 137 set_menu (edit_menu); 138 }); 139 menu.items.add (edit); 140 141 MenuItem undo = add_menu_item (t_("Undo"), "undo"); 142 undo.action.connect (() => { 143 TabContent.undo (); 144 show_menu = false; 145 }); 146 edit_menu.items.add (undo); 147 148 MenuItem redo = add_menu_item (t_("Redo"), "redo"); 149 redo.action.connect (() => { 150 TabContent.redo (); 151 show_menu = false; 152 }); 153 edit_menu.items.add (redo); 154 155 MenuItem copy = add_menu_item (t_("Copy"), "copy"); 156 copy.action.connect (() => { 157 ClipTool.copy (); 158 show_menu = false; 159 }); 160 edit_menu.items.add (copy); 161 162 MenuItem paste = add_menu_item (t_("Paste"), "paste"); 163 paste.action.connect (() => { 164 ClipTool.paste (); 165 show_menu = false; 166 }); 167 edit_menu.items.add (paste); 168 169 MenuItem paste_in_place = add_menu_item (t_("Paste In Place"), "paste in place"); 170 paste_in_place.action.connect (() => { 171 ClipTool.paste_in_place (); 172 show_menu = false; 173 }); 174 edit_menu.items.add (paste_in_place); 175 176 MenuItem select_all_paths = add_menu_item (t_("Select All Paths"), "select all paths"); 177 select_all_paths.action.connect (() => { 178 MainWindow.select_all_paths (); 179 show_menu = false; 180 }); 181 edit_menu.items.add (select_all_paths); 182 183 MenuItem move_to_baseline = add_menu_item (t_("Move To Baseline"), "move to baseline"); 184 move_to_baseline.action.connect (() => { 185 MenuTab.move_to_baseline (); 186 show_menu = false; 187 }); 188 edit_menu.items.add (move_to_baseline); 189 190 MenuItem search = add_menu_item (t_("Search"), "search"); 191 search.action.connect (() => { 192 OverView.search (); 193 show_menu = false; 194 }); 195 edit_menu.items.add (search); 196 197 MenuItem export_glyph = add_menu_item (t_("Export Glyph as SVG"), "export glyph as svg"); 198 export_glyph.action.connect (() => { 199 ExportTool.export_current_glyph (); 200 show_menu = false; 201 }); 202 edit_menu.items.add (export_glyph); 203 204 MenuItem import_svg = add_menu_item (t_("Import SVG file"), "import svg file"); 205 import_svg.action.connect (() => { 206 SvgParser.import (); 207 show_menu = false; 208 }); 209 edit_menu.items.add (import_svg); 210 211 MenuItem import_background_image = add_menu_item (t_("Import Background Image"), "import background image"); 212 import_background_image.action.connect (() => { 213 MenuTab.show_background_tab (); 214 show_menu = false; 215 }); 216 edit_menu.items.add (import_background_image); 217 218 MenuItem simplify_path = add_menu_item (t_("Simplify Path"), "simplify path"); 219 simplify_path.action.connect (() => { 220 MenuTab.simplify_path (); 221 show_menu = false; 222 }); 223 edit_menu.items.add (simplify_path); 224 225 MenuItem close_path = add_menu_item (t_("Close Path"), "close path"); 226 close_path.action.connect (() => { 227 PenTool.close_all_paths (); 228 show_menu = false; 229 }); 230 edit_menu.items.add (close_path); 231 232 MenuItem glyph_sequence = add_menu_item (t_("Glyph Sequence"), "glyph sequence"); 233 glyph_sequence.action.connect (() => { 234 MainWindow.update_glyph_sequence (); 235 show_menu = false; 236 }); 237 edit_menu.items.add (glyph_sequence); 238 239 MenuItem set_background_glyph = add_menu_item (t_("Set Background Glyph"), "set background glyph"); 240 set_background_glyph.action.connect (() => { 241 MenuTab.use_current_glyph_as_background (); 242 show_menu = false; 243 }); 244 edit_menu.items.add (set_background_glyph); 245 246 MenuItem remove_background_glyph = add_menu_item (t_("Remove Background Glyph"), "remove background glyph"); 247 remove_background_glyph.action.connect (() => { 248 MenuTab.reset_glyph_background (); 249 show_menu = false; 250 }); 251 edit_menu.items.add (remove_background_glyph); 252 253 MenuItem create_guide = add_menu_item (t_("Create Guide"), "create guide"); 254 create_guide.action.connect (() => { 255 MainWindow.get_current_glyph ().add_custom_guide (); 256 show_menu = false; 257 }); 258 edit_menu.items.add (create_guide); 259 260 MenuItem select_point_above = add_menu_item (t_("Select Point Above"), "select point above"); 261 select_point_above.action.connect (() => { 262 PenTool.select_point_up (); 263 show_menu = false; 264 }); 265 select_point_above.display = "Glyph"; 266 edit_menu.items.add (select_point_above); 267 268 MenuItem select_next_point = add_menu_item (t_("Select Next Point"), "select next point"); 269 select_next_point.action.connect (() => { 270 PenTool.select_point_right (); 271 show_menu = false; 272 }); 273 select_next_point.display = "Glyph"; 274 edit_menu.items.add (select_next_point); 275 276 MenuItem select_previous_point = add_menu_item (t_("Select Previous Point"), "select previous point"); 277 select_previous_point.action.connect (() => { 278 PenTool.select_point_left (); 279 show_menu = false; 280 }); 281 select_previous_point.display = "Glyph"; 282 edit_menu.items.add (select_previous_point); 283 284 MenuItem select_point_below = add_menu_item (t_("Select Point Below"), "select point below"); 285 select_point_below.action.connect (() => { 286 PenTool.select_point_down (); 287 show_menu = false; 288 }); 289 select_point_below.display = "Glyph"; 290 edit_menu.items.add (select_point_below); 291 292 // tab menu 293 MenuItem tab = add_menu_item (t_("Tab")); 294 tab.action.connect (() => { 295 set_menu (tab_menu); 296 }); 297 menu.items.add (tab); 298 299 MenuItem next_tab = add_menu_item (t_("Next Tab"), "next tab"); 300 next_tab.action.connect (() => { 301 MainWindow.next_tab (); 302 show_menu = false; 303 }); 304 tab_menu.items.add (next_tab); 305 306 MenuItem previous_tab = add_menu_item (t_("Previous Tab"), "previous tab"); 307 previous_tab.action.connect (() => { 308 MainWindow.previous_tab (); 309 show_menu = false; 310 }); 311 tab_menu.items.add (previous_tab); 312 313 MenuItem close_tab = add_menu_item (t_("Close Tab"), "close tab"); 314 close_tab.action.connect (() => { 315 MainWindow.close_tab (); 316 show_menu = false; 317 }); 318 tab_menu.items.add (close_tab); 319 320 MenuItem close_all_tabs = add_menu_item (t_("Close All Tabs"), "close all tabs"); 321 close_all_tabs.action.connect (() => { 322 MainWindow.close_all_tabs (); 323 show_menu = false; 324 }); 325 tab_menu.items.add (close_all_tabs); 326 327 // tab menu 328 MenuItem kerning = add_menu_item (t_("Spacing and Kerning")); 329 kerning.action.connect (() => { 330 set_menu (kerning_menu); 331 }); 332 menu.items.add (kerning); 333 334 MenuItem spacing_tab = add_menu_item (t_("Show Spacing Tab"), "show spacing tab"); 335 spacing_tab.action.connect (() => { 336 MenuTab.show_spacing_tab (); 337 show_menu = false; 338 }); 339 kerning_menu.items.add (spacing_tab); 340 341 MenuItem kerning_tab = add_menu_item (t_("Show Kerning Tab"), "show kerning tab"); 342 kerning_tab.action.connect (() => { 343 MenuTab.show_kerning_context (); 344 show_menu = false; 345 }); 346 kerning_menu.items.add (kerning_tab); 347 348 MenuItem list_kernings = add_menu_item (t_("List Kerning Pairs"), "list kerning pairs"); 349 list_kernings.action.connect (() => { 350 MenuTab.list_all_kerning_pairs (); 351 show_menu = false; 352 }); 353 kerning_menu.items.add (list_kernings); 354 355 MenuItem show_spacing = add_menu_item (t_("Spacing Classes"), "show spacing tab"); 356 show_spacing.action.connect (() => { 357 MenuTab.show_spacing_class_tab (); 358 show_menu = false; 359 }); 360 kerning_menu.items.add (show_spacing); 361 362 MenuItem next_kerning_pair = add_menu_item (t_("Select Next Kerning Pair"), "select next kerning pair"); 363 next_kerning_pair.action.connect (() => { 364 KerningDisplay.next_pair (); 365 show_menu = false; 366 }); 367 next_kerning_pair.display = "Kerning"; 368 kerning_menu.items.add (next_kerning_pair); 369 370 MenuItem previous_kerning_pair = add_menu_item (t_("Select Previous Kerning Pair"), "select previous kerning pair"); 371 previous_kerning_pair.action.connect (() => { 372 KerningDisplay.previous_pair (); 373 show_menu = false; 374 }); 375 previous_kerning_pair.display = "Kerning"; 376 kerning_menu.items.add (previous_kerning_pair); 377 378 // ligature menu 379 MenuItem ligature = add_menu_item (t_("Ligatures")); 380 ligature.action.connect (() => { 381 set_menu (ligature_menu); 382 }); 383 menu.items.add (ligature); 384 385 MenuItem ligature_tab = add_menu_item (t_("Show Ligatures"), "show ligature tab"); 386 ligature_tab.action.connect (() => { 387 MenuTab.show_ligature_tab (); 388 show_menu = false; 389 }); 390 ligature_menu.items.add (ligature_tab); 391 392 MenuItem add_ligature = add_menu_item (t_("Add Ligature"), "add ligature"); 393 add_ligature.action.connect (() => { 394 MenuTab.add_ligature (); 395 show_menu = false; 396 }); 397 ligature_menu.items.add (add_ligature); 398 399 // git menu 400 if (BirdFont.has_argument ("--test")) { 401 MenuItem git = add_menu_item (t_("Git")); 402 git.action.connect (() => { 403 set_menu (git_menu); 404 }); 405 menu.items.add (git); 406 407 MenuItem save_bfp = add_menu_item (t_("Save As .bfp"), "save as .bfp"); 408 save_bfp.action.connect (() => { 409 MenuTab.save_as_bfp (); 410 show_menu = false; 411 }); 412 git_menu.items.add (save_bfp); 413 } 414 415 // show overview 416 MenuItem overview = add_menu_item (t_("Overview")); 417 overview.action.connect (() => { 418 MenuTab.select_overview (); 419 show_menu = false; 420 }); 421 menu.items.add (overview); 422 423 // settings 424 MenuItem settings = add_menu_item (t_("Settings"), "settings"); 425 settings.action.connect (() => { 426 MenuTab.show_settings_tab (); 427 show_menu = false; 428 }); 429 menu.items.add (settings); 430 431 MenuItem description = add_menu_item (t_("Name and Description"), "name and description"); 432 description.action.connect (() => { 433 MenuTab.show_description (); 434 show_menu = false; 435 }); 436 menu.items.add (description); 437 438 current_menu = menu; 439 top_menu = menu; 440 allocation = new WidgetAllocation (); 441 442 add_tool_key_bindings (); 443 load_key_bindings (); 444 } 445 446 public void process_key_binding_events (uint keyval) { 447 string display; 448 FontDisplay current_display = MainWindow.get_current_display (); 449 450 unichar c = (unichar) keyval; 451 452 foreach (MenuItem item in sorted_menu_items) { 453 if (item.key == (unichar) keyval && item.modifiers == KeyBindings.modifier) { 454 455 if (current_display is Glyph && item.display == "Glyph") { 456 item.action (); 457 } 458 459 display = current_display.get_name (); 460 461 if (!(display == "Kerning" && item.modifiers == NONE)) { 462 if (!SettingsDisplay.update_key_bindings 463 && (item.display == "" || item.display == display)) { 464 item.action (); 465 } 466 467 if (item is ToolItem) { 468 MainWindow.get_toolbox ().set_current_tool (((ToolItem) item).tool); 469 } 470 } 471 } 472 } 473 } 474 475 void load_key_bindings () { 476 File default_key_bindings = SearchPaths.find_file (null, "key_bindings.xml"); 477 File user_key_bindings = get_child (BirdFont.get_settings_directory (), "key_bindings.xml"); 478 479 if (default_key_bindings.query_exists ()) { 480 parse_key_bindings (default_key_bindings); 481 } 482 483 if (user_key_bindings.query_exists ()) { 484 parse_key_bindings (user_key_bindings); 485 } 486 } 487 488 void parse_key_bindings (File f) { 489 string xml_data; 490 XmlParser parser; 491 492 try { 493 FileUtils.get_contents((!) f.get_path (), out xml_data); 494 parser = new XmlParser (xml_data); 495 parse_bindings (parser.get_root_tag ()); 496 } catch (GLib.Error e) { 497 warning (e.message); 498 } 499 } 500 501 void parse_bindings (Tag tag) { 502 foreach (Tag t in tag) { 503 if (t.get_name () == "action") { 504 parse_binding (t.get_attributes ()); 505 } 506 } 507 } 508 509 void parse_binding (Attributes attr) { 510 uint modifier = NONE; 511 unichar key = '\0'; 512 string action = ""; 513 MenuItem menu_action; 514 MenuItem? ma; 515 516 foreach (Attribute a in attr) { 517 if (a.get_name () == "key") { 518 key = a.get_content ().get_char (0); 519 } 520 521 if (a.get_name () == "ctrl" && a.get_content () == "true") { 522 modifier |= CTRL; 523 } 524 525 if (a.get_name () == "alt" && a.get_content () == "true") { 526 modifier |= ALT; 527 } 528 529 if (a.get_name () == "command" && a.get_content () == "true") { 530 modifier |= LOGO; 531 } 532 533 if (a.get_name () == "shift" && a.get_content () == "true") { 534 modifier |= SHIFT; 535 } 536 537 if (a.get_name () == "action") { 538 action = a.get_content (); 539 } 540 } 541 542 ma = menu_items.get (action); 543 if (ma != null) { 544 menu_action = (!) ma; 545 menu_action.modifiers = modifier; 546 menu_action.key = key; 547 } 548 } 549 550 MenuItem add_menu_item (string label, string description = "") { 551 MenuItem i = new MenuItem (label, description); 552 553 if (description != "") { 554 menu_items.set (description, i); 555 sorted_menu_items.add (i); 556 } 557 558 return i; 559 } 560 561 public void button_release (int button, double ex, double ey) { 562 double y = 0; 563 double x = allocation.width - width; 564 565 if (button == 1) { 566 foreach (MenuItem item in current_menu.items) { 567 if (x <= ex < allocation.width && y <= ey <= y + height) { 568 item.action (); 569 GlyphCanvas.redraw (); 570 return; 571 } 572 573 y += height; 574 } 575 576 menu_visibility = false; 577 current_menu = (!) top_menu; 578 GlyphCanvas.redraw (); 579 } 580 } 581 582 void add_tool_key_bindings () { 583 ToolItem tool_item; 584 foreach (ToolCollection tool_set in MainWindow.get_toolbox ().tool_sets) { 585 foreach (Expander e in tool_set.get_expanders ()) { 586 foreach (Tool t in e.tool) { 587 tool_item = new ToolItem (t); 588 if (tool_item.identifier != "" && !has_menu_item (tool_item.identifier)) { 589 menu_items.set (tool_item.identifier, tool_item); 590 sorted_menu_items.add (tool_item); 591 } 592 } 593 } 594 } 595 } 596 597 public bool has_menu_item (string identifier) { 598 foreach (MenuItem mi in sorted_menu_items) { 599 if (mi.identifier == identifier) { 600 return true; 601 } 602 } 603 604 return false; 605 } 606 607 public void set_menu (SubMenu m) { 608 current_menu = m; 609 GlyphCanvas.redraw (); 610 } 611 612 public double layout_width () { 613 Text key_binding = new Text (); 614 double font_size = 17 * MainWindow.units;; 615 double w; 616 617 width = 0; 618 foreach (MenuItem item in current_menu.items) { 619 key_binding.set_text (item.get_key_bindings ()); 620 621 item.label.set_font_size (font_size); 622 key_binding.set_font_size (font_size); 623 624 w = item.label.get_extent (); 625 w += key_binding.get_extent (); 626 w += 3 * height * MainWindow.units; 627 628 if (w > width) { 629 width = w; 630 } 631 } 632 633 return width; 634 } 635 636 public void draw (WidgetAllocation allocation, Context cr) { 637 double y; 638 double x; 639 double label_x; 640 double label_y; 641 double font_size; 642 Text key_binding; 643 double binding_extent; 644 645 width = layout_width (); 646 647 key_binding = new Text (); 648 649 x = allocation.width - width; 650 y = 0; 651 font_size = 17 * MainWindow.units; 652 this.allocation = allocation; 653 654 foreach (MenuItem item in current_menu.items) { 655 cr.save (); 656 cr.set_source_rgba (38 / 255.0, 39 / 255.0, 43 / 255.0, 1); 657 cr.rectangle (x, y, width, height); 658 cr.fill (); 659 cr.restore (); 660 661 cr.save (); 662 label_x = allocation.width - width + 0.7 * height * MainWindow.units; 663 label_y = y + font_size - 1 * MainWindow.units; 664 item.label.set_source_rgba (101 / 255.0, 108 / 255.0, 116 / 255.0, 1); 665 item.label.draw_at_baseline (cr, label_x, label_y); 666 667 key_binding.set_text (item.get_key_bindings ()); 668 key_binding.set_font_size (font_size); 669 binding_extent = key_binding.get_extent (); 670 label_x = x + width - binding_extent - 0.6 * height * MainWindow.units; 671 key_binding.set_font_size (font_size); 672 key_binding.set_source_rgba (101 / 255.0, 108 / 255.0, 116 / 255.0, 1); 673 key_binding.draw_at_baseline (cr, label_x, label_y); 674 675 y += height; 676 } 677 } 678 679 public void write_key_bindings () { 680 DataOutputStream os; 681 File file; 682 bool has_ctrl, has_alt, has_command, has_shift; 683 684 file = get_child (BirdFont.get_settings_directory (), "key_bindings.xml"); 685 686 try { 687 if (file.query_exists ()) { 688 file.delete (); 689 } 690 } catch (GLib.Error e) { 691 warning (e.message); 692 } 693 694 try { 695 os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION)); 696 os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>"""); 697 os.put_string ("\n"); 698 699 os.put_string ("<bindings>\n"); 700 foreach (MenuItem item in sorted_menu_items) { 701 os.put_string ("\t<action "); 702 703 os.put_string (@"key=\"$((!)item.key.to_string ())\" "); 704 705 has_ctrl = (item.modifiers & CTRL) > 0; 706 os.put_string (@"ctrl=\"$(has_ctrl.to_string ())\" "); 707 708 has_alt = (item.modifiers & ALT) > 0; 709 os.put_string (@"alt=\"$(has_alt.to_string ())\" "); 710 711 has_command = (item.modifiers & LOGO) > 0; 712 os.put_string (@"command=\"$(has_command.to_string ())\" "); 713 714 has_shift = (item.modifiers & SHIFT) > 0; 715 os.put_string (@"shift=\"$(has_shift.to_string ())\" "); 716 717 os.put_string (@"action=\"$(item.identifier)\" "); 718 719 os.put_string ("/>\n"); 720 } 721 os.put_string ("</bindings>\n"); 722 723 os.close (); 724 } catch (GLib.Error e) { 725 warning (e.message); 726 } 727 } 728 729 public class SubMenu : GLib.Object { 730 public Gee.ArrayList<MenuItem> items; 731 732 public SubMenu () { 733 items = new Gee.ArrayList<MenuItem> (); 734 } 735 } 736 } 737 738 } 739