.
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 if (current_display is Glyph && item.in_display ("Glyph")) {
475 item.action ();
476 return;
477 }
478
479 display = current_display.get_name ();
480
481 if (current_display is Glyph) {
482 display = "Glyph";
483 }
484
485 if (!current_display.needs_modifier () || item.modifiers != NONE) {
486 if (!SettingsDisplay.update_key_bindings
487 && item.in_display (display)
488 && !(item is ToolItem)) {
489 item.action ();
490 return;
491 }
492
493 if (item is ToolItem) {
494 tm = (ToolItem) item;
495
496 if (tm.in_display (display)) {
497 if (tm.tool.editor_events) {
498 MainWindow.get_toolbox ().set_current_tool (tm.tool);
499 tm.tool.select_action (tm.tool);
500 return;
501 } else {
502 tm.tool.select_action (tm.tool);
503 return;
504 }
505 }
506 }
507 }
508 }
509 }
510 }
511
512 void load_key_bindings () {
513 File default_key_bindings = SearchPaths.find_file (null, "key_bindings.xml");
514 File user_key_bindings = get_child (BirdFont.get_settings_directory (), "key_bindings.xml");
515
516 if (default_key_bindings.query_exists ()) {
517 parse_key_bindings (default_key_bindings);
518 }
519
520 if (user_key_bindings.query_exists ()) {
521 parse_key_bindings (user_key_bindings);
522 }
523 }
524
525 void parse_key_bindings (File f) {
526 string xml_data;
527 XmlParser parser;
528
529 try {
530 FileUtils.get_contents((!) f.get_path (), out xml_data);
531 parser = new XmlParser (xml_data);
532 parse_bindings (parser.get_root_tag ());
533 } catch (GLib.Error e) {
534 warning (e.message);
535 }
536 }
537
538 void parse_bindings (Tag tag) {
539 foreach (Tag t in tag) {
540 if (t.get_name () == "action") {
541 parse_binding (t.get_attributes ());
542 }
543 }
544 }
545
546 void parse_binding (Attributes attr) {
547 uint modifier = NONE;
548 unichar key = '\0';
549 string action = "";
550 MenuItem menu_action;
551 MenuItem? ma;
552
553 foreach (Attribute a in attr) {
554 if (a.get_name () == "key") {
555 key = a.get_content ().get_char (0);
556 }
557
558 if (a.get_name () == "ctrl" && a.get_content () == "true") {
559 modifier |= CTRL;
560 }
561
562 if (a.get_name () == "alt" && a.get_content () == "true") {
563 modifier |= ALT;
564 }
565
566 if (a.get_name () == "command" && a.get_content () == "true") {
567 modifier |= LOGO;
568 }
569
570 if (a.get_name () == "shift" && a.get_content () == "true") {
571 modifier |= SHIFT;
572 }
573
574 if (a.get_name () == "action") {
575 action = a.get_content ();
576 }
577 }
578
579 ma = menu_items.get (action);
580 if (ma != null) {
581 menu_action = (!) ma;
582 menu_action.modifiers = modifier;
583 menu_action.key = key;
584 }
585 }
586
587 MenuItem add_menu_item (string label, string description = "", string display = "") {
588 MenuItem i = new MenuItem (label, description);
589
590 if (description != "") {
591 menu_items.set (description, i);
592 sorted_menu_items.add (i);
593 }
594
595 if (display != "") {
596 i.add_display (display);
597 }
598
599 return i;
600 }
601
602 public void button_release (int button, double ex, double ey) {
603 double y = 0;
604 double x = allocation.width - width;
605
606 if (button == 1) {
607 foreach (MenuItem item in current_menu.items) {
608 if (x <= ex < allocation.width && y <= ey <= y + height) {
609 item.action ();
610 GlyphCanvas.redraw ();
611 return;
612 }
613
614 y += height;
615 }
616
617 menu_visibility = false;
618 current_menu = (!) top_menu;
619 GlyphCanvas.redraw ();
620 }
621 }
622
623 void add_tool_key_bindings () {
624 ToolItem tool_item;
625 foreach (ToolCollection tool_set in MainWindow.get_toolbox ().tool_sets) {
626 foreach (Expander e in tool_set.get_expanders ()) {
627 foreach (Tool t in e.tool) {
628 tool_item = new ToolItem (t);
629 if (tool_item.identifier != "" && !has_menu_item (tool_item.identifier)) {
630 menu_items.set (tool_item.identifier, tool_item);
631 sorted_menu_items.add (tool_item);
632 }
633
634 foreach (string d in tool_set.get_displays ()) {
635 tool_item.add_display (d);
636 }
637 }
638 }
639 }
640 }
641
642 public bool has_menu_item (string identifier) {
643 foreach (MenuItem mi in sorted_menu_items) {
644 if (mi.identifier == identifier) {
645 return true;
646 }
647 }
648
649 return false;
650 }
651
652 public void set_menu (SubMenu m) {
653 current_menu = m;
654 GlyphCanvas.redraw ();
655 }
656
657 public double layout_width () {
658 Text key_binding = new Text ();
659 double font_size = 17 * MainWindow.units;;
660 double w;
661
662 width = 0;
663 foreach (MenuItem item in current_menu.items) {
664 key_binding.set_text (item.get_key_bindings ());
665
666 item.label.set_font_size (font_size);
667 key_binding.set_font_size (font_size);
668
669 w = item.label.get_extent ();
670 w += key_binding.get_extent ();
671 w += 3 * height * MainWindow.units;
672
673 if (w > width) {
674 width = w;
675 }
676 }
677
678 return width;
679 }
680
681 public void draw (WidgetAllocation allocation, Context cr) {
682 double y;
683 double x;
684 double label_x;
685 double label_y;
686 double font_size;
687 Text key_binding;
688 double binding_extent;
689
690 width = layout_width ();
691
692 key_binding = new Text ();
693
694 x = allocation.width - width;
695 y = 0;
696 font_size = 17 * MainWindow.units;
697 this.allocation = allocation;
698
699 foreach (MenuItem item in current_menu.items) {
700 cr.save ();
701 Theme.color (cr, "Background 3");
702 cr.rectangle (x, y, width, height);
703 cr.fill ();
704 cr.restore ();
705
706 cr.save ();
707 label_x = allocation.width - width + 0.7 * height * MainWindow.units;
708 label_y = y + font_size - 1 * MainWindow.units;
709 Theme.text_color (item.label, "Menu Foreground");
710 item.label.draw_at_baseline (cr, label_x, label_y);
711
712 key_binding.set_text (item.get_key_bindings ());
713 key_binding.set_font_size (font_size);
714 binding_extent = key_binding.get_extent ();
715 label_x = x + width - binding_extent - 0.6 * height * MainWindow.units;
716 key_binding.set_font_size (font_size);
717 Theme.text_color (key_binding, "Menu Foreground");
718 key_binding.draw_at_baseline (cr, label_x, label_y);
719
720 y += height;
721 }
722 }
723
724 public void write_key_bindings () {
725 DataOutputStream os;
726 File file;
727 bool has_ctrl, has_alt, has_command, has_shift;
728
729 file = get_child (BirdFont.get_settings_directory (), "key_bindings.xml");
730
731 try {
732 if (file.query_exists ()) {
733 file.delete ();
734 }
735 } catch (GLib.Error e) {
736 warning (e.message);
737 }
738
739 try {
740 os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION));
741 os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>""");
742 os.put_string ("\n");
743
744 os.put_string ("<bindings>\n");
745
746 foreach (MenuItem item in sorted_menu_items) {
747 os.put_string ("\t<action ");
748
749 os.put_string (@"key=\"$((!)item.key.to_string ())\" ");
750
751 has_ctrl = (item.modifiers & CTRL) > 0;
752 os.put_string (@"ctrl=\"$(has_ctrl.to_string ())\" ");
753
754 has_alt = (item.modifiers & ALT) > 0;
755 os.put_string (@"alt=\"$(has_alt.to_string ())\" ");
756
757 has_command = (item.modifiers & LOGO) > 0;
758 os.put_string (@"command=\"$(has_command.to_string ())\" ");
759
760 has_shift = (item.modifiers & SHIFT) > 0;
761 os.put_string (@"shift=\"$(has_shift.to_string ())\" ");
762
763 os.put_string (@"action=\"$(item.identifier)\" ");
764
765 os.put_string ("/>\n");
766 }
767 os.put_string ("</bindings>\n");
768
769 os.close ();
770 } catch (GLib.Error e) {
771 warning (e.message);
772 }
773 }
774
775 public class SubMenu : GLib.Object {
776 public Gee.ArrayList<MenuItem> items;
777
778 public SubMenu () {
779 items = new Gee.ArrayList<MenuItem> ();
780 }
781 }
782 }
783
784 }
785