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