.
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", "Glyph");
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", "Glyph");
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", "Glyph");
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", "Glyph");
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", "Glyph");
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", "Glyph");
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", "Glyph");
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", "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", "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 show_guide_guide = add_menu_item (t_("List Guides"), "show guide tab");
261 show_guide_guide.action.connect (() => {
262 MenuTab.show_guide_tab ();
263 show_menu = false;
264 });
265 edit_menu.items.add (show_guide_guide);
266
267 MenuItem select_point_above = add_menu_item (t_("Select Point Above"), "select point above", "Glyph");
268 select_point_above.action.connect (() => {
269 PenTool.select_point_up ();
270 show_menu = false;
271 });
272 edit_menu.items.add (select_point_above);
273
274 MenuItem select_next_point = add_menu_item (t_("Select Next Point"), "select next point", "Glyph");
275 select_next_point.action.connect (() => {
276 PenTool.select_point_right ();
277 show_menu = false;
278 });
279 edit_menu.items.add (select_next_point);
280
281 MenuItem select_previous_point = add_menu_item (t_("Select Previous Point"), "select previous point", "Glyph");
282 select_previous_point.action.connect (() => {
283 PenTool.select_point_left ();
284 show_menu = false;
285 });
286 edit_menu.items.add (select_previous_point);
287
288 MenuItem select_point_below = add_menu_item (t_("Select Point Below"), "select point below", "Glyph");
289 select_point_below.action.connect (() => {
290 PenTool.select_point_down ();
291 show_menu = false;
292 });
293 edit_menu.items.add (select_point_below);
294
295 // tab menu
296 MenuItem tab = add_menu_item (t_("Tab"));
297 tab.action.connect (() => {
298 set_menu (tab_menu);
299 });
300 menu.items.add (tab);
301
302 MenuItem next_tab = add_menu_item (t_("Next Tab"), "next tab");
303 next_tab.action.connect (() => {
304 MainWindow.next_tab ();
305 show_menu = false;
306 });
307 tab_menu.items.add (next_tab);
308
309 MenuItem previous_tab = add_menu_item (t_("Previous Tab"), "previous tab");
310 previous_tab.action.connect (() => {
311 MainWindow.previous_tab ();
312 show_menu = false;
313 });
314 tab_menu.items.add (previous_tab);
315
316 MenuItem close_tab = add_menu_item (t_("Close Tab"), "close tab");
317 close_tab.action.connect (() => {
318 MainWindow.close_tab ();
319 show_menu = false;
320 });
321 tab_menu.items.add (close_tab);
322
323 MenuItem close_all_tabs = add_menu_item (t_("Close All Tabs"), "close all tabs");
324 close_all_tabs.action.connect (() => {
325 MainWindow.close_all_tabs ();
326 show_menu = false;
327 });
328 tab_menu.items.add (close_all_tabs);
329
330 // tab menu
331 MenuItem kerning = add_menu_item (t_("Spacing and Kerning"));
332 kerning.action.connect (() => {
333 set_menu (kerning_menu);
334 });
335 menu.items.add (kerning);
336
337 MenuItem spacing_tab = add_menu_item (t_("Show Spacing Tab"), "show spacing tab");
338 spacing_tab.action.connect (() => {
339 MenuTab.show_spacing_tab ();
340 show_menu = false;
341 });
342 kerning_menu.items.add (spacing_tab);
343
344 MenuItem kerning_tab = add_menu_item (t_("Show Kerning Tab"), "show kerning tab");
345 kerning_tab.action.connect (() => {
346 MenuTab.show_kerning_context ();
347 show_menu = false;
348 });
349 kerning_menu.items.add (kerning_tab);
350
351 MenuItem list_kernings = add_menu_item (t_("List Kerning Pairs"), "list kerning pairs");
352 list_kernings.action.connect (() => {
353 MenuTab.list_all_kerning_pairs ();
354 show_menu = false;
355 });
356 kerning_menu.items.add (list_kernings);
357
358 MenuItem show_spacing = add_menu_item (t_("Spacing Classes"), "show spacing classes");
359 show_spacing.action.connect (() => {
360 MenuTab.show_spacing_class_tab ();
361 show_menu = false;
362 });
363 kerning_menu.items.add (show_spacing);
364
365 MenuItem next_kerning_pair = add_menu_item (t_("Select Next Kerning Pair"), "select next kerning pair");
366 next_kerning_pair.action.connect (() => {
367 KerningDisplay.next_pair ();
368 show_menu = false;
369 });
370 next_kerning_pair.add_display("Kerning");
371 next_kerning_pair.add_display("Spacing");
372 kerning_menu.items.add (next_kerning_pair);
373
374 MenuItem previous_kerning_pair = add_menu_item (t_("Select Previous Kerning Pair"), "select previous kerning pair");
375 previous_kerning_pair.action.connect (() => {
376 KerningDisplay.previous_pair ();
377 show_menu = false;
378 });
379 previous_kerning_pair.add_display("Kerning");
380 previous_kerning_pair.add_display("Spacing");
381 kerning_menu.items.add (previous_kerning_pair);
382
383 MenuItem load_kerning_strings = add_menu_item (t_("Load Kerning Strings"), "load kerning strings");
384 load_kerning_strings.action.connect (() => {
385 BirdFont.get_current_font ().kerning_strings.load_file ();
386 show_menu = false;
387 });
388 kerning_menu.items.add (load_kerning_strings);
389
390 MenuItem reload_kerning_strings = add_menu_item (t_("Reload Kerning Strings"), "reloadload kerning strings");
391 reload_kerning_strings.action.connect (() => {
392 Font f = BirdFont.get_current_font ();
393 f.kerning_strings.load (f);
394 show_menu = false;
395 });
396 kerning_menu.items.add (reload_kerning_strings);
397
398 // ligature menu
399 MenuItem ligature = add_menu_item (t_("Ligatures"));
400 ligature.action.connect (() => {
401 set_menu (ligature_menu);
402 });
403 menu.items.add (ligature);
404
405 MenuItem ligature_tab = add_menu_item (t_("Show Ligatures"), "show ligature tab");
406 ligature_tab.action.connect (() => {
407 MenuTab.show_ligature_tab ();
408 show_menu = false;
409 });
410 ligature_menu.items.add (ligature_tab);
411
412 MenuItem add_ligature = add_menu_item (t_("Add Ligature"), "add ligature");
413 add_ligature.action.connect (() => {
414 MenuTab.add_ligature ();
415 show_menu = false;
416 });
417 ligature_menu.items.add (add_ligature);
418
419 // git menu
420 if (BirdFont.has_argument ("--test")) {
421 MenuItem git = add_menu_item (t_("Git"));
422 git.action.connect (() => {
423 set_menu (git_menu);
424 });
425 menu.items.add (git);
426
427 MenuItem save_bfp = add_menu_item (t_("Save As .bfp"), "save as .bfp");
428 save_bfp.action.connect (() => {
429 MenuTab.save_as_bfp ();
430 show_menu = false;
431 });
432 git_menu.items.add (save_bfp);
433 }
434
435 // show overview
436 MenuItem overview = add_menu_item (t_("Overview"));
437 overview.action.connect (() => {
438 MenuTab.select_overview ();
439 show_menu = false;
440 });
441 menu.items.add (overview);
442
443 // settings
444 MenuItem settings = add_menu_item (t_("Settings"), "settings");
445 settings.action.connect (() => {
446 MenuTab.show_settings_tab ();
447 show_menu = false;
448 });
449 menu.items.add (settings);
450
451 MenuItem description = add_menu_item (t_("Name and Description"), "name and description");
452 description.action.connect (() => {
453 MenuTab.show_description ();
454 show_menu = false;
455 });
456 menu.items.add (description);
457
458 current_menu = menu;
459 top_menu = menu;
460 allocation = new WidgetAllocation ();
461
462 add_tool_key_bindings ();
463 load_key_bindings ();
464 }
465
466 public void process_key_binding_events (uint keyval) {
467 string display;
468 FontDisplay current_display = MainWindow.get_current_display ();
469 ToolItem tm;
470
471 foreach (MenuItem item in sorted_menu_items) {
472 if (item.key == (unichar) keyval && item.modifiers == KeyBindings.modifier) {
473
474 display = current_display.get_name ();
475
476 if (current_display is Glyph) {
477 display = "Glyph";
478 }
479
480 if (!current_display.needs_modifier () || item.modifiers != NONE) {
481 if (!SettingsDisplay.update_key_bindings
482 && item.in_display (display)
483 && !(item is ToolItem)) {
484 item.action ();
485 return;
486 }
487
488 if (item is ToolItem) {
489 tm = (ToolItem) item;
490
491 if (tm.in_display (display)) {
492 if (tm.tool.editor_events) {
493 MainWindow.get_toolbox ().set_current_tool (tm.tool);
494 tm.tool.select_action (tm.tool);
495 return;
496 } else {
497 tm.tool.select_action (tm.tool);
498 return;
499 }
500 }
501 }
502 }
503 }
504 }
505 }
506
507 void load_key_bindings () {
508 File default_key_bindings = SearchPaths.find_file (null, "key_bindings.xml");
509 File user_key_bindings = get_child (BirdFont.get_settings_directory (), "key_bindings.xml");
510
511 if (default_key_bindings.query_exists ()) {
512 parse_key_bindings (default_key_bindings);
513 }
514
515 if (user_key_bindings.query_exists ()) {
516 parse_key_bindings (user_key_bindings);
517 }
518 }
519
520 void parse_key_bindings (File f) {
521 string xml_data;
522 XmlParser parser;
523
524 try {
525 FileUtils.get_contents((!) f.get_path (), out xml_data);
526 parser = new XmlParser (xml_data);
527 parse_bindings (parser.get_root_tag ());
528 } catch (GLib.Error e) {
529 warning (e.message);
530 }
531 }
532
533 void parse_bindings (Tag tag) {
534 foreach (Tag t in tag) {
535 if (t.get_name () == "action") {
536 parse_binding (t.get_attributes ());
537 }
538 }
539 }
540
541 void parse_binding (Attributes attr) {
542 uint modifier = NONE;
543 unichar key = '\0';
544 string action = "";
545 MenuItem menu_action;
546 MenuItem? ma;
547
548 foreach (Attribute a in attr) {
549 if (a.get_name () == "key") {
550 key = a.get_content ().get_char (0);
551 }
552
553 if (a.get_name () == "ctrl" && a.get_content () == "true") {
554 modifier |= CTRL;
555 }
556
557 if (a.get_name () == "alt" && a.get_content () == "true") {
558 modifier |= ALT;
559 }
560
561 if (a.get_name () == "command" && a.get_content () == "true") {
562 modifier |= LOGO;
563 }
564
565 if (a.get_name () == "shift" && a.get_content () == "true") {
566 modifier |= SHIFT;
567 }
568
569 if (a.get_name () == "action") {
570 action = a.get_content ();
571 }
572 }
573
574 ma = menu_items.get (action);
575 if (ma != null) {
576 menu_action = (!) ma;
577 menu_action.modifiers = modifier;
578 menu_action.key = key;
579 }
580 }
581
582 MenuItem add_menu_item (string label, string description = "", string display = "") {
583 MenuItem i = new MenuItem (label, description);
584
585 if (description != "") {
586 menu_items.set (description, i);
587 sorted_menu_items.add (i);
588 }
589
590 if (display != "") {
591 i.add_display (display);
592 }
593
594 return i;
595 }
596
597 public void button_release (int button, double ex, double ey) {
598 double y = 0;
599 double x = allocation.width - width;
600
601 if (button == 1) {
602 foreach (MenuItem item in current_menu.items) {
603 if (x <= ex < allocation.width && y <= ey <= y + height) {
604 item.action ();
605 GlyphCanvas.redraw ();
606 return;
607 }
608
609 y += height;
610 }
611
612 menu_visibility = false;
613 current_menu = (!) top_menu;
614 GlyphCanvas.redraw ();
615 }
616 }
617
618 void add_tool_key_bindings () {
619 ToolItem tool_item;
620 foreach (ToolCollection tool_set in MainWindow.get_toolbox ().tool_sets) {
621 foreach (Expander e in tool_set.get_expanders ()) {
622 foreach (Tool t in e.tool) {
623 tool_item = new ToolItem (t);
624 if (tool_item.identifier != "" && !has_menu_item (tool_item.identifier)) {
625 menu_items.set (tool_item.identifier, tool_item);
626 sorted_menu_items.add (tool_item);
627 }
628
629 foreach (string d in tool_set.get_displays ()) {
630 tool_item.add_display (d);
631 }
632 }
633 }
634 }
635 }
636
637 public bool has_menu_item (string identifier) {
638 foreach (MenuItem mi in sorted_menu_items) {
639 if (mi.identifier == identifier) {
640 return true;
641 }
642 }
643
644 return false;
645 }
646
647 public void set_menu (SubMenu m) {
648 current_menu = m;
649 GlyphCanvas.redraw ();
650 }
651
652 public double layout_width () {
653 Text key_binding = new Text ();
654 double font_size = 17 * MainWindow.units;;
655 double w;
656
657 width = 0;
658 foreach (MenuItem item in current_menu.items) {
659 key_binding.set_text (item.get_key_bindings ());
660
661 item.label.set_font_size (font_size);
662 key_binding.set_font_size (font_size);
663
664 w = item.label.get_extent ();
665 w += key_binding.get_extent ();
666 w += 3 * height * MainWindow.units;
667
668 if (w > width) {
669 width = w;
670 }
671 }
672
673 return width;
674 }
675
676 public void draw (WidgetAllocation allocation, Context cr) {
677 double y;
678 double x;
679 double label_x;
680 double label_y;
681 double font_size;
682 Text key_binding;
683 double binding_extent;
684
685 width = layout_width ();
686
687 key_binding = new Text ();
688
689 x = allocation.width - width;
690 y = 0;
691 font_size = 17 * MainWindow.units;
692 this.allocation = allocation;
693
694 foreach (MenuItem item in current_menu.items) {
695 cr.save ();
696 Theme.color (cr, "Background 3");
697 cr.rectangle (x, y, width, height);
698 cr.fill ();
699 cr.restore ();
700
701 cr.save ();
702 label_x = allocation.width - width + 0.7 * height * MainWindow.units;
703 label_y = y + font_size - 1 * MainWindow.units;
704 Theme.text_color (item.label, "Menu Foreground");
705 item.label.draw_at_baseline (cr, label_x, label_y);
706
707 key_binding.set_text (item.get_key_bindings ());
708 key_binding.set_font_size (font_size);
709 binding_extent = key_binding.get_extent ();
710 label_x = x + width - binding_extent - 0.6 * height * MainWindow.units;
711 key_binding.set_font_size (font_size);
712 Theme.text_color (key_binding, "Menu Foreground");
713 key_binding.draw_at_baseline (cr, label_x, label_y);
714
715 y += height;
716 }
717 }
718
719 public void write_key_bindings () {
720 DataOutputStream os;
721 File file;
722 bool has_ctrl, has_alt, has_command, has_shift;
723
724 file = get_child (BirdFont.get_settings_directory (), "key_bindings.xml");
725
726 try {
727 if (file.query_exists ()) {
728 file.delete ();
729 }
730 } catch (GLib.Error e) {
731 warning (e.message);
732 }
733
734 try {
735 os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION));
736 os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>""");
737 os.put_string ("\n");
738
739 os.put_string ("<bindings>\n");
740
741 foreach (MenuItem item in sorted_menu_items) {
742 os.put_string ("\t<action ");
743
744 os.put_string (@"key=\"$((!)item.key.to_string ())\" ");
745
746 has_ctrl = (item.modifiers & CTRL) > 0;
747 os.put_string (@"ctrl=\"$(has_ctrl.to_string ())\" ");
748
749 has_alt = (item.modifiers & ALT) > 0;
750 os.put_string (@"alt=\"$(has_alt.to_string ())\" ");
751
752 has_command = (item.modifiers & LOGO) > 0;
753 os.put_string (@"command=\"$(has_command.to_string ())\" ");
754
755 has_shift = (item.modifiers & SHIFT) > 0;
756 os.put_string (@"shift=\"$(has_shift.to_string ())\" ");
757
758 os.put_string (@"action=\"$(item.identifier)\" ");
759
760 os.put_string ("/>\n");
761 }
762 os.put_string ("</bindings>\n");
763
764 os.close ();
765 } catch (GLib.Error e) {
766 warning (e.message);
767 }
768 }
769
770 public class SubMenu : GLib.Object {
771 public Gee.ArrayList<MenuItem> items;
772
773 public SubMenu () {
774 items = new Gee.ArrayList<MenuItem> ();
775 }
776 }
777 }
778
779 }
780