[latexila/symbols] Most used symbols: refactoring



commit bd42ce7f4ad09ab39b46687a896d32a4dd1e2b8e
Author: SÃbastien Wilmet <swilmet src gnome org>
Date:   Fri May 4 00:58:10 2012 +0200

    Most used symbols: refactoring
    
    The ListStore is sorted in descending order (with the column containing
    the number of times the symbol is used). And then a TreeModelFilter is
    used to take only the N first symbols, depending on the preferences.
    
    In the XML, only two things are saved now for each symbol: the id, and
    the number of times the symbol has been used. Before, the latex command
    and the package were also saved, but it will be cleaner to take these
    information from the other categories (this is not already done). If for
    example the information is modified (e.g. because the latex command is
    not correct, or the package required was missing, ...), it will get the
    right information instead of keeping the old data in the XML.

 src/app_settings.vala      |   15 ---
 src/latexila.vala          |    2 +-
 src/most_used_symbols.vala |  281 +++++++++++++++++++++++---------------------
 src/symbols.vala           |   91 ++++++---------
 src/symbols_view.vala      |   12 +-
 5 files changed, 188 insertions(+), 213 deletions(-)
---
diff --git a/src/app_settings.vala b/src/app_settings.vala
index 9efd9b3..365d62f 100644
--- a/src/app_settings.vala
+++ b/src/app_settings.vala
@@ -23,7 +23,6 @@ public class AppSettings : GLib.Settings
 
     private Settings editor;
     private Settings desktop_interface;
-    private uint timeout_id = 0;
 
     public string system_font { get; private set; }
 
@@ -147,20 +146,6 @@ public class AppSettings : GLib.Settings
             foreach (Document doc in Latexila.get_instance ().get_documents ())
                 doc.tab.auto_save_interval = val;
         });
-
-        /*
-        editor.changed["nb-most-used-symbols"].connect ((setting, key) =>
-        {
-            if (timeout_id != 0)
-                Source.remove (timeout_id);
-            timeout_id = Timeout.add_seconds (1, () =>
-            {
-                timeout_id = 0;
-                Symbols.reload_most_used_symbols ();
-                return false;
-            });
-        });
-        */
     }
 
     private void set_font (string font)
diff --git a/src/latexila.vala b/src/latexila.vala
index 31178a6..41626af 100644
--- a/src/latexila.vala
+++ b/src/latexila.vala
@@ -48,7 +48,7 @@ public class Latexila : Gtk.Application
             hold ();
             Projects.get_default ().save ();
             BuildTools.get_default ().save ();
-//            MostUsedSymbols.get_default ().save ();
+            MostUsedSymbols.get_default ().save ();
             release ();
         });
 
diff --git a/src/most_used_symbols.vala b/src/most_used_symbols.vala
index cf30056..0dfb058 100644
--- a/src/most_used_symbols.vala
+++ b/src/most_used_symbols.vala
@@ -1,7 +1,7 @@
 /*
  * This file is part of LaTeXila.
  *
- * Copyright  2010-2011 SÃbastien Wilmet
+ * Copyright  2010-2012 SÃbastien Wilmet
  *
  * LaTeXila is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,159 +17,162 @@
  * along with LaTeXila.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-using Gee;
-
-public struct MostUsedSymbol
-{
-    public string id;
-    public string latex_command;
-    public string package_required;
-    public uint num;
-}
+using Gtk;
 
 public class MostUsedSymbols : GLib.Object
 {
-    private static MostUsedSymbols instance = null;
+    private static MostUsedSymbols _instance = null;
+    private GLib.Settings _settings;
+    private bool _modified = false;
 
-    private LinkedList<MostUsedSymbol?> most_used_symbols;
-    private bool modified = false;
-    private GLib.Settings settings;
+    private ListStore _store;
+    private TreeModelFilter _model_filter;
 
     private MostUsedSymbols ()
     {
-        most_used_symbols = new LinkedList<MostUsedSymbol?> ();
-        settings = new GLib.Settings ("org.gnome.latexila.preferences.editor");
-
-        /* load most used symbols from the XML file */
-        File file = get_xml_file ();
-        if (! file.query_exists ())
-            return;
+        _settings = new GLib.Settings ("org.gnome.latexila.preferences.editor");
 
-        string? contents = Utils.load_file (file);
-        if (contents == null)
-            return;
+        init_models ();
+        load_data ();
 
-        try
+        _settings.changed["nb-most-used-symbols"].connect (() =>
         {
-            MarkupParser parser = { parser_start, null, null, null, null };
-            MarkupParseContext context = new MarkupParseContext (parser, 0, this, null);
-            context.parse (contents, -1);
-        }
-        catch (GLib.Error e)
-        {
-            warning ("Impossible to load the most used symbols: %s", e.message);
-        }
+            _model_filter.refilter ();
+        });
     }
 
     // singleton
     public static MostUsedSymbols get_default ()
     {
-        if (instance == null)
-            instance = new MostUsedSymbols ();
-        return instance;
+        if (_instance == null)
+            _instance = new MostUsedSymbols ();
+
+        return _instance;
     }
 
-    public Iterator<MostUsedSymbol?> iterator ()
+    private void init_models ()
     {
-        uint max;
-        settings.get ("nb-most-used-symbols", "u", out max);
+        // There is one more column, to store the number of times the symbol
+        // has been used.
+        _store = new ListStore (SymbolColumn.N_COLUMNS + 1,
+            typeof (Gdk.Pixbuf),
+            typeof (string), // command
+            typeof (string), // tooltip
+            typeof (string), // id
+            typeof (int)     // number of times used
+        );
+
+        _store.set_sort_column_id (SymbolColumn.N_COLUMNS, SortType.DESCENDING);
+
+        _model_filter = new TreeModelFilter (_store, null);
+        _model_filter.set_visible_func ((model, iter) =>
+        {
+            TreePath? path = _store.get_path (iter);
+            if (path == null)
+                return false;
+
+            int pos = path.get_indices ()[0];
+
+            uint max;
+            _settings.get ("nb-most-used-symbols", "u", out max);
 
-        int slice_max = int.min ((int) max, most_used_symbols.size);
-        var slice = most_used_symbols.slice (0, slice_max);
+            return pos < max;
+        });
+    }
 
-        return (Iterator<MostUsedSymbol?>) slice.iterator ();
+    public TreeModel get_model ()
+    {
+        return _model_filter as TreeModel;
     }
 
     public void clear ()
     {
-        modified = true;
-        most_used_symbols.clear ();
+        _store.clear ();
     }
 
-    public void add_symbol (string id, string command, string? package)
+    public void increment_symbol (string id)
     {
-        modified = true;
-        uint max;
-        settings.get ("nb-most-used-symbols", "u", out max);
+        TreeIter iter;
 
-        int i = 0;
-        foreach (MostUsedSymbol mus in most_used_symbols)
+        if (! get_iter_at_symbol_id (id, out iter))
+            add_symbol (id, 1);
+        else
         {
-            if (mus.id == id)
-            {
-                mus.num++;
-                // keep the list sorted
-                int new_i = sort (i, mus);
-                if (new_i != i && new_i < max)
-                {
-                    if (i >= max)
-                    {
-                        Symbols.remove_most_used_symbol ((int) max - 1);
-                        Symbols.insert_most_used_symbol (new_i, mus);
-                    }
-                    else
-                        Symbols.swap_most_used_symbol (i, new_i);
-                }
-                return;
-            }
-            i++;
-        }
+            int num;
+            TreeModel model = _store as TreeModel;
+            model.get (iter, SymbolColumn.N_COLUMNS, out num);
 
-        // not found, insert the new symbol
-        MostUsedSymbol new_symbol = MostUsedSymbol ();
-        new_symbol.id = id;
-        new_symbol.latex_command = command;
-        new_symbol.package_required = package;
-        new_symbol.num = 1;
-
-        most_used_symbols.add (new_symbol);
+            _store.set (iter, SymbolColumn.N_COLUMNS, num + 1);
+        }
 
-        if (most_used_symbols.size <= max)
-            Symbols.insert_most_used_symbol (most_used_symbols.size - 1, new_symbol);
+        _modified = true;
     }
 
-    private int sort (int index, MostUsedSymbol mus)
+    private bool get_iter_at_symbol_id (string id, out TreeIter iter)
     {
-        if (index == 0)
-        {
-            most_used_symbols[index] = mus;
-            return 0;
-        }
+        if (! _store.get_iter_first (out iter))
+            return false;
 
-        int new_index;
-        for (new_index = index - 1 ; new_index >= 0 ; new_index--)
+        do
         {
-            MostUsedSymbol symbol = most_used_symbols[new_index];
-            if (symbol.num >= mus.num)
-            {
-                new_index++;
-                break;
-            }
+            string cur_id;
+            TreeModel model = _store as TreeModel;
+            model.get (iter, SymbolColumn.ID, out cur_id);
+
+            if (cur_id == id)
+                return true;
         }
+        while (_store.iter_next (ref iter));
 
-        // if the for loop didn't break
-        if (new_index < 0)
-            new_index = 0;
+        return false;
+    }
 
-        if (new_index < index)
-        {
-            most_used_symbols.remove_at (index);
-            most_used_symbols.insert (new_index, mus);
-        }
-        else
-            most_used_symbols[index] = mus;
+    private void add_symbol (string id, int nb_times_used)
+    {
+        Gdk.Pixbuf? pixbuf = Symbols.get_pixbuf (id);
+        if (pixbuf == null)
+            return;
 
-        return new_index;
+        TreeIter iter;
+        _store.append (out iter);
+        _store.set (iter,
+            SymbolColumn.PIXBUF, pixbuf,
+            SymbolColumn.COMMAND, "",
+            SymbolColumn.TOOLTIP, "",
+            SymbolColumn.ID, id,
+            SymbolColumn.N_COLUMNS, nb_times_used
+        );
     }
 
-    /*
-    private void print_summary ()
+    private File get_xml_file ()
     {
-        stdout.printf ("\n=== Most Used Symbols ===\n");
-        foreach (MostUsedSymbol symbol in most_used_symbols)
-            stdout.printf ("%s (%s) - %u\n", symbol.id, symbol.latex_command, symbol.num);
+        string path = Path.build_filename (Environment.get_user_data_dir (),
+            "latexila", "most_used_symbols.xml");
+
+        return File.new_for_path (path);
+    }
+
+    private void load_data ()
+    {
+        File file = get_xml_file ();
+        if (! file.query_exists ())
+            return;
+
+        string? contents = Utils.load_file (file);
+        if (contents == null)
+            return;
+
+        try
+        {
+            MarkupParser parser = { parser_start, null, null, null, null };
+            MarkupParseContext context = new MarkupParseContext (parser, 0, this, null);
+            context.parse (contents, -1);
+        }
+        catch (GLib.Error e)
+        {
+            warning ("Impossible to load the most used symbols: %s", e.message);
+        }
     }
-    */
 
     private void parser_start (MarkupParseContext context, string name,
         string[] attr_names, string[] attr_values) throws MarkupError
@@ -180,30 +183,33 @@ public class MostUsedSymbols : GLib.Object
                 return;
 
             case "symbol":
-                MostUsedSymbol symbol = MostUsedSymbol ();
+                string id = null;
+                int num = 0;
+
                 for (int i = 0 ; i < attr_names.length ; i++)
                 {
                     switch (attr_names[i])
                     {
                         case "id":
-                            symbol.id = attr_values[i];
+                            id = attr_values[i];
                             break;
-                        case "command":
-                            symbol.latex_command = attr_values[i];
+
+                        case "num":
+                            num = int.parse (attr_values[i]);
                             break;
+
+                        case "command":
                         case "package":
-                            symbol.package_required =
-                                attr_values[i] != "" ? attr_values[i] : null;
-                            break;
-                        case "num":
-                            symbol.num = (uint) int.parse (attr_values[i]);
+                            // Used in the past but no longer required.
                             break;
+
                         default:
                             throw new MarkupError.UNKNOWN_ATTRIBUTE (
                                 "unknown attribute \"" + attr_names[i] + "\"");
                     }
                 }
-                most_used_symbols.add (symbol);
+
+                add_symbol (id, num);
                 break;
 
             default:
@@ -212,34 +218,41 @@ public class MostUsedSymbols : GLib.Object
         }
     }
 
-    private File get_xml_file ()
-    {
-        string path = Path.build_filename (Environment.get_user_data_dir (),
-            "latexila", "most_used_symbols.xml", null);
-        return File.new_for_path (path);
-    }
-
     public void save ()
     {
-        if (! modified)
+        if (! _modified)
             return;
 
+        _modified = false;
+
         File file = get_xml_file ();
 
-        // if empty, delete the file
-        if (most_used_symbols.size == 0)
+        TreeIter iter;
+        bool is_empty = ! _store.get_iter_first (out iter);
+
+        if (is_empty)
         {
             Utils.delete_file (file);
             return;
         }
 
         string content = "<symbols>\n";
-        foreach (MostUsedSymbol symbol in most_used_symbols)
+
+        do
         {
-            content += "  <symbol id=\"%s\" command=\"%s\" package=\"%s\" num=\"%u\" />\n".printf (
-                symbol.id, symbol.latex_command, symbol.package_required ?? "",
-                symbol.num);
+            string id;
+            int num;
+
+            TreeModel model = _store as TreeModel;
+            model.get (iter,
+                SymbolColumn.ID, out id,
+                SymbolColumn.N_COLUMNS, out num
+            );
+
+            content += "  <symbol id=\"%s\" num=\"%d\" />\n".printf (id, num);
         }
+        while (_store.iter_next (ref iter));
+
         content += "</symbols>\n";
 
         Utils.save_file (file, content);
diff --git a/src/symbols.vala b/src/symbols.vala
index c8d4542..a103662 100644
--- a/src/symbols.vala
+++ b/src/symbols.vala
@@ -75,15 +75,13 @@ public class Symbols : GLib.Object
             typeof (SymbolsCategoryType),
             typeof (string), // the icon
             typeof (string), // the name
-            typeof (ListStore)
+            typeof (TreeModel)
         );
 
-        /* Normal categories */
         foreach (CategoryInfo info in _normal_categories)
             add_normal_category (info);
 
-        /* Most used symbols */
-        //add_new_category (Stock.ABOUT, _("Most Used"));
+        add_most_used_category ();
     }
 
     public static Symbols get_default ()
@@ -113,50 +111,44 @@ public class Symbols : GLib.Object
         );
     }
 
-    /* MOST USED SYMBOLS */
-    public static void reload_most_used_symbols ()
+    private void add_most_used_category ()
     {
-        /*
-        _most_used_store.clear ();
+        TreeModel model = MostUsedSymbols.get_default ().get_model ();
 
-        foreach (MostUsedSymbol mus in MostUsedSymbols.get_default ())
-        {
-            var symbol = get_symbol_info_from_most_used (mus);
-            insert_symbol (_most_used_store, -1, symbol);
-        }
-        */
+        TreeIter iter;
+        _categories_store.append (out iter);
+        _categories_store.set (iter,
+            SymbolsCategoryColumn.TYPE, SymbolsCategoryType.MOST_USED,
+            SymbolsCategoryColumn.ICON, Stock.ABOUT,
+            SymbolsCategoryColumn.NAME, _("Most Used"),
+            SymbolsCategoryColumn.SYMBOLS_STORE, model
+        );
     }
 
-    public static void insert_most_used_symbol (int index, MostUsedSymbol symbol)
+    public static string get_tooltip (string latex_command, string? package_required)
     {
-//        insert_symbol (_most_used_store, index, get_symbol_info_from_most_used (symbol));
-    }
+        // Some characters ('<' for example) generate errors for the tooltip,
+        // so the text must be escaped.
+        string tooltip = Markup.escape_text (latex_command);
 
-    public static void remove_most_used_symbol (int index)
-    {
-        /*
-        TreePath path = new TreePath.from_indices (index, -1);
-        TreeIter iter;
-        if (_most_used_store.get_iter (out iter, path))
-            _most_used_store.remove (iter);
-        */
+        if (package_required != null)
+            tooltip += " (package %s)".printf (package_required);
+
+        return tooltip;
     }
 
-    public static void swap_most_used_symbol (int current_index, int new_index)
+    public static Gdk.Pixbuf? get_pixbuf (string symbol_id)
     {
-        /*
-        TreePath current_path = new TreePath.from_indices (current_index, -1);
-        TreePath new_path = new TreePath.from_indices (new_index, -1);
-
-        TreeIter current_iter = {};
-        TreeIter new_iter = {};
-
-        if (_most_used_store.get_iter (out current_iter, current_path)
-            && _most_used_store.get_iter (out new_iter, new_path))
+        try
+        {
+            return Gdk.MyPixbuf.from_resource (
+                "/org/gnome/latexila/symbols/" + symbol_id);
+        }
+        catch (Error e)
         {
-            _most_used_store.move_before (ref current_iter, new_iter);
+            warning ("Impossible to load the symbol '%s': %s", symbol_id, e.message);
+            return null;
         }
-        */
     }
 }
 
@@ -181,7 +173,6 @@ private class NormalSymbols : ListStore
             typeof (Gdk.Pixbuf),
             typeof (string), // command
             typeof (string), // tooltip
-            typeof (string), // package
             typeof (string)  // id
         };
 
@@ -212,27 +203,15 @@ private class NormalSymbols : ListStore
 
     private void add_symbol (SymbolInfo symbol)
     {
-        Gdk.Pixbuf pixbuf;
-
-        try
-        {
-            pixbuf = Gdk.MyPixbuf.from_resource (_resource_path + symbol.icon_file);
-        }
-        catch (Error e)
-        {
-            warning ("Impossible to load the symbol: %s", e.message);
-            return;
-        }
-
-        // Some characters ('<' for example) generate errors for the tooltip,
-        // so the text must be escaped.
-        string tooltip = Markup.escape_text (symbol.latex_command);
-
-        if (symbol.package_required != null)
-            tooltip += " (package %s)".printf (symbol.package_required);
+        string tooltip = Symbols.get_tooltip (symbol.latex_command,
+            symbol.package_required);
 
         string id = "%s/%s".printf (_category_id, symbol.icon_file);
 
+        Gdk.Pixbuf? pixbuf = Symbols.get_pixbuf (id);
+        if (pixbuf == null)
+            return;
+
         TreeIter iter;
         append (out iter);
         set (iter,
diff --git a/src/symbols_view.vala b/src/symbols_view.vala
index b1b9b42..c0a36c9 100644
--- a/src/symbols_view.vala
+++ b/src/symbols_view.vala
@@ -167,8 +167,7 @@ public class SymbolsView : Grid
                 _main_window.active_view.grab_focus ();
 
                 // insert to most used symbol
-//                MostUsedSymbols.get_default ().add_symbol (id, latex_command,
-//                    package != "" ? package : null);
+                MostUsedSymbols.get_default ().increment_symbol (id);
             }
         });
     }
@@ -177,10 +176,9 @@ public class SymbolsView : Grid
     {
         _clear_button = new Button.from_stock (Stock.CLEAR);
 
-//        _clear_button.clicked.connect (() =>
-//        {
-//            _most_used_store.clear ();
-//            MostUsedSymbols.get_default ().clear ();
-//        });
+        _clear_button.clicked.connect (() =>
+        {
+            MostUsedSymbols.get_default ().clear ();
+        });
     }
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]