Commit 32cdb16
search for characters in unicode character database
Changed files (7)
modifiedbirdfont/GtkWindow.valamodifiedlibbirdfont/CharDatabase.valamodifiedlibbirdfont/DefaultCharactersSet.valamodifiedlibbirdfont/Font.valamodifiedlibbirdfont/GlyphRange.valamodifiedlibbirdfont/MainWindow.valamodifiedlibbirdfont/OverView.vala
Diff
@@ -538,6 +538,11 @@ public class GtkWindow : Gtk.Window, NativeWindow { }); select_all_item.add_accelerator ("activate", accel_group, 'A', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE); + Gtk.MenuItem search_item = new Gtk.MenuItem.with_mnemonic (_("Search"));+ edit_menu.append (search_item);+ search_item.activate.connect (() => { OverView.search (); }); + search_item.add_accelerator ("activate", accel_group, 'F', Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE);+ Gtk.MenuItem export_svg_item = new Gtk.MenuItem.with_mnemonic (_("_Export Glyph as SVG")); edit_menu.append (export_svg_item); export_svg_item.activate.connect (() => { ExportTool.export_current_glyph (); }); @@ -12,89 +12,166 @@ Lesser General Public License for more details. */ +using Gee;+ namespace BirdFont { -class CharDatabase {+public class CharDatabase {+ + static HashMap<string, string> entries;+ static HashMultiMap<string, string> index; + static GlyphRange full_unicode_range;+ static bool database_is_loaded = false;+ public CharDatabase () {+ entries = new HashMap<string, string> ();+ index = new HashMultiMap<string, string> ();+ + full_unicode_range = new GlyphRange ();++ IdleSource idle = new IdleSource ();+ idle.set_callback (() => {+ show_loading_message ();+ parse_all_entries ();+ database_is_loaded = true;+ return false;+ });+ idle.attach (null); } - public static bool has_ascender (unichar c) {- if (!c.islower()) return true;+ public static GlyphRange search (string s) {+ GlyphRange result = new GlyphRange ();+ GlyphRange ucd_result = new GlyphRange ();+ unichar c;+ string i;+ string? iv; - switch (c) {- case 'b': return true;- case 'd': return true;- case 'f': return true;- case 'h': return true;- case 'i': return true;- case 'j': return true;- case 'k': return true;- case 'l': return true; + if (!database_is_loaded) {+ show_loading_message (); } - if ('à' <= c <= 'å') return true;- if ('è' <= c <= 'ö') return true;- if ('ù' <= c <= 'ă') return true;- if ('ć' <= c <= 'ė') return true;-- return false;+ return_if_fail (result.get_length () == 0);+ + if (s.has_prefix ("U+") || s.has_prefix ("u+")) {+ c = Font.to_unichar (s.down ());+ + if (c != '\0') {+ result.add_single (c);+ }+ }+ + if (s.char_count () == 1) {+ result.add_single (s.get_char ()); + }+ + var it = index.get (s).iterator ();+ for (var has_next = it.first (); has_next; has_next = it.next ()) {+ iv = it.get ();+ if (iv != null) {+ i = (string) iv;+ c = Font.to_unichar ("U+" + i.down ());+ ucd_result.add_single (c);+ }+ }+ + if (ucd_result.get_length () > 0) {+ ucd_result.sort ();+ result.parse_ranges (ucd_result.get_all_ranges ());+ }+ + return result; } - public static bool has_descender (unichar c) {- switch (c) {- case 'g': return true;- case 'j': return true;- case 'p': return true;- case 'q': return true;- case 'y': return true;+ private void add_entry (string data) {+ string[] e;+ string[] r;+ string[] d;+ string index_values;+ unichar ch;+ + string unicode_hex;+ + if (data.has_prefix ("@")) { // ignore comments+ return; } - return false; + index_values = data.down ();+ index_values = index_values.replace ("\tx", "");+ index_values = index_values.replace ("\t*", "");+ index_values = index_values.replace ("\t=", "");+ index_values = index_values.replace ("\t#", "");+ index_values = index_values.replace (" - ", " ");+ index_values = index_values.replace ("(", "");+ index_values = index_values.replace (")", "");+ index_values = index_values.replace ("<font>", "");+ index_values = index_values.replace (" a ", " ");+ index_values = index_values.replace (" is ", " ");+ index_values = index_values.replace (" the ", " ");+ + e = index_values.split ("\t");++ return_if_fail (e.length > 0);+ + unicode_hex = e[0].up ();+ + ch = Font.to_unichar ("U+" + unicode_hex.down ());+ full_unicode_range.add_single (ch);+ Tool.yield ();++ entries.set (unicode_hex, data);+ + foreach (string s in e) {+ r = s.split ("\n");+ foreach (string t in r) { + d = t.split (" ");+ foreach (string token in d) {+ if (token != "") {+ index.set (token, unicode_hex);+ Tool.yield ();+ }+ }+ }+ }+ + Tool.yield (); }- - public static string get_unicode_database_entry (unichar c) {++ private void parse_all_entries () { FileInputStream fin; DataInputStream din; string? line; string data;- string hex_char; string description = ""; File file;- + file = get_unicode_database ();- hex_char = Font.to_hex (c).replace ("U+", "");- - if (hex_char.char_count () == 2) {- hex_char = "00" + hex_char;- }- - if (hex_char.char_count () == 6 && hex_char.has_prefix ("0")) {- hex_char = hex_char.substring (1);- }- - hex_char += "\t";- hex_char = hex_char.up (); try { fin = file.read (); din = new DataInputStream (fin); - while ((line = din.read_line (null)) != null) {+ line = din.read_line (null);+ while (true) { data = (!) line;- if (data.has_prefix (hex_char)) {- description = data;- - while ((line = din.read_line (null)) != null) {- data = (!) line;- if (data.has_prefix ("\t")) {- description += "\n";- description += data;- } else {- break;+ description = data;+ + while ((line = din.read_line (null)) != null) {+ data = (!) line;+ if (data.has_prefix ("\t")) {+ description += "\n";+ description += data;+ } else {+ if (description.index_of ("<not a character>") == -1) {+ add_entry (description); }+ break; } + Tool.yield ();+ }+ + if (line == null) { break; } }@@ -107,102 +184,82 @@ class CharDatabase { din.close (); } catch (GLib.Error e) { warning (e.message);- stderr.printf ("when reading %s", (!) get_unicode_database ().get_path ());+ warning ("In %s", (!) get_unicode_database ().get_path ()); }+ }++ public static bool has_ascender (unichar c) {+ if (!c.islower()) return true; - if (description == "") {- description = Font.to_hex (c).replace ("U+", "") + "\tUNICODE CHARACTER";+ switch (c) {+ case 'b': return true;+ case 'd': return true;+ case 'f': return true;+ case 'h': return true;+ case 'k': return true;+ case 'l': return true; + }++ return false;+ }++ public static bool has_descender (unichar c) {+ switch (c) {+ case 'g': return true;+ case 'j': return true;+ case 'p': return true;+ case 'q': return true;+ case 'y': return true; } - return description; + return false; } - public static void get_full_unicode (GlyphRange glyph_range) {- File file;- FileInputStream fin;- DataInputStream din;- DataOutputStream os;- string? line;- string data;- string[] range;+ /** Convert from the U+xx form to the unicode database hex value. */ + static string to_database_hex (unichar c) {+ string hex_char = Font.to_hex (c).replace ("U+", "");++ if (hex_char.char_count () == 2) {+ hex_char = "00" + hex_char;+ } - try { - file = BirdFont.get_settings_directory ().get_child ("full_unicode_range");- - if (file.query_exists ()) {- // read cached glyph ranges- fin = file.read ();- din = new DataInputStream (fin);- while ((line = din.read_line (null)) != null) {- data = (!) line;- - if (data == "") {- break;- }- - range = data.split (" - ");- return_if_fail (range.length == 2);- - glyph_range.add_range (Font.to_unichar ("U+" + range[0]), Font.to_unichar ("U+" + range[1]));- }- fin.close ();- din.close (); - } else {- parse_full_unicode_database (glyph_range);- - // write cache- os = new DataOutputStream(file.create (FileCreateFlags.REPLACE_DESTINATION));- os.put_string (glyph_range.get_all_ranges ());- os.close ();- }- } catch (GLib.Error e) {- warning (e.message);+ if (hex_char.char_count () == 6 && hex_char.has_prefix ("0")) {+ hex_char = hex_char.substring (1); }+ + hex_char = hex_char.up (); + return hex_char; } - /** Obtain full unicode range from unicode database. */- public static void parse_full_unicode_database (GlyphRange glyph_range) {- FileInputStream fin;- DataInputStream din;- string? line;- string data;- string hex_char;- File file;- unichar ch;+ public static string get_unicode_database_entry (unichar c) {+ string description;+ string? d; - file = get_unicode_database ();+ d = entries.get (to_database_hex (c)); - if (!file.query_exists ()) {- warning ("Can not find unicode database.");- return;+ if (d == null) {+ description = Font.to_hex (c).replace ("U+", "") + "\tUNICODE CHARACTER";+ } else {+ description = (!) d; }-+ + return description; + }+ + static void show_loading_message () {+ MainWindow.set_status (_("Loading the unicode database") + " ...");+ }+ + public static void get_full_unicode (GlyphRange glyph_range) {+ if (!database_is_loaded) {+ show_loading_message ();+ }+ try {- fin = file.read ();- din = new DataInputStream (fin);- - while ((line = din.read_line (null)) != null) {- data = (!) line;- - if (data.has_prefix ("\t") || data.has_prefix (";") || data.has_prefix ("@")) {- continue;- }- - if (data.index_of ("<not a character>") != -1) {- continue;- }- - hex_char = "U+" + data.substring (0, data.index_of ("\t")).down ();- - ch = Font.to_unichar (hex_char);- glyph_range.add_single (ch);- }- - fin.close ();- din.close ();- } catch (GLib.Error e) {+ glyph_range.parse_ranges (full_unicode_range.get_all_ranges ());+ } catch (MarkupError e) { warning (e.message);- } + } } static File get_unicode_database () {@@ -261,6 +261,7 @@ public class DefaultCharacterSet { CharDatabase.get_full_unicode (gr); if (gr.get_length () == 0) {+ warning ("No items obtained from UCD."); gr.add_range ('\0', (unichar) 0xFFF8); } }@@ -718,7 +718,7 @@ public class Font : GLib.Object { unichar rc = 0; bool r; - if (unlikely (!unicode.has_prefix ("U+"))) {+ if (unlikely (!unicode.has_prefix ("U+") && !unicode.has_prefix ("u+"))) { warning (@"All unicode values must begin with U+ ($unicode)"); return '\0'; }@@ -136,6 +136,8 @@ public class GlyphRange { add_single (' '); } else if (w == "divis") { add_single ('-');+ } else if (w == "null") {+ add_single ('\0'); } else if (w.index_of ("-") > -1) { parse_range (w); } else {@@ -179,7 +181,11 @@ public class GlyphRange { if (c == '-') { return "divis"; }- ++ if (c == '\0') {+ return "null";+ }+ s.append_unichar (c); return s.str; }@@ -192,17 +198,30 @@ public class GlyphRange { if (c == "divis") { return "-"; }++ if (c == "null") {+ return "\0";+ } return c; } private void parse_range (string s) throws MarkupError { string[] r = s.split ("-");+ bool null_range = false; - if (r.length != 2- || unserialize (r[0]).char_count () != 1 - || unserialize (r[1]).char_count () != 1) {- throw new MarkupError.PARSE (@"$s is not a valid range, it should be on the form A-Z.");+ if (r.length == 2 && r[0] == "null" && r[1] == "null") {+ null_range = true;+ } else if (r.length == 2 && r[0] == "null" && unserialize (r[1]).char_count () == 1) {+ null_range = true;+ } + + if (!null_range) {+ if (r.length != 2+ || unserialize (r[0]).char_count () != 1 + || unserialize (r[1]).char_count () != 1) {+ throw new MarkupError.PARSE (@"$s is not a valid range, it should be on the form A-Z.");+ } } append_range (unserialize (r[0]).get_char (), unserialize (r[1]).get_char ());@@ -28,8 +28,8 @@ public class MainWindow { public static OverView over_view; public static TabBar tabs; public static NativeWindow native_window; - public static KerningDisplay kerning_display; + public static CharDatabase character_database; public MainWindow () { singleton = this; @@ -42,6 +42,7 @@ public class MainWindow { tool_tip = new TooltipArea (); over_view = new OverView(); kerning_display = new KerningDisplay (); + character_database = new CharDatabase (); } public static DrawingTools get_drawing_tools () { @@ -60,10 +61,6 @@ public class MainWindow { return glyph_canvas; } - public static OverView get_over_view () { - return over_view; - } - public static Glyph get_current_glyph () { if (unlikely (is_null (BirdFont.current_glyph))) { warning ("No default glyph have been set yet.\n"); @@ -32,9 +32,9 @@ public class OverView : FontDisplay { public signal void open_glyph_signal (GlyphCollection c); GlyphRange glyph_range;+ string search_query = ""; List<OverViewItem> visible_items = new List<OverViewItem> ();- List<GlyphCollection> deleted_glyphs = new List<GlyphCollection> (); bool all_available = true;@@ -51,7 +51,7 @@ public class OverView : FontDisplay { gr = new GlyphRange (); set_glyph_range (gr); }- + reset_zoom (); this.open_glyph_signal.connect ((glyph_collection) => {@@ -104,6 +104,26 @@ public class OverView : FontDisplay { update_scrollbar (); } + public static void search () {+ OverView ow = MainWindow.get_overview ();+ TextListener listener = new TextListener (_("Search"), ow.search_query, _("Filter"));+ + listener.signal_text_input.connect ((text) => {+ OverView o = MainWindow.get_overview ();+ o.search_query = text;+ });+ + listener.signal_submit.connect (() => {+ OverView o = MainWindow.get_overview ();+ GlyphRange r = CharDatabase.search (o.search_query);+ o.set_glyph_range (r);+ MainWindow.native_window.hide_text_input ();+ MainWindow.get_tab_bar ().select_tab_name ("Overview");+ });+ + MainWindow.native_window.set_text_listener (listener);+ }+ public Glyph? get_current_glyph () requires (selected > 0 && selected < visible_items.length ()) { OverViewItem oi = visible_items.nth (selected).data;