.
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 return false;
215 });
216 idle.attach (null);
217 }
218
219 public static void signal_file_saved () {
220 IdleSource idle = new IdleSource ();
221 idle.set_callback (() => {
222 save_callback.file_saved ();
223 return false;
224 });
225 idle.attach (null);
226 }
227
228 public static void signal_file_loaded () {
229 IdleSource idle = new IdleSource ();
230 idle.set_callback (() => {
231 load_callback.file_loaded ();
232 MainWindow.native_window.font_loaded ();
233 return false;
234 });
235 idle.attach (null);
236 }
237
238 public static void apply_font_setting (Font f) {
239 DrawingTools.background_scale.set_value (f.background_scale);
240
241 DrawingTools.grid_expander.tool.clear ();
242
243 if (f.grid_width.size == 0) {
244 f.grid_width.add ("1");
245 f.grid_width.add ("2");
246 f.grid_width.add ("4");
247 }
248
249 foreach (string grid in f.grid_width) {
250 DrawingTools.add_new_grid (double.parse (grid), false);
251 }
252
253 string sw = f.settings.get_setting ("stroke_width");
254 if (sw != ""){
255 StrokeTool.stroke_width = double.parse (sw);
256 DrawingTools.object_stroke.set_value_round (StrokeTool.stroke_width);
257 }
258
259 string pt = f.settings.get_setting ("point_type");
260 DrawingTools.set_default_point_type (pt);
261
262 string stroke = f.settings.get_setting ("apply_stroke");
263 bool s = bool.parse (stroke);
264 DrawingTools.add_stroke.set_selected (s);
265 StrokeTool.add_stroke = s;
266
267 string lc = f.settings.get_setting ("line_cap");
268
269 if (lc == "butt") {
270 StrokeTool.line_cap = LineCap.BUTT;
271 } else if (lc == "square") {
272 StrokeTool.line_cap = LineCap.SQUARE;
273 } else if (lc == "round") {
274 StrokeTool.line_cap = LineCap.ROUND;
275 }
276
277 DrawingTools.set_stroke_tool_visibility ();
278
279 string lock_grid = f.settings.get_setting ("lock_grid");
280 bool lg = bool.parse (lock_grid);
281 GridTool.lock_grid = lg;
282 DrawingTools.lock_grid.selected = GridTool.lock_grid;
283
284 string skew_overview = f.settings.get_setting ("skew_overview");
285 if (skew_overview != "") {
286 double so = double.parse (skew_overview);
287 OverviewTools.skew.set_value_round (so);
288 }
289
290 string autotrace_resolution = f.settings.get_setting ("autotrace_resolution");
291 if (autotrace_resolution != "") {
292 double ar = double.parse (autotrace_resolution);
293 DrawingTools.background_threshold.set_value_round (ar);
294 }
295
296 string autotrace_threshold = f.settings.get_setting ("autotrace_threshold");
297 if (autotrace_threshold != "") {
298 double at = double.parse (autotrace_threshold);
299 DrawingTools.auto_trace_resolution.set_value_round (at);
300 }
301
302 string autotrace_simplification = f.settings.get_setting ("autotrace_simplification");
303 if (autotrace_simplification != "") {
304 double asi = double.parse (autotrace_simplification);
305 DrawingTools.auto_trace_simplify.set_value_round (asi);
306 }
307
308 string kerning_zoom = f.settings.get_setting ("kerning_zoom");
309 if (kerning_zoom != "") {
310 double k = double.parse (kerning_zoom);
311 if (!is_null (KerningTools.zoom_bar)) {
312 KerningTools.zoom_bar.zoom_level = k;
313 KerningTools.zoom_bar.new_zoom (k);
314 }
315 }
316
317 string spacing_zoom = f.settings.get_setting ("spacing_zoom");
318 if (spacing_zoom != "") {
319 double sz = double.parse (spacing_zoom);
320 if (!is_null (SpacingTools.zoom_bar)) {
321 SpacingTools.zoom_bar.zoom_level = sz;
322 SpacingTools.zoom_bar.new_zoom (sz);
323 }
324 }
325
326 MainWindow.get_toolbox ().update_expanders ();
327 MainWindow.get_toolbox ().update_all_expanders ();
328 Toolbox.redraw_tool_box ();
329 }
330
331 // FIXME: background thread
332 public static void save_as_bfp () {
333 FileChooser fc = new FileChooser ();
334
335 if (suppress_event) {
336 warn_if_test ("Event suppressed");
337 return;
338 }
339
340 if (!set_suppress_event (true)) {
341 return;
342 }
343
344 fc.file_selected.connect((fn) => {
345 Font f = BirdFont.get_current_font ();
346
347 if (fn != null) {
348 f.init_bfp ((!) fn);
349 }
350
351 set_suppress_event (false);
352 });
353
354 MainWindow.file_chooser (t_("Save"), fc, FileChooser.SAVE);
355 }
356
357 public static void new_file () {
358 Font font;
359 SaveDialogListener dialog = new SaveDialogListener ();
360
361 if (suppress_event) {
362 warn_if_test ("Event suppressed");
363 return;
364 }
365
366 font = BirdFont.get_current_font ();
367
368 dialog.signal_discard.connect (() => {
369 MainWindow.close_all_tabs ();
370
371 BirdFont.new_font ();
372 MainWindow.native_window.font_loaded ();
373
374 show_default_characters ();
375
376 GlyphCanvas.redraw ();
377 });
378
379 dialog.signal_save.connect (() => {
380 MenuTab.save_callback = new SaveCallback ();
381 MenuTab.save_callback.file_saved.connect (() => {
382 dialog.signal_discard ();
383 });
384 save_callback.save ();
385 });
386
387 dialog.signal_cancel.connect (() => {
388 MainWindow.hide_dialog ();
389 });
390
391 if (!font.is_modified ()) {
392 dialog.signal_discard ();
393 } else {
394 MainWindow.show_dialog (new SaveDialog (dialog));
395 }
396
397 return;
398 }
399
400 public static void quit () {
401 if (suppress_event) {
402 warn_if_test ("Event suppressed");
403 return;
404 }
405
406 TabContent.hide_text_input ();
407
408 SaveDialogListener dialog = new SaveDialogListener ();
409 Font font = BirdFont.get_current_font ();
410
411 Preferences.save ();
412
413 dialog.signal_discard.connect (() => {
414 ensure_main_loop_is_empty ();
415 MainWindow.native_window.quit ();
416 });
417
418 dialog.signal_save.connect (() => {
419 MenuTab.set_save_callback (new SaveCallback ());
420 MenuTab.save_callback.file_saved.connect (() => {
421 ensure_main_loop_is_empty ();
422 MainWindow.native_window.quit ();
423 });
424 save_callback.save ();
425 });
426
427 dialog.signal_cancel.connect (() => {
428 MainWindow.hide_dialog ();
429 });
430
431 if (!font.is_modified ()) {
432 dialog.signal_discard ();
433 } else {
434 MainWindow.show_dialog (new SaveDialog (dialog));
435 }
436 }
437
438 public static void show_export_settings_tab () {
439 MainWindow.get_tab_bar ().add_unique_tab (new ExportSettings ());
440 }
441
442 public static void show_description () {
443 MainWindow.get_tab_bar ().add_unique_tab (new DescriptionDisplay ());
444 }
445
446 public static void show_kerning_context () {
447 if (suppress_event) {
448 warn_if_test ("Event suppressed");
449 return;
450 }
451
452 KerningDisplay kd = MainWindow.get_kerning_display ();
453 MainWindow.get_tab_bar ().add_unique_tab (kd);
454 }
455
456 public static void show_spacing_tab () {
457 if (suppress_event) {
458 warn_if_test ("Event suppressed");
459 return;
460 }
461
462 SpacingTab s = MainWindow.get_spacing_tab ();
463 MainWindow.get_tab_bar ().add_unique_tab (s);
464 }
465
466 public static void show_ligature_tab () {
467 if (suppress_event) {
468 warn_if_test ("Event suppressed");
469 return;
470 }
471
472 LigatureList d = MainWindow.get_ligature_display ();
473 MainWindow.get_tab_bar ().add_unique_tab (d);
474 }
475
476 public static void preview () {
477 Font font = BirdFont.get_current_font ();
478
479 if (suppress_event) {
480 warn_if_test ("Event suppressed");
481 return;
482 }
483
484 if (font.font_file == null) {
485 save_callback = new SaveCallback ();
486 save_callback.file_saved.connect (() => {
487 show_preview_tab ();
488 });
489 save_callback.save ();
490 } else {
491 show_preview_tab ();
492 }
493 }
494
495 public static void show_preview_tab () {
496 OverWriteDialogListener listener = new OverWriteDialogListener ();
497 TabBar tab_bar = MainWindow.get_tab_bar ();
498 FontFormat format = BirdFont.get_current_font ().format;
499
500 if (suppress_event) {
501 warn_if_test ("Event suppressed");
502 return;
503 }
504
505 listener.overwrite_signal.connect (() => {
506 KeyBindings.set_modifier (NONE);
507 tab_bar.add_unique_tab (new Preview (), true);
508 PreviewTools.update_preview ();
509 });
510
511 if ((format == FontFormat.SVG || format == FontFormat.FREETYPE) && !OverWriteDialogListener.dont_ask_again) {
512 MainWindow.show_dialog (new OverwriteDialog (listener));
513 } else {
514 listener.overwrite ();
515 }
516 }
517
518 /** Display the language selection tab. */
519 public static void select_language () {
520 if (suppress_event) {
521 warn_if_test ("Event suppressed");
522 return;
523 }
524
525 MainWindow.get_tab_bar ().add_unique_tab (new LanguageSelectionTab ());
526 }
527
528 public static void use_current_glyph_as_background () {
529 if (suppress_event) {
530 warn_if_test ("Event suppressed");
531 return;
532 }
533
534 Glyph.background_glyph = MainWindow.get_current_glyph ();
535
536 if (MainWindow.get_current_display () is OverView) {
537 Glyph.background_glyph = MainWindow.get_overview ().get_current_glyph ();
538 }
539 }
540
541 public static void reset_glyph_background () {
542 Glyph.background_glyph = null;
543 }
544
545 public static void remove_all_kerning_pairs () {
546 if (suppress_event) {
547 warn_if_test ("Event suppressed");
548 return;
549 }
550
551 KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();
552 classes.remove_all_pairs ();
553 KerningTools.update_kerning_classes ();
554 }
555
556 public static void list_all_kerning_pairs () {
557 if (suppress_event) {
558 warn_if_test ("Event suppressed");
559 return;
560 }
561
562 MainWindow.get_tab_bar ().add_unique_tab (new KerningList ());
563 }
564
565 public static void ensure_main_loop_is_empty () {
566 unowned MainContext context;
567 bool acquired;
568
569 context = MainContext.default ();
570 acquired = context.acquire ();
571
572 if (unlikely (!acquired)) {
573 warning ("Failed to acquire main loop.\n");
574 return;
575 }
576
577 while (context.pending ()) {
578 context.iteration (true);
579 }
580 context.release ();
581 }
582
583 public static void save_as () {
584 if (MenuTab.has_suppress_event () || !save_callback.is_done) {
585 warn_if_test ("Event suppressed");
586 return;
587 }
588
589 MenuTab.set_save_callback (new SaveCallback ());
590 MenuTab.save_callback.save_as();
591 }
592
593 public static void save () {
594 if (MenuTab.has_suppress_event () && !save_callback.is_done) {
595 warn_if_test ("Event suppressed");
596 return;
597 }
598
599 MenuTab.set_save_callback (new SaveCallback ());
600 MenuTab.save_callback.save ();
601 }
602
603 public static void load () {
604 if (MenuTab.has_suppress_event ()) {
605 warn_if_test ("Event suppressed");
606 return;
607 }
608
609 MenuTab.load_callback = new LoadCallback ();
610 MenuTab.load_callback.load ();
611
612 MenuTab.load_callback.file_loaded.connect (() => {
613 Font f = BirdFont.get_current_font ();
614 MenuTab.apply_font_setting (f);
615 });
616 }
617
618 public static void move_to_baseline () {
619 if (suppress_event) {
620 warn_if_test ("Event suppressed");
621 return;
622 }
623
624 DrawingTools.move_tool.move_to_baseline ();
625 }
626
627 public static void show_file_dialog_tab (string title, FileChooser action, bool folder) {
628 FileDialogTab ft = new FileDialogTab (title, action, folder);
629 MainWindow.get_tab_bar ().add_tab (ft);
630 }
631
632 public static void simplify_path () {
633 if (suppress_event) {
634 warn_if_test ("Event suppressed");
635 return;
636 }
637
638 Task t = new Task (simplify);
639 MainWindow.run_blocking_task (t);
640 }
641
642 private static void simplify () {
643 Glyph g = MainWindow.get_current_glyph ();
644 Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> ();
645
646 // selected objects
647 foreach (Path p in g.active_paths) {
648 paths.add (PenTool.simplify (p, false, PenTool.simplification_threshold));
649 }
650
651 // selected segments
652 if (paths.size == 0) {
653 foreach (Path p in g.get_all_paths ()) {
654 g.add_active_path (null, p);
655 }
656
657 foreach (Path p in g.active_paths) {
658 paths.add (PenTool.simplify (p, true, PenTool.simplification_threshold));
659 }
660 }
661
662 g.store_undo_state ();
663
664 foreach (Path p in g.active_paths) {
665 g.layers.remove_path (p);
666 }
667
668 foreach (Path p in g.active_paths) {
669 g.layers.remove_path (p);
670 }
671
672 foreach (Path p in paths) {
673 g.add_path (p);
674 g.add_active_path (null, p);
675 }
676
677 g.active_paths.clear ();
678 g.update_view ();
679 }
680
681 public static void show_spacing_class_tab () {
682 SpacingClassTab t = MainWindow.get_spacing_class_tab ();
683 MainWindow.get_tab_bar ().add_unique_tab (t);
684 }
685
686 public static void add_ligature () {
687 TextListener listener;
688 string ligature_name = "";
689
690 if (suppress_event) {
691 warn_if_test ("Event suppressed");
692 return;
693 }
694
695 listener = new TextListener (t_("Name"), "", t_("Add ligature"));
696
697 listener.signal_text_input.connect ((text) => {
698 ligature_name = text;
699 });
700
701 listener.signal_submit.connect (() => {
702 Font font = BirdFont.get_current_font ();
703 GlyphCollection? fg;
704 Glyph glyph;
705 GlyphCollection glyph_collection;
706 OverView o = MainWindow.get_overview ();
707
708 fg = font.get_glyph_collection_by_name (ligature_name);
709
710 if (fg == null) {
711 glyph_collection = new GlyphCollection ('\0', ligature_name);
712
713 glyph = new Glyph (ligature_name, '\0');
714 glyph_collection.set_unassigned (true);
715 glyph_collection.insert_glyph (glyph, true);
716
717 font.add_glyph_collection (glyph_collection);
718 }
719
720 o.display_all_available_glyphs ();
721 o.scroll_to_glyph (ligature_name);
722
723 TabContent.hide_text_input ();
724 show_all_available_characters ();
725 });
726
727 TabContent.show_text_input (listener);
728 }
729
730 public static void show_default_characters () {
731 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
732 OverView o = MainWindow.get_overview ();
733 GlyphRange gr = new GlyphRange ();
734
735 if (!BirdFont.get_current_font ().initialised) {
736 MenuTab.new_file ();
737 }
738
739 DefaultCharacterSet.use_default_range (gr);
740 o.set_current_glyph_range (gr);
741
742 MainWindow.get_tab_bar ().select_tab_name ("Overview");
743 }
744
745 public static void show_all_available_characters () {
746 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
747
748 if (!BirdFont.get_current_font ().initialised) {
749 MenuTab.new_file ();
750 }
751
752 MainWindow.get_tab_bar ().select_tab_name ("Overview");
753 OverviewTools.show_all_available_characters ();
754 }
755
756 public static void show_background_tab () {
757 BackgroundTab bt;
758
759 if (suppress_event) {
760 warn_if_test ("Event suppressed");
761 return;
762 }
763
764 bt = BackgroundTab.get_instance ();
765 MainWindow.get_tab_bar ().add_unique_tab (bt);
766 }
767
768 public static void show_settings_tab () {
769 MainWindow.get_tab_bar ().add_unique_tab (new SettingsTab ());
770 }
771
772 public static void show_theme_tab () {
773 MainWindow.get_tab_bar ().add_unique_tab (new ThemeTab ());
774 }
775
776 public static void show_guide_tab () {
777 MainWindow.get_tab_bar ().add_unique_tab (new GuideTab ());
778 }
779 }
780
781 }
782