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