Commit 840ae54
Save, export and load fonts in background threads
Changed files (31)
modifiedbirdfont/GtkWindow.valamodifiedfonts/Decibel.bfmodifiedlibbirdfont/BackgroundSelection.valamodifiedlibbirdfont/BirdFont.valamodifiedlibbirdfont/BirdFontFile.valamodifiedlibbirdfont/BirdFontPart.valamodifiedlibbirdfont/CharDatabase.valamodifiedlibbirdfont/DrawingTools.valaaddedlibbirdfont/ExportCallback.valamodifiedlibbirdfont/ExportTool.valamodifiedlibbirdfont/FileTab.valamodifiedlibbirdfont/Font.valamodifiedlibbirdfont/FontDisplay.valamodifiedlibbirdfont/GlyphCanvas.valamodifiedlibbirdfont/KerningList.valamodifiedlibbirdfont/KeyBindings.valaaddedlibbirdfont/LoadCallback.valamodifiedlibbirdfont/MainWindow.valamodifiedlibbirdfont/MenuTab.valamodifiedlibbirdfont/NativeWindow.valamodifiedlibbirdfont/OpenFontFormat/CodePageBits.valamodifiedlibbirdfont/OpenFontFormat/DirectoryTable.valamodifiedlibbirdfont/OpenFontFormat/FontData.valamodifiedlibbirdfont/OpenFontFormat/OpenFontFormatReader.valamodifiedlibbirdfont/OpenFontFormat/OpenFontFormatWriter.valamodifiedlibbirdfont/Preview.valamodifiedlibbirdfont/PreviewTools.valaaddedlibbirdfont/SaveCallback.valaaddedlibbirdfont/TabContent.valamodifiedlibbirdfont/TestCases.valamodifiedlibbirdfont/TooltipArea.vala
Diff
@@ -119,9 +119,11 @@ public class GtkWindow : Gtk.Window, NativeWindow { warning ("Failed to load html into canvas."); } + // show the webview when loading has finished html_box.set_visible (true); glyph_canvas_area.set_visible (false); description.canvas.set_visible (false);+ } else { html_box.set_visible (false); glyph_canvas_area.set_visible (true);@@ -188,14 +190,7 @@ public class GtkWindow : Gtk.Window, NativeWindow { key_press_event.connect ((t, event) => { if (!GtkWindow.text_input_is_active) {- FontDisplay fd = MainWindow.glyph_canvas.current_display;- - if (fd is Glyph) {- MainWindow.tools.key_press (event.keyval);- }- - MainWindow.glyph_canvas.key_press (event.keyval);- KeyBindings.add_modifier_from_keyval (event.keyval);+ TabContent.key_press (event.keyval); } return false;@@ -203,13 +198,7 @@ public class GtkWindow : Gtk.Window, NativeWindow { key_release_event.connect ((t, event) => { if (!GtkWindow.text_input_is_active) {- FontDisplay fd = MainWindow.glyph_canvas.current_display;- - if (fd is Glyph) {- MainWindow.glyph_canvas.key_release (event.keyval);- }- - KeyBindings.remove_modifier_from_keyval (event.keyval);+ TabContent.key_release (event.keyval); } return false;@@ -446,7 +435,7 @@ public class GtkWindow : Gtk.Window, NativeWindow { Gtk.MenuItem export_item = new Gtk.MenuItem.with_mnemonic (t_("_Export")); file_menu.append (export_item);- export_item.activate.connect (() => { MenuTab.export_font (); });+ export_item.activate.connect (() => { MenuTab.export_fonts_in_background (); }); export_item.add_accelerator ("activate", accel_group, 'E', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE); Gtk.MenuItem preview_item = new Gtk.MenuItem.with_mnemonic(t_("_Preview"));@@ -869,6 +858,48 @@ public class GtkWindow : Gtk.Window, NativeWindow { public void hide_tooltip () { tooltip_window.hide (); }+ + /** Run export in a background thread. */+ public void export () {+ unowned Thread<void*> export_thread;+ MenuTab.start_background_thread ();+ export_thread = Thread.create<void*> (this.export_thread, true);+ }+ + public void* export_thread () {+ ExportCallback.export_fonts ();+ MenuTab.stop_background_thread ();+ MenuTab.signal_file_exported ();+ return null;+ }+ + /** Load font in a background thread. */+ public void load () {+ unowned Thread<void*> thread;+ MenuTab.start_background_thread ();+ thread = Thread.create<void*> (this.loading_thread, true);+ }+ + public void* loading_thread () {+ BirdFont.get_current_font ().load ();+ MenuTab.stop_background_thread ();+ MenuTab.signal_file_loaded ();+ return null;+ }++ /** Save font in a background thread. */+ public void save () {+ unowned Thread<void*> thread;+ MenuTab.start_background_thread ();+ thread = Thread.create<void*> (this.saving_thread, true);+ }+ + public void* saving_thread () {+ BirdFont.get_current_font ().save ();+ MenuTab.stop_background_thread ();+ MenuTab.signal_file_saved ();+ return null;+ } } class TabbarCanvas : DrawingArea {@@ -1028,7 +1059,7 @@ public class GlyphCanvasArea : DrawingArea { Surface s = new Surface.similar (cw.get_target (), Cairo.Content.COLOR_ALPHA, alloc.width, alloc.height); Context c = new Context (s); - glyph_canvas.current_display.draw (alloc, c);+ TabContent.draw (alloc, c); cw.save (); cw.set_source_surface (c.get_target (), 0, 0);@@ -1042,9 +1073,9 @@ public class GlyphCanvasArea : DrawingArea { set_modifier ((int) e.state); if (e.type == EventType.BUTTON_PRESS) {- glyph_canvas.current_display.button_press (e.button, e.x, e.y); + TabContent.button_press (e.button, e.x, e.y); } else if (e.type == EventType.2BUTTON_PRESS) {- glyph_canvas.current_display.double_click (e.button, e.x, e.y);+ TabContent.double_click (e.button, e.x, e.y); } return true;@@ -1052,12 +1083,12 @@ public class GlyphCanvasArea : DrawingArea { button_release_event.connect ((t, e)=> { set_modifier ((int) e.state);- glyph_canvas.current_display.button_release ((int) e.button, e.x, e.y);+ TabContent.button_release ((int) e.button, e.x, e.y); return true; }); motion_notify_event.connect ((t, e)=> {- glyph_canvas.current_display.motion_notify (e.x, e.y); + TabContent.motion_notify (e.x, e.y); return true; }); @@ -1065,12 +1096,12 @@ public class GlyphCanvasArea : DrawingArea { set_modifier ((int) e.state); if (e.direction == Gdk.ScrollDirection.UP) {- glyph_canvas.current_display.scroll_wheel_up (e.x, e.y);+ TabContent.scroll_wheel_up (e.x, e.y); } else if (e.direction == Gdk.ScrollDirection.DOWN) {- glyph_canvas.current_display.scroll_wheel_down (e.x, e.y) ;+ TabContent.scroll_wheel_down (e.x, e.y) ; } - glyph_canvas.current_display.button_release (2, e.x, e.y);+ TabContent.button_release (2, e.x, e.y); return true; }); @@ -154,13 +154,19 @@ </glyph> </collection> <collection unicode="U+41">- <selected id="1"/>+ <selected id="2"/> <glyph id="1" left="-24" right="62"> <path stroke="0" data="S 4.0055442724,71.9352515311 L 18.0463917526,72.0412371134 L 9.6455835478,45.5115295875 L -8.0360824742,-0.0412371134 L -24.0279708939,-0.0233893099 L 4.0055442724,71.9352515311" /> <path stroke="0" data="S 51.8969072165,0.0000000000 L 31.9793814433,0.0000000000 L 14.3158133394,45.5061415559 L 6.0326460481,72.0412371134 D 10.5378006873,72.0412371134 15.9914089347,72.0412371134 24.0532646048,72.0412371134 L 51.8969072165,0.0000000000" /> <path stroke="0" data="S -7.9931271478,16.0014213972 L -8.0714066578,27.0000000000 L 31.9217204899,27.0000000000 L 32.0000000000,16.0014213972 L -7.9931271478,16.0014213972" /> <path stroke="0" data="S -23.9831081081,82.0000000000 L -23.9831081081,82.0000000000 O" /> </glyph>+ <glyph id="2" left="-24" right="62">+ <path stroke="0" data="S 4.0055442724,71.9352515311 L 16.1931543425,72.0412371134 L 7.7923461377,45.5115295875 L -9.8893198843,-0.0412371134 L -24.0279708939,-0.0233893099 L 4.0055442724,71.9352515311" />+ <path stroke="0" data="S 51.8969072165,0.0000000000 L 31.9793814433,0.0000000000 L 14.3158133394,45.5061415559 L 6.0326460481,72.0412371134 D 10.5378006873,72.0412371134 15.9914089347,72.0412371134 24.0532646048,72.0412371134 L 51.8969072165,0.0000000000" />+ <path stroke="0" data="S -7.9931271478,16.0014213972 L -8.0714066578,27.0000000000 L 31.9217204899,27.0000000000 L 32.0000000000,16.0014213972 L -7.9931271478,16.0014213972" />+ <path stroke="0" data="S -23.9831081081,82.0000000000 L -23.9831081081,82.0000000000 O" />+ </glyph> </collection> <collection unicode="U+42"> <selected id="1"/>@@ -594,7 +600,7 @@ <path stroke="0" data="S -19.6216336698,29.0115051400 Q -24.6642999998,34.0827683025 -24.7943474080,39.3941248032 Q -23.4181212441,57.2974975135 -3.5611624153,55.7935764075 Q 2.5434328041,55.3116443254 6.3828545964,52.9578825017 L 3.4468747669,41.6000127886 Q 0.1138148249,43.4801399907 -1.7445173835,42.9093020106 Q -4.5648263329,42.4259237995 -4.6986422096,39.7974020980 Q -4.7962978415,37.8791697174 -3.0230534494,35.6510299118 Q -1.3657386278,33.1666149714 4.1805167941,27.0870576542 Q 9.2414493007,22.1003106144 9.3663220078,16.6416077220 Q 7.9352288741,-1.2574627930 -11.8668629849,0.2421561176 Q -19.8547035521,0.8383097625 -25.5044515042,5.3427619029 L -22.5565512787,17.1457227289 Q -18.8941733398,12.8792420661 -14.8999626389,12.5222337432 Q -10.9078406894,12.2421557008 -10.7041540850,16.2259130571 Q -10.7614257889,18.1274994331 -12.4049719508,20.3847026134 Q -14.7448029856,23.5981651711 -19.6216336698,29.0115051400" /> </glyph> <glyph id="4" left="-26" right="31">- <path stroke="0" data="S -24.6506973900,39.3042035314 Q -20.8846140121,55.2317158339 -0.0304797517,55.7853855507 Q 13.9812680115,56.0561959654 22.0000000000,52.0000000000 L 16.0000000000,40.0000000000 Q 9.9740634006,44.0345821326 2.0000000000,44.0000000000 Q -1.6214924920,44.0309419081 -3.8693599553,40.7040203038 Q -4.1738495827,39.9653262146 -4.1183761347,39.4377133603 Q -4.0317213369,38.6135319404 -3.5898305978,38.2600191479 Q -1.0038080600,35.3538666192 9.9268433552,32.9042111360 Q 22.8629176372,29.9411203928 22.5213230531,16.5517274890 Q 21.9956772334,-4.0518731988 -11.4699782750,0.1402674997 Q -19.4710094189,0.5203947531 -25.2403925531,4.8705624808 L -22.6124508970,16.7488587665 Q -14.8109879409,13.6008263064 -10.6377551222,12.6651790320 Q 0.3331107811,10.4194878842 0.3298537462,15.1142600408 Q 0.3273158350,18.7570408674 -6.9279797684,20.3664905602 Q -26.8649974518,24.7636313260 -24.6506973900,39.3042035314" />+ <path stroke="0" data="S 9.9268433552,32.9042111360 Q 22.8629176372,29.9411203928 22.5213230531,16.5517274890 Q 21.9956772334,-4.0518731988 -11.4699782750,0.1402674997 Q -19.4710094189,0.5203947531 -25.2403925531,4.8705624808 L -22.6124508970,16.7488587665 Q -14.8109879409,13.6008263064 -10.6377551222,12.6651790320 Q 0.3331107811,10.4194878842 0.3298537462,15.1142600408 Q 0.3273158350,18.7570408674 -6.9279797684,20.3664905602 Q -26.8649974518,24.7636313260 -24.6506973900,39.3042035314 Q -20.8846140121,55.2317158339 -0.0304797517,55.7853855507 Q 13.9812680115,56.0561959654 22.0000000000,52.0000000000 L 16.0000000000,40.0000000000 Q 9.9730905353,43.8707314526 2.0000000000,44.0000000000 Q -1.8456291911,44.0712733683 -3.4501269963,42.5157277724 Q -5.5860726921,40.4435347740 -3.6513689768,38.3236737229 Q -1.1523666620,35.5855088907 9.9268433552,32.9042111360" /> </glyph> </collection> <collection unicode="U+74">@@ -944,7 +950,7 @@ <kerning left="o" right="w" hadjustment="-7.0000000000" /> <kerning left="e" right="e" hadjustment="-3.0000000000" /> <kerning left="j" right="o" hadjustment="-1.0000000000" />-<kerning left="o" right="x" hadjustment="-12.6035701094" />+<kerning left="o" right="x" hadjustment="-9.6035701094" /> <kerning left="e" right="f" hadjustment="-4.0000000000" /> <kerning left="v" right="e" hadjustment="-1.0000000000" /> <kerning left="e" right="g" hadjustment="-3.0000000000" />@@ -952,6 +958,7 @@ <kerning left="V" right="O" hadjustment="-4.5454545455" /> <kerning left="v" right="i" hadjustment="3.0000000000" /> <kerning left="C" right="o" hadjustment="-4.0000000000" />+<kerning left="T" right="n" hadjustment="-12.0000000000" /> <kerning left="e" right="l" hadjustment="-1.0000000000" /> <kerning left="v" right="k" hadjustment="-5.0000000000" /> <kerning left="quote" right="i" hadjustment="1.1990407674" />@@ -1027,6 +1034,7 @@ <kerning left="x" right="e" hadjustment="-3.0000000000" /> <kerning left="E" right="l" hadjustment="2.0000000000" /> <kerning left="g" right="h" hadjustment="1.0000000000" />+<kerning left="J" right="u" hadjustment="2.0000000000" /> <kerning left="L" right="Y" hadjustment="-15.0000000000" /> <kerning left="V" right="Ä" hadjustment="-11.0000000000" /> <kerning left="?" right="!" hadjustment="-6.0000000000" />@@ -1296,6 +1304,7 @@ <kerning left="ä" right="t" hadjustment="-3.0000000000" /> <kerning left="T" right="A" hadjustment="-12.0000000000" /> <kerning left="a" right="y" hadjustment="-2.0000000000" />+<kerning left="K" right="r" hadjustment="-3.0000000000" /> <kerning left="W" right="h" hadjustment="1.0000000000" /> <kerning left="W" right="i" hadjustment="3.5971223022" /> <kerning left="A" right="b" hadjustment="-2.0000000000" />@@ -21,7 +21,7 @@ class BackgroundSelection : FontDisplay { int active_box = -1; ImageSurface add_icon;- List <string> background_images = new List <string> ();+ Gee.ArrayList <string> background_images = new Gee.ArrayList<string> (); WidgetAllocation allocation; Glyph glyph; @@ -195,11 +195,11 @@ class BackgroundSelection : FontDisplay { font = BirdFont.get_current_font (); - if (!(0 <= active_box - 1 < background_images.length ())) {+ if (!(0 <= active_box - 1 < background_images.size)) { return; } - file = background_images.nth (active_box - 1).data;+ file = background_images.get (active_box - 1); bg = new GlyphBackgroundImage (file); tb = MainWindow.get_tab_bar (); @@ -223,7 +223,7 @@ class BackgroundSelection : FontDisplay { } } - background_images.prepend (file);+ background_images.insert (0, file); } public override void selected_canvas () {@@ -251,9 +251,7 @@ class BackgroundSelection : FontDisplay { FileInfo fi; File img_file; - while (background_images.length () > 0) {- background_images.delete_link (background_images.first ());- }+ background_images.clear (); foreach (string file in font.background_images) { add_unique_background (file);@@ -34,6 +34,7 @@ public static int run_export (string[] arg) { bool write_ttf = false; bool write_svg = false; File directory;+ Font font; stdout.printf ("birdfont-export version %s\n", VERSION); stdout.printf ("built on %s\n", BUILD_TIMESTAMP);@@ -104,7 +105,9 @@ public static int run_export (string[] arg) { BirdFont.current_font = new Font (); BirdFont.current_glyph = new Glyph (""); - if (!BirdFont.get_current_font ().load (file_name, false)) {+ font = BirdFont.get_current_font ();+ font.set_file (file_name);+ if (!font.load ()) { warning (@"Failed to load font $file_name.\n"); if (!file_name.has_suffix (".bf")) {@@ -36,9 +36,7 @@ class BirdFontFile : GLib.Object { Parser.init (); - while (font.background_images.length () > 0) {- font.background_images.remove_link (font.background_images.first ());- }+ font.background_images.clear (); font.font_file = path; tr = new TextReader.filename (path);@@ -98,8 +96,6 @@ class BirdFontFile : GLib.Object { } public bool write_font_file (string path, bool backup = false) {- TabBar.start_wheel ();- try { DataOutputStream os; File file;@@ -137,7 +133,7 @@ class BirdFontFile : GLib.Object { os.put_string ("\n"); // FIXME: add this to a font specific settings file- if (font.background_images.length () > 0) {+ if (font.background_images.size > 0) { os.put_string (@"<images>\n"); foreach (string f in font.background_images) {@@ -202,8 +198,7 @@ class BirdFontFile : GLib.Object { warning (@"$(e.message) \n"); return false; }- - TabBar.stop_wheel (); + return true; } @@ -240,8 +235,6 @@ class BirdFontFile : GLib.Object { os.put_string ("hadjustment=\""); os.put_string (round (KerningClasses.get_instance ().classes_kerning.get (i).val)); os.put_string ("\" />\n");-- Tool.yield (); } KerningClasses.get_instance ().get_single_position_pairs ((l, r, k) => {@@ -664,7 +657,6 @@ class BirdFontFile : GLib.Object { } TooltipArea.show_text (t_("Loading XML data."));- Tool.yield (); } TooltipArea.show_text ("");@@ -727,9 +719,7 @@ class BirdFontFile : GLib.Object { if (attr_name == "hadjustment") { hadjustment = double.parse (attr_content);- }- - Tool.yield ();+ } } if (range_left.get_length () > 1) {@@ -887,7 +877,7 @@ class BirdFontFile : GLib.Object { attr_content = prop->children->content; if (attr_name == "src") {- font.background_images.append (attr_content);+ font.background_images.add (attr_content); } } }@@ -900,7 +890,7 @@ class BirdFontFile : GLib.Object { string attr_content = prop->children->content; if (attr_name == "width") {- font.grid_width.append (attr_content);+ font.grid_width.add (attr_content); } } }@@ -43,10 +43,8 @@ class BirdFontPart : GLib.Object{ find_all_parts (bfp_file); font.set_bfp (true); - while (font.background_images.length () > 0) {- font.background_images.remove_link (font.background_images.first ());- }- + font.background_images.clear ();+ bfp_dir = File.new_for_path (root_directory); image_dir = bfp_dir.get_child ("images"); copy_backgrounds ((!) image_dir.get_path ());@@ -131,7 +131,7 @@ public class CharDatabase { } public static void show_loading_message () {- MainWindow.set_status (t_("Loading the unicode character database") + " ...");+ TooltipArea.show_text (t_("Loading the unicode character database") + " ..."); } public static void get_full_unicode (GlyphRange glyph_range) {@@ -554,8 +554,6 @@ public class DrawingTools : ToolCollection { Glyph? g = null; FontDisplay fd = MainWindow.get_current_display (); TooltipArea.show_text (t_("Creating thumbnails"));- - Tool.yield (); if (fd is Glyph) { g = (Glyph) fd;new file mode 100644@@ -0,0 +1,45 @@+/*+ Copyright (C) 2014 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++namespace BirdFont {++public class ExportCallback : GLib.Object {+ + public signal void file_exported ();+ + public ExportCallback () {+ }++ public void export_fonts_in_background () {+ Font font = BirdFont.get_current_font (); + if (font.font_file == null) {+ MenuTab.save_callback = new SaveCallback ();+ MenuTab.save_callback.file_saved.connect (() => {+ MainWindow.native_window.export ();+ });+ MenuTab.save_callback.save ();+ } else {+ MainWindow.native_window.export ();+ }+ }+ + /** Export TTF, EOT and SVG fonts. */+ public static void export_fonts () {+ ExportTool.export_ttf_font_sync ();+ ExportTool.export_svg_font ();+ TooltipArea.show_text (t_("Three font files have been created.")); + }+}++}@@ -154,7 +154,9 @@ public class ExportTool : GLib.Object { } } - /** Font must be saves before export. */+ /* Font must be saved before export in order to know where the+ * generated files should be stored.+ */ internal static void export_all () { Font font = BirdFont.get_current_font (); @@ -519,7 +521,7 @@ os.put_string ( string[] spawn_args = get_birdfont_export (folder, temp_file); string c = ""; - MainWindow.set_status (t_("Writing TTF and EOT files."));+ TooltipArea.show_text (t_("Writing TTF and EOT files.")); foreach (string s in spawn_args) { c += s + " ";@@ -533,7 +535,7 @@ os.put_string ( public static bool export_svg_font () { Font font = BirdFont.get_current_font ();- MainWindow.set_status (t_("Writing SVG file."));+ TooltipArea.show_text (t_("Writing SVG file.")); return export_svg_font_path (font.get_folder ()); } @@ -601,7 +603,8 @@ os.put_string ( if (async) { printd ("Loading the intermediate file.\n"); f = new Font ();- if (!f.load (ffi, false)) {+ f.set_file (ffi);+ if (!f.load ()) { warning (@"Can't read $ffi"); } } else {@@ -55,7 +55,8 @@ public class FileTab : FontDisplay { MainWindow.clear_glyph_cache (); MainWindow.close_all_tabs (); - loaded = f.load (fn);+ f.set_file (fn);+ loaded = f.load (); // FIXME: background thread if (!unlikely (loaded)) { warning (@"Failed to load font $fn");@@ -35,7 +35,7 @@ public class Font : GLib.Object { /** Table with ligatures. */ public GlyphTable ligature; - public List <string> background_images = new List <string> ();+ public Gee.ArrayList<string> background_images = new Gee.ArrayList<string> (); public string background_scale = "1"; /** Top margin */@@ -79,7 +79,7 @@ public class Font : GLib.Object { OpenFontFormatReader otf; bool otf_font = false; - public List<string> grid_width;+ public Gee.ArrayList<string> grid_width; /** File format. */ public FontFormat format = FontFormat.BIRDFONT;@@ -106,7 +106,7 @@ public class Font : GLib.Object { glyph_name = new GlyphTable (); ligature = new GlyphTable (); - grid_width = new List<string> ();+ grid_width = new Gee.ArrayList<string> (); kerning_classes = new KerningClasses (); @@ -464,7 +464,7 @@ public class Font : GLib.Object { } public void add_background_image (string file) {- background_images.append (file);+ background_images.add (file); } /** Delete temporary rescue files. */@@ -529,38 +529,51 @@ public class Font : GLib.Object { return bfp; } + public void save () {+ if (is_bfp ()) {+ save_bfp ();+ } else {+ save_bf ();+ }+ }+ public bool save_bfp () { return bfp_file.save (); } - public bool save (string path) {+ public void save_bf () { Font font; BirdFontFile birdfont_file = new BirdFontFile (this);- bool file_written = birdfont_file.write_font_file (path);+ string path;+ bool file_written;+ + if (font_file == null) {+ warning ("File name not set.");+ return;+ }+ + path = (!) font_file;+ file_written = birdfont_file.write_font_file (path); if (read_only) { warning (@"$path is write protected.");- return false; + TooltipArea.show_text (t_("The file is write protected."));+ return; } if (!path.has_suffix (".bf")) { warning ("Expecting .bf format.");- return false;+ return; } if (file_written) {- font_file = path;- - // delete backup when font is saved+ // delete the backup when the font is saved font = BirdFont.get_current_font (); font.delete_backup (); } modified = false;- Preferences.add_recent_files (get_path ());- - return file_written; } public void set_font_file (string path) {@@ -576,14 +589,25 @@ public class Font : GLib.Object { return (glyph_name.length () == 0); } - public bool load (string path, bool recent = true) {+ public void set_file (string path, bool recent = true) {+ font_file = path;+ }++ public bool load () {+ string path; bool loaded = false;+ initialised = true; otf_font = false; - while (grid_width.length () > 0) {- grid_width.remove_link (grid_width.first ());+ if (font_file == null) {+ warning ("No file name.");+ return false; }++ path = (!) font_file;++ grid_width.clear (); // empty cache and fill it with new glyphs from disk glyph_cache.remove_all ();@@ -592,7 +616,6 @@ public class Font : GLib.Object { if (path.has_suffix (".svg")) { Toolbox.select_tool_by_name ("cubic_points");- font_file = path; loaded = parse_svg_file (path); if (!loaded) {@@ -603,25 +626,21 @@ public class Font : GLib.Object { } if (path.has_suffix (".ffi")) {- font_file = path; loaded = parse_bf_file (path); format = FontFormat.FFI; } if (path.has_suffix (".bf")) {- font_file = path; loaded = parse_bf_file (path); format = FontFormat.BIRDFONT; } if (path.has_suffix (".bfp")) {- font_file = path; loaded = parse_bfp_file (path); format = FontFormat.BIRDFONT_PART; } if (path.has_suffix (".ttf")) {- font_file = path; loaded = parse_freetype_file (path); if (!loaded) {@@ -639,10 +658,11 @@ public class Font : GLib.Object { warning (e.message); } }+ + font_file = null; // make sure BirdFont asks where to save the file } if (path.has_suffix (".otf")) {- font_file = path; loaded = parse_freetype_file (path); if (!loaded) {@@ -650,12 +670,12 @@ public class Font : GLib.Object { } format = FontFormat.FREETYPE;+ + font_file = null; } - if (recent) {- Preferences.add_recent_files (get_path ());- }- + Preferences.add_recent_files (get_path ());+ return loaded; } @@ -40,32 +40,6 @@ public abstract class FontDisplay : GLib.Object { public virtual void scroll_to (double percent) { }-- public static string path_to_uri (string path) {- string uri = path;- string wp;- - // wine uri hack- if (BirdFont.win32) {- wp = wine_to_unix_path (uri);- - if (find_file (wp, "").query_exists ()) {- uri = wp;- }-- if (uri.index_of ("\\") > -1) {- uri = uri.replace ("\\", "/");- }- }-- if (uri.index_of ("/") == 0) {- uri = @"file://$uri";- } else {- uri = @"file:///$uri";- }- - return uri;- } public virtual void draw (WidgetAllocation allocation, Context cr) { }@@ -18,7 +18,9 @@ namespace BirdFont { public class GlyphCanvas : GLib.Object { - public FontDisplay current_display; + /** Tab content. */ + internal static FontDisplay current_display; + public signal void signal_redraw_area (int x, int y, int w, int h); public static WidgetAllocation allocation; @@ -127,7 +127,6 @@ public class KerningList : FontDisplay { int y = 0; string l, r; string[] p;- double k; l = ""; r = "";@@ -110,7 +110,9 @@ public class KeyBindings { } public static void remove_modifier_from_keyval (uint keyval) {- uint mod = get_mod_from_key (keyval);+ uint mod;+ + mod = get_mod_from_key (keyval); set_modifier (modifier ^ mod); } new file mode 100644@@ -0,0 +1,84 @@+/*+ Copyright (C) 2014 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++namespace BirdFont {+ +public class LoadCallback : GLib.Object {++ public signal void file_loaded ();+ + public LoadCallback () { + }++ public void load () {+ SaveDialogListener dialog;+ Font font;+ + if (MenuTab.suppress_event) {+ warn_if_test ("Event suppressed");+ return;+ }++ dialog = new SaveDialogListener ();+ font = BirdFont.get_current_font ();+ + MainWindow.close_all_tabs ();+ + dialog.signal_discard.connect (() => {+ load_new_font ();+ });++ dialog.signal_save.connect (() => {+ MenuTab.save ();+ load_new_font ();+ });+ + if (!font.is_modified ()) {+ dialog.signal_discard ();+ } else {+ MainWindow.native_window.set_save_dialog (dialog);+ }+ }++ private void load_new_font () {+ string? fn;+ Font f;++ if (MenuTab.suppress_event) {+ warn_if_test ("Event suppressed");+ return;+ }+ + f = BirdFont.get_current_font ();+ fn = MainWindow.file_chooser_open (t_("Open"));+ + if (fn != null) {+ f.delete_backup ();+ + f = BirdFont.new_font ();+ + MainWindow.clear_glyph_cache ();+ + f.set_file ((!) fn);+ MainWindow.native_window.load ();+ + file_loaded.connect (() => {+ KerningTools.update_kerning_classes ();+ MenuTab.select_overview ();+ });+ }+ }+}+ +}@@ -14,7 +14,7 @@ namespace BirdFont { -public class MainWindow { +public class MainWindow : GLib.Object { public static Toolbox tools; public static GlyphCanvas glyph_canvas; @@ -190,13 +190,6 @@ public class MainWindow { return t_(s); } - public static void set_status (string s) { - if (!is_null (tooltip)) { - TooltipArea.show_text (s); - Tool.yield (); - } - } - public static KerningDisplay get_kerning_display () { return kerning_display; } @@ -16,40 +16,52 @@ namespace BirdFont { public class MenuTab : FontDisplay { - /** Ignore new actions when export is in progress.- * - * BirdFont runs in a single thread but the glib main loop will still- * execute events with the idle priority in order to update the - * progress bar while export, save and possibly other actions are - * in progress. + /** Ignore input events when the background thread is running. * * Do always check the return value of set_suppress_event when this * variable is updated. */- public static bool suppress_event = false;+ public static bool suppress_event;++ /** A notification sent when the file has been saved. */+ public static SaveCallback save_callback;++ /** A notification sent when the file has been loaded. */+ public static LoadCallback load_callback;++ /** A notification sent when the file has been loaded. */+ public static ExportCallback export_callback; public MenuTab () {+ save_callback = new SaveCallback ();+ load_callback = new LoadCallback ();+ export_callback = new ExportCallback ();+ + suppress_event = false; }- - public static void export_font () {- Font font = BirdFont.get_current_font (); - if (font.font_file == null) {- if (MenuTab.save ()) {- export_all ();- }- } else {- export_all ();- }- }- - public static void export_all () {++ public static void start_background_thread () { if (!set_suppress_event (true)) { warning ("suppressed event"); return; }- - ExportTool.export_all ();- set_suppress_event (false); + + TabBar.start_wheel ();+ }++ public static void stop_background_thread () {+ IdleSource idle = new IdleSource ();+ idle.set_callback (() => {+ set_suppress_event (false);+ TabBar.stop_wheel ();+ return false;+ });+ idle.attach (null);+ }+ + public static void export_fonts_in_background () {+ MenuTab.export_callback = new ExportCallback ();+ MenuTab.export_callback.export_fonts_in_background (); } public static bool set_suppress_event (bool e) {@@ -81,92 +93,26 @@ public class MenuTab : FontDisplay { Toolbox.select_tool_by_name ("available_characters"); } }-- public static bool save_as () {- string? fn = null;- string f;- string file_name;- File file;- bool saved = false;- Font font = BirdFont.get_current_font ();- int i;- - if (suppress_event) {- warn_if_test ("Event suppressed");- return false;- }- - fn = MainWindow.file_chooser_save (t_("Save"));- - if (fn != null) {- f = (!) fn;- - if (f.has_suffix (".bf")) {- f = f.replace (".bf", "");- }- - file_name = @"$(f).bf";- file = File.new_for_path (file_name);- i = 2;- while (file.query_exists ()) {- file_name = @"$(f)_$i.bf";- file = File.new_for_path (file_name);- i++;- }- - font.font_file = file_name;- save ();- saved = true;- }- - return saved;+ + public static void signal_file_exported () {+ export_callback.file_exported ();+ }+ + public static void signal_file_saved () {+ save_callback.file_saved (); } - public static bool save () {- Font f;- string fn;- bool saved = false;-- if (suppress_event) {- warn_if_test ("Event suppressed");- return false;- }- - if (!set_suppress_event (true)) {- return false;- }-- f = BirdFont.get_current_font ();-- if (f.is_bfp ()) {- saved = f.save_bfp ();- set_suppress_event (false);- } else {- f.delete_backup ();- fn = f.get_path ();- - if (f.font_file != null && fn.has_suffix (".bf")) {- set_font_setting_from_tools (f); - saved = f.save (fn);- set_suppress_event (false);- } else {- set_suppress_event (false);- saved = save_as ();- }- }- - return saved;+ public static void signal_file_loaded () {+ load_callback.file_loaded (); }- + public static void set_font_setting_from_tools (Font f) { f.background_scale = MainWindow.get_drawing_tools ().background_scale.get_display_value (); - while (f.grid_width.length () > 0) {- f.grid_width.remove_link (f.grid_width.first ());- }+ f.grid_width.clear (); foreach (SpinButton s in GridTool.sizes) {- f.grid_width.append (s.get_display_value ());+ f.grid_width.add (s.get_display_value ()); } } @@ -237,59 +183,6 @@ public class MenuTab : FontDisplay { return; }- - public static void load () {- SaveDialogListener dialog = new SaveDialogListener ();- Font font = BirdFont.get_current_font ();- - if (suppress_event) {- warn_if_test ("Event suppressed");- return;- }- - MainWindow.close_all_tabs ();- - dialog.signal_discard.connect (() => {- load_new_font ();- });-- dialog.signal_save.connect (() => {- MenuTab.save ();- load_new_font ();- });- - if (!font.is_modified ()) {- dialog.signal_discard ();- } else {- MainWindow.native_window.set_save_dialog (dialog);- }- }-- private static void load_new_font () {- string? fn;- Font f;-- if (suppress_event) {- warn_if_test ("Event suppressed");- return;- }- - f = BirdFont.get_current_font ();- fn = MainWindow.file_chooser_open (t_("Open"));- - if (fn != null) {- f.delete_backup ();- - f = BirdFont.new_font ();- - MainWindow.clear_glyph_cache ();- f.load ((!)fn);- - KerningTools.update_kerning_classes ();- - select_overview (); - }- } public static void quit () { SaveDialogListener dialog = new SaveDialogListener ();@@ -353,9 +246,11 @@ public class MenuTab : FontDisplay { } if (font.font_file == null) {- if (MenuTab.save ()) {+ save_callback = new SaveCallback ();+ save_callback.file_saved.connect (() => { show_preview_tab ();- }+ });+ save_callback.save (); } else { show_preview_tab (); }@@ -446,5 +341,22 @@ public class MenuTab : FontDisplay { } context.release (); }+ + public static void save_as () {+ MenuTab.save_callback = new SaveCallback ();+ MenuTab.save_callback.save_as();+ }++ public static void save () {+ MenuTab.save_callback = new SaveCallback ();+ MenuTab.save_callback.save ();+ }+ + public static void load () {+ load_callback = new LoadCallback ();+ load_callback.load ();+ }+ }+ }@@ -57,6 +57,16 @@ public interface NativeWindow : GLib.Object { /** Hide help text. */ public abstract void hide_tooltip ();+ + /** Export fonts in a background thread. */+ public abstract void export ();++ /** Load font in a background thread. */+ public abstract void load ();+ + /** Save font in a background thread. */+ public abstract void save ();+ } }@@ -42,8 +42,6 @@ public class CodePageBits : GLib.Object { } else { set_bits_for_glyph (g, ref p0, ref p1); }- - Tool.yield (); } } @@ -66,7 +66,7 @@ public class DirectoryTable : Table { glyf_table.process (); gasp_table.process (); gdef_table.process ();- gsub_table.process ();+ // FIXME: implement gsub_table.process (); cmap_table.process (glyf_table); cvt_table.process (); hmtx_table.process ();@@ -55,7 +55,6 @@ public class FontData : Object { public void write_table (OtfInputStream dis, uint32 offset, uint32 length) throws Error { uint32 l = length + (length % 4); // padding after end of table- uint32 added_bytes = 0; uint8 b; if (length >= l) {@@ -74,11 +73,6 @@ public class FontData : Object { while (wp < l) { b = dis.read_byte (); add (b);- added_bytes++;- - if (added_bytes % 1024 == 0) {- Tool.yield ();- } } rp = 0;@@ -189,8 +189,6 @@ public class OpenFontFormatReader : Object { bf_kerning.append (@"$kerning".replace (",", ".")); bf_kerning.append ("\" />\n"); }- - Tool.yield (); } } catch (GLib.Error e) { warning (@"Failed to parse font. $(e.message)");@@ -46,8 +46,6 @@ public class OpenFontFormatWriter : Object { unowned List<Table> tables; FontData fd; uint l;-- TabBar.start_wheel (); font = nfont; @@ -70,8 +68,6 @@ public class OpenFontFormatWriter : Object { os.put_byte (data[j]); } }- - TabBar.stop_wheel (); } public void close () throws Error {@@ -85,7 +85,7 @@ public class Preview : FontDisplay { } public static string get_uri () {- return path_to_uri ((!) get_html_file ().get_path ());+ return TabContent.path_to_uri ((!) get_html_file ().get_path ()); } public static string get_windows_uri () {@@ -149,9 +149,9 @@ public class Preview : FontDisplay { warning ("Failed to copy svg file."); } while ((line = dis.read_line (null)) != null) {- line = ((!) line).replace (@"$(font.get_full_name ()).ttf", @"$(FontDisplay.path_to_uri ((!) f_ttf.get_path ()))?$rid");- line = ((!) line).replace (@"$(font.get_full_name ()).eot", @"$(FontDisplay.path_to_uri ((!) f_eot.get_path ()))?$rid");- line = ((!) line).replace (@"$(font.get_full_name ()).svg", @"$(FontDisplay.path_to_uri ((!) f_svg.get_path ()))?$rid");+ line = ((!) line).replace (@"$(font.get_full_name ()).ttf", @"$(TabContent.path_to_uri ((!) f_ttf.get_path ()))?$rid");+ line = ((!) line).replace (@"$(font.get_full_name ()).eot", @"$(TabContent.path_to_uri ((!) f_eot.get_path ()))?$rid");+ line = ((!) line).replace (@"$(font.get_full_name ()).svg", @"$(TabContent.path_to_uri ((!) f_svg.get_path ()))?$rid"); sb.append ((!) line); } } catch (Error e) {@@ -18,8 +18,6 @@ public class PreviewTools : ToolCollection { public List<Expander> expanders; public Expander classes; - private static bool suppress_event = false;- public PreviewTools () { Expander webview_tools = new Expander (); @@ -31,7 +29,7 @@ public class PreviewTools : ToolCollection { Tool export_fonts_button = new Tool ("export_fonts", t_("Export fonts")); export_fonts_button.select_action.connect ((self) => {- export_fonts ();+ MenuTab.export_fonts_in_background (); }); webview_tools.add_tool (export_fonts_button); @@ -46,29 +44,26 @@ public class PreviewTools : ToolCollection { expanders.append (webview_tools); } - /** Export fonts and update html canvas. */ public static void update_preview () {- export_fonts ();-- if (!Preview.has_html_document ()) {- Preview.generate_html_document ();- }- - MainWindow.tabs.select_tab_name ("Preview");+ MenuTab.export_callback = new ExportCallback ();+ MenuTab.export_callback.file_exported.connect (signal_preview_updated);+ MenuTab.export_callback.export_fonts_in_background (); }++ private static void signal_preview_updated () {+ IdleSource idle = new IdleSource ();++ idle.set_callback (() => {+ if (!Preview.has_html_document ()) {+ Preview.generate_html_document ();+ }+ + MainWindow.tabs.select_tab_name ("Preview");+ return false;+ }); - /** Export TTF, EOT and SVG fonts. */- public static void export_fonts () {- if (!suppress_event) {- suppress_event = true;- ExportTool.export_ttf_font_sync ();- ExportTool.export_svg_font ();- TooltipArea.show_text (t_("Three font files have been created.")); - suppress_event = false;- } else {- warn_if_test ("Export event suppressed");- }+ idle.attach (null); } /** Generate the preview document. */new file mode 100644@@ -0,0 +1,92 @@+/*+ Copyright (C) 2014 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++namespace BirdFont {++public class SaveCallback : GLib.Object {+ + public signal void file_saved ();+ + public SaveCallback () { + }+ + public bool save_as () {+ string? fn = null;+ string f;+ string file_name;+ File file;+ bool saved = false;+ Font font = BirdFont.get_current_font ();+ int i;+ + if (MenuTab.suppress_event) {+ warn_if_test ("Event suppressed");+ return false;+ }+ + fn = MainWindow.file_chooser_save (t_("Save"));+ + if (fn != null) {+ f = (!) fn;+ + if (f.has_suffix (".bf")) {+ f = f.replace (".bf", "");+ }+ + file_name = @"$(f).bf";+ file = File.new_for_path (file_name);+ i = 2;+ while (file.query_exists ()) {+ file_name = @"$(f)_$i.bf";+ file = File.new_for_path (file_name);+ i++;+ }+ + font.font_file = file_name;+ save ();+ saved = true;+ }+ + return saved;+ }++ public void save () {+ Font f;+ string fn;+ + if (MenuTab.suppress_event) {+ warn_if_test ("Event suppressed");+ return;+ }+ + f = BirdFont.get_current_font ();++ if (f.is_bfp ()) {+ MainWindow.native_window.save ();+ } else {+ f.delete_backup ();+ fn = f.get_path ();+ + if (f.font_file != null && fn.has_suffix (".bf")) {+ MenuTab.set_font_setting_from_tools (f);+ f.set_font_file (fn);+ MainWindow.native_window.save ();+ } else {+ save_as ();+ }+ }+ }+}+ +}new file mode 100644@@ -0,0 +1,152 @@+/*+ Copyright (C) 2014 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++using Cairo;++namespace BirdFont {++/** Interface for events from native window to the current tab. */+public class TabContent : GLib.Object {+ + public static bool has_scrollbar () {+ if (MenuTab.suppress_event) {+ return false;+ }+ + return GlyphCanvas.current_display.has_scrollbar ();+ }+ + public static void scroll_to (double percent) {+ if (MenuTab.suppress_event) {+ return;+ }+ + GlyphCanvas.current_display.scroll_to (percent);+ }+ + public static void draw (WidgetAllocation allocation, Context cr) {+ if (MenuTab.suppress_event) {+ cr.save ();+ cr.set_source_rgba (1, 1, 1, 1);+ cr.rectangle (0, 0, allocation.width, allocation.height);+ cr.fill ();+ cr.restore ();+ } else {+ GlyphCanvas.current_display.draw (allocation, cr);+ }+ }+ + public static void key_press (uint keyval) {+ if (MenuTab.suppress_event) {+ return;+ }++ if (GlyphCanvas.current_display is Glyph) {+ MainWindow.tools.key_press (keyval);+ }+ + KeyBindings.add_modifier_from_keyval (keyval);+ + GlyphCanvas.current_display.key_press (keyval);+ }+ + public static void key_release (uint keyval) {+ if (MenuTab.suppress_event) {+ return;+ }+ + if (GlyphCanvas.current_display is Glyph) {+ GlyphCanvas.current_display.key_release (keyval);+ }+ + KeyBindings.remove_modifier_from_keyval (keyval);+ }+ + public static void motion_notify (double x, double y) {+ if (MenuTab.suppress_event) {+ return;+ }+ + GlyphCanvas.current_display.motion_notify (x, y);+ }+ + public static void button_release (int button, double x, double y) {+ if (MenuTab.suppress_event) {+ return;+ }+ + GlyphCanvas.current_display.button_release (button, x, y);+ }+ + public static void button_press (uint button, double x, double y) {+ if (MenuTab.suppress_event) {+ return;+ }+ + GlyphCanvas.current_display.button_press (button, x, y);+ }++ public static void double_click (uint button, double ex, double ey) {+ if (MenuTab.suppress_event) {+ return;+ }+ + GlyphCanvas.current_display.double_click (button, ex, ey);+ }+ + public static void scroll_wheel_up (double x, double y) {+ if (MenuTab.suppress_event) {+ return;+ }+ + GlyphCanvas.current_display.scroll_wheel_up (x, y);+ }+ + public static void scroll_wheel_down (double x, double y) {+ if (MenuTab.suppress_event) {+ return;+ }+ + GlyphCanvas.current_display.scroll_wheel_down (x, y);+ }++ public static string path_to_uri (string path) {+ string uri = path;+ string wp;+ + // wine uri hack+ if (BirdFont.win32) {+ wp = wine_to_unix_path (uri);+ + if (SearchPaths.find_file (wp, "").query_exists ()) {+ uri = wp;+ }++ if (uri.index_of ("\\") > -1) {+ uri = uri.replace ("\\", "/");+ }+ }++ if (uri.index_of ("/") == 0) {+ uri = @"file://$uri";+ } else {+ uri = @"file:///$uri";+ }+ + return uri;+ }+}++}+@@ -104,7 +104,8 @@ class TestCases { f.set_read_only (true); - if (!f.load (fn)) {+ f.set_file (fn);+ if (!f.load ()) { warning (@"Failed to load fond $fn"); return; }@@ -222,8 +223,7 @@ class TestCases { } Tool.yield ();- - font.load ((!) f.get_path ());+ font.load (); } public static void test_parse_quadratic_paths () {@@ -32,12 +32,13 @@ public class TooltipArea : GLib.Object { } public static void show_text (string text) {- if (is_null (MainWindow.get_tooltip ())) {- return;- }- - MainWindow.get_tooltip ().tooltip = text;- MainWindow.get_tooltip ().redraw ();+ IdleSource idle = new IdleSource ();+ idle.set_callback (() => {+ MainWindow.get_tooltip ().tooltip = text;+ MainWindow.get_tooltip ().redraw ();+ return false;+ });+ idle.attach (null); } public void set_text_from_tool () {