~voldyman/slingshot/fixes-1263999

« back to all changes in this revision

Viewing changes to src/Backend/AppSystem.vala

  • Committer: RabbitBot
  • Author(s): Corentin Noël
  • Date: 2014-01-14 13:05:03 UTC
  • mfrom: (396.1.1 slingshot)
  • Revision ID: rabbitbot-20140114130503-vl01yvhtdlu0dvs4
* Removed every using statement
* Changed VWidgets and HWidgets to Widgets with the corresponding orientation (Gtk deprecation)
* Ported Zeitgeist parts to version 2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
17
//
18
18
 
19
 
using GLib;
20
 
using GMenu;
21
 
using Gee;
22
 
 
23
 
namespace Slingshot.Backend {
24
 
 
25
 
    public class AppSystem : Object {
26
 
 
27
 
        private ArrayList<TreeDirectory> categories = null;
28
 
        private HashMap<string, ArrayList<App>> apps = null;
29
 
        private GMenu.Tree apps_menu = null;
30
 
 
31
 
        private RelevancyService rl_service;
32
 
 
33
 
        public signal void changed ();
34
 
        private bool index_changed = false;
35
 
 
36
 
        construct {
37
 
 
38
 
            rl_service = new RelevancyService ();
39
 
            rl_service.update_complete.connect (update_popularity);
40
 
 
41
 
            apps_menu = GMenu.Tree.lookup ("pantheon-applications.menu", TreeFlags.INCLUDE_EXCLUDED);
42
 
            apps_menu.add_monitor ((menu) => {
43
 
 
44
 
                debug ("Apps menu tree changed. Updating…");
45
 
                index_changed = true;
46
 
                update_app_system ();
47
 
                changed ();
48
 
 
49
 
            });
50
 
 
51
 
            apps_menu.set_sort_key (TreeSortKey.DISPLAY_NAME);
 
19
public class Slingshot.Backend.AppSystem : Object {
 
20
 
 
21
    private Gee.ArrayList<GMenu.TreeDirectory> categories = null;
 
22
    private Gee.HashMap<string, Gee.ArrayList<App>> apps = null;
 
23
    private GMenu.Tree apps_menu = null;
 
24
 
 
25
    private RelevancyService rl_service;
 
26
 
 
27
    public signal void changed ();
 
28
    private bool index_changed = false;
 
29
 
 
30
    construct {
 
31
 
 
32
        rl_service = new RelevancyService ();
 
33
        rl_service.update_complete.connect (update_popularity);
 
34
 
 
35
        apps_menu = GMenu.Tree.lookup ("pantheon-applications.menu", GMenu.TreeFlags.INCLUDE_EXCLUDED);
 
36
        apps_menu.add_monitor ((menu) => {
 
37
 
 
38
            debug ("Apps menu tree changed. Updating…");
 
39
            index_changed = true;
52
40
            update_app_system ();
53
 
 
54
 
        }
55
 
 
56
 
        private void update_app_system () {
57
 
 
58
 
            rl_service.refresh_popularity ();
59
 
 
60
 
            update_categories_index ();
61
 
            update_apps ();
62
 
 
63
 
        }
64
 
 
65
 
        private void update_categories_index () {
66
 
 
67
 
            var root_tree = apps_menu.get_root_directory ();
68
 
 
69
 
            if (categories == null || index_changed) {
70
 
                categories = new ArrayList<TreeDirectory> ();
71
 
 
72
 
                foreach (TreeItem item in root_tree.get_contents ()) {
73
 
                    if (item.get_type () == TreeItemType.DIRECTORY)
74
 
                        if (((TreeDirectory) item).get_is_nodisplay () == false)
75
 
                            categories.add ((TreeDirectory) item);
76
 
                }
77
 
            }
78
 
 
79
 
        }
80
 
 
81
 
        private void update_popularity () {
82
 
 
83
 
            foreach (ArrayList<App> category in apps.values)
84
 
                foreach (App app in category)
85
 
                    app.popularity = rl_service.get_app_popularity (app.desktop_id);
86
 
        }
87
 
 
88
 
        private void update_apps () {
89
 
 
90
 
            if (index_changed) {
91
 
                apps.clear ();
92
 
                apps = null;
93
 
                index_changed = false;
94
 
            }
95
 
 
96
 
            if (apps == null) {
97
 
 
98
 
                apps = new HashMap<string, ArrayList<App>> ();
99
 
 
100
 
                foreach (TreeDirectory cat in categories) {
101
 
                    apps.set (cat.get_name (), get_apps_by_category (cat));
102
 
                }
103
 
 
104
 
            }
105
 
 
106
 
        }
107
 
 
108
 
        public ArrayList<TreeDirectory> get_categories () {
109
 
 
110
 
            return categories;
111
 
 
112
 
        }
113
 
 
114
 
        public ArrayList<App> get_apps_by_category (TreeDirectory category) {
115
 
 
116
 
            var app_list = new ArrayList<App> ();
117
 
 
118
 
            foreach (TreeItem item in category.get_contents ()) {
119
 
                App app;
120
 
                switch (item.get_type ()) {
121
 
                    case TreeItemType.DIRECTORY:
122
 
                        app_list.add_all (get_apps_by_category ((TreeDirectory) item));
123
 
                        break;
124
 
                    case TreeItemType.ENTRY:
125
 
                        if (is_entry ((TreeEntry) item)) {
126
 
                            app = new App ((TreeEntry) item);
127
 
                            app.launched.connect (rl_service.app_launched);
128
 
                            app_list.add (app);
129
 
                        }
130
 
                        break;
131
 
                }
132
 
            }
133
 
            return app_list;
134
 
 
135
 
        }
136
 
 
137
 
        private bool is_entry (TreeEntry entry) {
138
 
 
139
 
            if (entry.get_launch_in_terminal () == false
140
 
                && entry.get_is_excluded () == false
141
 
                && entry.get_is_nodisplay () == false) {
142
 
                return true;
143
 
            } else {
144
 
                return false;
145
 
            }
146
 
 
147
 
        }
148
 
 
149
 
        public HashMap<string, ArrayList<App>> get_apps () {
150
 
 
151
 
            return apps;
152
 
 
153
 
        }
154
 
 
155
 
        public SList<App> get_apps_by_popularity () {
156
 
 
157
 
            var sorted_apps = new SList<App> ();
158
 
 
159
 
            foreach (ArrayList<App> category in apps.values) {
160
 
                foreach (App app in category) {
161
 
                    sorted_apps.insert_sorted_with_data (app, Utils.sort_apps_by_popularity);
162
 
                }
163
 
            }
164
 
 
165
 
            return sorted_apps;
166
 
 
167
 
        }
168
 
 
169
 
        public SList<App> get_apps_by_name () {
170
 
 
171
 
            var sorted_apps = new SList<App> ();
172
 
            string[] sorted_apps_execs = {};
173
 
 
174
 
            foreach (ArrayList<App> category in apps.values) {
175
 
                foreach (App app in category) {
176
 
                    if (!(app.exec in sorted_apps_execs)) {
177
 
                        sorted_apps.insert_sorted_with_data (app, Utils.sort_apps_by_name);
178
 
                        sorted_apps_execs += app.exec;
 
41
            changed ();
 
42
 
 
43
        });
 
44
 
 
45
        apps_menu.set_sort_key (GMenu.TreeSortKey.DISPLAY_NAME);
 
46
        update_app_system ();
 
47
 
 
48
    }
 
49
 
 
50
    private void update_app_system () {
 
51
 
 
52
        rl_service.refresh_popularity ();
 
53
 
 
54
        update_categories_index ();
 
55
        update_apps ();
 
56
 
 
57
    }
 
58
 
 
59
    private void update_categories_index () {
 
60
 
 
61
        var root_tree = apps_menu.get_root_directory ();
 
62
 
 
63
        if (categories == null || index_changed) {
 
64
            categories = new Gee.ArrayList<GMenu.TreeDirectory> ();
 
65
 
 
66
            foreach (GMenu.TreeItem item in root_tree.get_contents ()) {
 
67
                if (item.get_type () == GMenu.TreeItemType.DIRECTORY)
 
68
                    if (((GMenu.TreeDirectory) item).get_is_nodisplay () == false)
 
69
                        categories.add ((GMenu.TreeDirectory) item);
 
70
            }
 
71
        }
 
72
 
 
73
    }
 
74
 
 
75
    private void update_popularity () {
 
76
 
 
77
        foreach (Gee.ArrayList<App> category in apps.values)
 
78
            foreach (App app in category)
 
79
                app.popularity = rl_service.get_app_popularity (app.desktop_id);
 
80
    }
 
81
 
 
82
    private void update_apps () {
 
83
 
 
84
        if (index_changed) {
 
85
            apps.clear ();
 
86
            apps = null;
 
87
            index_changed = false;
 
88
        }
 
89
 
 
90
        if (apps == null) {
 
91
 
 
92
            apps = new Gee.HashMap<string, Gee.ArrayList<App>> ();
 
93
 
 
94
            foreach (GMenu.TreeDirectory cat in categories) {
 
95
                apps.set (cat.get_name (), get_apps_by_category (cat));
 
96
            }
 
97
 
 
98
        }
 
99
 
 
100
    }
 
101
 
 
102
    public Gee.ArrayList<GMenu.TreeDirectory> get_categories () {
 
103
 
 
104
        return categories;
 
105
 
 
106
    }
 
107
 
 
108
    public Gee.ArrayList<App> get_apps_by_category (GMenu.TreeDirectory category) {
 
109
 
 
110
        var app_list = new Gee.ArrayList<App> ();
 
111
 
 
112
        foreach (GMenu.TreeItem item in category.get_contents ()) {
 
113
            App app;
 
114
            switch (item.get_type ()) {
 
115
                case GMenu.TreeItemType.DIRECTORY:
 
116
                    app_list.add_all (get_apps_by_category ((GMenu.TreeDirectory) item));
 
117
                    break;
 
118
                case GMenu.TreeItemType.ENTRY:
 
119
                    if (is_entry ((GMenu.TreeEntry) item)) {
 
120
                        app = new App ((GMenu.TreeEntry) item);
 
121
                        app.launched.connect (rl_service.app_launched);
 
122
                        app_list.add (app);
179
123
                    }
 
124
                    break;
 
125
            }
 
126
        }
 
127
        return app_list;
 
128
 
 
129
    }
 
130
 
 
131
    private bool is_entry (GMenu.TreeEntry entry) {
 
132
 
 
133
        if (entry.get_launch_in_terminal () == false
 
134
            && entry.get_is_excluded () == false
 
135
            && entry.get_is_nodisplay () == false) {
 
136
            return true;
 
137
        } else {
 
138
            return false;
 
139
        }
 
140
 
 
141
    }
 
142
 
 
143
    public Gee.HashMap<string, Gee.ArrayList<App>> get_apps () {
 
144
 
 
145
        return apps;
 
146
 
 
147
    }
 
148
 
 
149
    public SList<App> get_apps_by_popularity () {
 
150
 
 
151
        var sorted_apps = new SList<App> ();
 
152
 
 
153
        foreach (Gee.ArrayList<App> category in apps.values) {
 
154
            foreach (App app in category) {
 
155
                sorted_apps.insert_sorted_with_data (app, Utils.sort_apps_by_popularity);
 
156
            }
 
157
        }
 
158
 
 
159
        return sorted_apps;
 
160
 
 
161
    }
 
162
 
 
163
    public SList<App> get_apps_by_name () {
 
164
 
 
165
        var sorted_apps = new SList<App> ();
 
166
        string[] sorted_apps_execs = {};
 
167
 
 
168
        foreach (Gee.ArrayList<App> category in apps.values) {
 
169
            foreach (App app in category) {
 
170
                if (!(app.exec in sorted_apps_execs)) {
 
171
                    sorted_apps.insert_sorted_with_data (app, Utils.sort_apps_by_name);
 
172
                    sorted_apps_execs += app.exec;
180
173
                }
181
174
            }
182
 
 
183
 
            return sorted_apps;
184
 
 
185
175
        }
186
176
 
187
 
        public async ArrayList<App> search_results (string search) {
188
 
 
189
 
            Idle.add (search_results.callback, Priority.HIGH);
190
 
            yield;
191
 
 
192
 
            var filtered = new ArrayList<App> ();
193
 
 
194
 
            /** It's a bit stupid algorithm, simply check if the char is present
195
 
             * some of the App values, then assign it a double. This is very simple:
196
 
             * if an App name coincide with the search string they have obvious the
197
 
             * same length, then the fraction will be 1.0.
198
 
             * I've added a small multiplier when matching to a exec name, to give
199
 
             * more priority to app.name
200
 
            **/
201
 
            string[] sorted_apps_execs = {};
202
 
 
203
 
            foreach (ArrayList<App> category in apps.values) {
204
 
                foreach (App app in category) {
205
 
                    if (!(app.exec in sorted_apps_execs)) {
206
 
                        sorted_apps_execs += app.exec;
207
 
                        if (search in app.name.down ()) {
208
 
                            if (search == app.name.down ()[0:search.length])
209
 
                                app.relevancy = 0.5 - app.popularity; // It must be minor than 1.0
210
 
                            else
211
 
                                app.relevancy = app.name.length / search.length - app.popularity;
212
 
                            filtered.add (app);
213
 
                        } else if (search in app.exec.down ()) {
214
 
                            app.relevancy = app.exec.length / search.length * 10.0 - app.popularity;
215
 
                            filtered.add (app);
216
 
                        } else if (search in app.description.down ()) {
217
 
                            app.relevancy = app.description.length / search.length - app.popularity;
218
 
                            filtered.add (app);
219
 
                        } else if (search in app.generic_name.down ()) {
220
 
                            app.relevancy = app.generic_name.length / search.length - app.popularity;
221
 
                            filtered.add (app);
222
 
                        }  else if (app.keywords != null) {
223
 
                            app.relevancy = 0;
224
 
                            foreach (string keyword in app.keywords) {
225
 
                                foreach (string search_word in search.split (" ")) {
226
 
                                    if (search_word in keyword.down ()) {
227
 
                                        app.relevancy += (keyword.length / search_word.length) * (app.keywords.length / search.split (" ").length) - app.popularity;
228
 
                                        filtered.add (app);
229
 
                                    }
 
177
        return sorted_apps;
 
178
 
 
179
    }
 
180
 
 
181
    public async Gee.ArrayList<App> search_results (string search) {
 
182
 
 
183
        Idle.add (search_results.callback, Priority.HIGH);
 
184
        yield;
 
185
 
 
186
        var filtered = new Gee.ArrayList<App> ();
 
187
 
 
188
        /** It's a bit stupid algorithm, simply check if the char is present
 
189
         * some of the App values, then assign it a double. This is very simple:
 
190
         * if an App name coincide with the search string they have obvious the
 
191
         * same length, then the fraction will be 1.0.
 
192
         * I've added a small multiplier when matching to a exec name, to give
 
193
         * more priority to app.name
 
194
        **/
 
195
        string[] sorted_apps_execs = {};
 
196
 
 
197
        foreach (Gee.ArrayList<App> category in apps.values) {
 
198
            foreach (App app in category) {
 
199
                if (!(app.exec in sorted_apps_execs)) {
 
200
                    sorted_apps_execs += app.exec;
 
201
                    if (search in app.name.down ()) {
 
202
                        if (search == app.name.down ()[0:search.length])
 
203
                            app.relevancy = 0.5 - app.popularity; // It must be minor than 1.0
 
204
                        else
 
205
                            app.relevancy = app.name.length / search.length - app.popularity;
 
206
                        filtered.add (app);
 
207
                    } else if (search in app.exec.down ()) {
 
208
                        app.relevancy = app.exec.length / search.length * 10.0 - app.popularity;
 
209
                        filtered.add (app);
 
210
                    } else if (search in app.description.down ()) {
 
211
                        app.relevancy = app.description.length / search.length - app.popularity;
 
212
                        filtered.add (app);
 
213
                    } else if (search in app.generic_name.down ()) {
 
214
                        app.relevancy = app.generic_name.length / search.length - app.popularity;
 
215
                        filtered.add (app);
 
216
                    }  else if (app.keywords != null) {
 
217
                        app.relevancy = 0;
 
218
                        foreach (string keyword in app.keywords) {
 
219
                            foreach (string search_word in search.split (" ")) {
 
220
                                if (search_word in keyword.down ()) {
 
221
                                    app.relevancy += (keyword.length / search_word.length) * (app.keywords.length / search.split (" ").length) - app.popularity;
 
222
                                    filtered.add (app);
230
223
                                }
231
224
                            }
232
225
                        }
233
226
                    }
234
227
                }
235
228
            }
236
 
 
237
 
            filtered.sort ((a, b) => Utils.sort_apps_by_relevancy ((App) a, (App) b));
238
 
 
239
 
            if (filtered.size > 20) {
240
 
                return (ArrayList<App>) filtered[0:20];
241
 
            } else {
242
 
                return filtered;
243
 
            }
244
 
 
 
229
        }
 
230
 
 
231
        filtered.sort ((a, b) => Utils.sort_apps_by_relevancy ((App) a, (App) b));
 
232
 
 
233
        if (filtered.size > 20) {
 
234
            return (Gee.ArrayList<App>) filtered[0:20];
 
235
        } else {
 
236
            return filtered;
245
237
        }
246
238
 
247
239
    }