.
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 return true;
109 }
110
111 public static void export_fonts_in_background () {
112 Font f;
113
114 if (suppress_event || !MainWindow.native_window.can_export ()) {
115 return;
116 }
117
118 f = BirdFont.get_current_font ();
119
120 if (f.font_file == null) {
121 MainWindow.show_message (t_("You need to save your font before exporting it."));
122 return;
123 }
124
125 if (!validate_metadata ()) {
126 return;
127 }
128
129 if (!ExportSettings.has_export_settings (f)) {
130 show_export_settings_tab ();
131 } else {
132 MenuTab.export_callback = new ExportCallback ();
133 MenuTab.export_callback.export_fonts_in_background ();
134 }
135 }
136
137 public static bool set_suppress_event (bool e) {
138 if (suppress_event && e) {
139 warning ("suppress_event is already set");
140 return false;
141 }
142 background_thread = e;
143 suppress_event = e;
144 return true;
145 }
146
147 public override string get_label () {
148 return t_("Menu");
149 }
150
151 public override string get_name () {
152 return "Menu";
153 }
154
155 public static void select_overview () {
156 if (suppress_event) {
157 warn_if_test ("Event suppressed");
158 return;
159 }
160
161 if (BirdFont.get_current_font ().is_empty ()) {
162 show_default_characters ();
163 } else {
164 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
165 MainWindow.get_tab_bar ().select_tab_name ("Overview");
166 }
167 }
168
169 public static void signal_file_exported () {
170 IdleSource idle = new IdleSource ();
171 idle.set_callback (() => {
172 export_callback.file_exported ();
173 return false;
174 });
175 idle.attach (null);
176 }
177
178 public static void signal_file_saved () {
179 IdleSource idle = new IdleSource ();
180 idle.set_callback (() => {
181 save_callback.file_saved ();
182 return false;
183 });
184 idle.attach (null);
185 }
186
187 public static void signal_file_loaded () {
188 IdleSource idle = new IdleSource ();
189 idle.set_callback (() => {
190 load_callback.file_loaded ();
191 MainWindow.native_window.font_loaded ();
192 return false;
193 });
194 idle.attach (null);
195 }
196
197 public static void set_font_setting_from_tools (Font f) {
198 string stroke_width;
199
200 f.background_scale = DrawingTools.background_scale.get_display_value ();
201
202 f.grid_width.clear ();
203
204 foreach (SpinButton s in GridTool.sizes) {
205 f.grid_width.add (s.get_display_value ());
206 }
207
208 stroke_width = f.settings.get_setting ("stroke_width");
209
210 if (stroke_width != "") {
211 DrawingTools.object_stroke.set_value (stroke_width);
212 StrokeTool.stroke_width = DrawingTools.object_stroke.get_value ();
213 }
214 }
215
216 // FIXME: background thread
217 public static void save_as_bfp () {
218 FileChooser fc = new FileChooser ();
219
220 if (suppress_event) {
221 warn_if_test ("Event suppressed");
222 return;
223 }
224
225 if (!set_suppress_event (true)) {
226 return;
227 }
228
229 fc.file_selected.connect((fn) => {
230 Font f = BirdFont.get_current_font ();
231
232 if (fn != null) {
233 f.init_bfp ((!) fn);
234 }
235
236 set_suppress_event (false);
237 });
238
239 MainWindow.file_chooser (t_("Save"), fc, FileChooser.SAVE);
240 }
241
242 public static void new_file () {
243 Font font;
244 SaveDialogListener dialog = new SaveDialogListener ();
245
246 if (suppress_event) {
247 warn_if_test ("Event suppressed");
248 return;
249 }
250
251 font = BirdFont.get_current_font ();
252
253 dialog.signal_discard.connect (() => {
254 MainWindow.close_all_tabs ();
255
256 BirdFont.new_font ();
257 MainWindow.native_window.font_loaded ();
258
259 show_default_characters ();
260
261 GlyphCanvas.redraw ();
262 });
263
264 dialog.signal_save.connect (() => {
265 MenuTab.save_callback = new SaveCallback ();
266 MenuTab.save_callback.file_saved.connect (() => {
267 dialog.signal_discard ();
268 });
269 save_callback.save ();
270 });
271
272 dialog.signal_cancel.connect (() => {
273 MainWindow.hide_dialog ();
274 });
275
276 if (!font.is_modified ()) {
277 dialog.signal_discard ();
278 } else {
279 MainWindow.show_dialog (new SaveDialog (dialog));
280 }
281
282 return;
283 }
284
285 public static void quit () {
286 if (suppress_event) {
287 warn_if_test ("Event suppressed");
288 return;
289 }
290
291 TabContent.hide_text_input ();
292
293 SaveDialogListener dialog = new SaveDialogListener ();
294 Font font = BirdFont.get_current_font ();
295
296 Preferences.save ();
297
298 dialog.signal_discard.connect (() => {
299 ensure_main_loop_is_empty ();
300 MainWindow.native_window.quit ();
301 });
302
303 dialog.signal_save.connect (() => {
304 MenuTab.set_save_callback (new SaveCallback ());
305 MenuTab.save_callback.file_saved.connect (() => {
306 ensure_main_loop_is_empty ();
307 MainWindow.native_window.quit ();
308 });
309 save_callback.save ();
310 });
311
312 dialog.signal_cancel.connect (() => {
313 MainWindow.hide_dialog ();
314 });
315
316 if (!font.is_modified ()) {
317 dialog.signal_discard ();
318 } else {
319 MainWindow.show_dialog (new SaveDialog (dialog));
320 }
321 }
322
323 public static void show_export_settings_tab () {
324 MainWindow.get_tab_bar ().add_unique_tab (new ExportSettings ());
325 }
326
327 public static void show_description () {
328 MainWindow.get_tab_bar ().add_unique_tab (new DescriptionDisplay ());
329 }
330
331 public static void show_kerning_context () {
332 if (suppress_event) {
333 warn_if_test ("Event suppressed");
334 return;
335 }
336
337 KerningDisplay kd = MainWindow.get_kerning_display ();
338 MainWindow.get_tab_bar ().add_unique_tab (kd);
339 }
340
341 public static void show_spacing_tab () {
342 if (suppress_event) {
343 warn_if_test ("Event suppressed");
344 return;
345 }
346
347 SpacingTab s = MainWindow.get_spacing_tab ();
348 MainWindow.get_tab_bar ().add_unique_tab (s);
349 }
350
351 public static void show_ligature_tab () {
352 if (suppress_event) {
353 warn_if_test ("Event suppressed");
354 return;
355 }
356
357 LigatureList d = MainWindow.get_ligature_display ();
358 MainWindow.get_tab_bar ().add_unique_tab (d);
359 }
360
361 public static void preview () {
362 Font font = BirdFont.get_current_font ();
363
364 if (suppress_event) {
365 warn_if_test ("Event suppressed");
366 return;
367 }
368
369 if (font.font_file == null) {
370 save_callback = new SaveCallback ();
371 save_callback.file_saved.connect (() => {
372 show_preview_tab ();
373 });
374 save_callback.save ();
375 } else {
376 show_preview_tab ();
377 }
378 }
379
380 public static void show_preview_tab () {
381 OverWriteDialogListener dialog = new OverWriteDialogListener ();
382 TabBar tab_bar = MainWindow.get_tab_bar ();
383 FontFormat format = BirdFont.get_current_font ().format;
384
385 if (suppress_event) {
386 warn_if_test ("Event suppressed");
387 return;
388 }
389
390 dialog.overwrite_signal.connect (() => {
391 KeyBindings.set_modifier (NONE);
392 tab_bar.add_unique_tab (new Preview (), true);
393 PreviewTools.update_preview ();
394 });
395
396 if ((format == FontFormat.SVG || format == FontFormat.FREETYPE) && !OverWriteDialogListener.dont_ask_again) {
397 MainWindow.native_window.set_overwrite_dialog (dialog);
398 } else {
399 dialog.overwrite ();
400 }
401 }
402
403 /** Display the language selection tab. */
404 public static void select_language () {
405 if (suppress_event) {
406 warn_if_test ("Event suppressed");
407 return;
408 }
409
410 MainWindow.get_tab_bar ().add_unique_tab (new LanguageSelectionTab ());
411 }
412
413 public static void use_current_glyph_as_background () {
414 if (suppress_event) {
415 warn_if_test ("Event suppressed");
416 return;
417 }
418
419 Glyph.background_glyph = MainWindow.get_current_glyph ();
420
421 if (MainWindow.get_current_display () is OverView) {
422 Glyph.background_glyph = MainWindow.get_overview ().get_current_glyph ();
423 }
424 }
425
426 public static void reset_glyph_background () {
427 Glyph.background_glyph = null;
428 }
429
430 public static void remove_all_kerning_pairs () {
431 if (suppress_event) {
432 warn_if_test ("Event suppressed");
433 return;
434 }
435
436 KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();
437 classes.remove_all_pairs ();
438 KerningTools.update_kerning_classes ();
439 }
440
441 public static void list_all_kerning_pairs () {
442 if (suppress_event) {
443 warn_if_test ("Event suppressed");
444 return;
445 }
446
447 MainWindow.get_tab_bar ().add_unique_tab (new KerningList ());
448 }
449
450 public static void ensure_main_loop_is_empty () {
451 unowned MainContext context;
452 bool acquired;
453
454 context = MainContext.default ();
455 acquired = context.acquire ();
456
457 if (unlikely (!acquired)) {
458 warning ("Failed to acquire main loop.\n");
459 return;
460 }
461
462 while (context.pending ()) {
463 context.iteration (true);
464 }
465 context.release ();
466 }
467
468 public static void save_as () {
469 if (MenuTab.suppress_event || !save_callback.is_done) {
470 warn_if_test ("Event suppressed");
471 return;
472 }
473
474 MenuTab.set_save_callback (new SaveCallback ());
475 MenuTab.save_callback.save_as();
476 }
477
478 public static void save () {
479 if (MenuTab.suppress_event && !save_callback.is_done) {
480 warn_if_test ("Event suppressed");
481 return;
482 }
483
484 MenuTab.set_save_callback (new SaveCallback ());
485 MenuTab.save_callback.save ();
486 }
487
488 public static void load () {
489 if (MenuTab.suppress_event) {
490 warn_if_test ("Event suppressed");
491 return;
492 }
493
494 MenuTab.load_callback = new LoadCallback ();
495 MenuTab.load_callback.load ();
496
497 MenuTab.load_callback.file_loaded.connect (() => {
498 Font f = BirdFont.get_current_font ();
499 MenuTab.set_font_setting_from_tools (f);
500 });
501 }
502
503 public static void move_to_baseline () {
504 if (suppress_event) {
505 warn_if_test ("Event suppressed");
506 return;
507 }
508
509 DrawingTools.move_tool.move_to_baseline ();
510 }
511
512 public static void show_file_dialog_tab (string title, FileChooser action) {
513 MainWindow.get_tab_bar ().add_tab (new FileDialogTab (title, action));
514 }
515
516 public static void simplify_path () {
517 if (suppress_event) {
518 warn_if_test ("Event suppressed");
519 return;
520 }
521
522 Task t = new Task ();
523 t.task.connect (simplify);
524 MainWindow.native_window.run_background_thread (t);
525 }
526
527 private static void simplify () {
528 Glyph g = MainWindow.get_current_glyph ();
529 Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> ();
530
531 // selected objects
532 foreach (Path p in g.active_paths) {
533 paths.add (PenTool.simplify (p, false, PenTool.simplification_threshold));
534 }
535
536 // selected segments
537 if (paths.size == 0) {
538 foreach (Path p in g.path_list) {
539 g.add_active_path (p);
540 }
541
542 foreach (Path p in g.active_paths) {
543 paths.add (PenTool.simplify (p, true, PenTool.simplification_threshold));
544 }
545 }
546
547 g.store_undo_state ();
548
549 foreach (Path p in g.active_paths) {
550 g.path_list.remove (p);
551 }
552
553 foreach (Path p in g.active_paths) {
554 g.path_list.remove (p);
555 }
556
557 foreach (Path p in paths) {
558 g.add_path (p);
559 g.add_active_path (p);
560 }
561
562 g.active_paths.clear ();
563 g.update_view ();
564 }
565
566 public static void show_spacing_class_tab () {
567 SpacingClassTab t = MainWindow.get_spacing_class_tab ();
568 MainWindow.get_tab_bar ().add_unique_tab (t);
569 }
570
571 public static void add_ligature () {
572 TextListener listener;
573 string ligature_name = "";
574
575 if (suppress_event) {
576 warn_if_test ("Event suppressed");
577 return;
578 }
579
580 listener = new TextListener (t_("Name"), "", t_("Add ligature"));
581
582 listener.signal_text_input.connect ((text) => {
583 ligature_name = text;
584 });
585
586 listener.signal_submit.connect (() => {
587 Font font = BirdFont.get_current_font ();
588 GlyphCollection? fg;
589 Glyph glyph;
590 GlyphCollection glyph_collection;
591 OverView o = MainWindow.get_overview ();
592
593 fg = font.get_glyph_collection_by_name (ligature_name);
594
595 if (fg == null) {
596 glyph_collection = new GlyphCollection ('\0', ligature_name);
597
598 glyph = new Glyph (ligature_name, '\0');
599 glyph_collection.set_unassigned (true);
600 glyph_collection.insert_glyph (glyph, true);
601
602 font.add_glyph_collection (glyph_collection);
603 }
604
605 o.display_all_available_glyphs ();
606 o.scroll_to_glyph (ligature_name);
607
608 TabContent.hide_text_input ();
609 show_all_available_characters ();
610 });
611
612 TabContent.show_text_input (listener);
613 }
614
615 public static void show_default_characters () {
616 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
617 OverView o = MainWindow.get_overview ();
618 GlyphRange gr = new GlyphRange ();
619
620 if (!BirdFont.get_current_font ().initialised) {
621 MenuTab.new_file ();
622 }
623
624 DefaultCharacterSet.use_default_range (gr);
625 o.set_glyph_range (gr);
626
627 MainWindow.get_tab_bar ().select_tab_name ("Overview");
628 }
629
630 public static void show_all_available_characters () {
631 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
632
633 if (!BirdFont.get_current_font ().initialised) {
634 MenuTab.new_file ();
635 }
636
637 MainWindow.get_tab_bar ().select_tab_name ("Overview");
638 OverviewTools.show_all_available_characters ();
639 }
640
641 public static void show_background_tab () {
642 BackgroundTab bt;
643
644 if (suppress_event) {
645 warn_if_test ("Event suppressed");
646 return;
647 }
648
649 bt = BackgroundTab.get_instance ();
650 MainWindow.get_tab_bar ().add_unique_tab (bt);
651 }
652
653 public static void show_settings_tab () {
654 MainWindow.get_tab_bar ().add_unique_tab (new SettingsTab ());
655 }
656
657 public static void show_theme_tab () {
658 MainWindow.get_tab_bar ().add_unique_tab (new ThemeTab ());
659 }
660
661 public static void show_guide_tab () {
662 MainWindow.get_tab_bar ().add_unique_tab (new GuideTab ());
663 }
664 }
665
666 }
667