16
16
// along with this program. If not, see <http://www.gnu.org/licenses/>.
23
namespace Slingshot.Backend {
25
public class AppSystem : Object {
27
private ArrayList<TreeDirectory> categories = null;
28
private HashMap<string, ArrayList<App>> apps = null;
29
private GMenu.Tree apps_menu = null;
31
private RelevancyService rl_service;
33
public signal void changed ();
34
private bool index_changed = false;
38
rl_service = new RelevancyService ();
39
rl_service.update_complete.connect (update_popularity);
41
apps_menu = GMenu.Tree.lookup ("pantheon-applications.menu", TreeFlags.INCLUDE_EXCLUDED);
42
apps_menu.add_monitor ((menu) => {
44
debug ("Apps menu tree changed. Updating…");
51
apps_menu.set_sort_key (TreeSortKey.DISPLAY_NAME);
19
public class Slingshot.Backend.AppSystem : Object {
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;
25
private RelevancyService rl_service;
27
public signal void changed ();
28
private bool index_changed = false;
32
rl_service = new RelevancyService ();
33
rl_service.update_complete.connect (update_popularity);
35
apps_menu = GMenu.Tree.lookup ("pantheon-applications.menu", GMenu.TreeFlags.INCLUDE_EXCLUDED);
36
apps_menu.add_monitor ((menu) => {
38
debug ("Apps menu tree changed. Updating…");
52
40
update_app_system ();
56
private void update_app_system () {
58
rl_service.refresh_popularity ();
60
update_categories_index ();
65
private void update_categories_index () {
67
var root_tree = apps_menu.get_root_directory ();
69
if (categories == null || index_changed) {
70
categories = new ArrayList<TreeDirectory> ();
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);
81
private void update_popularity () {
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);
88
private void update_apps () {
93
index_changed = false;
98
apps = new HashMap<string, ArrayList<App>> ();
100
foreach (TreeDirectory cat in categories) {
101
apps.set (cat.get_name (), get_apps_by_category (cat));
108
public ArrayList<TreeDirectory> get_categories () {
114
public ArrayList<App> get_apps_by_category (TreeDirectory category) {
116
var app_list = new ArrayList<App> ();
118
foreach (TreeItem item in category.get_contents ()) {
120
switch (item.get_type ()) {
121
case TreeItemType.DIRECTORY:
122
app_list.add_all (get_apps_by_category ((TreeDirectory) item));
124
case TreeItemType.ENTRY:
125
if (is_entry ((TreeEntry) item)) {
126
app = new App ((TreeEntry) item);
127
app.launched.connect (rl_service.app_launched);
137
private bool is_entry (TreeEntry entry) {
139
if (entry.get_launch_in_terminal () == false
140
&& entry.get_is_excluded () == false
141
&& entry.get_is_nodisplay () == false) {
149
public HashMap<string, ArrayList<App>> get_apps () {
155
public SList<App> get_apps_by_popularity () {
157
var sorted_apps = new SList<App> ();
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);
169
public SList<App> get_apps_by_name () {
171
var sorted_apps = new SList<App> ();
172
string[] sorted_apps_execs = {};
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;
45
apps_menu.set_sort_key (GMenu.TreeSortKey.DISPLAY_NAME);
50
private void update_app_system () {
52
rl_service.refresh_popularity ();
54
update_categories_index ();
59
private void update_categories_index () {
61
var root_tree = apps_menu.get_root_directory ();
63
if (categories == null || index_changed) {
64
categories = new Gee.ArrayList<GMenu.TreeDirectory> ();
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);
75
private void update_popularity () {
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);
82
private void update_apps () {
87
index_changed = false;
92
apps = new Gee.HashMap<string, Gee.ArrayList<App>> ();
94
foreach (GMenu.TreeDirectory cat in categories) {
95
apps.set (cat.get_name (), get_apps_by_category (cat));
102
public Gee.ArrayList<GMenu.TreeDirectory> get_categories () {
108
public Gee.ArrayList<App> get_apps_by_category (GMenu.TreeDirectory category) {
110
var app_list = new Gee.ArrayList<App> ();
112
foreach (GMenu.TreeItem item in category.get_contents ()) {
114
switch (item.get_type ()) {
115
case GMenu.TreeItemType.DIRECTORY:
116
app_list.add_all (get_apps_by_category ((GMenu.TreeDirectory) item));
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);
131
private bool is_entry (GMenu.TreeEntry entry) {
133
if (entry.get_launch_in_terminal () == false
134
&& entry.get_is_excluded () == false
135
&& entry.get_is_nodisplay () == false) {
143
public Gee.HashMap<string, Gee.ArrayList<App>> get_apps () {
149
public SList<App> get_apps_by_popularity () {
151
var sorted_apps = new SList<App> ();
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);
163
public SList<App> get_apps_by_name () {
165
var sorted_apps = new SList<App> ();
166
string[] sorted_apps_execs = {};
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;
187
public async ArrayList<App> search_results (string search) {
189
Idle.add (search_results.callback, Priority.HIGH);
192
var filtered = new ArrayList<App> ();
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
201
string[] sorted_apps_execs = {};
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
211
app.relevancy = app.name.length / search.length - app.popularity;
213
} else if (search in app.exec.down ()) {
214
app.relevancy = app.exec.length / search.length * 10.0 - app.popularity;
216
} else if (search in app.description.down ()) {
217
app.relevancy = app.description.length / search.length - app.popularity;
219
} else if (search in app.generic_name.down ()) {
220
app.relevancy = app.generic_name.length / search.length - app.popularity;
222
} else if (app.keywords != null) {
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;
181
public async Gee.ArrayList<App> search_results (string search) {
183
Idle.add (search_results.callback, Priority.HIGH);
186
var filtered = new Gee.ArrayList<App> ();
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
195
string[] sorted_apps_execs = {};
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
205
app.relevancy = app.name.length / search.length - app.popularity;
207
} else if (search in app.exec.down ()) {
208
app.relevancy = app.exec.length / search.length * 10.0 - app.popularity;
210
} else if (search in app.description.down ()) {
211
app.relevancy = app.description.length / search.length - app.popularity;
213
} else if (search in app.generic_name.down ()) {
214
app.relevancy = app.generic_name.length / search.length - app.popularity;
216
} else if (app.keywords != null) {
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;
237
filtered.sort ((a, b) => Utils.sort_apps_by_relevancy ((App) a, (App) b));
239
if (filtered.size > 20) {
240
return (ArrayList<App>) filtered[0:20];
231
filtered.sort ((a, b) => Utils.sort_apps_by_relevancy ((App) a, (App) b));
233
if (filtered.size > 20) {
234
return (Gee.ArrayList<App>) filtered[0:20];