.
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 namespace BirdFont {
16
17 public class MenuTab : FontDisplay {
18
19 /** Ignore input events when the background thread is running.
20 *
21 * Do always check the return value of set_suppress_event when this
22 * variable is updated.
23 *
24 * This variable is used only in the gui thread.
25 */
26 public static bool suppress_event;
27
28 /** True if the background thread is running. */
29 public static bool background_thread;
30
31 /** A notification sent when the file has been saved. */
32 public static SaveCallback save_callback;
33
34 /** A notification sent when the file has been loaded. */
35 public static LoadCallback load_callback;
36
37 /** A notification sent when the file has been loaded. */
38 public static ExportCallback export_callback;
39
40 public MenuTab () {
41 save_callback = new SaveCallback ();
42 save_callback.is_done = true;
43
44 load_callback = new LoadCallback ();
45 export_callback = new ExportCallback ();
46
47 suppress_event = false;
48 background_thread = false;
49 }
50
51 public static void set_save_callback (SaveCallback c) {
52 if (!save_callback.is_done) {
53 warning ("Prevoius save command has not finished");
54 }
55
56 save_callback = c;
57 }
58
59 public static void start_background_thread () {
60 if (!set_suppress_event (true)) {
61 warning ("suppressed event");
62 return;
63 }
64
65 TabBar.start_wheel ();
66 }
67
68 public static void stop_background_thread () {
69 IdleSource idle = new IdleSource ();
70 idle.set_callback (() => {
71 set_suppress_event (false);
72 TabBar.stop_wheel ();
73 GlyphCanvas.redraw ();
74 return false;
75 });
76 idle.attach (null);
77 }
78
79 public static bool validate_metadata () {
80 Font font = BirdFont.get_current_font ();
81 string m = t_("Missing metadata in font:") + "\n";
82
83 if (font.postscript_name == "") {
84 MainWindow.show_message (m + t_("PostScript Name"));
85 return false;
86 }
87
88 if (font.name == "") {
89 MainWindow.show_message (m + t_("Name"));
90 return false;
91 }
92
93 if (font.subfamily == "") {
94 MainWindow.show_message (m + t_("Style"));
95 return false;
96 }
97
98 if (font.full_name == "") {
99 MainWindow.show_message (m + t_("Full Name (Name and Style)"));
100 return false;
101 }
102
103 if (font.unique_identifier == "") {
104 MainWindow.show_message (m + t_("Unique Identifier"));
105 return false;
106 }
107
108 Font current_font = BirdFont.get_current_font ();
109 string ttf_name = ExportSettings.get_file_name (current_font) + ".ttf";
110 string ttf_name_mac = ExportSettings.get_file_name_mac (current_font) + ".ttf";
111
112 print (@"$ttf_name == $ttf_name_mac");
113 if (ttf_name == ttf_name_mac) {
114 MainWindow.show_message (t_("You need to choose a different name for the TTF file with Mac adjustmets."));
115 ttf_name_mac = ExportSettings.get_file_name_mac (current_font) + " Mac.ttf";
116 return false;
117 }
118
119 return true;
120 }
121
122 public static void export_fonts_in_background () {
123 Font f;
124
125 if (suppress_event || !MainWindow.native_window.can_export ()) {
126 return;
127 }
128
129 f = BirdFont.get_current_font ();
130
131 if (f.font_file == null) {
132 MainWindow.show_message (t_("You need to save your font before exporting it."));
133 return;
134 }
135
136 if (!validate_metadata ()) {
137 return;
138 }
139
140 if (!ExportSettings.has_export_settings (f)) {
141 show_export_settings_tab ();
142 } else {
143 MenuTab.export_callback = new ExportCallback ();
144 MenuTab.export_callback.export_fonts_in_background ();
145 }
146 }
147
148 public static bool set_suppress_event (bool e) {
149 if (suppress_event && e) {
150 warning ("suppress_event is already set");
151 return false;
152 }
153 background_thread = e;
154 suppress_event = e;
155
156 // key up for all modifiers will be ignored if events are suppressed
157 if (suppress_event) {
158 KeyBindings.reset ();
159 }
160
161 return true;
162 }
163
164 public override string get_label () {
165 return t_("Menu");
166 }
167
168 public override string get_name () {
169 return "Menu";
170 }
171
172 public static void select_overview () {
173 if (suppress_event) {
174 warn_if_test ("Event suppressed");
175 return;
176 }
177
178 if (BirdFont.get_current_font ().is_empty ()) {
179 show_default_characters ();
180 } else {
181 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
182 MainWindow.get_tab_bar ().select_tab_name ("Overview");
183 }
184 }
185
186 public static void signal_file_exported () {
187 IdleSource idle = new IdleSource ();
188 idle.set_callback (() => {
189 export_callback.file_exported ();
190 return false;
191 });
192 idle.attach (null);
193 }
194
195 public static void signal_file_saved () {
196 IdleSource idle = new IdleSource ();
197 idle.set_callback (() => {
198 save_callback.file_saved ();
199 return false;
200 });
201 idle.attach (null);
202 }
203
204 public static void signal_file_loaded () {
205 IdleSource idle = new IdleSource ();
206 idle.set_callback (() => {
207 load_callback.file_loaded ();
208 MainWindow.native_window.font_loaded ();
209 return false;
210 });
211 idle.attach (null);
212 }
213
214 public static void apply_font_setting (Font f) {
215 DrawingTools.background_scale.set_value (f.background_scale);
216
217 DrawingTools.grid_expander.tool.clear ();
218
219 if (f.grid_width.size == 0) {
220 f.grid_width.add ("1");
221 f.grid_width.add ("2");
222 f.grid_width.add ("4");
223 }
224
225 foreach (string grid in f.grid_width) {
226 DrawingTools.add_new_grid (double.parse (grid), false);
227 }
228
229 string sw = f.settings.get_setting ("stroke_width");
230 if (sw != ""){
231 StrokeTool.stroke_width = double.parse (sw);
232 DrawingTools.object_stroke.set_value_round (StrokeTool.stroke_width);
233 }
234
235 string pt = f.settings.get_setting ("point_type");
236 DrawingTools.set_default_point_type (pt);
237
238 string stroke = f.settings.get_setting ("apply_stroke");
239 bool s = bool.parse (stroke);
240 DrawingTools.add_stroke.set_selected (s);
241 StrokeTool.add_stroke = s;
242
243 string lc = f.settings.get_setting ("line_cap");
244
245 if (lc == "butt") {
246 StrokeTool.line_cap = LineCap.BUTT;
247 } else if (lc == "square") {
248 StrokeTool.line_cap = LineCap.SQUARE;
249 } else if (lc == "round") {
250 StrokeTool.line_cap = LineCap.ROUND;
251 }
252
253 DrawingTools.set_stroke_tool_visibility ();
254
255 string lock_grid = f.settings.get_setting ("lock_grid");
256 bool lg = bool.parse (lock_grid);
257 GridTool.lock_grid = lg;
258 DrawingTools.lock_grid.selected = GridTool.lock_grid;
259
260 string skew_overview = f.settings.get_setting ("skew_overview");
261 if (skew_overview != "") {
262 double so = double.parse (skew_overview);
263 OverviewTools.skew.set_value_round (so);
264 }
265
266 string autotrace_resolution = f.settings.get_setting ("autotrace_resolution");
267 if (autotrace_resolution != "") {
268 double ar = double.parse (autotrace_resolution);
269 DrawingTools.background_threshold.set_value_round (ar);
270 }
271
272 string autotrace_threshold = f.settings.get_setting ("autotrace_threshold");
273 if (autotrace_threshold != "") {
274 double at = double.parse (autotrace_threshold);
275 DrawingTools.auto_trace_resolution.set_value_round (at);
276 }
277
278 string autotrace_simplification = f.settings.get_setting ("autotrace_simplification");
279 if (autotrace_simplification != "") {
280 double asi = double.parse (autotrace_simplification);
281 DrawingTools.auto_trace_simplify.set_value_round (asi);
282 }
283
284 string kerning_zoom = f.settings.get_setting ("kerning_zoom");
285 if (kerning_zoom != "") {
286 double k = double.parse (kerning_zoom);
287 if (!is_null (KerningTools.zoom_bar)) {
288 KerningTools.zoom_bar.zoom_level = k;
289 KerningTools.zoom_bar.new_zoom (k);
290 }
291 }
292
293 string spacing_zoom = f.settings.get_setting ("spacing_zoom");
294 if (spacing_zoom != "") {
295 double sz = double.parse (spacing_zoom);
296 if (!is_null (SpacingTools.zoom_bar)) {
297 SpacingTools.zoom_bar.zoom_level = sz;
298 SpacingTools.zoom_bar.new_zoom (sz);
299 }
300 }
301
302 MainWindow.get_toolbox ().update_expanders ();
303 MainWindow.get_toolbox ().update_all_expanders ();
304 Toolbox.redraw_tool_box ();
305 }
306
307 // FIXME: background thread
308 public static void save_as_bfp () {
309 FileChooser fc = new FileChooser ();
310
311 if (suppress_event) {
312 warn_if_test ("Event suppressed");
313 return;
314 }
315
316 if (!set_suppress_event (true)) {
317 return;
318 }
319
320 fc.file_selected.connect((fn) => {
321 Font f = BirdFont.get_current_font ();
322
323 if (fn != null) {
324 f.init_bfp ((!) fn);
325 }
326
327 set_suppress_event (false);
328 });
329
330 MainWindow.file_chooser (t_("Save"), fc, FileChooser.SAVE);
331 }
332
333 public static void new_file () {
334 Font font;
335 SaveDialogListener dialog = new SaveDialogListener ();
336
337 if (suppress_event) {
338 warn_if_test ("Event suppressed");
339 return;
340 }
341
342 font = BirdFont.get_current_font ();
343
344 dialog.signal_discard.connect (() => {
345 MainWindow.close_all_tabs ();
346
347 BirdFont.new_font ();
348 MainWindow.native_window.font_loaded ();
349
350 show_default_characters ();
351
352 GlyphCanvas.redraw ();
353 });
354
355 dialog.signal_save.connect (() => {
356 MenuTab.save_callback = new SaveCallback ();
357 MenuTab.save_callback.file_saved.connect (() => {
358 dialog.signal_discard ();
359 });
360 save_callback.save ();
361 });
362
363 dialog.signal_cancel.connect (() => {
364 MainWindow.hide_dialog ();
365 });
366
367 if (!font.is_modified ()) {
368 dialog.signal_discard ();
369 } else {
370 MainWindow.show_dialog (new SaveDialog (dialog));
371 }
372
373 return;
374 }
375
376 public static void quit () {
377 if (suppress_event) {
378 warn_if_test ("Event suppressed");
379 return;
380 }
381
382 TabContent.hide_text_input ();
383
384 SaveDialogListener dialog = new SaveDialogListener ();
385 Font font = BirdFont.get_current_font ();
386
387 Preferences.save ();
388
389 dialog.signal_discard.connect (() => {
390 ensure_main_loop_is_empty ();
391 MainWindow.native_window.quit ();
392 });
393
394 dialog.signal_save.connect (() => {
395 MenuTab.set_save_callback (new SaveCallback ());
396 MenuTab.save_callback.file_saved.connect (() => {
397 ensure_main_loop_is_empty ();
398 MainWindow.native_window.quit ();
399 });
400 save_callback.save ();
401 });
402
403 dialog.signal_cancel.connect (() => {
404 MainWindow.hide_dialog ();
405 });
406
407 if (!font.is_modified ()) {
408 dialog.signal_discard ();
409 } else {
410 MainWindow.show_dialog (new SaveDialog (dialog));
411 }
412 }
413
414 public static void show_export_settings_tab () {
415 MainWindow.get_tab_bar ().add_unique_tab (new ExportSettings ());
416 }
417
418 public static void show_description () {
419 MainWindow.get_tab_bar ().add_unique_tab (new DescriptionDisplay ());
420 }
421
422 public static void show_kerning_context () {
423 if (suppress_event) {
424 warn_if_test ("Event suppressed");
425 return;
426 }
427
428 KerningDisplay kd = MainWindow.get_kerning_display ();
429 MainWindow.get_tab_bar ().add_unique_tab (kd);
430 }
431
432 public static void show_spacing_tab () {
433 if (suppress_event) {
434 warn_if_test ("Event suppressed");
435 return;
436 }
437
438 SpacingTab s = MainWindow.get_spacing_tab ();
439 MainWindow.get_tab_bar ().add_unique_tab (s);
440 }
441
442 public static void show_ligature_tab () {
443 if (suppress_event) {
444 warn_if_test ("Event suppressed");
445 return;
446 }
447
448 LigatureList d = MainWindow.get_ligature_display ();
449 MainWindow.get_tab_bar ().add_unique_tab (d);
450 }
451
452 public static void preview () {
453 Font font = BirdFont.get_current_font ();
454
455 if (suppress_event) {
456 warn_if_test ("Event suppressed");
457 return;
458 }
459
460 if (font.font_file == null) {
461 save_callback = new SaveCallback ();
462 save_callback.file_saved.connect (() => {
463 show_preview_tab ();
464 });
465 save_callback.save ();
466 } else {
467 show_preview_tab ();
468 }
469 }
470
471 public static void show_preview_tab () {
472 OverWriteDialogListener dialog = new OverWriteDialogListener ();
473 TabBar tab_bar = MainWindow.get_tab_bar ();
474 FontFormat format = BirdFont.get_current_font ().format;
475
476 if (suppress_event) {
477 warn_if_test ("Event suppressed");
478 return;
479 }
480
481 dialog.overwrite_signal.connect (() => {
482 KeyBindings.set_modifier (NONE);
483 tab_bar.add_unique_tab (new Preview (), true);
484 PreviewTools.update_preview ();
485 });
486
487 if ((format == FontFormat.SVG || format == FontFormat.FREETYPE) && !OverWriteDialogListener.dont_ask_again) {
488 MainWindow.native_window.set_overwrite_dialog (dialog);
489 } else {
490 dialog.overwrite ();
491 }
492 }
493
494 /** Display the language selection tab. */
495 public static void select_language () {
496 if (suppress_event) {
497 warn_if_test ("Event suppressed");
498 return;
499 }
500
501 MainWindow.get_tab_bar ().add_unique_tab (new LanguageSelectionTab ());
502 }
503
504 public static void use_current_glyph_as_background () {
505 if (suppress_event) {
506 warn_if_test ("Event suppressed");
507 return;
508 }
509
510 Glyph.background_glyph = MainWindow.get_current_glyph ();
511
512 if (MainWindow.get_current_display () is OverView) {
513 Glyph.background_glyph = MainWindow.get_overview ().get_current_glyph ();
514 }
515 }
516
517 public static void reset_glyph_background () {
518 Glyph.background_glyph = null;
519 }
520
521 public static void remove_all_kerning_pairs () {
522 if (suppress_event) {
523 warn_if_test ("Event suppressed");
524 return;
525 }
526
527 KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();
528 classes.remove_all_pairs ();
529 KerningTools.update_kerning_classes ();
530 }
531
532 public static void list_all_kerning_pairs () {
533 if (suppress_event) {
534 warn_if_test ("Event suppressed");
535 return;
536 }
537
538 MainWindow.get_tab_bar ().add_unique_tab (new KerningList ());
539 }
540
541 public static void ensure_main_loop_is_empty () {
542 unowned MainContext context;
543 bool acquired;
544
545 context = MainContext.default ();
546 acquired = context.acquire ();
547
548 if (unlikely (!acquired)) {
549 warning ("Failed to acquire main loop.\n");
550 return;
551 }
552
553 while (context.pending ()) {
554 context.iteration (true);
555 }
556 context.release ();
557 }
558
559 public static void save_as () {
560 if (MenuTab.suppress_event || !save_callback.is_done) {
561 warn_if_test ("Event suppressed");
562 return;
563 }
564
565 MenuTab.set_save_callback (new SaveCallback ());
566 MenuTab.save_callback.save_as();
567 }
568
569 public static void save () {
570 if (MenuTab.suppress_event && !save_callback.is_done) {
571 warn_if_test ("Event suppressed");
572 return;
573 }
574
575 MenuTab.set_save_callback (new SaveCallback ());
576 MenuTab.save_callback.save ();
577 }
578
579 public static void load () {
580 if (MenuTab.suppress_event) {
581 warn_if_test ("Event suppressed");
582 return;
583 }
584
585 MenuTab.load_callback = new LoadCallback ();
586 MenuTab.load_callback.load ();
587
588 MenuTab.load_callback.file_loaded.connect (() => {
589 Font f = BirdFont.get_current_font ();
590 MenuTab.apply_font_setting (f);
591 });
592 }
593
594 public static void move_to_baseline () {
595 if (suppress_event) {
596 warn_if_test ("Event suppressed");
597 return;
598 }
599
600 DrawingTools.move_tool.move_to_baseline ();
601 }
602
603 public static void show_file_dialog_tab (string title, FileChooser action, bool folder) {
604 FileDialogTab ft = new FileDialogTab (title, action, folder);
605 MainWindow.get_tab_bar ().add_tab (ft);
606 }
607
608 public static void simplify_path () {
609 if (suppress_event) {
610 warn_if_test ("Event suppressed");
611 return;
612 }
613
614 Task t = new Task ();
615 t.task.connect (simplify);
616 MainWindow.native_window.run_background_thread (t);
617 }
618
619 private static void simplify () {
620 Glyph g = MainWindow.get_current_glyph ();
621 Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> ();
622
623 // selected objects
624 foreach (Path p in g.active_paths) {
625 paths.add (PenTool.simplify (p, false, PenTool.simplification_threshold));
626 }
627
628 // selected segments
629 if (paths.size == 0) {
630 foreach (Path p in g.get_all_paths ()) {
631 g.add_active_path (null, p);
632 }
633
634 foreach (Path p in g.active_paths) {
635 paths.add (PenTool.simplify (p, true, PenTool.simplification_threshold));
636 }
637 }
638
639 g.store_undo_state ();
640
641 foreach (Path p in g.active_paths) {
642 g.layers.remove_path (p);
643 }
644
645 foreach (Path p in g.active_paths) {
646 g.layers.remove_path (p);
647 }
648
649 foreach (Path p in paths) {
650 g.add_path (p);
651 g.add_active_path (null, p);
652 }
653
654 g.active_paths.clear ();
655 g.update_view ();
656 }
657
658 public static void show_spacing_class_tab () {
659 SpacingClassTab t = MainWindow.get_spacing_class_tab ();
660 MainWindow.get_tab_bar ().add_unique_tab (t);
661 }
662
663 public static void add_ligature () {
664 TextListener listener;
665 string ligature_name = "";
666
667 if (suppress_event) {
668 warn_if_test ("Event suppressed");
669 return;
670 }
671
672 listener = new TextListener (t_("Name"), "", t_("Add ligature"));
673
674 listener.signal_text_input.connect ((text) => {
675 ligature_name = text;
676 });
677
678 listener.signal_submit.connect (() => {
679 Font font = BirdFont.get_current_font ();
680 GlyphCollection? fg;
681 Glyph glyph;
682 GlyphCollection glyph_collection;
683 OverView o = MainWindow.get_overview ();
684
685 fg = font.get_glyph_collection_by_name (ligature_name);
686
687 if (fg == null) {
688 glyph_collection = new GlyphCollection ('\0', ligature_name);
689
690 glyph = new Glyph (ligature_name, '\0');
691 glyph_collection.set_unassigned (true);
692 glyph_collection.insert_glyph (glyph, true);
693
694 font.add_glyph_collection (glyph_collection);
695 }
696
697 o.display_all_available_glyphs ();
698 o.scroll_to_glyph (ligature_name);
699
700 TabContent.hide_text_input ();
701 show_all_available_characters ();
702 });
703
704 TabContent.show_text_input (listener);
705 }
706
707 public static void show_default_characters () {
708 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
709 OverView o = MainWindow.get_overview ();
710 GlyphRange gr = new GlyphRange ();
711
712 if (!BirdFont.get_current_font ().initialised) {
713 MenuTab.new_file ();
714 }
715
716 DefaultCharacterSet.use_default_range (gr);
717 o.set_current_glyph_range (gr);
718
719 MainWindow.get_tab_bar ().select_tab_name ("Overview");
720 }
721
722 public static void show_all_available_characters () {
723 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
724
725 if (!BirdFont.get_current_font ().initialised) {
726 MenuTab.new_file ();
727 }
728
729 MainWindow.get_tab_bar ().select_tab_name ("Overview");
730 OverviewTools.show_all_available_characters ();
731 }
732
733 public static void show_background_tab () {
734 BackgroundTab bt;
735
736 if (suppress_event) {
737 warn_if_test ("Event suppressed");
738 return;
739 }
740
741 bt = BackgroundTab.get_instance ();
742 MainWindow.get_tab_bar ().add_unique_tab (bt);
743 }
744
745 public static void show_settings_tab () {
746 MainWindow.get_tab_bar ().add_unique_tab (new SettingsTab ());
747 }
748
749 public static void show_theme_tab () {
750 MainWindow.get_tab_bar ().add_unique_tab (new ThemeTab ());
751 }
752
753 public static void show_guide_tab () {
754 MainWindow.get_tab_bar ().add_unique_tab (new GuideTab ());
755 }
756 }
757
758 }
759