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