.
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 MenuTab.set_save_callback (new SaveCallback ());
442 MenuTab.save_callback.save_as();
443 }
444
445 public static void save () {
446 MenuTab.set_save_callback (new SaveCallback ());
447 MenuTab.save_callback.save ();
448 }
449
450 public static void load () {
451 MenuTab.load_callback = new LoadCallback ();
452 MenuTab.load_callback.load ();
453 }
454
455 public static void move_to_baseline () {
456 if (suppress_event) {
457 warn_if_test ("Event suppressed");
458 return;
459 }
460
461 DrawingTools.move_tool.move_to_baseline ();
462 }
463
464 public static void show_file_dialog_tab (string title, FileChooser action) {
465 MainWindow.get_tab_bar ().add_tab (new FileDialogTab (title, action));
466 }
467
468 public static void simplify_path () {
469 if (suppress_event) {
470 warn_if_test ("Event suppressed");
471 return;
472 }
473
474 Task t = new Task ();
475 t.task.connect (simplify);
476 MainWindow.native_window.run_background_thread (t);
477 }
478
479 private static void simplify () {
480 Glyph g = MainWindow.get_current_glyph ();
481 Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> ();
482
483 // selected objects
484 foreach (Path p in g.active_paths) {
485 paths.add (PenTool.simplify (p, false, PenTool.simplification_threshold));
486 }
487
488 // selected segments
489 if (paths.size == 0) {
490 foreach (Path p in g.path_list) {
491 g.add_active_path (p);
492 }
493
494 foreach (Path p in g.active_paths) {
495 paths.add (PenTool.simplify (p, true, PenTool.simplification_threshold));
496 }
497 }
498
499 g.store_undo_state ();
500
501 foreach (Path p in g.active_paths) {
502 g.path_list.remove (p);
503 }
504
505 foreach (Path p in g.active_paths) {
506 g.path_list.remove (p);
507 }
508
509 foreach (Path p in paths) {
510 g.add_path (p);
511 g.add_active_path (p);
512 }
513
514 g.active_paths.clear ();
515 g.update_view ();
516 }
517
518 public static void show_spacing_class_tab () {
519 SpacingClassTab t = MainWindow.get_spacing_class_tab ();
520 MainWindow.get_tab_bar ().add_unique_tab (t);
521 }
522
523 public static void add_ligature () {
524 TextListener listener;
525 string ligature_name = "";
526
527 if (suppress_event) {
528 warn_if_test ("Event suppressed");
529 return;
530 }
531
532 listener = new TextListener (t_("Name"), "", t_("Add ligature"));
533
534 listener.signal_text_input.connect ((text) => {
535 ligature_name = text;
536 });
537
538 listener.signal_submit.connect (() => {
539 Font font = BirdFont.get_current_font ();
540 GlyphCollection? fg;
541 Glyph glyph;
542 GlyphCollection glyph_collection;
543 OverView o = MainWindow.get_overview ();
544
545 fg = font.get_glyph_collection_by_name (ligature_name);
546
547 if (fg == null) {
548 glyph_collection = new GlyphCollection ('\0', ligature_name);
549
550 glyph = new Glyph (ligature_name, '\0');
551 glyph_collection.set_unassigned (true);
552 glyph_collection.insert_glyph (glyph, true);
553
554 font.add_glyph_collection (glyph_collection);
555 }
556
557 o.display_all_available_glyphs ();
558 o.scroll_to_glyph (ligature_name);
559
560 TabContent.hide_text_input ();
561 show_all_available_characters ();
562 });
563
564 TabContent.show_text_input (listener);
565 }
566
567 public static void show_default_characters () {
568 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
569 OverView o = MainWindow.get_overview ();
570 GlyphRange gr = new GlyphRange ();
571
572 if (!BirdFont.get_current_font ().initialised) {
573 MenuTab.new_file ();
574 }
575
576 DefaultCharacterSet.use_default_range (gr);
577 o.set_glyph_range (gr);
578
579 MainWindow.get_tab_bar ().select_tab_name ("Overview");
580 }
581
582 public static void show_all_available_characters () {
583 MainWindow.get_tab_bar ().add_unique_tab (new OverView ());
584
585 if (!BirdFont.get_current_font ().initialised) {
586 MenuTab.new_file ();
587 }
588
589 MainWindow.get_tab_bar ().select_tab_name ("Overview");
590 OverviewTools.show_all_available_characters ();
591 }
592
593 public static void show_background_tab () {
594 BackgroundTab bt;
595
596 if (suppress_event) {
597 warn_if_test ("Event suppressed");
598 return;
599 }
600
601 bt = BackgroundTab.get_instance ();
602 MainWindow.get_tab_bar ().add_unique_tab (bt);
603 }
604
605 public static void show_settings_tab () {
606 MainWindow.get_tab_bar ().add_unique_tab (new SettingsDisplay ());
607 }
608
609 public static void show_guide_tab () {
610 MainWindow.get_tab_bar ().add_unique_tab (new GuideTab ());
611 }
612 }
613
614 }
615