Merge lp:~jamesh/unity-lens-applications/split-scope into lp:unity-lens-applications
- split-scope
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michal Hruby |
Approved revision: | 380 |
Merged at revision: | 356 |
Proposed branch: | lp:~jamesh/unity-lens-applications/split-scope |
Merge into: | lp:unity-lens-applications |
Diff against target: |
4225 lines (+1769/-1504) 21 files modified
.bzrignore (+33/-40) Makefile.am (+0/-17) configure.ac (+3/-3) data/Makefile.am (+23/-1) data/applications.scope.in.in (+3/-0) data/commands.scope.in.in (+2/-0) data/scopes.scope.in.in (+15/-0) data/unity-scope-applications.service.in (+1/-1) debian/changelog (+6/-0) debian/rules (+1/-0) po/POTFILES.in (+5/-3) po/POTFILES.skip (+2/-2) src/Makefile.am (+25/-28) src/commands-scope.vala (+289/-254) src/daemon.vala (+775/-1046) src/main.vala (+24/-88) src/scopes-scope.vala (+510/-0) src/software-center-data-cache.vala (+3/-3) src/software-center-data-provider.vala (+12/-12) tests/unit/software-center-data-provider-mock.vala (+2/-2) tests/unit/test-software-center-data-cache.vala (+35/-4) |
To merge this branch: | bzr merge lp:~jamesh/unity-lens-applications/split-scope |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michal Hruby (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+175982@code.launchpad.net |
Commit message
Convert the Applications scope over to the Unity.AbstractScope API.
Description of the change
Convert the Applications scope over to the Unity.AbstractScope API.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 368. By James Henstridge
-
Merge from trunk, fixing conflict
James Henstridge (jamesh) wrote : | # |
Note that this branch depends on there being an apps master scope in place, so should probably not be fully approved until we're ready to bring that back. I'd still appreciate review feedback though.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:368
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 369. By James Henstridge
-
Change "-rpath" to "-R", since that is libtool's equivalent flag.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:369
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michal Hruby (mhr3) wrote : | # |
First of all, could we rename the main apps scope to something more appropriate? A scope with ID applications-
1315 + var search_string = context.
AbstractScope has normalize_
1491 + /* XXX:
1492 if (model.get_n_rows () == 0)
Another prop that's not exposed by the new API? We should add it to ResultSet imo, thoughts?
2258 + public override Unity.AbstractP
Isn't this is doing blocking dbus calls to get the preview? Why isn't it async then?
2844 + private class RunnerScope : Unity.AbstractScope
Pls rename to CommandsScope, not sure why we have the disparity in the code and the .scope file.
2940 + // force a search to refresh history order (TODO: be more clever in the future)
2941 + // XXX: No way to queue result update
2942 + //scope.
There is now :)
3541 + public ScopesScope (ApplicationsScope appscope)
2869 + public RunnerScope (ApplicationsScope appscope)
Not liking the circular dependencies, but I suppose it doesn't make much sense to try to get rid of them.
2849 + // The ApplicationsScope holds a reference to us, so avoid a
2850 + // reference cycle.
But, can we at least get rid of this? Seems it's just one method AppsScope uses, can we make that one static?
That's it for the first round... The diff is huge but it's mostly moving stuff around, so it's not too bad.
- 370. By James Henstridge
-
Rename RunnerScope to CommandsScope, to match scope name.
- 371. By James Henstridge
-
Use results_invalidted in CommandsScope too.
- 372. By James Henstridge
-
No need to normalise query in search method.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:372
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 373. By James Henstridge
-
Make software-center D-Bus calls in previewers async.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:373
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 374. By James Henstridge
-
Fix tests to account for changing Software Center code to async.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:374
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michal Hruby (mhr3) wrote : | # |
The installed scope files are not pointing to correct .so location (cause it's using multiarch), therefore the final scope doesn't work.
- 375. By James Henstridge
-
Substitute @libdir@ in the scope files so we correctly handle multi-lib.
- 376. By James Henstridge
-
Bump version number.
- 377. By James Henstridge
-
Don't install the libtool .la file.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:377
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michal Hruby (mhr3) wrote : | # |
There's a couple functional issues:
1) The scope loader daemon is trying to exit after a couple of seconds of inactivity, but that is not desired as the startup of the xapian-and-io-heavy apps scope is very expensive. Also, for some reason it failed to exit and that resulted it apps no longer appearing in the dash.
2) The ordering of the items in the apps scope is now undefined, but according to design scopes should be displayed after installed (or available) apps. But this should be brought up to design, maybe we want a separate category for scopes now, similarly to the phone.
3) Performing a search in the apps lens causes the results to flicker a lot. This will be because finishing a scope search flushes the results, so if the scopes scope finishes very quickly it'll flush empty result set, so the dash will remove everything and a few frames later the apps scope will flush its non-empty result set causing another redraw.
- 378. By Michal Hruby
-
Mark scope results to aid sorting in the master scope.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:378
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 379. By James Henstridge
-
Merge from trunk, fixing conflicts
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:379
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 380. By James Henstridge
-
Disable timeout for the applications scope.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:380
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michal Hruby (mhr3) wrote : | # |
Should be good with the fixes that landed in home scope and libunity.
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2011-08-03 21:52:17 +0000 |
3 | +++ .bzrignore 2013-08-06 08:19:29 +0000 |
4 | @@ -1,8 +1,8 @@ |
5 | - |
6 | -src/.deps |
7 | -tests/.deps |
8 | -src/.libs |
9 | -tests/.libs |
10 | +.deps |
11 | +.libs |
12 | +*.lo |
13 | +*.la |
14 | +ABOUT-NLS |
15 | INSTALL |
16 | Makefile |
17 | Makefile.in |
18 | @@ -13,6 +13,7 @@ |
19 | config.h |
20 | config.h.in |
21 | config.log |
22 | +config.rpath |
23 | config.status |
24 | config.sub |
25 | configure |
26 | @@ -20,33 +21,27 @@ |
27 | install-sh |
28 | libtool |
29 | ltmain.sh |
30 | -m4/ |
31 | missing |
32 | -stamp-h1 |
33 | -data/Makefile |
34 | -data/Makefile.in |
35 | -src/Makefile |
36 | -src/Makefile.in |
37 | -src/main.c |
38 | -src/unity-place-applications |
39 | -src/unity_place_applications.vala.stamp |
40 | -tests/Makefile |
41 | -tests/Makefile.in |
42 | -tests/test-place-applications |
43 | -tests/test-place-applications.c |
44 | -tests/test_place_applications_vala.stamp |
45 | -vapi/Makefile |
46 | -vapi/Makefile.in |
47 | -unity-apps_version.tar.gz |
48 | -tests/place-applications-check-results.xml |
49 | mkinstalldirs |
50 | -data/unity-place-applications.service |
51 | +stamp-* |
52 | +data/*.directory |
53 | +data/*.gschema.valid |
54 | +data/*.gschema.xml |
55 | +data/*.gschema.xml.in |
56 | +data/*.menu |
57 | +data/*.scope |
58 | +data/*.scope.in |
59 | +data/*.service |
60 | +m4/intltool.m4 |
61 | +m4/libtool.m4 |
62 | +m4/ltoptions.m4 |
63 | +m4/ltsugar.m4 |
64 | +m4/ltversion.m4 |
65 | +m4/lt~obsolete.m4 |
66 | +po/*.gmo |
67 | po/POTFILES |
68 | po/stamp-it |
69 | -src/applications.c |
70 | -src/utils.c |
71 | po/Makefile.in.in |
72 | -unity-place-applications.desktop |
73 | po/.intltool-merge-cache |
74 | po/Makevars.template |
75 | po/Rules-quot |
76 | @@ -57,17 +52,15 @@ |
77 | po/remove-potcdate.sin |
78 | po/boldquot.sed |
79 | po/quot.sed |
80 | -ABOUT-NLS |
81 | -config.rpath |
82 | -po/unity-place-applications.pot |
83 | -applications.place |
84 | -applications.place.in |
85 | -data/X-Unity-All-Applications.directory |
86 | -data/unity-place-applications.menu |
87 | -po/da.gmo |
88 | -src/config.c |
89 | +po/unity-lens-applications.pot |
90 | +src/*.c |
91 | src/config.vala |
92 | -src/daemon.c |
93 | -src/schemas.c |
94 | -src/unity-applications-daemon |
95 | -src/unity_applications_daemon.vala.stamp |
96 | +src/unity-scope-applications.vala.stamp |
97 | +tests/unit/*.c |
98 | +tests/unit/*.stamp |
99 | +tests/unit/test-purchase-info-helper |
100 | +tests/unit/test-software-center-data-cache |
101 | +tests/unit/test-software-center-details |
102 | +tests/unit/test-software-center-utils |
103 | +tests/unit/test-utils |
104 | +tests/unit/test-xapian-utils |
105 | |
106 | === modified file 'Makefile.am' |
107 | --- Makefile.am 2013-07-28 10:26:17 +0000 |
108 | +++ Makefile.am 2013-08-06 08:19:29 +0000 |
109 | @@ -1,21 +1,6 @@ |
110 | NULL = |
111 | SUBDIRS = src data po tests/unit |
112 | |
113 | -# |
114 | -# Install the applications.lens file |
115 | -# |
116 | -scope_in_files = applications.scope.in commands.scope.in |
117 | - |
118 | -scopedir = $(datadir)/unity/scopes |
119 | -scope_DATA = commands.scope |
120 | -appscopedir = $(scopedir)/applications |
121 | -appscope_DATA = applications.scope |
122 | - |
123 | -icondir = $(datadir)/unity/themes |
124 | -icon_DATA = applications.png |
125 | - |
126 | -@INTLTOOL_SCOPE_RULE@ |
127 | - |
128 | DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall |
129 | |
130 | # ChangeLog file created at distcheck time |
131 | @@ -34,9 +19,7 @@ |
132 | fi |
133 | |
134 | EXTRA_DIST = \ |
135 | - applications.png \ |
136 | autogen.sh \ |
137 | - $(scope_in_files) \ |
138 | AUTHORS \ |
139 | COPYING \ |
140 | HACKING \ |
141 | |
142 | === modified file 'configure.ac' |
143 | --- configure.ac 2013-05-16 21:13:36 +0000 |
144 | +++ configure.ac 2013-08-06 08:19:29 +0000 |
145 | @@ -1,7 +1,9 @@ |
146 | -AC_INIT(unity-lens-applications, 7.0.0, https://launchpad.net/unity-lens-applications) |
147 | +AC_INIT(unity-lens-applications, 7.1.0, https://launchpad.net/unity-lens-applications) |
148 | AC_COPYRIGHT([Copyright 2010-2012 Canonical]) |
149 | AM_INIT_AUTOMAKE([1.11]) |
150 | |
151 | +LT_INIT |
152 | + |
153 | ############################################# |
154 | # Silent build rules |
155 | ############################################# |
156 | @@ -166,8 +168,6 @@ |
157 | ############################################# |
158 | AC_CONFIG_FILES([ |
159 | Makefile |
160 | - applications.scope.in |
161 | - commands.scope.in |
162 | data/com.canonical.Unity.AppsLens.gschema.xml.in |
163 | data/Makefile |
164 | data/X-Unity-All-Applications.directory |
165 | |
166 | === modified file 'data/Makefile.am' |
167 | --- data/Makefile.am 2013-05-16 11:37:05 +0000 |
168 | +++ data/Makefile.am 2013-08-06 08:19:29 +0000 |
169 | @@ -1,4 +1,22 @@ |
170 | ############################################################# |
171 | +# Unity scope files |
172 | +############################################################# |
173 | +@INTLTOOL_SCOPE_RULE@ |
174 | + |
175 | +%.scope.in: %.scope.in.in |
176 | + sed -e "s|\@prefix\@|$(prefix)|" -e "s|\@libdir\@|$(libdir)|" $< > $@ |
177 | + |
178 | +scope_in_in_files = applications.scope.in.in scopes.scope.in.in commands.scope.in.in |
179 | + |
180 | +scopedir = $(datadir)/unity/scopes |
181 | +scope_DATA = commands.scope |
182 | +appscopedir = $(scopedir)/applications |
183 | +appscope_DATA = applications.scope scopes.scope |
184 | + |
185 | +icondir = $(datadir)/unity/themes |
186 | +icon_DATA = applications.png |
187 | + |
188 | +############################################################# |
189 | # DBus service files |
190 | ############################################################# |
191 | dbus_servicesdir = $(DBUSSERVICEDIR) |
192 | @@ -6,7 +24,7 @@ |
193 | dbus_services_DATA = $(service_in_files:.service.in=.service) |
194 | |
195 | %.service: %.service.in |
196 | - sed -e "s|\@pkglibexecdir\@|$(pkglibexecdir)|" $< > $@ |
197 | + sed -e "s|\@bindir\@|$(bindir)|" $< > $@ |
198 | |
199 | ############################################################# |
200 | # XDG Menu files |
201 | @@ -33,12 +51,16 @@ |
202 | @GSETTINGS_RULES@ |
203 | |
204 | EXTRA_DIST = \ |
205 | + applications.png \ |
206 | + $(scope_in_in_files) \ |
207 | com.canonical.Unity.AppsLens.gschema.xml.in.in \ |
208 | $(service_in_files) \ |
209 | $(directory_in_files) \ |
210 | $(menu_in_files) |
211 | |
212 | CLEANFILES = \ |
213 | + $(scope_DATA) \ |
214 | + $(scope_in_in_files:.scope.in.in=.scope.in) \ |
215 | unity-scope-applications.service \ |
216 | $(gsettings_SCHEMAS) |
217 | |
218 | |
219 | === renamed file 'applications.png' => 'data/applications.png' |
220 | === renamed file 'applications.scope.in.in' => 'data/applications.scope.in.in' |
221 | --- applications.scope.in.in 2013-06-20 13:11:17 +0000 |
222 | +++ data/applications.scope.in.in 2013-08-06 08:19:29 +0000 |
223 | @@ -1,6 +1,9 @@ |
224 | [Scope] |
225 | DBusName=com.canonical.Unity.Scope.Applications |
226 | DBusPath=/com/canonical/unity/scope/applications |
227 | +Module=@libdir@/unity/unity-scope-applications.so |
228 | +ModuleType=C |
229 | +Timeout=-1 |
230 | Icon=@prefix@/share/unity/icons/lens-nav-app.svg |
231 | _Name=Applications |
232 | _Description=This is an Ubuntu search plugin that enables information from local applications to be searched and displayed in the Dash underneath the Applications header. If you do not wish to search this content source, you can disable this search plugin. |
233 | |
234 | === renamed file 'commands.scope.in.in' => 'data/commands.scope.in.in' |
235 | --- commands.scope.in.in 2013-05-16 11:37:05 +0000 |
236 | +++ data/commands.scope.in.in 2013-08-06 08:19:29 +0000 |
237 | @@ -1,6 +1,8 @@ |
238 | [Scope] |
239 | DBusName=com.canonical.Unity.Scope.Applications |
240 | DBusPath=/com/canonical/unity/scope/commands |
241 | +Module=@libdir@/unity/unity-scope-applications.so |
242 | +ModuleType=C |
243 | _Name=Commands |
244 | _Description=This is an Ubuntu search plugin that enables information from local binaries to be searched and displayed in the Dash. If you do not wish to search this content source, you can disable this search plugin. |
245 | Icon= |
246 | |
247 | === added file 'data/scopes.scope.in.in' |
248 | --- data/scopes.scope.in.in 1970-01-01 00:00:00 +0000 |
249 | +++ data/scopes.scope.in.in 2013-08-06 08:19:29 +0000 |
250 | @@ -0,0 +1,15 @@ |
251 | +[Scope] |
252 | +DBusName=com.canonical.Unity.Scope.Applications |
253 | +DBusPath=/com/canonical/unity/scope/scopes |
254 | +Module=@libdir@/unity/unity-scope-applications.so |
255 | +ModuleType=C |
256 | +Icon=@prefix@/share/unity/icons/lens-nav-app.svg |
257 | +_Name=Scopes |
258 | +_Description=This is an Ubuntu search plugin that enables information from available search plugins to be searched and displayed in the Dash underneath the Applications header. If you do not wish to search this content source, you can disable this search plugin. |
259 | +Type=applications |
260 | +_SearchHint=Search search plugins |
261 | +OptionalMetadata=is_scope[u] |
262 | + |
263 | +[Desktop Entry] |
264 | +X-Ubuntu-Gettext-Domain=unity-lens-applications |
265 | + |
266 | |
267 | === modified file 'data/unity-scope-applications.service.in' |
268 | --- data/unity-scope-applications.service.in 2013-05-16 11:37:05 +0000 |
269 | +++ data/unity-scope-applications.service.in 2013-08-06 08:19:29 +0000 |
270 | @@ -1,3 +1,3 @@ |
271 | [D-BUS Service] |
272 | Name=com.canonical.Unity.Scope.Applications |
273 | -Exec=@pkglibexecdir@/unity-applications-daemon |
274 | +Exec=@bindir@/unity-scope-loader applications/applications.scope applications/scopes.scope commands.scope |
275 | |
276 | === modified file 'debian/changelog' |
277 | --- debian/changelog 2013-07-29 04:02:27 +0000 |
278 | +++ debian/changelog 2013-08-06 08:19:29 +0000 |
279 | @@ -1,3 +1,9 @@ |
280 | +unity-lens-applications (7.1.0-0ubuntu1) UNRELEASED; urgency=low |
281 | + |
282 | + * Convert scope to run as a unity-scope-loader plugin. |
283 | + |
284 | + -- James Henstridge <james.henstridge@canonical.com> Fri, 26 Jul 2013 17:09:10 +0800 |
285 | + |
286 | unity-lens-applications (7.0.0+13.10.20130729-0ubuntu1) saucy; urgency=low |
287 | |
288 | [ Michal Hruby ] |
289 | |
290 | === modified file 'debian/rules' |
291 | --- debian/rules 2013-04-12 12:21:04 +0000 |
292 | +++ debian/rules 2013-08-06 08:19:29 +0000 |
293 | @@ -10,3 +10,4 @@ |
294 | |
295 | override_dh_install: |
296 | dh_install --fail-missing |
297 | + rm -f debian/unity-lens-applications/usr/lib/*/unity/*.la |
298 | |
299 | === modified file 'po/POTFILES.in' |
300 | --- po/POTFILES.in 2013-05-16 11:37:05 +0000 |
301 | +++ po/POTFILES.in 2013-08-06 08:19:29 +0000 |
302 | @@ -1,8 +1,10 @@ |
303 | [encoding: UTF-8] |
304 | src/daemon.vala |
305 | -src/runner.vala |
306 | +src/commands-scope.vala |
307 | +src/scopes-scope.vala |
308 | src/utils.vala |
309 | src/main.vala |
310 | data/X-Unity-All-Applications.directory.in |
311 | -[type: gettext/ini]applications.scope.in.in |
312 | -[type: gettext/ini]commands.scope.in.in |
313 | +[type: gettext/ini]data/applications.scope.in.in |
314 | +[type: gettext/ini]data/commands.scope.in.in |
315 | +[type: gettext/ini]data/scopes.scope.in.in |
316 | |
317 | === modified file 'po/POTFILES.skip' |
318 | --- po/POTFILES.skip 2011-03-10 14:49:07 +0000 |
319 | +++ po/POTFILES.skip 2013-08-06 08:19:29 +0000 |
320 | @@ -1,5 +1,5 @@ |
321 | src/main.c |
322 | src/daemon.c |
323 | src/utils.c |
324 | -src/runner.c |
325 | - |
326 | +src/commands-scope.c |
327 | +src/scopes-scope.c |
328 | |
329 | === modified file 'src/Makefile.am' |
330 | --- src/Makefile.am 2013-05-16 11:37:05 +0000 |
331 | +++ src/Makefile.am 2013-08-06 08:19:29 +0000 |
332 | @@ -1,14 +1,14 @@ |
333 | NULL = |
334 | BUILT_SOURCES = |
335 | CLEANFILES = |
336 | -EXTRA_DIST = |
337 | +EXTRA_DIST = |
338 | |
339 | DATADIR = $(datadir) |
340 | |
341 | -pkglibexec_PROGRAMS = \ |
342 | - unity-applications-daemon |
343 | +unitylibdir = $(libdir)/unity |
344 | +unitylib_LTLIBRARIES = unity-scope-applications.la |
345 | |
346 | -unity_applications_daemon_CPPFLAGS = \ |
347 | +AM_CPPFLAGS = \ |
348 | -DDATADIR=\"$(DATADIR)\" \ |
349 | -DPKGDATADIR=\"$(PKGDATADIR)\" \ |
350 | -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ |
351 | @@ -21,12 +21,14 @@ |
352 | -g |
353 | |
354 | if !ENABLE_C_WARNINGS |
355 | - unity_applications_daemon_CPPFLAGS += -w |
356 | + AM_CPPFLAGS += -w |
357 | endif |
358 | |
359 | +AM_CXXFLAGS = --std=c++11 |
360 | + |
361 | # Note that we require glib 2.26 because libzeitgeist |
362 | # expects us to use g_ptr_array_unref instead of g_ptr_array_free. |
363 | -unity_applications_daemon_VALAFLAGS = \ |
364 | +AM_VALAFLAGS = \ |
365 | -C \ |
366 | --vapidir=$(top_srcdir)/vapi \ |
367 | --pkg posix \ |
368 | @@ -47,23 +49,24 @@ |
369 | |
370 | unity_ratings_db_libs = -ldb |
371 | |
372 | -unity_applications_daemon_LDADD = \ |
373 | +unity_scope_applications_la_LIBADD = \ |
374 | $(LENS_DAEMON_LIBS) \ |
375 | $(unity_package_search_libs) \ |
376 | $(unity_ratings_db_libs) \ |
377 | - unity-package-search.o \ |
378 | - unity-ratings-db.o \ |
379 | $(NULL) |
380 | |
381 | -unity_applications_daemon_LDFLAGS = -rpath $(PROTOCOLPRIVATELIBDIR) $(COVERAGE_LDFLAGS) |
382 | +unity_scope_applications_la_LDFLAGS = \ |
383 | + -module -shared -avoid-version \ |
384 | + -R $(PROTOCOLPRIVATELIBDIR) $(COVERAGE_LDFLAGS) |
385 | |
386 | -unity_applications_daemon_VALASOURCES = \ |
387 | +unity_scope_applications_la_VALASOURCES = \ |
388 | app-watcher.vala \ |
389 | config.vala \ |
390 | daemon.vala \ |
391 | launcher-client.vala \ |
392 | main.vala \ |
393 | - runner.vala \ |
394 | + commands-scope.vala \ |
395 | + scopes-scope.vala \ |
396 | schemas.vala \ |
397 | utils.vala \ |
398 | aptd-client.vala \ |
399 | @@ -75,40 +78,34 @@ |
400 | xapian-utils.vala \ |
401 | $(NULL) |
402 | |
403 | -unity-package-search.o : $(srcdir)/unity-package-search.cc $(srcdir)/unity-package-search.h |
404 | - $(AM_V_GEN)$(CXX) --std=c++11 $(COVERAGE_CFLAGS) -g $(unity_package_search_libs) -DGMENU_I_KNOW_THIS_IS_UNSTABLE `pkg-config --cflags --libs glib-2.0 libgnome-menu-3.0 unity gee-1.0 libcolumbus0` -c $(srcdir)/unity-package-search.cc |
405 | - |
406 | -unity-ratings-db.o : $(srcdir)/unity-ratings-db.c $(srcdir)/unity-ratings-db.h |
407 | - $(AM_V_CC)$(CC) -g $(COVERAGE_CFLAGS) $(unity_ratings_db_libs) `pkg-config --cflags --libs glib-2.0` -c $(srcdir)/unity-ratings-db.c |
408 | - |
409 | -unity_applications_daemon_SOURCES = \ |
410 | - $(unity_applications_daemon_VALASOURCES:.vala=.c) \ |
411 | +unity_scope_applications_la_SOURCES = \ |
412 | + $(unity_scope_applications_la_VALASOURCES:.vala=.c) \ |
413 | + unity-package-search.cc \ |
414 | unity-package-search.h \ |
415 | + unity-ratings-db.c \ |
416 | unity-ratings-db.h \ |
417 | $(NULL) |
418 | |
419 | BUILT_SOURCES += \ |
420 | - unity_applications_daemon.vala.stamp \ |
421 | + unity-scope-applications.vala.stamp \ |
422 | $(NULL) |
423 | |
424 | EXTRA_DIST += \ |
425 | - unity_applications_daemon.vala.stamp \ |
426 | - $(unity_applications_daemon_VALASOURCES) \ |
427 | + unity-scope-applications.vala.stamp \ |
428 | + $(unity_scope_applications_la_VALASOURCES) \ |
429 | unity-package-search.cc \ |
430 | unity-package-search.h \ |
431 | unity-ratings-db.c \ |
432 | unity-ratings-db.h \ |
433 | $(NULL) |
434 | |
435 | -unity_applications_daemon.vala.stamp: $(unity_applications_daemon_VALASOURCES) |
436 | - $(AM_V_GEN) $(VALAC) $(unity_applications_daemon_VALAFLAGS) $^ |
437 | +unity-scope-applications.vala.stamp: $(unity_scope_applications_la_VALASOURCES) |
438 | + $(AM_V_GEN) $(VALAC) $(AM_VALAFLAGS) $^ |
439 | @touch $@ |
440 | |
441 | CLEANFILES += \ |
442 | *.stamp \ |
443 | *.vapi \ |
444 | - $(unity_applications_daemon_VALASOURCES:.vala=.c) \ |
445 | - unity-package-search.o \ |
446 | + $(unity_scope_applications_la_VALASOURCES:.vala=.c) \ |
447 | unity-package-search.h.gch \ |
448 | - unity-ratings-db.o \ |
449 | $(NULL) |
450 | |
451 | === renamed file 'src/runner.vala' => 'src/commands-scope.vala' |
452 | --- src/runner.vala 2013-07-01 12:17:01 +0000 |
453 | +++ src/commands-scope.vala 2013-08-06 08:19:29 +0000 |
454 | @@ -1,5 +1,6 @@ |
455 | +/* -*- mode: vala; c-basic-offset: 2; indent-tabs-mode: nil -*- */ |
456 | /* |
457 | - * Copyright (C) 2011 Canonical Ltd |
458 | + * Copyright (C) 2011-2013 Canonical Ltd |
459 | * |
460 | * This program is free software: you can redistribute it and/or modify |
461 | * it under the terms of the GNU General Public License version 3 as |
462 | @@ -38,47 +39,25 @@ |
463 | } |
464 | } |
465 | |
466 | - public class Runner: GLib.Object |
467 | + private class CommandsScope : Unity.AbstractScope |
468 | { |
469 | - |
470 | - private Unity.ApplicationsLens.Daemon daemon; |
471 | - private const string BUS_NAME_PREFIX = "com.canonical.Unity.ApplicationsLens.Runner"; |
472 | + // The ApplicationsScope holds a reference to us, so avoid a |
473 | + // reference cycle. |
474 | + public unowned ApplicationsScope appscope; |
475 | |
476 | /* for now, load the same keys as gnome-panel */ |
477 | private const string HISTORY_KEY = "history"; |
478 | private const int MAX_HISTORY = 10; |
479 | |
480 | - public Unity.DeprecatedScope scope; |
481 | - |
482 | - private Gee.HashMap<string,AboutEntry> about_entries; |
483 | - private Gee.List<string> history; |
484 | - private ExecSearcher exec_searcher = new ExecSearcher (); |
485 | + public Gee.HashMap<string,AboutEntry> about_entries; |
486 | + public Gee.List<string> history; |
487 | + public ExecSearcher exec_searcher = new ExecSearcher (); |
488 | |
489 | private Settings gp_settings; |
490 | |
491 | - public Runner (Unity.ApplicationsLens.Daemon daemon) |
492 | + public CommandsScope (ApplicationsScope appscope) |
493 | { |
494 | - /* First create scope */ |
495 | - scope = new Unity.DeprecatedScope ("/com/canonical/unity/scope/commands", |
496 | - "commands"); |
497 | - scope.search_in_global = false; |
498 | - scope.search_hint = _("Run a command"); |
499 | - scope.visible = false; |
500 | - populate_categories (); |
501 | - |
502 | - /* Listen for changes to the lens scope search */ |
503 | - scope.search_changed.connect ((lens_search, search_type, cancellable) => |
504 | - { |
505 | - update_search.begin (lens_search); |
506 | - }); |
507 | - |
508 | - /* Make sure we ignore extra whitespace on searches */ |
509 | - scope.generate_search_key.connect ((lens_search) => |
510 | - { |
511 | - return lens_search.search_string.strip (); |
512 | - }); |
513 | - |
514 | - scope.activate_uri.connect (daemon.activate); |
515 | + this.appscope = appscope; |
516 | |
517 | /* create the private about entries */ |
518 | about_entries = new Gee.HashMap<string,AboutEntry> (); |
519 | @@ -87,20 +66,92 @@ |
520 | this.gp_settings = new Settings ("com.canonical.Unity.Runner"); |
521 | history = new Gee.ArrayList<string> (); |
522 | load_history (); |
523 | - |
524 | - this.daemon = daemon; |
525 | - |
526 | - try |
527 | - { |
528 | - scope.export (); |
529 | - } |
530 | - catch (Error err) |
531 | - { |
532 | - error ("Unable to export scope: %s", err.message); |
533 | - } |
534 | - } |
535 | - |
536 | - private void populate_categories () |
537 | + } |
538 | + |
539 | + public void add_history (string last_command) |
540 | + { |
541 | + |
542 | + // new history list: better, greatest, latest! |
543 | + var new_history = new Gee.ArrayList<string> (); |
544 | + var history_store = new string [this.history.size + 1]; |
545 | + int i = 1; |
546 | + |
547 | + new_history.add (last_command); |
548 | + history_store[0] = last_command; |
549 | + for (var j = 0; (j < this.history.size) && (i < MAX_HISTORY); j++) |
550 | + { |
551 | + if (this.history[j] == last_command) |
552 | + continue; |
553 | + |
554 | + new_history.add(history[j]); |
555 | + history_store[i] = history[j]; |
556 | + i++; |
557 | + } |
558 | + this.history = new_history; |
559 | + |
560 | + // store in gsettings |
561 | + this.gp_settings.set_strv (HISTORY_KEY, history_store); |
562 | + |
563 | + // force a search to refresh history order (TODO: be more clever in the future) |
564 | + results_invalidated (SearchType.DEFAULT); |
565 | + } |
566 | + |
567 | + private void load_history () |
568 | + { |
569 | + int i = 0; |
570 | + string[] history_store = this.gp_settings.get_strv (HISTORY_KEY); |
571 | + foreach (var command in history_store) |
572 | + { |
573 | + if (i >= MAX_HISTORY) |
574 | + break; |
575 | + this.history.add((string) command.data); |
576 | + i++; |
577 | + } |
578 | + } |
579 | + |
580 | + private void load_about_entries () |
581 | + { |
582 | + AboutEntry entry; |
583 | + string name; |
584 | + string exec; |
585 | + Icon icon; |
586 | + |
587 | + // first about:config |
588 | + name = "about:config"; |
589 | + exec = "ccsm -p unityshell"; |
590 | + try { |
591 | + icon = Icon.new_for_string (@"$(Config.PREFIX)/share/ccsm/icons/hicolor/64x64/apps/plugin-unityshell.png"); |
592 | + } |
593 | + catch (Error err) { |
594 | + warning ("Can't find unityshell icon: %s", err.message); |
595 | + icon = new ThemedIcon ("gtk-execute"); |
596 | + } |
597 | + entry = new AboutEntry (name, exec, icon); |
598 | + |
599 | + about_entries[name] = entry; |
600 | + about_entries[exec] = entry; |
601 | + |
602 | + // second about:robots |
603 | + name = "Robots have a plan."; |
604 | + exec = "firefox about:robots"; |
605 | + entry = new AboutEntry (name, exec, icon = new ThemedIcon ("battery")); |
606 | + |
607 | + about_entries["about:robots"] = entry; |
608 | + about_entries[exec] = entry; |
609 | + |
610 | + } |
611 | + |
612 | + public override string get_group_name () |
613 | + { |
614 | + return "com.canonical.Unity.Scope.Applications"; |
615 | + } |
616 | + |
617 | + public override string get_unique_name () |
618 | + { |
619 | + return "/com/canonical/unity/scope/commands"; |
620 | + } |
621 | + |
622 | + public override Unity.CategorySet get_categories () |
623 | { |
624 | var categories = new Unity.CategorySet (); |
625 | var icon_dir = File.new_for_path (ICON_PATH); |
626 | @@ -113,33 +164,98 @@ |
627 | |
628 | categories.add (cat); |
629 | |
630 | - scope.categories = categories; |
631 | - } |
632 | - |
633 | - private void add_result (Dee.Model model, string uri, string icon_hint, |
634 | - uint category_id, string mimetype, string title) |
635 | - { |
636 | - Variant row_data[9]; |
637 | - model.append_row ( |
638 | - model.build_named_row_static (row_data, "uri", uri, |
639 | - "icon_hint", icon_hint, |
640 | - "category", category_id, |
641 | - "result_type", ResultType.DEFAULT, |
642 | - "mimetype", mimetype, |
643 | - "title", title, |
644 | - "comment", "", |
645 | - "dnd_uri", uri)); |
646 | - } |
647 | - |
648 | - private async void update_search (DeprecatedScopeSearch search) |
649 | - { |
650 | - var model = search.results_model; |
651 | + return categories; |
652 | + } |
653 | + |
654 | + public override Unity.FilterSet get_filters () |
655 | + { |
656 | + var filters = new Unity.FilterSet (); |
657 | + return filters; |
658 | + } |
659 | + |
660 | + public override Unity.Schema get_schema () |
661 | + { |
662 | + var schema = new Unity.Schema (); |
663 | + return schema; |
664 | + } |
665 | + |
666 | + public override string get_search_hint () |
667 | + { |
668 | + return _("Run a command"); |
669 | + } |
670 | + |
671 | + public override string normalize_search_query (string search_query) |
672 | + { |
673 | + return search_query.strip(); |
674 | + } |
675 | + |
676 | + public override Unity.ScopeSearchBase create_search_for_query (Unity.SearchContext search_context) |
677 | + { |
678 | + return new CommandsSearch (this, search_context); |
679 | + } |
680 | + |
681 | + public override Unity.ResultPreviewer create_previewer (Unity.ScopeResult result, Unity.SearchMetadata metadata) |
682 | + { |
683 | + return null; |
684 | + } |
685 | + |
686 | + public override Unity.ActivationResponse? activate (Unity.ScopeResult result, Unity.SearchMetadata metadata, string? action_id) |
687 | + { |
688 | + return appscope.activate (result, metadata, action_id); |
689 | + } |
690 | + } |
691 | + |
692 | + private class CommandsSearch : Unity.ScopeSearchBase |
693 | + { |
694 | + private CommandsScope scope; |
695 | + |
696 | + public CommandsSearch (CommandsScope scope, Unity.SearchContext search_context) |
697 | + { |
698 | + this.scope = scope; |
699 | + set_search_context (search_context); |
700 | + } |
701 | + |
702 | + public override void run () |
703 | + { |
704 | + var ml = new MainLoop (); |
705 | + run_async (() => { ml.quit (); }); |
706 | + ml.run (); |
707 | + } |
708 | + |
709 | + public override void run_async (Unity.ScopeSearchBaseCallback async_callback) |
710 | + { |
711 | + update_search.begin ( |
712 | + () => { |
713 | + async_callback (this); |
714 | + }); |
715 | + } |
716 | + |
717 | + private void add_result (Unity.ResultSet result_set, string uri, |
718 | + string icon_hint, uint category_id, |
719 | + string mimetype, string title) |
720 | + { |
721 | + var result = Unity.ScopeResult (); |
722 | + result.uri = uri; |
723 | + result.icon_hint = icon_hint; |
724 | + result.category = category_id; |
725 | + result.result_type = ResultType.DEFAULT; |
726 | + result.mimetype = mimetype; |
727 | + result.title = title; |
728 | + result.comment = ""; |
729 | + result.dnd_uri = uri; |
730 | + result.metadata = new HashTable<string, Variant> (str_hash, str_equal); |
731 | + result_set.add_result (result); |
732 | + } |
733 | + |
734 | + private async void update_search () |
735 | + { |
736 | + var context = this.search_context; |
737 | + var result_set = context.result_set; |
738 | var executables_match = new Gee.ArrayList<string> (); |
739 | var dirs_match = new Gee.ArrayList<string> (); |
740 | - model.clear (); |
741 | |
742 | - var search_string = search.search_string; |
743 | - bool has_search = !Utils.is_search_empty (search.search_string); |
744 | + var search_string = context.search_query; |
745 | + bool has_search = !Utils.is_search_empty (search_string); |
746 | |
747 | string uri; |
748 | Icon icon; |
749 | @@ -147,16 +263,15 @@ |
750 | string display_name; |
751 | var category_id = RunnerCategory.HISTORY; |
752 | |
753 | - foreach (var command in this.history) |
754 | + foreach (var command in scope.history) |
755 | { |
756 | display_name = get_icon_uri_and_mimetype (command, out icon, out uri, out mimetype); |
757 | - add_result (model, uri, icon.to_string (), category_id, mimetype, |
758 | + add_result (result_set, uri, icon.to_string (), category_id, mimetype, |
759 | display_name); |
760 | } |
761 | |
762 | if (!has_search) |
763 | { |
764 | - search.finished (); |
765 | return; |
766 | } |
767 | |
768 | @@ -168,9 +283,8 @@ |
769 | uri = "about:blank"; |
770 | string commenteaster = _("There is no easter egg in Unity"); |
771 | icon = new ThemedIcon ("gnome-panel-fish"); |
772 | - add_result (model, uri, icon.to_string (), 0, "text/plain", |
773 | + add_result (result_set, uri, icon.to_string (), 0, "text/plain", |
774 | commenteaster); |
775 | - search.finished (); |
776 | return; |
777 | } |
778 | else if (search_string == "gegls from outer space") |
779 | @@ -178,9 +292,8 @@ |
780 | uri = "about:blank"; |
781 | string commentnoeaster = _("Still no easter egg in Unity"); |
782 | icon = new ThemedIcon ("gnome-panel-fish"); |
783 | - add_result (model, uri, icon.to_string (), 0, "text/plain", |
784 | + add_result (result_set, uri, icon.to_string (), 0, "text/plain", |
785 | commentnoeaster); |
786 | - search.finished (); |
787 | return; |
788 | |
789 | } |
790 | @@ -238,7 +351,7 @@ |
791 | /* complete again system executables */ |
792 | else |
793 | { |
794 | - var matching_executables = yield exec_searcher.find_prefixed (search_string); |
795 | + var matching_executables = yield scope.exec_searcher.find_prefixed (search_string); |
796 | foreach (var matching_exec in matching_executables) |
797 | { |
798 | executables_match.add (matching_exec); |
799 | @@ -255,7 +368,7 @@ |
800 | if ((executables_match.size == 0) && (dirs_match.size == 0)) |
801 | { |
802 | display_name = get_icon_uri_and_mimetype (search_string, out icon, out uri, out mimetype); |
803 | - add_result (model, uri.strip (), icon.to_string (), |
804 | + add_result (result_set, uri.strip (), icon.to_string (), |
805 | category_id, mimetype, display_name); |
806 | } |
807 | |
808 | @@ -265,7 +378,7 @@ |
809 | foreach (var dir in dirs_match) |
810 | { |
811 | uri = UNITY_RUNNER_PREFIX + dir; |
812 | - add_result (model, uri, icon.to_string (), |
813 | + add_result (result_set, uri, icon.to_string (), |
814 | category_id, mimetype, dir); |
815 | } |
816 | |
817 | @@ -275,104 +388,13 @@ |
818 | // TODO: try to match to a desktop file for the icon |
819 | uri = UNITY_RUNNER_PREFIX + final_exec; |
820 | display_name = get_icon_uri_and_mimetype (final_exec, out icon, out uri, out mimetype); |
821 | - add_result (model, uri, icon.to_string (), |
822 | + add_result (result_set, uri, icon.to_string (), |
823 | category_id, mimetype, display_name); |
824 | } |
825 | |
826 | timer.stop (); |
827 | debug ("Entry search listed %i dir matches and %i exec matches in %fms for search: %s", |
828 | dirs_match.size, executables_match.size, timer.elapsed ()*1000, search_string); |
829 | - |
830 | - |
831 | - search.finished (); |
832 | - } |
833 | - |
834 | - private class ExecSearcher: Object |
835 | - { |
836 | - public ExecSearcher () |
837 | - { |
838 | - Object (); |
839 | - } |
840 | - |
841 | - private Gee.List<string> executables; |
842 | - |
843 | - construct |
844 | - { |
845 | - executables = new Gee.ArrayList<string> (); |
846 | - listing_status = ListingStatus.NOT_STARTED; |
847 | - } |
848 | - |
849 | - // TODO: add reload |
850 | - private async void find_system_executables () |
851 | - { |
852 | - if (this.executables.size > 0) |
853 | - return; |
854 | - |
855 | - foreach (var path_directory in Environment.get_variable ("PATH").split(":")) |
856 | - { |
857 | - var dir = File.new_for_path (path_directory); |
858 | - try { |
859 | - var e = yield dir.enumerate_children_async (FileAttribute.STANDARD_NAME + "," + FileAttribute.ACCESS_CAN_EXECUTE, |
860 | - 0, Priority.DEFAULT, null); |
861 | - while (true) { |
862 | - var files = yield e.next_files_async (64, Priority.DEFAULT, null); |
863 | - if (files == null) |
864 | - break; |
865 | - |
866 | - foreach (var info in files) { |
867 | - if (info.get_attribute_boolean (FileAttribute.ACCESS_CAN_EXECUTE)) |
868 | - { |
869 | - this.executables.add (info.get_name ()); |
870 | - } |
871 | - } |
872 | - } |
873 | - } |
874 | - catch (Error err) { |
875 | - warning("Error listing directory executables: %s\n", err.message); |
876 | - } |
877 | - } |
878 | - } |
879 | - |
880 | - private enum ListingStatus |
881 | - { |
882 | - NOT_STARTED, |
883 | - STARTED, |
884 | - FINISHED |
885 | - } |
886 | - |
887 | - public int listing_status { get; private set; } |
888 | - |
889 | - public async Gee.Collection<string> find_prefixed (string search_string) |
890 | - { |
891 | - // initialize the available binaries lazily |
892 | - if (listing_status == ListingStatus.NOT_STARTED) |
893 | - { |
894 | - listing_status = ListingStatus.STARTED; |
895 | - yield find_system_executables (); |
896 | - listing_status = ListingStatus.FINISHED; |
897 | - } |
898 | - else if (listing_status == ListingStatus.STARTED) |
899 | - { |
900 | - var sig_id = this.notify["listing-status"].connect (() => |
901 | - { |
902 | - if (listing_status == ListingStatus.FINISHED) |
903 | - find_prefixed.callback (); |
904 | - }); |
905 | - yield; |
906 | - SignalHandler.disconnect (this, sig_id); |
907 | - } |
908 | - |
909 | - var matching = new Gee.ArrayList<string> (); |
910 | - foreach (var exec_candidate in executables) |
911 | - { |
912 | - if (exec_candidate.has_prefix (search_string)) |
913 | - { |
914 | - matching.add (exec_candidate); |
915 | - } |
916 | - } |
917 | - |
918 | - return matching; |
919 | - } |
920 | } |
921 | |
922 | private string get_icon_uri_and_mimetype (string exec_string, out Icon? icon, out string? uri, out string? mimetype) |
923 | @@ -381,7 +403,7 @@ |
924 | AboutEntry? entry = null; |
925 | |
926 | mimetype = "application/x-unity-run"; |
927 | - entry = about_entries[exec_string]; |
928 | + entry = scope.about_entries[exec_string]; |
929 | if (entry != null) |
930 | { |
931 | uri = UNITY_RUNNER_PREFIX + entry.exec; |
932 | @@ -401,9 +423,10 @@ |
933 | } |
934 | |
935 | var s = exec_string.delimit ("-", '_').split (" ", 0)[0]; |
936 | - var appresults = this.daemon.appsearcher.search (@"type:Application AND exec:$s", 0, |
937 | - Unity.Package.SearchType.EXACT, |
938 | - Unity.Package.Sort.BY_NAME); |
939 | + var appresults = scope.appscope.appsearcher.search ( |
940 | + @"type:Application AND exec:$s", 0, |
941 | + Unity.Package.SearchType.EXACT, |
942 | + Unity.Package.Sort.BY_NAME); |
943 | foreach (unowned Unity.Package.PackageInfo pkginfo in appresults.results) |
944 | { |
945 | |
946 | @@ -411,7 +434,7 @@ |
947 | continue; |
948 | |
949 | // pick the first one |
950 | - icon = this.daemon.find_pkg_icon (pkginfo.desktop_file, pkginfo.icon); |
951 | + icon = scope.appscope.find_pkg_icon (pkginfo.desktop_file, pkginfo.icon); |
952 | return exec_string; |
953 | |
954 | } |
955 | @@ -421,81 +444,93 @@ |
956 | return exec_string; |
957 | |
958 | } |
959 | - |
960 | - |
961 | - public void add_history (string last_command) |
962 | - { |
963 | - |
964 | - // new history list: better, greatest, latest! |
965 | - var new_history = new Gee.ArrayList<string> (); |
966 | - var history_store = new string [this.history.size + 1]; |
967 | - int i = 1; |
968 | - |
969 | - new_history.add (last_command); |
970 | - history_store[0] = last_command; |
971 | - for (var j = 0; (j < this.history.size) && (i < MAX_HISTORY); j++) |
972 | - { |
973 | - if (this.history[j] == last_command) |
974 | - continue; |
975 | - |
976 | - new_history.add(history[j]); |
977 | - history_store[i] = history[j]; |
978 | - i++; |
979 | - } |
980 | - this.history = new_history; |
981 | - |
982 | - // store in gsettings |
983 | - this.gp_settings.set_strv (HISTORY_KEY, history_store); |
984 | - |
985 | - // force a search to refresh history order (TODO: be more clever in the future) |
986 | - scope.queue_search_changed (SearchType.DEFAULT); |
987 | - } |
988 | - |
989 | - private void load_history () |
990 | - { |
991 | - int i = 0; |
992 | - string[] history_store = this.gp_settings.get_strv (HISTORY_KEY); |
993 | - foreach (var command in history_store) |
994 | - { |
995 | - if (i >= MAX_HISTORY) |
996 | - break; |
997 | - this.history.add((string) command.data); |
998 | - i++; |
999 | - } |
1000 | - } |
1001 | - |
1002 | - private void load_about_entries () |
1003 | - { |
1004 | - AboutEntry entry; |
1005 | - string name; |
1006 | - string exec; |
1007 | - Icon icon; |
1008 | - |
1009 | - // first about:config |
1010 | - name = "about:config"; |
1011 | - exec = "ccsm -p unityshell"; |
1012 | - try { |
1013 | - icon = Icon.new_for_string (@"$(Config.PREFIX)/share/ccsm/icons/hicolor/64x64/apps/plugin-unityshell.png"); |
1014 | - } |
1015 | - catch (Error err) { |
1016 | - warning ("Can't find unityshell icon: %s", err.message); |
1017 | - icon = new ThemedIcon ("gtk-execute"); |
1018 | - } |
1019 | - entry = new AboutEntry (name, exec, icon); |
1020 | - |
1021 | - about_entries[name] = entry; |
1022 | - about_entries[exec] = entry; |
1023 | - |
1024 | - // second about:robots |
1025 | - name = "Robots have a plan."; |
1026 | - exec = "firefox about:robots"; |
1027 | - entry = new AboutEntry (name, exec, icon = new ThemedIcon ("battery")); |
1028 | - |
1029 | - about_entries["about:robots"] = entry; |
1030 | - about_entries[exec] = entry; |
1031 | - |
1032 | - } |
1033 | - |
1034 | - } |
1035 | - |
1036 | + } |
1037 | + |
1038 | + private class ExecSearcher: Object |
1039 | + { |
1040 | + public ExecSearcher () |
1041 | + { |
1042 | + Object (); |
1043 | + } |
1044 | + |
1045 | + private Gee.List<string> executables; |
1046 | + |
1047 | + construct |
1048 | + { |
1049 | + executables = new Gee.ArrayList<string> (); |
1050 | + listing_status = ListingStatus.NOT_STARTED; |
1051 | + } |
1052 | + |
1053 | + // TODO: add reload |
1054 | + private async void find_system_executables () |
1055 | + { |
1056 | + if (this.executables.size > 0) |
1057 | + return; |
1058 | + |
1059 | + foreach (var path_directory in Environment.get_variable ("PATH").split(":")) |
1060 | + { |
1061 | + var dir = File.new_for_path (path_directory); |
1062 | + try { |
1063 | + var e = yield dir.enumerate_children_async (FileAttribute.STANDARD_NAME + "," + FileAttribute.ACCESS_CAN_EXECUTE, |
1064 | + 0, Priority.DEFAULT, null); |
1065 | + while (true) { |
1066 | + var files = yield e.next_files_async (64, Priority.DEFAULT, null); |
1067 | + if (files == null) |
1068 | + break; |
1069 | + |
1070 | + foreach (var info in files) { |
1071 | + if (info.get_attribute_boolean (FileAttribute.ACCESS_CAN_EXECUTE)) |
1072 | + { |
1073 | + this.executables.add (info.get_name ()); |
1074 | + } |
1075 | + } |
1076 | + } |
1077 | + } |
1078 | + catch (Error err) { |
1079 | + warning("Error listing directory executables: %s\n", err.message); |
1080 | + } |
1081 | + } |
1082 | + } |
1083 | + |
1084 | + private enum ListingStatus |
1085 | + { |
1086 | + NOT_STARTED, |
1087 | + STARTED, |
1088 | + FINISHED |
1089 | + } |
1090 | + |
1091 | + public int listing_status { get; private set; } |
1092 | + |
1093 | + public async Gee.Collection<string> find_prefixed (string search_string) |
1094 | + { |
1095 | + // initialize the available binaries lazily |
1096 | + if (listing_status == ListingStatus.NOT_STARTED) |
1097 | + { |
1098 | + listing_status = ListingStatus.STARTED; |
1099 | + yield find_system_executables (); |
1100 | + listing_status = ListingStatus.FINISHED; |
1101 | + } |
1102 | + else if (listing_status == ListingStatus.STARTED) |
1103 | + { |
1104 | + var sig_id = this.notify["listing-status"].connect (() => |
1105 | + { |
1106 | + if (listing_status == ListingStatus.FINISHED) |
1107 | + find_prefixed.callback (); |
1108 | + }); |
1109 | + yield; |
1110 | + SignalHandler.disconnect (this, sig_id); |
1111 | + } |
1112 | + |
1113 | + var matching = new Gee.ArrayList<string> (); |
1114 | + foreach (var exec_candidate in executables) |
1115 | + { |
1116 | + if (exec_candidate.has_prefix (search_string)) |
1117 | + { |
1118 | + matching.add (exec_candidate); |
1119 | + } |
1120 | + } |
1121 | + |
1122 | + return matching; |
1123 | + } |
1124 | + } |
1125 | } |
1126 | |
1127 | === modified file 'src/daemon.vala' |
1128 | --- src/daemon.vala 2013-07-18 21:11:13 +0000 |
1129 | +++ src/daemon.vala 2013-08-06 08:19:29 +0000 |
1130 | @@ -1,5 +1,6 @@ |
1131 | +/* -*- mode: vala; c-basic-offset: 2; indent-tabs-mode: nil -*- */ |
1132 | /* |
1133 | - * Copyright (C) 2010 Canonical Ltd |
1134 | + * Copyright (C) 2010-2013 Canonical Ltd |
1135 | * |
1136 | * This program is free software: you can redistribute it and/or modify |
1137 | * it under the terms of the GNU General Public License version 3 as |
1138 | @@ -42,66 +43,57 @@ |
1139 | |
1140 | const string ICON_PATH = Config.DATADIR + "/icons/unity-icon-theme/places/svg/"; |
1141 | const string GENERIC_APP_ICON = "applications-other"; |
1142 | - const string GENERIC_SCOPE_ICON = ICON_PATH + "service-generic.svg"; |
1143 | |
1144 | const string LIBUNITY_SCHEMA = "com.canonical.Unity.Lenses"; |
1145 | |
1146 | - public class Daemon : GLib.Object |
1147 | + private class ApplicationsScope : Unity.AbstractScope |
1148 | { |
1149 | - private Variant empty_asv; |
1150 | - private Zeitgeist.Log log; |
1151 | - private Zeitgeist.Index zg_index; |
1152 | + public Zeitgeist.Log log; |
1153 | + public Zeitgeist.Index zg_index; |
1154 | private Zeitgeist.Monitor monitor; |
1155 | |
1156 | - private Map<string, int> popularity_map; |
1157 | - private bool popularities_dirty; |
1158 | + public Map<string, int> popularity_map; |
1159 | + public bool popularities_dirty; |
1160 | |
1161 | /* The searcher for online material may be null if it fails to load |
1162 | * the Xapian index from the Software Center */ |
1163 | - private Unity.Package.Searcher? pkgsearcher; |
1164 | - private Unity.Package.Searcher? scopesearcher; |
1165 | + public Unity.Package.Searcher? pkgsearcher; |
1166 | public Unity.Package.Searcher appsearcher; |
1167 | |
1168 | /* Read the app ratings dumped by the Software Center */ |
1169 | private bool ratings_db_initialized = false; |
1170 | - private Unity.Ratings.Database? ratings = null; |
1171 | - |
1172 | - private Unity.DeprecatedScope scope; |
1173 | + public Unity.Ratings.Database? ratings = null; |
1174 | |
1175 | /* Support aptd dbus interface; created when application install/remove was requested by preview action */ |
1176 | private AptdProxy aptdclient; |
1177 | private AptdTransactionProxy aptd_transaction; |
1178 | |
1179 | - private SoftwareCenterUtils.MangledDesktopFileLookup sc_mangler; |
1180 | + public SoftwareCenterUtils.MangledDesktopFileLookup sc_mangler; |
1181 | |
1182 | /* Used for adding launcher icon on app installation from the preview */ |
1183 | private LauncherProxy launcherservice; |
1184 | |
1185 | /* Desktop file & icon name for unity-install:// install candidate displayed in last preview; we store |
1186 | * them here to avoid extra query for app details if app install action is activated */ |
1187 | - private string preview_installable_desktop_file; |
1188 | - private string preview_installable_icon_file; |
1189 | + public string preview_installable_desktop_file; |
1190 | + public string preview_installable_icon_file; |
1191 | |
1192 | - private string preview_developer_website; |
1193 | + public string preview_developer_website; |
1194 | |
1195 | /* SoftwareCenter data provider used for app preview details */ |
1196 | - private SoftwareCenterDataProviderProxy sc_data_provider; |
1197 | - |
1198 | - private Unity.ApplicationsLens.Runner runner; |
1199 | - |
1200 | - /* Keep references to the FilterOptions for sources */ |
1201 | - private FilterOption local_apps_option; |
1202 | - private FilterOption usc_apps_option; |
1203 | + public SoftwareCenterDataProviderProxy sc_data_provider; |
1204 | + |
1205 | + public Unity.ApplicationsLens.CommandsScope commands_scope; |
1206 | |
1207 | private Gee.List<string> image_extensions; |
1208 | private HashTable<string,Icon> file_icon_cache; |
1209 | |
1210 | /* Monitor the favorite apps in the launcher, so we can filter them |
1211 | * out of the results for Recent Apps */ |
1212 | - private Unity.LauncherFavorites favorite_apps; |
1213 | - private AppWatcher app_watcher; |
1214 | + public Unity.LauncherFavorites favorite_apps; |
1215 | + public AppWatcher app_watcher; |
1216 | |
1217 | - private PtrArray zg_templates; |
1218 | + public PtrArray zg_templates; |
1219 | |
1220 | /* Gnome menu structure - also used to check whether apps are installed */ |
1221 | private uint app_menu_changed_reindex_timeout = 0; |
1222 | @@ -111,7 +103,6 @@ |
1223 | private Regex mountable_regex; |
1224 | |
1225 | private Settings gp_settings; |
1226 | - private HashTable<unowned string, unowned string> disabled_scope_ids; |
1227 | |
1228 | private const string DISPLAY_RECENT_APPS_KEY = "display-recent-apps"; |
1229 | private const string DISPLAY_AVAILABLE_APPS_KEY = "display-available-apps"; |
1230 | @@ -120,10 +111,7 @@ |
1231 | public bool display_available_apps { get; set; default = true; } |
1232 | public bool force_small_icons_for_suggestions { get; set; default = true; } |
1233 | |
1234 | - private PurchaseInfoHelper purchase_info = null; |
1235 | - private Dee.Model remote_scopes_model; |
1236 | - private Dee.Index scopes_index; |
1237 | - private Dee.Analyzer analyzer; |
1238 | + public PurchaseInfoHelper purchase_info = null; |
1239 | |
1240 | construct |
1241 | { |
1242 | @@ -131,7 +119,6 @@ |
1243 | |
1244 | log = new Zeitgeist.Log(); |
1245 | zg_index = new Zeitgeist.Index(); |
1246 | - empty_asv = new Variant.array (new VariantType ("{sv}"), {}); |
1247 | monitor = new Zeitgeist.Monitor (new Zeitgeist.TimeRange.from_now (), |
1248 | zg_templates); |
1249 | monitor.events_inserted.connect (mark_dirty); |
1250 | @@ -168,55 +155,15 @@ |
1251 | image_extensions.add ("jpg"); |
1252 | |
1253 | build_app_menu_index (); |
1254 | - build_scope_index.begin (); |
1255 | |
1256 | file_icon_cache = new HashTable<string,Icon>(str_hash, str_equal); |
1257 | sc_mangler = new SoftwareCenterUtils.MangledDesktopFileLookup (); |
1258 | |
1259 | - scope = new Unity.DeprecatedScope ("/com/canonical/unity/scope/applications", |
1260 | - "applications"); |
1261 | - |
1262 | - scope.search_hint = _("Search applications"); |
1263 | - scope.search_in_global = true; |
1264 | - // scope.sources_display_name = _("Sources"); |
1265 | - // TRANSLATORS: Please make sure this string is short enough to fit |
1266 | - // into the filter button |
1267 | - local_apps_option = scope.sources.add_option ("local", _("Local apps")); |
1268 | - if (display_available_apps) |
1269 | - { |
1270 | - // TRANSLATORS: Please make sure this string is short enough to fit |
1271 | - // into the filter button |
1272 | - usc_apps_option = scope.sources.add_option ("usc", _("Software center")); |
1273 | - } |
1274 | - |
1275 | - populate_categories (); |
1276 | - populate_filters(); |
1277 | - //scope.icon = @"$(Config.PREFIX)/share/unity/themes/applications.png"; |
1278 | - |
1279 | - scope.generate_search_key.connect ((lens_search) => |
1280 | - { |
1281 | - return lens_search.search_string.strip (); |
1282 | - }); |
1283 | - /* Listen for changes to the lens scope search */ |
1284 | - scope.search_changed.connect ((lens_search, search_type, cancellable) => |
1285 | - { |
1286 | - dispatch_search.begin (lens_search, search_type, cancellable); |
1287 | - }); |
1288 | - |
1289 | - /* Re-do the search if the sources change */ |
1290 | - scope.active_sources_changed.connect (() => |
1291 | - { |
1292 | - scope.queue_search_changed (SearchType.DEFAULT); |
1293 | - }); |
1294 | - |
1295 | - scope.activate_uri.connect (activate); |
1296 | - scope.preview_uri.connect (preview); |
1297 | - |
1298 | /* Listen for changes in the installed applications */ |
1299 | AppInfoManager.get_default().changed.connect (mark_dirty); |
1300 | |
1301 | /* Now start the RunEntry */ |
1302 | - runner = new Unity.ApplicationsLens.Runner (this); |
1303 | + commands_scope = new Unity.ApplicationsLens.CommandsScope (this); |
1304 | |
1305 | try { |
1306 | uri_regex = new Regex ("^[a-z]+:.+$"); |
1307 | @@ -234,46 +181,9 @@ |
1308 | |
1309 | aptdclient = new AptdProxy (); |
1310 | launcherservice = new LauncherProxy (); |
1311 | - |
1312 | - try |
1313 | - { |
1314 | - scope.export (); |
1315 | - } |
1316 | - catch (Error err) |
1317 | - { |
1318 | - error ("Unable to export scope: %s", err.message); |
1319 | - } |
1320 | - |
1321 | - remote_scopes_model = new Dee.SharedModel ("com.canonical.Unity.SmartScopes.RemoteScopesModel"); |
1322 | - remote_scopes_model.set_schema ("s", "s", "s", "s", "s", "as"); |
1323 | - |
1324 | - scopes_index = Utils.prepare_index (remote_scopes_model, |
1325 | - RemoteScopesColumn.NAME, |
1326 | - (model, iter) => |
1327 | - { |
1328 | - unowned string name = model.get_string (iter, RemoteScopesColumn.NAME); |
1329 | - return "%s\n%s".printf (_("scope"), name); |
1330 | - }, out analyzer); |
1331 | - |
1332 | - disabled_scope_ids = new HashTable<unowned string, unowned string> (str_hash, str_equal); |
1333 | - update_disabled_scopes_hash (); |
1334 | - |
1335 | - var pref_man = PreferencesManager.get_default (); |
1336 | - pref_man.notify["disabled-scopes"].connect (update_disabled_scopes_hash); |
1337 | - } |
1338 | - |
1339 | - private void update_disabled_scopes_hash () |
1340 | - { |
1341 | - disabled_scope_ids.remove_all (); |
1342 | - var pref_man = PreferencesManager.get_default (); |
1343 | - foreach (unowned string scope_id in pref_man.disabled_scopes) |
1344 | - { |
1345 | - // using HashTable as a set (optimized in glib when done like this) |
1346 | - disabled_scope_ids[scope_id] = scope_id; |
1347 | - } |
1348 | - } |
1349 | - |
1350 | - private void init_ratings_db () |
1351 | + } |
1352 | + |
1353 | + public void init_ratings_db () |
1354 | { |
1355 | if (ratings_db_initialized) return; |
1356 | try |
1357 | @@ -288,35 +198,19 @@ |
1358 | ratings_db_initialized = true; |
1359 | } |
1360 | |
1361 | - private async void dispatch_search (DeprecatedScopeSearch search, |
1362 | - SearchType search_type, |
1363 | - GLib.Cancellable cancellable) |
1364 | - { |
1365 | - try |
1366 | - { |
1367 | - if (popularities_dirty) |
1368 | - { |
1369 | - popularities_dirty = false; |
1370 | - // we're not passing the cancellable, cause cancelling this search |
1371 | - // shouldn't cancel getting most popular apps |
1372 | - yield update_popularities (); |
1373 | - if (cancellable.is_cancelled ()) return; |
1374 | - } |
1375 | - |
1376 | - if (search_type == SearchType.DEFAULT) |
1377 | - yield update_scope_search (search, cancellable); |
1378 | - else |
1379 | - yield update_global_search (search, cancellable); |
1380 | - } |
1381 | - finally |
1382 | - { |
1383 | - search.finished (); |
1384 | - } |
1385 | - } |
1386 | - |
1387 | - private void populate_categories () |
1388 | - { |
1389 | - Unity.CategorySet categories = new Unity.CategorySet (); |
1390 | + public override string get_group_name () |
1391 | + { |
1392 | + return "com.canonical.Unity.Scope.Applications"; |
1393 | + } |
1394 | + |
1395 | + public override string get_unique_name () |
1396 | + { |
1397 | + return "/com/canonical/unity/scope/applications"; |
1398 | + } |
1399 | + |
1400 | + public override Unity.CategorySet get_categories () |
1401 | + { |
1402 | + var categories = new Unity.CategorySet (); |
1403 | File icon_dir = File.new_for_path (ICON_PATH); |
1404 | |
1405 | var cat = new Unity.Category ("apps", _("Applications"), |
1406 | @@ -339,12 +233,12 @@ |
1407 | new FileIcon (icon_dir.get_child ("group-treat-yourself.svg"))); |
1408 | categories.add (cat); |
1409 | |
1410 | - scope.categories = categories; |
1411 | + return categories; |
1412 | } |
1413 | |
1414 | - private void populate_filters() |
1415 | + public override Unity.FilterSet get_filters() |
1416 | { |
1417 | - Unity.FilterSet filters = new Unity.FilterSet (); |
1418 | + var filters = new Unity.FilterSet (); |
1419 | |
1420 | /* Type filter */ |
1421 | { |
1422 | @@ -369,23 +263,310 @@ |
1423 | filters.add (filter); |
1424 | } |
1425 | |
1426 | - scope.filters = filters; |
1427 | - } |
1428 | - |
1429 | - private bool local_apps_active (DeprecatedScopeSearch search) |
1430 | - { |
1431 | - var filter = search.get_filter (scope.sources.id) as Unity.OptionsFilter; |
1432 | - if (filter.filtering && local_apps_option != null) |
1433 | - return filter.get_option (local_apps_option.id).active; |
1434 | - return true; |
1435 | - } |
1436 | - |
1437 | - private bool usc_apps_active (DeprecatedScopeSearch search) |
1438 | - { |
1439 | - var filter = search.get_filter (scope.sources.id) as Unity.OptionsFilter; |
1440 | - if (filter.filtering && usc_apps_option != null) |
1441 | - return filter.get_option (usc_apps_option.id).active; |
1442 | - return true; |
1443 | + /* Sources */ |
1444 | + { |
1445 | + var filter = new CheckOptionFilter ("unity-sources", _("Sources")); |
1446 | + filter.sort_type = Unity.OptionsFilter.SortType.DISPLAY_NAME; |
1447 | + |
1448 | + // TRANSLATORS: Please make sure this string is short enough to fit |
1449 | + // into the filter button |
1450 | + filter.add_option ("local", _("Local apps")); |
1451 | + if (display_available_apps) |
1452 | + { |
1453 | + // TRANSLATORS: Please make sure this string is short enough to fit |
1454 | + // into the filter button |
1455 | + filter.add_option ("usc", _("Software center")); |
1456 | + } |
1457 | + |
1458 | + filters.add (filter); |
1459 | + } |
1460 | + |
1461 | + return filters; |
1462 | + } |
1463 | + |
1464 | + public override Unity.Schema get_schema () |
1465 | + { |
1466 | + var schema = new Unity.Schema (); |
1467 | + return schema; |
1468 | + } |
1469 | + |
1470 | + public override string get_search_hint () |
1471 | + { |
1472 | + return _("Search applications"); |
1473 | + } |
1474 | + |
1475 | + public override string normalize_search_query (string search_query) |
1476 | + { |
1477 | + return search_query.strip (); |
1478 | + } |
1479 | + |
1480 | + public override Unity.ScopeSearchBase create_search_for_query (Unity.SearchContext search_context) |
1481 | + { |
1482 | + return new ApplicationsSearch (this, search_context); |
1483 | + } |
1484 | + |
1485 | + public override Unity.ResultPreviewer create_previewer (Unity.ScopeResult result, Unity.SearchMetadata metadata) |
1486 | + { |
1487 | + return new ApplicationsResultPreviewer (this, result, metadata); |
1488 | + } |
1489 | + |
1490 | + public override Unity.ActivationResponse? activate (Unity.ScopeResult result, Unity.SearchMetadata metadata, string? action_id) |
1491 | + { |
1492 | + if (action_id == "buy") |
1493 | + return start_software_center (result.uri); |
1494 | + else if (action_id == "install") |
1495 | + return app_preview_install (result.uri); |
1496 | + else if (action_id == "install-paid") |
1497 | + return start_software_center (result.uri); |
1498 | + else if (action_id == "uninstall") |
1499 | + return app_preview_uninstall (result.uri); |
1500 | + else if (action_id == "website") |
1501 | + return app_preview_website (result.uri); |
1502 | + else |
1503 | + return app_launch (result, metadata); |
1504 | + } |
1505 | + |
1506 | + private Unity.ActivationResponse app_launch (Unity.ScopeResult result, Unity.SearchMetadata metadata) |
1507 | + { |
1508 | + string[] args; |
1509 | + string exec_or_dir = null; |
1510 | + if (result.uri.has_prefix ("unity-install://")) |
1511 | + { |
1512 | + var prv = create_previewer (result, metadata).run (); |
1513 | + if (prv is Unity.Preview) |
1514 | + { |
1515 | + return new Unity.ActivationResponse.with_preview ( |
1516 | + prv as Unity.Preview); |
1517 | + } |
1518 | + else |
1519 | + { |
1520 | + warning ("Failed to generate preview for %s", result.uri); |
1521 | + return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); |
1522 | + } |
1523 | + } |
1524 | + else if (result.uri.has_prefix (UNITY_RUNNER_PREFIX)) |
1525 | + { |
1526 | + string orig; |
1527 | + orig = result.uri.offset (UNITY_RUNNER_PREFIX.length); |
1528 | + if (orig.has_prefix("\\\\")) |
1529 | + orig = orig.replace ("\\\\","smb://"); |
1530 | + if (uri_regex != null && uri_regex.match (orig)) { |
1531 | + try { |
1532 | + /* this code ensures that a file manager will be used |
1533 | + * if uri it's a remote location that should be mounted */ |
1534 | + if (mountable_regex.match (orig)) { |
1535 | + var muris = new GLib.List<string>(); |
1536 | + muris.prepend (orig); |
1537 | + var file_manager = AppInfo.get_default_for_type("inode/directory", true); |
1538 | + file_manager.launch_uris(muris,null); |
1539 | + } else { |
1540 | + AppInfo.launch_default_for_uri (orig, null); |
1541 | + } |
1542 | + } catch (GLib.Error error) { |
1543 | + warning ("Failed to launch URI %s", orig); |
1544 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1545 | + } |
1546 | + return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
1547 | + |
1548 | + } else { |
1549 | + exec_or_dir = Utils.subst_tilde (orig); |
1550 | + args = exec_or_dir.split (" ", 0); |
1551 | + for (int i = 0; i < args.length; i++) |
1552 | + args[i] = Utils.subst_tilde (args[i]); |
1553 | + } |
1554 | + this.commands_scope.add_history (orig); |
1555 | + } |
1556 | + else |
1557 | + { |
1558 | + /* Activation of standard application:// uris */ |
1559 | + |
1560 | + /* Make sure fresh install learns quickly */ |
1561 | + if (popularity_map.size <= 5) popularities_dirty = true; |
1562 | + |
1563 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1564 | + } |
1565 | + |
1566 | + if ((exec_or_dir != null) && FileUtils.test (exec_or_dir, FileTest.IS_DIR)) |
1567 | + { |
1568 | + try { |
1569 | + AppInfo.launch_default_for_uri ("file://" + exec_or_dir, null); |
1570 | + } catch (GLib.Error err) { |
1571 | + warning ("Failed to open current folder '%s' in file manager: %s", |
1572 | + exec_or_dir, err.message); |
1573 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1574 | + } |
1575 | + } |
1576 | + else |
1577 | + { |
1578 | + try { |
1579 | + unowned string home_dir = GLib.Environment.get_home_dir (); |
1580 | + Process.spawn_async (home_dir, args, null, SpawnFlags.SEARCH_PATH, null, null); |
1581 | + } catch (SpawnError e) { |
1582 | + warning ("Failed to spawn software-center or direct URI activation '%s': %s", |
1583 | + result.uri, e.message); |
1584 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1585 | + } |
1586 | + } |
1587 | + |
1588 | + return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
1589 | + } |
1590 | + |
1591 | + private async void call_install_packages (string package_name, out string tid) throws IOError |
1592 | + { |
1593 | + tid = yield aptdclient.install_packages ({package_name}); |
1594 | + } |
1595 | + |
1596 | + private async void call_remove_packages (string package_name, out string tid) throws IOError |
1597 | + { |
1598 | + tid = yield aptdclient.remove_packages ({package_name}); |
1599 | + } |
1600 | + |
1601 | + /** |
1602 | + * Handler for free apps installation. |
1603 | + * Triggers package installation via apt-daemon DBus service |
1604 | + */ |
1605 | + private Unity.ActivationResponse app_preview_install (string uri) |
1606 | + { |
1607 | + if (uri.has_prefix ("unity-install://")) |
1608 | + { |
1609 | + string app = uri.substring (16); // trim "unity-install://" |
1610 | + string[] parts = app.split ("/"); |
1611 | + |
1612 | + if (parts.length > 1) |
1613 | + { |
1614 | + string pkgname = parts[0]; |
1615 | + string appname = parts[1]; |
1616 | + try |
1617 | + { |
1618 | + aptdclient.connect_to_aptd (); |
1619 | + } |
1620 | + catch (IOError e) |
1621 | + { |
1622 | + warning ("Failed to connect to aptd: '%s'", e.message); |
1623 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1624 | + } |
1625 | + call_install_packages.begin (pkgname, (obj, res) => |
1626 | + { |
1627 | + try { |
1628 | + string tid; |
1629 | + call_install_packages.end (res, out tid); |
1630 | + debug ("transaction started: %s, pkg: %s\n", tid, pkgname); |
1631 | + aptd_transaction = new AptdTransactionProxy (); |
1632 | + aptd_transaction.connect_to_aptd (tid); |
1633 | + aptd_transaction.simulate (); |
1634 | + aptd_transaction.run (); |
1635 | + |
1636 | + launcherservice.connect_to_launcher (); |
1637 | + string desktop_file = preview_installable_desktop_file; |
1638 | + Icon icon = find_pkg_icon (null, preview_installable_icon_file); |
1639 | + launcherservice.add_launcher_item_from_position.begin (appname, icon.to_string (), 0, 0, 32, desktop_file, tid); |
1640 | + } |
1641 | + catch (IOError e) |
1642 | + { |
1643 | + warning ("Package '%s' installation failed: %s", pkgname, e.message); |
1644 | + } |
1645 | + }); |
1646 | + } |
1647 | + else |
1648 | + { |
1649 | + warning ("Bad install uri: '%s'", uri); |
1650 | + } |
1651 | + } |
1652 | + else |
1653 | + { |
1654 | + warning ("Can't handle '%s' in app_preview_install handler", uri); |
1655 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1656 | + } |
1657 | + return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
1658 | + } |
1659 | + |
1660 | + private Unity.ActivationResponse start_software_center (string uri) |
1661 | + { |
1662 | + unowned string pkg = uri.offset (16); // strip off "unity-install://" prefix |
1663 | + debug ("Installing: %s", pkg); |
1664 | + |
1665 | + var args = new string[2]; |
1666 | + args[0] = "software-center"; |
1667 | + args[1] = pkg; |
1668 | + |
1669 | + try |
1670 | + { |
1671 | + Process.spawn_async (GLib.Environment.get_home_dir (), args, null, SpawnFlags.SEARCH_PATH, null, null); |
1672 | + } |
1673 | + catch (SpawnError e) |
1674 | + { |
1675 | + warning ("Failed to spawn software-center for uri '%s': %s", uri, e.message); |
1676 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1677 | + } |
1678 | + |
1679 | + return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
1680 | + } |
1681 | + |
1682 | + private Unity.ActivationResponse app_preview_website (string uri) |
1683 | + { |
1684 | + try |
1685 | + { |
1686 | + AppInfo.launch_default_for_uri (preview_developer_website, null); |
1687 | + return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
1688 | + } |
1689 | + catch (Error e) |
1690 | + { |
1691 | + warning ("Failed to launch a web browser for uri '%s': '%s'", uri, e.message); |
1692 | + } |
1693 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1694 | + } |
1695 | + |
1696 | + private Unity.ActivationResponse app_preview_uninstall (string uri) |
1697 | + { |
1698 | + if (uri.has_prefix ("application://")) |
1699 | + { |
1700 | + string desktopfile = uri.substring (14); // trim "application://" |
1701 | + |
1702 | + // de-mangle desktop file names back to what S-C expects |
1703 | + if (sc_mangler.contains (desktopfile)) |
1704 | + { |
1705 | + desktopfile = sc_mangler.get (desktopfile); |
1706 | + } |
1707 | + |
1708 | + var pkginfo = pkgsearcher.get_by_desktop_file (desktopfile); |
1709 | + |
1710 | + if (pkginfo != null && pkginfo.package_name != null) |
1711 | + { |
1712 | + try |
1713 | + { |
1714 | + aptdclient.connect_to_aptd (); |
1715 | + } |
1716 | + catch (IOError e) |
1717 | + { |
1718 | + warning ("Failed to connect to aptd: '%s'", e.message); |
1719 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1720 | + } |
1721 | + |
1722 | + call_remove_packages.begin (pkginfo.package_name, (obj, res) => |
1723 | + { |
1724 | + try { |
1725 | + string tid; |
1726 | + call_remove_packages.end (res, out tid); |
1727 | + debug ("transaction started: %s, pkg: %s\n", tid, pkginfo.package_name); |
1728 | + aptd_transaction = new AptdTransactionProxy (); |
1729 | + aptd_transaction.connect_to_aptd (tid); |
1730 | + aptd_transaction.simulate (); |
1731 | + aptd_transaction.run (); |
1732 | + } |
1733 | + catch (IOError e) |
1734 | + { |
1735 | + warning (@"Package '$(pkginfo.package_name)' removal failed: $(e.message)"); |
1736 | + } |
1737 | + }); |
1738 | + return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
1739 | + } |
1740 | + else |
1741 | + { |
1742 | + warning (@"Cannot find package info for $uri"); |
1743 | + } |
1744 | + } |
1745 | + warning (@"Can't handle '%s' in app_preview_uninstall handler", uri); |
1746 | + return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
1747 | } |
1748 | |
1749 | /* Load xdg menu info and build a Xapian index over it. |
1750 | @@ -447,22 +628,6 @@ |
1751 | return false; |
1752 | } |
1753 | |
1754 | - private async void build_scope_index () |
1755 | - { |
1756 | - try |
1757 | - { |
1758 | - var scope_registry = yield Unity.Protocol.ScopeRegistry.find_scopes ( |
1759 | - Config.PKGDATADIR + "/scopes"); |
1760 | - |
1761 | - debug ("Indexing scopes"); |
1762 | - scopesearcher = new Unity.Package.Searcher.for_scopes (scope_registry); |
1763 | - } |
1764 | - catch (Error err) |
1765 | - { |
1766 | - warning ("Unable to find scopes: %s", err.message); |
1767 | - } |
1768 | - } |
1769 | - |
1770 | private void populate_zg_templates () |
1771 | { |
1772 | /* Create a template that activation of applications */ |
1773 | @@ -477,11 +642,11 @@ |
1774 | |
1775 | private void mark_dirty () |
1776 | { |
1777 | - scope.queue_search_changed (SearchType.DEFAULT); |
1778 | - scope.queue_search_changed (SearchType.GLOBAL); |
1779 | + results_invalidated (SearchType.DEFAULT); |
1780 | + results_invalidated (SearchType.GLOBAL); |
1781 | } |
1782 | |
1783 | - private async void update_popularities () |
1784 | + public async void update_popularities () |
1785 | { |
1786 | try |
1787 | { |
1788 | @@ -515,6 +680,189 @@ |
1789 | } |
1790 | } |
1791 | |
1792 | + public Icon find_pkg_icon (string? desktop_file, string icon_name) |
1793 | + { |
1794 | + if (desktop_file != null) |
1795 | + { |
1796 | + string desktop_id = Path.get_basename (desktop_file); |
1797 | + bool installed = AppInfoManager.get_default().lookup (desktop_id) != null; |
1798 | + |
1799 | + /* If the app is already installed we should be able to pull the |
1800 | + * icon from the theme */ |
1801 | + if (installed) |
1802 | + return new ThemedIcon (icon_name); |
1803 | + } |
1804 | + |
1805 | + /* App is not installed - we need to find the right icon in the bowels |
1806 | + * of the software center */ |
1807 | + if (icon_name.has_prefix ("/")) |
1808 | + { |
1809 | + return new FileIcon (File.new_for_path (icon_name)); |
1810 | + } |
1811 | + else |
1812 | + { |
1813 | + Icon icon = file_icon_cache.lookup (icon_name); |
1814 | + |
1815 | + if (icon != null) |
1816 | + return icon; |
1817 | + |
1818 | + /* If the icon name contains a . it probably already have a |
1819 | + * type postfix - so test icon name directly */ |
1820 | + string path; |
1821 | + if ("." in icon_name) |
1822 | + { |
1823 | + path = @"$(Config.DATADIR)/app-install/icons/$(icon_name)"; |
1824 | + if (FileUtils.test (path, FileTest.EXISTS)) |
1825 | + { |
1826 | + icon = new FileIcon (File.new_for_path (path)); |
1827 | + file_icon_cache.insert (icon_name, icon); |
1828 | + return icon; |
1829 | + } |
1830 | + /* Try also software center cache dir */ |
1831 | + path = Path.build_filename (Environment.get_user_cache_dir (), |
1832 | + "software-center", |
1833 | + "icons", |
1834 | + icon_name); |
1835 | + if (FileUtils.test (path, FileTest.EXISTS)) |
1836 | + { |
1837 | + icon = new FileIcon (File.new_for_path (path)); |
1838 | + file_icon_cache.insert (icon_name, icon); |
1839 | + return icon; |
1840 | + } |
1841 | + } |
1842 | + |
1843 | + /* Now try appending all the image extensions we know */ |
1844 | + foreach (var ext in image_extensions) |
1845 | + { |
1846 | + path = @"$(Config.DATADIR)/app-install/icons/$(icon_name).$(ext)"; |
1847 | + if (FileUtils.test (path, FileTest.EXISTS)) |
1848 | + { |
1849 | + /* Got it! Cache the icon path and return the icon */ |
1850 | + icon = new FileIcon (File.new_for_path (path)); |
1851 | + file_icon_cache.insert (icon_name, icon); |
1852 | + return icon; |
1853 | + } |
1854 | + } |
1855 | + } |
1856 | + |
1857 | + /* Cache the fact that we couldn't find this icon */ |
1858 | + var icon = new ThemedIcon (GENERIC_APP_ICON); |
1859 | + file_icon_cache.insert (icon_name, icon); |
1860 | + |
1861 | + return icon; |
1862 | + } |
1863 | + |
1864 | + public async SoftwareCenterData.AppDetailsData get_app_details (string appname, string pkgname) throws Error |
1865 | + { |
1866 | + if (sc_data_provider == null) |
1867 | + { |
1868 | + sc_data_provider = new SoftwareCenterDataCache (TOP_RATED_ITEMS_CACHE_LIFETIME); |
1869 | + } |
1870 | + |
1871 | + debug ("Requesting pkg info: %s, %s\n", pkgname, appname); |
1872 | + return yield sc_data_provider.get_app_details (appname, pkgname); |
1873 | + } |
1874 | + |
1875 | + public async bool get_version_and_screenshot (string app_id, out string version, out string screenshot) |
1876 | + { |
1877 | + version = null; |
1878 | + screenshot = null; |
1879 | + // de-mangle desktop file names back to what S-C expects |
1880 | + string mangled_id; |
1881 | + if (sc_mangler.contains (app_id)) |
1882 | + { |
1883 | + mangled_id = sc_mangler.get (app_id); |
1884 | + } |
1885 | + else |
1886 | + { |
1887 | + mangled_id = app_id; |
1888 | + } |
1889 | + var pkginfo = pkgsearcher.get_by_desktop_file (mangled_id); |
1890 | + if (pkginfo != null) |
1891 | + { |
1892 | + SoftwareCenterData.AppDetailsData? details; |
1893 | + try { |
1894 | + details = yield get_app_details(pkginfo.application_name, |
1895 | + pkginfo.package_name); |
1896 | + } catch (Error e) { |
1897 | + return false; |
1898 | + } |
1899 | + if (details != null) { |
1900 | + version = details.version; |
1901 | + screenshot = details.screenshot; |
1902 | + return true; |
1903 | + } |
1904 | + } |
1905 | + return false; |
1906 | + } |
1907 | + } |
1908 | + |
1909 | + private class ApplicationsSearch : Unity.ScopeSearchBase |
1910 | + { |
1911 | + private ApplicationsScope scope; |
1912 | + |
1913 | + public ApplicationsSearch (ApplicationsScope scope, Unity.SearchContext search_context) |
1914 | + { |
1915 | + this.scope = scope; |
1916 | + set_search_context (search_context); |
1917 | + } |
1918 | + |
1919 | + public override void run () |
1920 | + { |
1921 | + var ml = new MainLoop (); |
1922 | + run_async (() => { ml.quit (); }); |
1923 | + ml.run (); |
1924 | + } |
1925 | + |
1926 | + public override void run_async (Unity.ScopeSearchBaseCallback async_callback) |
1927 | + { |
1928 | + dispatch_search.begin ( |
1929 | + () => { |
1930 | + async_callback (this); |
1931 | + }); |
1932 | + } |
1933 | + |
1934 | + private async void dispatch_search () |
1935 | + { |
1936 | + var context = this.search_context; |
1937 | + |
1938 | + if (scope.popularities_dirty) |
1939 | + { |
1940 | + scope.popularities_dirty = false; |
1941 | + // we're not passing the cancellable, cause cancelling this search |
1942 | + // shouldn't cancel getting most popular apps |
1943 | + yield scope.update_popularities (); |
1944 | + if (context.cancellable.is_cancelled ()) return; |
1945 | + } |
1946 | + |
1947 | + if (context.search_type == SearchType.DEFAULT) |
1948 | + yield search_default (); |
1949 | + else |
1950 | + yield search_global (); |
1951 | + } |
1952 | + |
1953 | + private bool local_apps_active () |
1954 | + { |
1955 | + var filter = search_context.filter_state.get_filter_by_id ("unity-sources") as Unity.OptionsFilter; |
1956 | + if (filter.filtering) |
1957 | + { |
1958 | + var option = filter.get_option ("local"); |
1959 | + return option == null || option.active; |
1960 | + } |
1961 | + return true; |
1962 | + } |
1963 | + |
1964 | + private bool usc_apps_active () |
1965 | + { |
1966 | + var filter = search_context.filter_state.get_filter_by_id ("unity-sources") as Unity.OptionsFilter; |
1967 | + if (filter.filtering) |
1968 | + { |
1969 | + var option = filter.get_option ("usc"); |
1970 | + return option == null || option.active; |
1971 | + } |
1972 | + return true; |
1973 | + } |
1974 | + |
1975 | /* Returns TRUE if application is NOT installed */ |
1976 | public bool filter_cb (Unity.Package.PackageInfo pkginfo) |
1977 | { |
1978 | @@ -523,41 +871,33 @@ |
1979 | return app == null; |
1980 | } |
1981 | |
1982 | - private bool is_phablet_ui (DeprecatedScopeSearch search) |
1983 | + private bool is_phablet_ui () |
1984 | { |
1985 | - // XXX: this will need changing when the hint is finalised. |
1986 | - unowned Variant? v = search.hints["form-factor"]; |
1987 | - if (v != null) |
1988 | - { |
1989 | - string form_factor = v.get_string(); |
1990 | - return form_factor == "phone" || form_factor == "tablet"; |
1991 | - } |
1992 | - return false; |
1993 | + var form_factor = search_context.search_metadata.form_factor; |
1994 | + |
1995 | + return form_factor == "phone" || form_factor == "tablet"; |
1996 | } |
1997 | |
1998 | - private async void update_scope_search (DeprecatedScopeSearch search, |
1999 | - GLib.Cancellable cancellable) |
2000 | + private async void search_default () |
2001 | { |
2002 | - var model = search.results_model; |
2003 | + var context = this.search_context; |
2004 | + var result_set = context.result_set; |
2005 | /* We'll clear the model once we finish waiting for the dbus-call |
2006 | * to finish, to prevent flicker. */ |
2007 | |
2008 | - var search_string = search.search_string.strip (); |
2009 | + var search_string = context.search_query; |
2010 | debug ("Searching for: %s", search_string); |
2011 | |
2012 | - var type_filter = search.get_filter ("type") as OptionsFilter; |
2013 | + var type_filter = context.filter_state.get_filter_by_id ("type") as OptionsFilter; |
2014 | |
2015 | string pkg_search_string = XapianUtils.prepare_pkg_search_string (search_string, type_filter); |
2016 | |
2017 | bool has_filter = (type_filter != null && type_filter.filtering); |
2018 | bool has_search = !Utils.is_search_empty (search_string); |
2019 | - bool running_on_phablet = is_phablet_ui (search); |
2020 | + bool running_on_phablet = is_phablet_ui (); |
2021 | |
2022 | Timer timer = new Timer (); |
2023 | |
2024 | - var transaction = new Dee.Transaction (model); |
2025 | - transaction.clear (); |
2026 | - |
2027 | /* Even though the Installed apps search is super fast, we wait here |
2028 | * for the Most Popular search to finish, because otherwise we'll update |
2029 | * the Installed category too soon and this will cause flicker |
2030 | @@ -565,16 +905,16 @@ |
2031 | |
2032 | Set<string> installed_uris = new HashSet<string> (); |
2033 | Set<string> available_uris = new HashSet<string> (); |
2034 | - var appresults = appsearcher.search (pkg_search_string, 0, |
2035 | - Unity.Package.SearchType.PREFIX, |
2036 | - has_search ? |
2037 | - Unity.Package.Sort.BY_RELEVANCY : |
2038 | - Unity.Package.Sort.BY_NAME); |
2039 | - if (local_apps_active (search)) |
2040 | + var appresults = scope.appsearcher.search ( |
2041 | + pkg_search_string, 0, Unity.Package.SearchType.PREFIX, |
2042 | + has_search ? |
2043 | + Unity.Package.Sort.BY_RELEVANCY : |
2044 | + Unity.Package.Sort.BY_NAME); |
2045 | + if (local_apps_active ()) |
2046 | { |
2047 | if (has_search) resort_pkg_search_results (appresults); |
2048 | add_pkg_search_result (appresults, installed_uris, available_uris, |
2049 | - transaction, Category.INSTALLED, |
2050 | + result_set, Category.INSTALLED, |
2051 | 0, running_on_phablet); |
2052 | } |
2053 | |
2054 | @@ -582,7 +922,7 @@ |
2055 | debug ("Entry search listed %i Installed apps in %fms for query: %s", |
2056 | appresults.num_hits, timer.elapsed ()*1000, pkg_search_string); |
2057 | |
2058 | - if (local_apps_active (search) && display_recent_apps) |
2059 | + if (local_apps_active () && scope.display_recent_apps) |
2060 | { |
2061 | try |
2062 | { |
2063 | @@ -592,15 +932,16 @@ |
2064 | * the search query */ |
2065 | var zg_search_string = XapianUtils.prepare_zg_search_string ("", type_filter); |
2066 | |
2067 | - var results = yield zg_index.search (zg_search_string, |
2068 | - new Zeitgeist.TimeRange.anytime(), |
2069 | - zg_templates, |
2070 | - 0, |
2071 | - 20, |
2072 | - Zeitgeist.ResultType.MOST_RECENT_SUBJECTS, |
2073 | - cancellable); |
2074 | + var results = yield scope.zg_index.search ( |
2075 | + zg_search_string, |
2076 | + new Zeitgeist.TimeRange.anytime (), |
2077 | + scope.zg_templates, |
2078 | + 0, |
2079 | + 20, |
2080 | + Zeitgeist.ResultType.MOST_RECENT_SUBJECTS, |
2081 | + context.cancellable.get_gcancellable ()); |
2082 | |
2083 | - append_events_with_category (results, transaction, Category.RECENT, |
2084 | + append_events_with_category (results, result_set, Category.RECENT, |
2085 | false, 6, installed_uris); |
2086 | |
2087 | timer.stop (); |
2088 | @@ -616,36 +957,21 @@ |
2089 | } |
2090 | } |
2091 | |
2092 | - // FIXME: use the new API and ResultSet.flush () instead |
2093 | - try |
2094 | - { |
2095 | - transaction.commit (); |
2096 | - } |
2097 | - catch (Error err) |
2098 | - { |
2099 | - warning ("Unable to commit transaction: %s", err.message); |
2100 | - } |
2101 | - |
2102 | - // add scopes (if filter is active) |
2103 | - if (!has_filter || type_filter.get_option ("scopes").active) |
2104 | - { |
2105 | - add_local_scopes_results (search_string, model, installed_uris); |
2106 | - add_remote_scopes_results (search_string, model); |
2107 | - } |
2108 | - |
2109 | - purchase_info = new PurchaseInfoHelper (); |
2110 | + result_set.flush (); |
2111 | + |
2112 | + scope.purchase_info = new PurchaseInfoHelper (); |
2113 | |
2114 | /* If we don't have a search we display 6 random apps */ |
2115 | - if (usc_apps_active (search) && display_available_apps && pkgsearcher != null) |
2116 | + if (usc_apps_active () && scope.display_available_apps && scope.pkgsearcher != null) |
2117 | { |
2118 | if (has_search) |
2119 | { |
2120 | timer.start (); |
2121 | - var pkgresults = pkgsearcher.search (pkg_search_string, 50, |
2122 | - Unity.Package.SearchType.PREFIX, |
2123 | - Unity.Package.Sort.BY_RELEVANCY); |
2124 | + var pkgresults = scope.pkgsearcher.search ( |
2125 | + pkg_search_string, 50, Unity.Package.SearchType.PREFIX, |
2126 | + Unity.Package.Sort.BY_RELEVANCY); |
2127 | add_pkg_search_result (pkgresults, installed_uris, available_uris, |
2128 | - model, Category.AVAILABLE, |
2129 | + result_set, Category.AVAILABLE, |
2130 | 0, running_on_phablet); |
2131 | timer.stop (); |
2132 | debug ("Entry search listed %i Available apps in %fms for query: %s", |
2133 | @@ -656,9 +982,9 @@ |
2134 | timer.start (); |
2135 | string? filter_query = XapianUtils.prepare_pkg_search_string (search_string, type_filter); |
2136 | |
2137 | - var pkgresults = pkgsearcher.get_apps (filter_query, MAX_APP_FOR_DOWNLOAD_FOR_EMPTY_QUERY, filter_cb); |
2138 | - purchase_info.from_pkgresults (pkgresults); |
2139 | - add_pkg_search_result (pkgresults, installed_uris, available_uris, model, Category.AVAILABLE, MAX_APP_FOR_DOWNLOAD_FOR_EMPTY_QUERY, running_on_phablet); |
2140 | + var pkgresults = scope.pkgsearcher.get_apps (filter_query, MAX_APP_FOR_DOWNLOAD_FOR_EMPTY_QUERY, filter_cb); |
2141 | + scope.purchase_info.from_pkgresults (pkgresults); |
2142 | + add_pkg_search_result (pkgresults, installed_uris, available_uris, result_set, Category.AVAILABLE, MAX_APP_FOR_DOWNLOAD_FOR_EMPTY_QUERY, running_on_phablet); |
2143 | timer.stop (); |
2144 | debug ("Entry search listed %i Available apps in %fms", |
2145 | pkgresults.num_hits, timer.elapsed ()*1000); |
2146 | @@ -672,20 +998,20 @@ |
2147 | { |
2148 | Set<string> duplicates_lookup = new HashSet<string> (); |
2149 | |
2150 | - if (sc_data_provider == null) |
2151 | - sc_data_provider = new SoftwareCenterDataCache (TOP_RATED_ITEMS_CACHE_LIFETIME); |
2152 | - |
2153 | - var whats_new = sc_data_provider.get_items_for_category ("unity-whats-new"); |
2154 | - var query = purchase_info.create_pkgsearch_query (whats_new); |
2155 | - var tmpresults = pkgsearcher.get_by_exact_names (query); |
2156 | - purchase_info.from_pkgresults (tmpresults); |
2157 | - hits = add_sc_category_results (whats_new, model, Category.AVAILABLE, ref duplicates_lookup, MAX_WHATS_NEW_APPS_FOR_EMPTY_QUERY); |
2158 | - |
2159 | - var top_rated = sc_data_provider.get_items_for_category ("unity-top-rated"); |
2160 | - query = purchase_info.create_pkgsearch_query (top_rated); |
2161 | - tmpresults = pkgsearcher.get_by_exact_names (query); |
2162 | - purchase_info.from_pkgresults (tmpresults); |
2163 | - hits += add_sc_category_results (top_rated, model, Category.AVAILABLE, ref duplicates_lookup, MAX_TOP_RATED_APPS_FOR_EMPTY_QUERY); |
2164 | + if (scope.sc_data_provider == null) |
2165 | + scope.sc_data_provider = new SoftwareCenterDataCache (TOP_RATED_ITEMS_CACHE_LIFETIME); |
2166 | + |
2167 | + var whats_new = yield scope.sc_data_provider.get_items_for_category ("unity-whats-new"); |
2168 | + var query = scope.purchase_info.create_pkgsearch_query (whats_new); |
2169 | + var tmpresults = scope.pkgsearcher.get_by_exact_names (query); |
2170 | + scope.purchase_info.from_pkgresults (tmpresults); |
2171 | + hits = add_sc_category_results (whats_new, result_set, Category.AVAILABLE, ref duplicates_lookup, MAX_WHATS_NEW_APPS_FOR_EMPTY_QUERY); |
2172 | + |
2173 | + var top_rated = yield scope.sc_data_provider.get_items_for_category ("unity-top-rated"); |
2174 | + query = scope.purchase_info.create_pkgsearch_query (top_rated); |
2175 | + tmpresults = scope.pkgsearcher.get_by_exact_names (query); |
2176 | + scope.purchase_info.from_pkgresults (tmpresults); |
2177 | + hits += add_sc_category_results (top_rated, result_set, Category.AVAILABLE, ref duplicates_lookup, MAX_TOP_RATED_APPS_FOR_EMPTY_QUERY); |
2178 | } |
2179 | catch (GLib.Error e) |
2180 | { |
2181 | @@ -698,61 +1024,63 @@ |
2182 | } |
2183 | } |
2184 | |
2185 | + /* XXX: |
2186 | if (model.get_n_rows () == 0) |
2187 | { |
2188 | search.set_reply_hint ("no-results-hint", |
2189 | _("Sorry, there are no applications that match your search.")); |
2190 | } |
2191 | + */ |
2192 | } |
2193 | |
2194 | - private async void update_global_search (DeprecatedScopeSearch search, |
2195 | - GLib.Cancellable cancellable) |
2196 | + private async void search_global () |
2197 | { |
2198 | + var context = this.search_context; |
2199 | + var result_set = context.result_set; |
2200 | /* |
2201 | * In global search, with a non-empty search string, we collate all |
2202 | * hits under one Applications category |
2203 | */ |
2204 | - var search_string = search.search_string.strip (); |
2205 | + var search_string = context.search_query; |
2206 | |
2207 | if (Utils.is_search_empty (search_string)) |
2208 | { |
2209 | - yield update_global_without_search (search, cancellable); |
2210 | + yield update_global_without_search (); |
2211 | return; |
2212 | } |
2213 | |
2214 | - bool running_on_phablet = is_phablet_ui (search); |
2215 | - var model = search.results_model; |
2216 | - |
2217 | - model.clear (); |
2218 | + bool running_on_phablet = is_phablet_ui (); |
2219 | |
2220 | var pkg_search_string = XapianUtils.prepare_pkg_search_string (search_string, null); |
2221 | Set<string> installed_uris = new HashSet<string> (); |
2222 | Set<string> available_uris = new HashSet<string> (); |
2223 | var timer = new Timer (); |
2224 | - var appresults = appsearcher.search (pkg_search_string, 0, |
2225 | - Unity.Package.SearchType.PREFIX, |
2226 | - Unity.Package.Sort.BY_RELEVANCY); |
2227 | + var appresults = scope.appsearcher.search ( |
2228 | + pkg_search_string, 0, |
2229 | + Unity.Package.SearchType.PREFIX, |
2230 | + Unity.Package.Sort.BY_RELEVANCY); |
2231 | resort_pkg_search_results (appresults); |
2232 | - add_pkg_search_result (appresults, installed_uris, available_uris, model, |
2233 | - Category.APPLICATIONS, 0, running_on_phablet); |
2234 | + add_pkg_search_result (appresults, installed_uris, available_uris, |
2235 | + result_set, Category.APPLICATIONS, 0, |
2236 | + running_on_phablet); |
2237 | |
2238 | timer.stop (); |
2239 | debug ("Global search listed %i Installed apps in %fms for query: %s", |
2240 | appresults.num_hits, timer.elapsed ()*1000, pkg_search_string); |
2241 | } |
2242 | |
2243 | - private async void update_global_without_search (DeprecatedScopeSearch search, |
2244 | - GLib.Cancellable cancellable) |
2245 | + private async void update_global_without_search () |
2246 | { |
2247 | /* |
2248 | * In global search, with an empty search string, we show just Recent Apps |
2249 | * Excluding apps with icons in the launcher (be they running or faves) |
2250 | */ |
2251 | - var model = search.results_model; |
2252 | + var context = this.search_context; |
2253 | + var result_set = context.result_set; |
2254 | |
2255 | Timer timer = new Timer (); |
2256 | |
2257 | - if (local_apps_active (search) && display_recent_apps) |
2258 | + if (local_apps_active () && scope.display_recent_apps) |
2259 | { |
2260 | try |
2261 | { |
2262 | @@ -760,16 +1088,16 @@ |
2263 | null); |
2264 | |
2265 | var time_range = new Zeitgeist.TimeRange.anytime (); |
2266 | - var results = yield log.find_events (time_range, |
2267 | - zg_templates, |
2268 | - Zeitgeist.StorageState.ANY, |
2269 | - 40, |
2270 | - Zeitgeist.ResultType.MOST_RECENT_SUBJECTS, |
2271 | - cancellable); |
2272 | + var results = yield scope.log.find_events ( |
2273 | + time_range, |
2274 | + scope.zg_templates, |
2275 | + Zeitgeist.StorageState.ANY, |
2276 | + 40, |
2277 | + Zeitgeist.ResultType.MOST_RECENT_SUBJECTS, |
2278 | + context.cancellable.get_gcancellable ()); |
2279 | |
2280 | - model.clear (); |
2281 | - append_events_with_category (results, model, Category.RECENT_APPS, |
2282 | - false); |
2283 | + append_events_with_category (results, result_set, |
2284 | + Category.RECENT_APPS, false); |
2285 | |
2286 | timer.stop (); |
2287 | debug ("Entry search found %u/%u Recently Used apps in %fms for query '%s'", |
2288 | @@ -781,81 +1109,9 @@ |
2289 | return; |
2290 | } catch (GLib.Error e) { |
2291 | warning ("Error performing search '%s': %s", |
2292 | - search.search_string, e.message); |
2293 | - } |
2294 | - } |
2295 | - } |
2296 | - |
2297 | - public Icon find_pkg_icon (string? desktop_file, string icon_name) |
2298 | - { |
2299 | - if (desktop_file != null) |
2300 | - { |
2301 | - string desktop_id = Path.get_basename (desktop_file); |
2302 | - bool installed = AppInfoManager.get_default().lookup (desktop_id) != null; |
2303 | - |
2304 | - /* If the app is already installed we should be able to pull the |
2305 | - * icon from the theme */ |
2306 | - if (installed) |
2307 | - return new ThemedIcon (icon_name); |
2308 | - } |
2309 | - |
2310 | - /* App is not installed - we need to find the right icon in the bowels |
2311 | - * of the software center */ |
2312 | - if (icon_name.has_prefix ("/")) |
2313 | - { |
2314 | - return new FileIcon (File.new_for_path (icon_name)); |
2315 | - } |
2316 | - else |
2317 | - { |
2318 | - Icon icon = file_icon_cache.lookup (icon_name); |
2319 | - |
2320 | - if (icon != null) |
2321 | - return icon; |
2322 | - |
2323 | - /* If the icon name contains a . it probably already have a |
2324 | - * type postfix - so test icon name directly */ |
2325 | - string path; |
2326 | - if ("." in icon_name) |
2327 | - { |
2328 | - path = @"$(Config.DATADIR)/app-install/icons/$(icon_name)"; |
2329 | - if (FileUtils.test (path, FileTest.EXISTS)) |
2330 | - { |
2331 | - icon = new FileIcon (File.new_for_path (path)); |
2332 | - file_icon_cache.insert (icon_name, icon); |
2333 | - return icon; |
2334 | - } |
2335 | - /* Try also software center cache dir */ |
2336 | - path = Path.build_filename (Environment.get_user_cache_dir (), |
2337 | - "software-center", |
2338 | - "icons", |
2339 | - icon_name); |
2340 | - if (FileUtils.test (path, FileTest.EXISTS)) |
2341 | - { |
2342 | - icon = new FileIcon (File.new_for_path (path)); |
2343 | - file_icon_cache.insert (icon_name, icon); |
2344 | - return icon; |
2345 | - } |
2346 | - } |
2347 | - |
2348 | - /* Now try appending all the image extensions we know */ |
2349 | - foreach (var ext in image_extensions) |
2350 | - { |
2351 | - path = @"$(Config.DATADIR)/app-install/icons/$(icon_name).$(ext)"; |
2352 | - if (FileUtils.test (path, FileTest.EXISTS)) |
2353 | - { |
2354 | - /* Got it! Cache the icon path and return the icon */ |
2355 | - icon = new FileIcon (File.new_for_path (path)); |
2356 | - file_icon_cache.insert (icon_name, icon); |
2357 | - return icon; |
2358 | - } |
2359 | - } |
2360 | - } |
2361 | - |
2362 | - /* Cache the fact that we couldn't find this icon */ |
2363 | - var icon = new ThemedIcon (GENERIC_APP_ICON); |
2364 | - file_icon_cache.insert (icon_name, icon); |
2365 | - |
2366 | - return icon; |
2367 | + search_context.search_query, e.message); |
2368 | + } |
2369 | + } |
2370 | } |
2371 | |
2372 | /* |
2373 | @@ -877,10 +1133,10 @@ |
2374 | int delta = (rel_a - rel_b).abs (); |
2375 | if (delta < 10) |
2376 | { |
2377 | - string id_a = sc_mangler.extract_desktop_id (a.desktop_file); |
2378 | - string id_b = sc_mangler.extract_desktop_id (b.desktop_file); |
2379 | - rel_a = popularity_map["application://" + id_a]; |
2380 | - rel_b = popularity_map["application://" + id_b]; |
2381 | + string id_a = scope.sc_mangler.extract_desktop_id (a.desktop_file); |
2382 | + string id_b = scope.sc_mangler.extract_desktop_id (b.desktop_file); |
2383 | + rel_a = scope.popularity_map["application://" + id_a]; |
2384 | + rel_b = scope.popularity_map["application://" + id_b]; |
2385 | } |
2386 | return rel_b - rel_a; // we want higher relevancy first |
2387 | }); |
2388 | @@ -910,7 +1166,7 @@ |
2389 | { |
2390 | annotated_icon.ribbon = _("Free"); |
2391 | } |
2392 | - if (force_small_icons_for_suggestions || use_small_icon |
2393 | + if (scope.force_small_icons_for_suggestions || use_small_icon |
2394 | || app_icon.to_string () == GENERIC_APP_ICON) |
2395 | { |
2396 | annotated_icon.size_hint = IconSizeHint.SMALL; |
2397 | @@ -923,7 +1179,7 @@ |
2398 | * Add all results obtained from SoftwareCenterDataProvider |
2399 | */ |
2400 | private uint add_sc_category_results (SoftwareCenterData.AppInfo?[] results, |
2401 | - Dee.Model model, |
2402 | + Unity.ResultSet result_set, |
2403 | Category category, |
2404 | ref Set<string> duplicates_lookup, |
2405 | uint max_results) |
2406 | @@ -936,8 +1192,9 @@ |
2407 | continue; |
2408 | |
2409 | string icon_obj; |
2410 | - Icon app_icon = find_pkg_icon (app.desktop_file, app.icon); |
2411 | - var pinfo = purchase_info.find (app.application_name, app.package_name); |
2412 | + Icon app_icon = scope.find_pkg_icon (app.desktop_file, app.icon); |
2413 | + var pinfo = scope.purchase_info.find ( |
2414 | + app.application_name, app.package_name); |
2415 | if (pinfo != null) |
2416 | { |
2417 | // magazines need to use large icons |
2418 | @@ -954,14 +1211,19 @@ |
2419 | icon_obj = app_icon.to_string (); |
2420 | } |
2421 | |
2422 | - model.append (uri, icon_obj, |
2423 | - category, |
2424 | - pinfo != null && pinfo.paid ? Unity.ResultType.PERSONAL : Unity.ResultType.DEFAULT, |
2425 | - "application/x-desktop", |
2426 | - app.application_name, |
2427 | - "", //comment |
2428 | - "file://" + app.desktop_file, |
2429 | - empty_asv); |
2430 | + var result = Unity.ScopeResult (); |
2431 | + result.uri = uri; |
2432 | + result.icon_hint = icon_obj; |
2433 | + result.category = category; |
2434 | + result.result_type = pinfo != null && pinfo.paid ? |
2435 | + Unity.ResultType.PERSONAL : Unity.ResultType.DEFAULT; |
2436 | + result.mimetype = "application/x-desktop"; |
2437 | + result.title = app.application_name; |
2438 | + result.comment = ""; |
2439 | + result.dnd_uri = "file://" + app.desktop_file; |
2440 | + result.metadata = new HashTable<string,Variant> (str_hash, str_equal); |
2441 | + |
2442 | + result_set.add_result (result); |
2443 | duplicates_lookup.add (uri); |
2444 | i++; |
2445 | if (i == max_results) |
2446 | @@ -973,7 +1235,7 @@ |
2447 | private void add_pkg_search_result (Unity.Package.SearchResult results, |
2448 | Set<string> installed_uris, |
2449 | Set<string> available_uris, |
2450 | - Dee.Model model, |
2451 | + Unity.ResultSet result_set, |
2452 | Category category, |
2453 | uint max_add, |
2454 | bool running_on_phablet) |
2455 | @@ -986,8 +1248,8 @@ |
2456 | if (pkginfo.desktop_file == null) |
2457 | continue; |
2458 | |
2459 | - string desktop_id = sc_mangler.extract_desktop_id (pkginfo.desktop_file, |
2460 | - category == Category.AVAILABLE); |
2461 | + string desktop_id = scope.sc_mangler.extract_desktop_id ( |
2462 | + pkginfo.desktop_file, category == Category.AVAILABLE); |
2463 | string full_path; |
2464 | |
2465 | AppInfo? app = appmanager.lookup (desktop_id); |
2466 | @@ -1026,25 +1288,34 @@ |
2467 | continue; |
2468 | |
2469 | /* Extract basic metadata and register de-dupe keys */ |
2470 | - string display_name; |
2471 | - string comment; |
2472 | + var res = Unity.ScopeResult(); |
2473 | + res.uri = uri; |
2474 | + res.category = category; |
2475 | + res.result_type = (category != Category.AVAILABLE && |
2476 | + !results.fuzzy_search) ? |
2477 | + Unity.ResultType.PERSONAL : Unity.ResultType.DEFAULT; |
2478 | + res.mimetype = "application/x-desktop"; |
2479 | + res.dnd_uri = full_path != null ? "file://" + full_path : ""; |
2480 | + res.metadata = new HashTable<string, Variant> (str_hash, str_equal); |
2481 | switch (category) |
2482 | { |
2483 | case Category.INSTALLED: |
2484 | case Category.APPLICATIONS: |
2485 | installed_uris.add (uri); |
2486 | - display_name = app.get_display_name (); |
2487 | - comment = sanitize_binary_name (app.get_executable ()); |
2488 | + res.title = app.get_display_name (); |
2489 | + res.comment = sanitize_binary_name (app.get_executable ()); |
2490 | break; |
2491 | case Category.AVAILABLE: |
2492 | available_uris.add (uri); |
2493 | - display_name = pkginfo.application_name; |
2494 | - comment = ""; |
2495 | + res.title = pkginfo.application_name; |
2496 | + res.comment = ""; |
2497 | break; |
2498 | default: |
2499 | warning (@"Illegal category for package search $(category)"); |
2500 | continue; |
2501 | } |
2502 | + if (res.title == null) res.title = ""; |
2503 | + if (res.comment == null) res.comment = ""; |
2504 | |
2505 | /* We can only chuck out NoDisplay and OnlyShowIn app infos after |
2506 | * we have registered a de-dupe key for them - which is done in the |
2507 | @@ -1052,8 +1323,7 @@ |
2508 | if (app != null && !app.should_show ()) |
2509 | continue; |
2510 | |
2511 | - Icon icon = find_pkg_icon (pkginfo.desktop_file, pkginfo.icon); |
2512 | - string icon_str; |
2513 | + Icon icon = scope.find_pkg_icon (pkginfo.desktop_file, pkginfo.icon); |
2514 | if (category == Category.AVAILABLE) |
2515 | { |
2516 | /* If we have an available item, which is not a dupe, but is |
2517 | @@ -1067,30 +1337,21 @@ |
2518 | * use the 'unity-install://pkgname/Full App Name' URI scheme, |
2519 | * but only use that after we've de-duped the results. |
2520 | * But only change the URI *after* we've de-duped the results! */ |
2521 | - uri = @"unity-install://$(pkginfo.package_name)/$(pkginfo.application_name)"; |
2522 | + res.uri = @"unity-install://$(pkginfo.package_name)/$(pkginfo.application_name)"; |
2523 | available_uris.add (uri); |
2524 | |
2525 | // magazines need to use large icons |
2526 | bool use_small_icon = pkginfo.desktop_file.has_suffix (".desktop"); |
2527 | - icon_str = get_annotated_icon (icon, pkginfo.price, |
2528 | - !pkginfo.needs_purchase, |
2529 | - use_small_icon); |
2530 | + res.icon_hint = get_annotated_icon (icon, pkginfo.price, |
2531 | + !pkginfo.needs_purchase, |
2532 | + use_small_icon); |
2533 | } |
2534 | else |
2535 | { |
2536 | - icon_str = icon.to_string (); |
2537 | + res.icon_hint = icon.to_string (); |
2538 | } |
2539 | |
2540 | - var result_type = (category != Category.AVAILABLE && !results.fuzzy_search) ? |
2541 | - Unity.ResultType.PERSONAL : Unity.ResultType.DEFAULT; |
2542 | - model.append (uri, icon_str, |
2543 | - category, |
2544 | - result_type, |
2545 | - "application/x-desktop", |
2546 | - display_name != null ? display_name : "", |
2547 | - comment != null ? comment : "", |
2548 | - full_path != null ? "file://" + full_path : "", |
2549 | - empty_asv); |
2550 | + result_set.add_result (res); |
2551 | |
2552 | /* Stop if we added the number of items requested */ |
2553 | n_added++; |
2554 | @@ -1099,329 +1360,131 @@ |
2555 | } |
2556 | } |
2557 | |
2558 | - private static Icon? get_default_scope_icon () |
2559 | - { |
2560 | - try |
2561 | - { |
2562 | - return Icon.new_for_string (GENERIC_SCOPE_ICON); |
2563 | - } |
2564 | - catch (Error err) |
2565 | - { |
2566 | - warning ("%s", err.message); |
2567 | - } |
2568 | - return null; |
2569 | - } |
2570 | - |
2571 | - private void add_local_scopes_results (string search_string, |
2572 | - Dee.Model model, |
2573 | - Set<string> installed_uris) |
2574 | - { |
2575 | - // If the local scopes Xapian index hasn't been built, return. |
2576 | - if (scopesearcher == null) |
2577 | - return; |
2578 | - |
2579 | - bool has_search = !Utils.is_search_empty (search_string); |
2580 | - var pkg_search_string = XapianUtils.prepare_pkg_search_string ( |
2581 | - search_string, null); |
2582 | - |
2583 | - var results = scopesearcher.search (pkg_search_string, 0, |
2584 | - Unity.Package.SearchType.PREFIX, |
2585 | - has_search ? |
2586 | - Unity.Package.Sort.BY_RELEVANCY : |
2587 | - Unity.Package.Sort.BY_NAME); |
2588 | - |
2589 | - foreach (unowned Unity.Package.PackageInfo info in results.results) |
2590 | - { |
2591 | - /* De-dupe by "application://foo.scope", since that is what is |
2592 | - * used for software-center results. */ |
2593 | - string dedup_key = @"application://$(info.desktop_file)"; |
2594 | - if (dedup_key in installed_uris) |
2595 | - continue; |
2596 | - installed_uris.add(dedup_key); |
2597 | - |
2598 | - /* Don't include master scopes in the search results. This is |
2599 | - * performed after deduping so the master scopes don't just |
2600 | - * move to hte "available" category. */ |
2601 | - if (info.is_master_scope) |
2602 | - continue; |
2603 | - |
2604 | - var uri = @"scope://$(info.desktop_file)"; |
2605 | - var name = info.application_name; |
2606 | - var icon_hint = info.icon; |
2607 | - var category = info.desktop_file in disabled_scope_ids ? |
2608 | - Category.AVAILABLE : Category.INSTALLED; |
2609 | - |
2610 | - try |
2611 | - { |
2612 | - Icon base_icon = icon_hint == null || icon_hint == "" ? |
2613 | - get_default_scope_icon () : Icon.new_for_string (icon_hint); |
2614 | - var anno_icon = new AnnotatedIcon (base_icon); |
2615 | - anno_icon.size_hint = IconSizeHint.SMALL; |
2616 | - icon_hint = anno_icon.to_string (); |
2617 | - } |
2618 | - catch (Error err) |
2619 | - { |
2620 | - icon_hint = ""; |
2621 | - } |
2622 | - |
2623 | - model.append (uri, |
2624 | - icon_hint, |
2625 | - category, |
2626 | - ResultType.DEFAULT, |
2627 | - "application/x-unity-scope", |
2628 | - name != "" ? name : uri, |
2629 | - "", |
2630 | - "", // dnd_uri ?! |
2631 | - empty_asv); |
2632 | - } |
2633 | - } |
2634 | - |
2635 | - private void add_remote_scopes_results (string search_string, |
2636 | - Dee.Model model) |
2637 | - { |
2638 | - var results = Utils.search_index (scopes_index, analyzer, search_string); |
2639 | - foreach (var iter in results) |
2640 | - { |
2641 | - unowned string scope_id = |
2642 | - remote_scopes_model.get_string (iter, RemoteScopesColumn.SCOPE_ID); |
2643 | - var uri = @"scope://$(scope_id)"; |
2644 | - var name = |
2645 | - remote_scopes_model.get_string (iter, RemoteScopesColumn.NAME); |
2646 | - var icon_hint = |
2647 | - remote_scopes_model.get_string (iter, RemoteScopesColumn.ICON_HINT); |
2648 | - |
2649 | - try |
2650 | - { |
2651 | - Icon base_icon = icon_hint == null || icon_hint == "" ? |
2652 | - get_default_scope_icon () : Icon.new_for_string (icon_hint); |
2653 | - var anno_icon = new AnnotatedIcon (base_icon); |
2654 | - anno_icon.size_hint = IconSizeHint.SMALL; |
2655 | - icon_hint = anno_icon.to_string (); |
2656 | - } |
2657 | - catch (Error err) |
2658 | - { |
2659 | - icon_hint = ""; |
2660 | - } |
2661 | - |
2662 | - var category = scope_id in disabled_scope_ids ? |
2663 | - Category.AVAILABLE : Category.INSTALLED; |
2664 | - |
2665 | - model.append (uri, |
2666 | - icon_hint, |
2667 | - category, |
2668 | - ResultType.DEFAULT, |
2669 | - "application/x-unity-scope", |
2670 | - name != "" ? name : uri, |
2671 | - "", |
2672 | - "", // dnd_uri ?! |
2673 | - empty_asv); |
2674 | - } |
2675 | - } |
2676 | - private async void call_install_packages (string package_name, out string tid) throws IOError |
2677 | - { |
2678 | - tid = yield aptdclient.install_packages ({package_name}); |
2679 | - } |
2680 | - |
2681 | - private async void call_remove_packages (string package_name, out string tid) throws IOError |
2682 | - { |
2683 | - tid = yield aptdclient.remove_packages ({package_name}); |
2684 | - } |
2685 | - |
2686 | - /** |
2687 | - * Handler for free apps installation. |
2688 | - * Triggers package installation via apt-daemon DBus service |
2689 | - */ |
2690 | - private Unity.ActivationResponse app_preview_install (string uri) |
2691 | - { |
2692 | - if (uri.has_prefix ("unity-install://")) |
2693 | - { |
2694 | - string app = uri.substring (16); // trim "unity-install://" |
2695 | - string[] parts = app.split ("/"); |
2696 | - |
2697 | - if (parts.length > 1) |
2698 | - { |
2699 | - string pkgname = parts[0]; |
2700 | - string appname = parts[1]; |
2701 | - try |
2702 | - { |
2703 | - aptdclient.connect_to_aptd (); |
2704 | - } |
2705 | - catch (IOError e) |
2706 | - { |
2707 | - warning ("Failed to connect to aptd: '%s'", e.message); |
2708 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
2709 | - } |
2710 | - call_install_packages.begin (pkgname, (obj, res) => |
2711 | - { |
2712 | - try { |
2713 | - string tid; |
2714 | - call_install_packages.end (res, out tid); |
2715 | - debug ("transaction started: %s, pkg: %s\n", tid, pkgname); |
2716 | - aptd_transaction = new AptdTransactionProxy (); |
2717 | - aptd_transaction.connect_to_aptd (tid); |
2718 | - aptd_transaction.simulate (); |
2719 | - aptd_transaction.run (); |
2720 | - |
2721 | - launcherservice.connect_to_launcher (); |
2722 | - string desktop_file = preview_installable_desktop_file; |
2723 | - Icon icon = find_pkg_icon (null, preview_installable_icon_file); |
2724 | - launcherservice.add_launcher_item_from_position.begin (appname, icon.to_string (), 0, 0, 32, desktop_file, tid); |
2725 | - } |
2726 | - catch (IOError e) |
2727 | - { |
2728 | - warning ("Package '%s' installation failed: %s", pkgname, e.message); |
2729 | - } |
2730 | - }); |
2731 | - } |
2732 | - else |
2733 | - { |
2734 | - warning ("Bad install uri: '%s'", uri); |
2735 | - } |
2736 | - } |
2737 | - else |
2738 | - { |
2739 | - warning ("Can't handle '%s' in app_preview_install handler", uri); |
2740 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
2741 | - } |
2742 | - return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
2743 | - } |
2744 | - |
2745 | - private Unity.ActivationResponse start_software_center (string uri) |
2746 | - { |
2747 | - unowned string pkg = uri.offset (16); // strip off "unity-install://" prefix |
2748 | - debug ("Installing: %s", pkg); |
2749 | - |
2750 | - var args = new string[2]; |
2751 | - args[0] = "software-center"; |
2752 | - args[1] = pkg; |
2753 | - |
2754 | - try |
2755 | - { |
2756 | - Process.spawn_async (GLib.Environment.get_home_dir (), args, null, SpawnFlags.SEARCH_PATH, null, null); |
2757 | - } |
2758 | - catch (SpawnError e) |
2759 | - { |
2760 | - warning ("Failed to spawn software-center for uri '%s': %s", uri, e.message); |
2761 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
2762 | - } |
2763 | - |
2764 | - return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
2765 | - } |
2766 | - |
2767 | - private Unity.ActivationResponse app_preview_website (string uri) |
2768 | - { |
2769 | - try |
2770 | - { |
2771 | - AppInfo.launch_default_for_uri (preview_developer_website, null); |
2772 | - return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
2773 | - } |
2774 | - catch (Error e) |
2775 | - { |
2776 | - warning ("Failed to launch a web browser for uri '%s': '%s'", uri, e.message); |
2777 | - } |
2778 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
2779 | - } |
2780 | - |
2781 | - private Unity.ActivationResponse app_preview_uninstall (string uri) |
2782 | - { |
2783 | - if (uri.has_prefix ("application://")) |
2784 | - { |
2785 | - string desktopfile = uri.substring (14); // trim "application://" |
2786 | - |
2787 | - // de-mangle desktop file names back to what S-C expects |
2788 | - if (sc_mangler.contains (desktopfile)) |
2789 | - { |
2790 | - desktopfile = sc_mangler.get (desktopfile); |
2791 | - } |
2792 | - |
2793 | - var pkginfo = pkgsearcher.get_by_desktop_file (desktopfile); |
2794 | - |
2795 | - if (pkginfo != null && pkginfo.package_name != null) |
2796 | - { |
2797 | - try |
2798 | - { |
2799 | - aptdclient.connect_to_aptd (); |
2800 | - } |
2801 | - catch (IOError e) |
2802 | - { |
2803 | - warning ("Failed to connect to aptd: '%s'", e.message); |
2804 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
2805 | - } |
2806 | - |
2807 | - call_remove_packages.begin (pkginfo.package_name, (obj, res) => |
2808 | - { |
2809 | - try { |
2810 | - string tid; |
2811 | - call_remove_packages.end (res, out tid); |
2812 | - debug ("transaction started: %s, pkg: %s\n", tid, pkginfo.package_name); |
2813 | - aptd_transaction = new AptdTransactionProxy (); |
2814 | - aptd_transaction.connect_to_aptd (tid); |
2815 | - aptd_transaction.simulate (); |
2816 | - aptd_transaction.run (); |
2817 | - } |
2818 | - catch (IOError e) |
2819 | - { |
2820 | - warning (@"Package '$(pkginfo.package_name)' removal failed: $(e.message)"); |
2821 | - } |
2822 | - }); |
2823 | - return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
2824 | - } |
2825 | - else |
2826 | - { |
2827 | - warning (@"Cannot find package info for $uri"); |
2828 | - } |
2829 | - } |
2830 | - warning (@"Can't handle '%s' in app_preview_uninstall handler", uri); |
2831 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
2832 | - } |
2833 | - |
2834 | - public Unity.Preview preview (string uri) |
2835 | - { |
2836 | - Unity.Preview? preview = null; |
2837 | - bool installed = uri.has_prefix ("application://"); |
2838 | - bool is_scope = uri.has_prefix ("scope://"); |
2839 | - |
2840 | - if (installed || uri.has_prefix ("unity-install://")) |
2841 | - { |
2842 | - preview = make_app_preview (uri, installed); |
2843 | - } |
2844 | - else if (is_scope) |
2845 | - { |
2846 | - preview = make_scope_preview (uri); |
2847 | - } |
2848 | + /* Appends the subject URIs from a set of Zeitgeist.Events to our Dee.Model |
2849 | + * assuming that these events are already sorted */ |
2850 | + public void append_events_with_category (Zeitgeist.ResultSet events, |
2851 | + Unity.ResultSet result_set, |
2852 | + uint category_id, |
2853 | + bool include_favorites, |
2854 | + int max_results = int.MAX, |
2855 | + Set<string>? allowed_uris = null) |
2856 | + { |
2857 | + int num_results = 0; |
2858 | + foreach (var ev in events) |
2859 | + { |
2860 | + string? app_uri = null; |
2861 | + if (ev.num_subjects () > 0) |
2862 | + app_uri = ev.get_subject (0).get_uri (); |
2863 | + |
2864 | + if (app_uri == null) |
2865 | + { |
2866 | + warning ("Unexpected event without subject"); |
2867 | + continue; |
2868 | + } |
2869 | + |
2870 | + /* Assert that we indeed have a known application as actor */ |
2871 | + string desktop_id = Utils.get_desktop_id_for_actor (app_uri); |
2872 | + |
2873 | + /* Discard Recently Used apps that are in the launcher */ |
2874 | + if ((category_id == Category.RECENT || |
2875 | + category_id == Category.RECENT_APPS) && |
2876 | + !include_favorites && |
2877 | + (scope.favorite_apps.has_app_id (desktop_id) |
2878 | + || scope.app_watcher.has_app_id (desktop_id))) |
2879 | + continue; |
2880 | + |
2881 | + var appmanager = AppInfoManager.get_default (); |
2882 | + AppInfo? app = appmanager.lookup (desktop_id); |
2883 | + |
2884 | + if (app == null) |
2885 | + continue; |
2886 | + |
2887 | + if (!app.should_show ()) |
2888 | + continue; |
2889 | + |
2890 | + /* HACK! when using the max_results together with allowed_uris, |
2891 | + * the limit serves as a truncation point - therefore results which |
2892 | + * are not displayed the first time won't be displayed later |
2893 | + * (consider results A, B, C - all of them are allowed and we use |
2894 | + * limit 2 - so only A and B is displayed, later we change allowed to |
2895 | + * B and C, so normally we would display both B and C, but that's not |
2896 | + * desired because C wasn't shown in the first place, so we display |
2897 | + * only B) */ |
2898 | + if (num_results++ >= max_results) break; |
2899 | + |
2900 | + if (allowed_uris != null && !(app_uri in allowed_uris)) continue; |
2901 | + |
2902 | + var result = Unity.ScopeResult (); |
2903 | + result.uri = app_uri; |
2904 | + result.icon_hint = app.get_icon ().to_string (); |
2905 | + result.category = category_id; |
2906 | + result.result_type = Unity.ResultType.DEFAULT; |
2907 | + result.mimetype = "application/x-desktop"; |
2908 | + result.title = app.get_display_name (); |
2909 | + result.comment = sanitize_binary_name (app.get_executable ()); |
2910 | + string full_path = appmanager.get_path (desktop_id); |
2911 | + result.dnd_uri = full_path != null ? "file://" + full_path : app_uri; |
2912 | + result.metadata = new HashTable<string,Variant> (str_hash, str_equal); |
2913 | + |
2914 | + result_set.add_result (result); |
2915 | + } |
2916 | + } |
2917 | + } |
2918 | + |
2919 | + private class ApplicationsResultPreviewer : Unity.ResultPreviewer |
2920 | + { |
2921 | + private ApplicationsScope scope; |
2922 | + |
2923 | + public ApplicationsResultPreviewer (ApplicationsScope scope, Unity.ScopeResult result, Unity.SearchMetadata metadata) |
2924 | + { |
2925 | + this.scope = scope; |
2926 | + set_scope_result (result); |
2927 | + set_search_metadata (metadata); |
2928 | + } |
2929 | + |
2930 | + public override Unity.AbstractPreview? run () |
2931 | + { |
2932 | + var ml = new MainLoop (); |
2933 | + Unity.AbstractPreview? preview = null; |
2934 | + run_async ((obj, p) => |
2935 | + { |
2936 | + preview = p; |
2937 | + ml.quit (); |
2938 | + }); |
2939 | + ml.run (); |
2940 | return preview; |
2941 | } |
2942 | |
2943 | - private SoftwareCenterData.AppDetailsData get_app_details (string appname, string pkgname) throws Error |
2944 | + public override void run_async (Unity.AbstractPreviewCallback callback) |
2945 | { |
2946 | - if (sc_data_provider == null) |
2947 | - { |
2948 | - sc_data_provider = new SoftwareCenterDataCache (TOP_RATED_ITEMS_CACHE_LIFETIME); |
2949 | - sc_data_provider.connect_to (); |
2950 | - } |
2951 | - |
2952 | - debug ("Requesting pkg info: %s, %s\n", pkgname, appname); |
2953 | - return sc_data_provider.get_app_details (appname, pkgname); |
2954 | + make_preview.begin ((obj, res) => |
2955 | + { |
2956 | + var preview = make_preview.end (res); |
2957 | + callback (this, preview); |
2958 | + }); |
2959 | } |
2960 | |
2961 | - private Unity.Preview make_app_preview (string uri, bool installed) |
2962 | + private async Unity.AbstractPreview? make_preview () |
2963 | { |
2964 | Unity.ApplicationPreview? preview = null; |
2965 | string desktopfile = null; |
2966 | string pkgname = ""; |
2967 | string appname = ""; |
2968 | |
2969 | + var uri = result.uri; |
2970 | + bool installed = uri.has_prefix ("application://"); |
2971 | + |
2972 | if (installed) |
2973 | { |
2974 | desktopfile = uri.substring (14); //remove "application://" prefix |
2975 | |
2976 | // de-mangle desktop file names back to what S-C expects |
2977 | - if (sc_mangler.contains (desktopfile)) |
2978 | + if (scope.sc_mangler.contains (desktopfile)) |
2979 | { |
2980 | - desktopfile = sc_mangler.get (desktopfile); |
2981 | + desktopfile = scope.sc_mangler.get (desktopfile); |
2982 | } |
2983 | |
2984 | - Unity.Package.PackageInfo pkginfo = pkgsearcher.get_by_desktop_file (desktopfile); |
2985 | + var pkginfo = scope.pkgsearcher.get_by_desktop_file (desktopfile); |
2986 | if (pkginfo != null) |
2987 | { |
2988 | appname = pkginfo.application_name; |
2989 | @@ -1442,14 +1505,14 @@ |
2990 | if (pkgname != "") |
2991 | { |
2992 | try { |
2993 | - var details = get_app_details (appname, pkgname); |
2994 | + var details = yield scope.get_app_details (appname, pkgname); |
2995 | |
2996 | Icon? icon = null; |
2997 | if (installed) |
2998 | icon = new GLib.ThemedIcon (details.icon); |
2999 | else |
3000 | { |
3001 | - icon = find_pkg_icon (null, details.icon); |
3002 | + icon = scope.find_pkg_icon (null, details.icon); |
3003 | if (icon.to_string () == GENERIC_APP_ICON && details.icon_url != null && details.icon_url != "") |
3004 | { |
3005 | icon = new GLib.FileIcon (File.new_for_uri (details.icon_url)); |
3006 | @@ -1476,11 +1539,11 @@ |
3007 | preview = new Unity.ApplicationPreview (details.name, subtitle, details.description, icon, screenshot); |
3008 | preview.license = details.license; |
3009 | |
3010 | - init_ratings_db (); |
3011 | - if (ratings != null) |
3012 | + scope.init_ratings_db (); |
3013 | + if (scope.ratings != null) |
3014 | { |
3015 | Unity.Ratings.Result result; |
3016 | - ratings.query (pkgname, out result); |
3017 | + scope.ratings.query (pkgname, out result); |
3018 | preview.set_rating (result.average_rating / 5.0f, result.total_rating); |
3019 | } |
3020 | |
3021 | @@ -1498,7 +1561,6 @@ |
3022 | buy_action.extra_text = details.price; |
3023 | } |
3024 | |
3025 | - buy_action.activated.connect (start_software_center); |
3026 | preview.add_action (buy_action); |
3027 | } |
3028 | else // uninstalled, purchased before |
3029 | @@ -1508,21 +1570,18 @@ |
3030 | if (details.raw_price == null || details.raw_price == "") |
3031 | { |
3032 | install_action = new Unity.PreviewAction ("install", _("Free Download"), null); |
3033 | - install_action.activated.connect (app_preview_install); |
3034 | } |
3035 | else |
3036 | { |
3037 | - install_action = new Unity.PreviewAction ("install", _("Install"), null); |
3038 | - install_action.activated.connect (start_software_center); |
3039 | + install_action = new Unity.PreviewAction ("install-paid", _("Install"), null); |
3040 | } |
3041 | preview.add_action (install_action); |
3042 | } |
3043 | |
3044 | if (details.website != null && details.website != "") |
3045 | { |
3046 | - preview_developer_website = details.website; |
3047 | + scope.preview_developer_website = details.website; |
3048 | var website_action = new Unity.PreviewAction ("website", _("Developer Site"), null); |
3049 | - website_action.activated.connect (app_preview_website); |
3050 | preview.add_action (website_action); |
3051 | } |
3052 | } |
3053 | @@ -1534,13 +1593,12 @@ |
3054 | if (!details.is_desktop_dependency) |
3055 | { |
3056 | var uninstall_action = new Unity.PreviewAction ("uninstall", _("Uninstall"), null); |
3057 | - uninstall_action.activated.connect (app_preview_uninstall); |
3058 | preview.add_action (uninstall_action); |
3059 | } |
3060 | } |
3061 | |
3062 | - preview_installable_desktop_file = details.desktop_file; |
3063 | - preview_installable_icon_file = details.icon; |
3064 | + scope.preview_installable_desktop_file = details.desktop_file; |
3065 | + scope.preview_installable_icon_file = details.icon; |
3066 | } |
3067 | catch (Error e) |
3068 | { |
3069 | @@ -1568,335 +1626,6 @@ |
3070 | } |
3071 | return preview; |
3072 | } |
3073 | - |
3074 | - private Unity.Preview make_scope_preview(string uri) |
3075 | - { |
3076 | - Unity.ApplicationPreview? preview = null; |
3077 | - var scope_id = uri.substring (8); |
3078 | - bool scope_disabled = scope_id in disabled_scope_ids; |
3079 | - |
3080 | - // figure out if the scope is remote |
3081 | - bool is_remote_scope = false; |
3082 | - var info = scopesearcher.get_by_desktop_file (scope_id); |
3083 | - |
3084 | - Dee.ModelIter found_iter = null; |
3085 | - if (info == null) |
3086 | - { |
3087 | - var iter = remote_scopes_model.get_first_iter (); |
3088 | - var end_iter = remote_scopes_model.get_last_iter (); |
3089 | - while (iter != end_iter) |
3090 | - { |
3091 | - if (remote_scopes_model.get_string (iter, 0) == scope_id) |
3092 | - { |
3093 | - is_remote_scope = true; |
3094 | - found_iter = iter; |
3095 | - break; |
3096 | - } |
3097 | - iter = remote_scopes_model.next (iter); |
3098 | - } |
3099 | - } |
3100 | - |
3101 | - if (is_remote_scope) |
3102 | - { |
3103 | - var name = remote_scopes_model.get_string (found_iter, 1); |
3104 | - if (name == null || name == "") |
3105 | - name = remote_scopes_model.get_string (found_iter, 0); |
3106 | - var description = remote_scopes_model.get_string (found_iter, 2); |
3107 | - if (description != null) |
3108 | - description = Markup.escape_text(description); |
3109 | - Icon? icon = null; |
3110 | - Icon? screenshot = null; |
3111 | - var icon_hint = remote_scopes_model.get_string (found_iter, 3); |
3112 | - var screenshot_url = remote_scopes_model.get_string (found_iter, 4); |
3113 | - try |
3114 | - { |
3115 | - if (icon_hint != "") icon = Icon.new_for_string (icon_hint); |
3116 | - if (screenshot_url != "") screenshot = Icon.new_for_string (screenshot_url); |
3117 | - } |
3118 | - catch (Error err) |
3119 | - { |
3120 | - warning ("%s", err.message); |
3121 | - } |
3122 | - |
3123 | - if (icon == null) icon = get_default_scope_icon (); |
3124 | - |
3125 | - preview = new Unity.ApplicationPreview (name, |
3126 | - "", |
3127 | - description, |
3128 | - icon, |
3129 | - screenshot); |
3130 | - } |
3131 | - else if (info != null) |
3132 | - { |
3133 | - var name = info.application_name; |
3134 | - if (name == null || name == "") |
3135 | - name = info.desktop_file; |
3136 | - var subtitle = ""; |
3137 | - var description = info.description; |
3138 | - if (description != null) |
3139 | - description = Markup.escape_text(description); |
3140 | - Icon? icon = null; |
3141 | - Icon? screenshot = null; |
3142 | - try |
3143 | - { |
3144 | - if (info.icon != null && info.icon != "") |
3145 | - icon = Icon.new_for_string (info.icon); |
3146 | - } |
3147 | - catch (Error err) |
3148 | - { |
3149 | - warning ("%s", err.message); |
3150 | - } |
3151 | - if (icon == null) |
3152 | - icon = get_default_scope_icon (); |
3153 | - |
3154 | - // de-mangle desktop file names back to what S-C expects |
3155 | - string mangled_id; |
3156 | - if (sc_mangler.contains (scope_id)) |
3157 | - { |
3158 | - mangled_id = sc_mangler.get (scope_id); |
3159 | - } |
3160 | - else |
3161 | - { |
3162 | - mangled_id = scope_id; |
3163 | - } |
3164 | - |
3165 | - Unity.Package.PackageInfo pkginfo = pkgsearcher.get_by_desktop_file (mangled_id); |
3166 | - if (pkginfo != null) |
3167 | - { |
3168 | - SoftwareCenterData.AppDetailsData? details; |
3169 | - try { |
3170 | - details = get_app_details(pkginfo.application_name, |
3171 | - pkginfo.package_name); |
3172 | - } catch (Error e) { |
3173 | - details = null; |
3174 | - } |
3175 | - if (details != null) { |
3176 | - if (details.version != "") |
3177 | - subtitle = _("Version %s").printf (details.version); |
3178 | - if (details.screenshot != null) |
3179 | - { |
3180 | - File scr_file = File.new_for_uri (details.screenshot); |
3181 | - screenshot = new FileIcon (scr_file); |
3182 | - } |
3183 | - } |
3184 | - } |
3185 | - |
3186 | - preview = new Unity.ApplicationPreview (name, |
3187 | - subtitle, |
3188 | - description, |
3189 | - icon, |
3190 | - screenshot); |
3191 | - } |
3192 | - if (preview != null && scope_id != "home.scope" && |
3193 | - scope_id != "applications.scope") |
3194 | - { |
3195 | - PreviewAction action; |
3196 | - preview.set_rating(-1.0f, 0); |
3197 | - if (scope_disabled) |
3198 | - { |
3199 | - action = new Unity.PreviewAction ("enable-scope", _("Enable"), null); |
3200 | - action.activated.connect (() => |
3201 | - { |
3202 | - enable_scope (scope_id); |
3203 | - return new ActivationResponse.with_preview (this.preview (uri)); |
3204 | - }); |
3205 | - } |
3206 | - else |
3207 | - { |
3208 | - action = new Unity.PreviewAction ("disable-scope", _("Disable"), null); |
3209 | - action.activated.connect (() => |
3210 | - { |
3211 | - disable_scope (scope_id); |
3212 | - return new ActivationResponse.with_preview (this.preview (uri)); |
3213 | - }); |
3214 | - } |
3215 | - preview.add_action (action); |
3216 | - } |
3217 | - return preview; |
3218 | - } |
3219 | - |
3220 | - private void disable_scope (string scope_id) |
3221 | - { |
3222 | - if (scope_id in disabled_scope_ids) return; |
3223 | - |
3224 | - var pref_man = PreferencesManager.get_default (); |
3225 | - var disabled_scopes = pref_man.disabled_scopes; |
3226 | - disabled_scopes += scope_id; |
3227 | - |
3228 | - var settings = new Settings (LIBUNITY_SCHEMA); |
3229 | - settings.set_strv ("disabled-scopes", disabled_scopes); |
3230 | - } |
3231 | - |
3232 | - private void enable_scope (string scope_id) |
3233 | - { |
3234 | - if (!(scope_id in disabled_scope_ids)) return; |
3235 | - |
3236 | - var pref_man = PreferencesManager.get_default (); |
3237 | - string[] disabled_scopes = {}; |
3238 | - foreach (unowned string disabled_scope_id in pref_man.disabled_scopes) |
3239 | - { |
3240 | - if (disabled_scope_id != scope_id) |
3241 | - disabled_scopes += disabled_scope_id; |
3242 | - } |
3243 | - |
3244 | - var settings = new Settings (LIBUNITY_SCHEMA); |
3245 | - settings.set_strv ("disabled-scopes", disabled_scopes); |
3246 | - } |
3247 | - /** |
3248 | - * Override of the default activation handler. The apps lens daemon |
3249 | - * can handle activation of installable apps using the Software Center |
3250 | - */ |
3251 | - public Unity.ActivationResponse activate (string uri) |
3252 | - { |
3253 | - string[] args; |
3254 | - string exec_or_dir = null; |
3255 | - if (uri.has_prefix ("unity-install://") || uri.has_prefix ("scope://")) |
3256 | - { |
3257 | - var prv = preview (uri); |
3258 | - if (prv != null) |
3259 | - { |
3260 | - return new Unity.ActivationResponse.with_preview (prv); |
3261 | - } |
3262 | - else |
3263 | - { |
3264 | - warning ("Failed to generate preview for %s", uri); |
3265 | - return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); |
3266 | - } |
3267 | - } |
3268 | - else if (uri.has_prefix (UNITY_RUNNER_PREFIX)) |
3269 | - { |
3270 | - string orig; |
3271 | - orig = uri.offset (UNITY_RUNNER_PREFIX.length); |
3272 | - if (orig.has_prefix("\\\\")) |
3273 | - orig = orig.replace ("\\\\","smb://"); |
3274 | - if (uri_regex != null && uri_regex.match (orig)) { |
3275 | - try { |
3276 | - /* this code ensures that a file manager will be used |
3277 | - * if uri it's a remote location that should be mounted */ |
3278 | - if (mountable_regex.match (orig)) { |
3279 | - var muris = new GLib.List<string>(); |
3280 | - muris.prepend (orig); |
3281 | - var file_manager = AppInfo.get_default_for_type("inode/directory", true); |
3282 | - file_manager.launch_uris(muris,null); |
3283 | - } else { |
3284 | - AppInfo.launch_default_for_uri (orig, null); |
3285 | - } |
3286 | - } catch (GLib.Error error) { |
3287 | - warning ("Failed to launch URI %s", orig); |
3288 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
3289 | - } |
3290 | - return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
3291 | - |
3292 | - } else { |
3293 | - exec_or_dir = Utils.subst_tilde (orig); |
3294 | - args = exec_or_dir.split (" ", 0); |
3295 | - for (int i = 0; i < args.length; i++) |
3296 | - args[i] = Utils.subst_tilde (args[i]); |
3297 | - } |
3298 | - this.runner.add_history (orig); |
3299 | - } |
3300 | - else |
3301 | - { |
3302 | - /* Activation of standard application:// uris */ |
3303 | - |
3304 | - /* Make sure fresh install learns quickly */ |
3305 | - if (popularity_map.size <= 5) popularities_dirty = true; |
3306 | - |
3307 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
3308 | - } |
3309 | - |
3310 | - if ((exec_or_dir != null) && FileUtils.test (exec_or_dir, FileTest.IS_DIR)) |
3311 | - { |
3312 | - try { |
3313 | - AppInfo.launch_default_for_uri ("file://" + exec_or_dir, null); |
3314 | - } catch (GLib.Error err) { |
3315 | - warning ("Failed to open current folder '%s' in file manager: %s", |
3316 | - exec_or_dir, err.message); |
3317 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
3318 | - } |
3319 | - } |
3320 | - else |
3321 | - { |
3322 | - try { |
3323 | - unowned string home_dir = GLib.Environment.get_home_dir (); |
3324 | - Process.spawn_async (home_dir, args, null, SpawnFlags.SEARCH_PATH, null, null); |
3325 | - } catch (SpawnError e) { |
3326 | - warning ("Failed to spawn software-center or direct URI activation '%s': %s", |
3327 | - uri, e.message); |
3328 | - return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED); |
3329 | - } |
3330 | - } |
3331 | - |
3332 | - return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH); |
3333 | - |
3334 | - } |
3335 | - |
3336 | - /* Appends the subject URIs from a set of Zeitgeist.Events to our Dee.Model |
3337 | - * assuming that these events are already sorted */ |
3338 | - public void append_events_with_category (Zeitgeist.ResultSet events, |
3339 | - Dee.Model results, |
3340 | - uint category_id, |
3341 | - bool include_favorites, |
3342 | - int max_results = int.MAX, |
3343 | - Set<string>? allowed_uris = null) |
3344 | - { |
3345 | - int num_results = 0; |
3346 | - foreach (var ev in events) |
3347 | - { |
3348 | - string? app_uri = null; |
3349 | - if (ev.num_subjects () > 0) |
3350 | - app_uri = ev.get_subject (0).get_uri (); |
3351 | - |
3352 | - if (app_uri == null) |
3353 | - { |
3354 | - warning ("Unexpected event without subject"); |
3355 | - continue; |
3356 | - } |
3357 | - |
3358 | - /* Assert that we indeed have a known application as actor */ |
3359 | - string desktop_id = Utils.get_desktop_id_for_actor (app_uri); |
3360 | - |
3361 | - /* Discard Recently Used apps that are in the launcher */ |
3362 | - if ((category_id == Category.RECENT || |
3363 | - category_id == Category.RECENT_APPS) && |
3364 | - !include_favorites && |
3365 | - (favorite_apps.has_app_id (desktop_id) |
3366 | - || app_watcher.has_app_id (desktop_id))) |
3367 | - continue; |
3368 | - |
3369 | - var appmanager = AppInfoManager.get_default (); |
3370 | - AppInfo? app = appmanager.lookup (desktop_id); |
3371 | - |
3372 | - if (app == null) |
3373 | - continue; |
3374 | - |
3375 | - if (!app.should_show ()) |
3376 | - continue; |
3377 | - |
3378 | - /* HACK! when using the max_results together with allowed_uris, |
3379 | - * the limit serves as a truncation point - therefore results which |
3380 | - * are not displayed the first time won't be displayed later |
3381 | - * (consider results A, B, C - all of them are allowed and we use |
3382 | - * limit 2 - so only A and B is displayed, later we change allowed to |
3383 | - * B and C, so normally we would display both B and C, but that's not |
3384 | - * desired because C wasn't shown in the first place, so we display |
3385 | - * only B) */ |
3386 | - if (num_results++ >= max_results) break; |
3387 | - |
3388 | - if (allowed_uris != null && !(app_uri in allowed_uris)) continue; |
3389 | - |
3390 | - string full_path = appmanager.get_path (desktop_id); |
3391 | - string full_uri = full_path != null ? "file://" + full_path : app_uri; |
3392 | - |
3393 | - results.append (app_uri, app.get_icon().to_string(), |
3394 | - category_id, Unity.ResultType.DEFAULT, |
3395 | - "application/x-desktop", app.get_display_name (), |
3396 | - sanitize_binary_name (app.get_executable ()), |
3397 | - full_uri, |
3398 | - empty_asv); |
3399 | - } |
3400 | - } |
3401 | - |
3402 | - } /* END: class Daemon */ |
3403 | + } |
3404 | |
3405 | } /* namespace */ |
3406 | |
3407 | === modified file 'src/main.vala' |
3408 | --- src/main.vala 2013-07-01 11:20:42 +0000 |
3409 | +++ src/main.vala 2013-08-06 08:19:29 +0000 |
3410 | @@ -1,5 +1,6 @@ |
3411 | +/* -*- mode: vala; c-basic-offset: 2; indent-tabs-mode: nil -*- */ |
3412 | /* |
3413 | - * Copyright (C) 2011 Canonical Ltd |
3414 | + * Copyright (C) 2011-2013 Canonical Ltd |
3415 | * |
3416 | * This program is free software: you can redistribute it and/or modify |
3417 | * it under the terms of the GNU General Public License version 3 as |
3418 | @@ -20,90 +21,25 @@ |
3419 | using GLib; |
3420 | using Config; |
3421 | |
3422 | -namespace Unity.ApplicationsLens { |
3423 | - |
3424 | - const string DBUS_NAME = "com.canonical.Unity.Scope.Applications"; |
3425 | - static Application? app = null; |
3426 | - static Daemon? daemon = null; |
3427 | - |
3428 | - /* Check if a given well known DBus is owned. |
3429 | - * WARNING: This does sync IO! */ |
3430 | - public static bool dbus_name_has_owner (string name) |
3431 | - { |
3432 | - try { |
3433 | - bool has_owner; |
3434 | - DBusConnection bus = Bus.get_sync (BusType.SESSION); |
3435 | - Variant result = bus.call_sync ("org.freedesktop.DBus", |
3436 | - "/org/freedesktop/dbus", |
3437 | - "org.freedesktop.DBus", |
3438 | - "NameHasOwner", |
3439 | - new Variant ("(s)", name), |
3440 | - new VariantType ("(b)"), |
3441 | - DBusCallFlags.NO_AUTO_START, |
3442 | - -1); |
3443 | - result.get ("(b)", out has_owner); |
3444 | - return has_owner; |
3445 | - } catch (Error e) { |
3446 | - warning ("Unable to decide whether '%s' is running: %s", name, e.message); |
3447 | - } |
3448 | - |
3449 | - return false; |
3450 | - } |
3451 | - |
3452 | - public static int main (string[] args) |
3453 | - { |
3454 | - /* Sort up locale to get translations but also sorting and |
3455 | - * punctuation right */ |
3456 | - GLib.Intl.textdomain (Config.PACKAGE); |
3457 | - GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALEDIR); |
3458 | - GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8"); |
3459 | - GLib.Intl.setlocale(GLib.LocaleCategory.ALL, ""); |
3460 | - |
3461 | - /* Make sure the desktop appinfos are picked up correctly */ |
3462 | - DesktopAppInfo.set_desktop_env ("Unity"); |
3463 | - |
3464 | - /* Sub processes inheriting our environment may be confused if they |
3465 | - * see a starter bus set */ |
3466 | - Environment.unset_variable ("DBUS_STARTER_ADDRESS"); |
3467 | - Environment.unset_variable ("DBUS_STARTER_BUS_TYPE"); |
3468 | - |
3469 | - /* Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=640714 |
3470 | - * GApplication.register() call owns our DBus name in a sync manner |
3471 | - * making it race against GDBus' worker thread to export our |
3472 | - * objects on the bus before/after owning our name and receiving |
3473 | - * method calls on our objects (which may not yet be up!)*/ |
3474 | - if (dbus_name_has_owner (DBUS_NAME)) |
3475 | - { |
3476 | - print ("Another instance of the Unity Applications Daemon " + |
3477 | - "already appears to be running.\nBailing out.\n"); |
3478 | - return 2; |
3479 | - } |
3480 | - |
3481 | - /* Now register our DBus objects *before* acquiring the name! |
3482 | - * See above for reasons */ |
3483 | - daemon = new Daemon (); |
3484 | - |
3485 | - /* Use GApplication directly for single instance app functionality */ |
3486 | - app = new Application (DBUS_NAME, ApplicationFlags.IS_SERVICE); |
3487 | - try { |
3488 | - app.register (); |
3489 | - } catch (Error e) { |
3490 | - /* FIXME: We get this error if another daemon is already running, |
3491 | - * but it uses a generic error so we can't detect this reliably... */ |
3492 | - print ("Failed to start applications daemon: %s\n", e.message); |
3493 | - return 1; |
3494 | - } |
3495 | - |
3496 | - if (app.get_is_remote ()) |
3497 | - { |
3498 | - print ("Another instance of the Unity Applications Daemon " + |
3499 | - "already appears to be running.\nBailing out.\n"); |
3500 | - return 2; |
3501 | - } |
3502 | - |
3503 | - /* Hold()ing the app makes sure the GApplication doesn't exit */ |
3504 | - app.hold(); |
3505 | - return app.run (); |
3506 | - } |
3507 | - |
3508 | -} /* namespace */ |
3509 | +public int unity_scope_module_get_version () |
3510 | +{ |
3511 | + return Unity.SCOPE_API_VERSION; |
3512 | +} |
3513 | + |
3514 | +public List<Unity.AbstractScope> unity_scope_module_load_scopes () throws Error |
3515 | +{ |
3516 | + /* Sort up locale to get translations but also sorting and |
3517 | + * punctuation right */ |
3518 | + GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALEDIR); |
3519 | + GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8"); |
3520 | + GLib.Intl.setlocale(GLib.LocaleCategory.ALL, ""); |
3521 | + |
3522 | + var scopes = new List<Unity.AbstractScope> (); |
3523 | + var apps_scope = new Unity.ApplicationsLens.ApplicationsScope (); |
3524 | + var scopes_scope = new Unity.ApplicationsLens.ScopesScope (apps_scope); |
3525 | + |
3526 | + scopes.append(apps_scope); |
3527 | + scopes.append(apps_scope.commands_scope); |
3528 | + scopes.append(scopes_scope); |
3529 | + return scopes; |
3530 | +} |
3531 | |
3532 | === added file 'src/scopes-scope.vala' |
3533 | --- src/scopes-scope.vala 1970-01-01 00:00:00 +0000 |
3534 | +++ src/scopes-scope.vala 2013-08-06 08:19:29 +0000 |
3535 | @@ -0,0 +1,510 @@ |
3536 | +/* -*- mode: vala; c-basic-offset: 2; indent-tabs-mode: nil -*- */ |
3537 | +/* |
3538 | + * Copyright (C) 2013 Canonical Ltd |
3539 | + * |
3540 | + * This program is free software: you can redistribute it and/or modify |
3541 | + * it under the terms of the GNU General Public License version 3 as |
3542 | + * published by the Free Software Foundation. |
3543 | + * |
3544 | + * This program is distributed in the hope that it will be useful, |
3545 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3546 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3547 | + * GNU General Public License for more details. |
3548 | + * |
3549 | + * You should have received a copy of the GNU General Public License |
3550 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3551 | + * |
3552 | + * Authored by James Henstridge <james.henstridge@canonical.com> |
3553 | + * |
3554 | + */ |
3555 | + |
3556 | +namespace Unity.ApplicationsLens { |
3557 | + const string GENERIC_SCOPE_ICON = ICON_PATH + "service-generic.svg"; |
3558 | + |
3559 | + private class ScopesScope : Unity.AbstractScope |
3560 | + { |
3561 | + public ApplicationsScope appscope; |
3562 | + public Unity.Package.Searcher? searcher; |
3563 | + public HashTable<unowned string, unowned string> disabled_scope_ids; |
3564 | + public Dee.Model remote_scopes_model; |
3565 | + public Dee.Index scopes_index; |
3566 | + public Dee.Analyzer analyzer; |
3567 | + |
3568 | + public ScopesScope (ApplicationsScope appscope) |
3569 | + { |
3570 | + this.appscope = appscope; |
3571 | + // Track the remote scopes |
3572 | + remote_scopes_model = new Dee.SharedModel ("com.canonical.Unity.SmartScopes.RemoteScopesModel"); |
3573 | + remote_scopes_model.set_schema ("s", "s", "s", "s", "s", "as"); |
3574 | + |
3575 | + scopes_index = Utils.prepare_index (remote_scopes_model, |
3576 | + RemoteScopesColumn.NAME, |
3577 | + (model, iter) => |
3578 | + { |
3579 | + unowned string name = model.get_string (iter, RemoteScopesColumn.NAME); |
3580 | + return "%s\n%s".printf (_("scope"), name); |
3581 | + }, out analyzer); |
3582 | + |
3583 | + disabled_scope_ids = new HashTable<unowned string, unowned string> (str_hash, str_equal); |
3584 | + update_disabled_scopes_hash (); |
3585 | + var pref_man = PreferencesManager.get_default (); |
3586 | + pref_man.notify["disabled-scopes"].connect (update_disabled_scopes_hash); |
3587 | + |
3588 | + build_scope_index.begin (); |
3589 | + } |
3590 | + |
3591 | + private void update_disabled_scopes_hash () |
3592 | + { |
3593 | + disabled_scope_ids.remove_all (); |
3594 | + var pref_man = PreferencesManager.get_default (); |
3595 | + foreach (unowned string scope_id in pref_man.disabled_scopes) |
3596 | + { |
3597 | + // using HashTable as a set (optimized in glib when done like this) |
3598 | + disabled_scope_ids[scope_id] = scope_id; |
3599 | + } |
3600 | + } |
3601 | + |
3602 | + private void disable_scope (string scope_id) |
3603 | + { |
3604 | + if (scope_id in disabled_scope_ids) return; |
3605 | + |
3606 | + var pref_man = PreferencesManager.get_default (); |
3607 | + var disabled_scopes = pref_man.disabled_scopes; |
3608 | + disabled_scopes += scope_id; |
3609 | + |
3610 | + var settings = new Settings (LIBUNITY_SCHEMA); |
3611 | + settings.set_strv ("disabled-scopes", disabled_scopes); |
3612 | + } |
3613 | + |
3614 | + private void enable_scope (string scope_id) |
3615 | + { |
3616 | + if (!(scope_id in disabled_scope_ids)) return; |
3617 | + |
3618 | + var pref_man = PreferencesManager.get_default (); |
3619 | + string[] disabled_scopes = {}; |
3620 | + foreach (unowned string disabled_scope_id in pref_man.disabled_scopes) |
3621 | + { |
3622 | + if (disabled_scope_id != scope_id) |
3623 | + disabled_scopes += disabled_scope_id; |
3624 | + } |
3625 | + |
3626 | + var settings = new Settings (LIBUNITY_SCHEMA); |
3627 | + settings.set_strv ("disabled-scopes", disabled_scopes); |
3628 | + } |
3629 | + |
3630 | + private async void build_scope_index () |
3631 | + { |
3632 | + try |
3633 | + { |
3634 | + var scope_registry = yield Unity.Protocol.ScopeRegistry.find_scopes ( |
3635 | + Config.PKGDATADIR + "/scopes"); |
3636 | + searcher = new Unity.Package.Searcher.for_scopes (scope_registry); |
3637 | + } |
3638 | + catch (Error err) |
3639 | + { |
3640 | + warning ("Unable to find scopes: %s", err.message); |
3641 | + } |
3642 | + } |
3643 | + |
3644 | + public override string get_group_name () |
3645 | + { |
3646 | + return "com.canonical.Unity.Scope.Applications"; |
3647 | + } |
3648 | + |
3649 | + public override string get_unique_name () |
3650 | + { |
3651 | + return "/com/canonical/unity/scope/scopes"; |
3652 | + } |
3653 | + |
3654 | + public override Unity.FilterSet get_filters () |
3655 | + { |
3656 | + var filters = new Unity.FilterSet (); |
3657 | + |
3658 | + /* Type filter */ |
3659 | + { |
3660 | + var filter = new CheckOptionFilter ("type", _("Type")); |
3661 | + filter.sort_type = Unity.OptionsFilter.SortType.DISPLAY_NAME; |
3662 | + |
3663 | + filter.add_option ("accessories", _("Accessories")); |
3664 | + filter.add_option ("education", _("Education")); |
3665 | + filter.add_option ("game", _("Games")); |
3666 | + filter.add_option ("graphics", _("Graphics")); |
3667 | + filter.add_option ("internet", _("Internet")); |
3668 | + filter.add_option ("fonts", _("Fonts")); |
3669 | + filter.add_option ("office", _("Office")); |
3670 | + filter.add_option ("media", _("Media")); |
3671 | + filter.add_option ("customization", _("Customization")); |
3672 | + filter.add_option ("accessibility", _("Accessibility")); |
3673 | + filter.add_option ("developer", _("Developer")); |
3674 | + filter.add_option ("science-and-engineering", _("Science & engineering")); |
3675 | + filter.add_option ("scopes", _("Search plugins")); |
3676 | + filter.add_option ("system", _("System")); |
3677 | + |
3678 | + filters.add (filter); |
3679 | + } |
3680 | + return filters; |
3681 | + } |
3682 | + |
3683 | + public override Unity.CategorySet get_categories () |
3684 | + { |
3685 | + Unity.CategorySet categories = new Unity.CategorySet (); |
3686 | + File icon_dir = File.new_for_path (ICON_PATH); |
3687 | + |
3688 | + var cat = new Unity.Category ("apps", _("Applications"), |
3689 | + new FileIcon (icon_dir.get_child ("group-apps.svg"))); |
3690 | + categories.add (cat); |
3691 | + |
3692 | + cat = new Unity.Category ("recently-used", _("Recently used"), |
3693 | + new FileIcon (icon_dir.get_child ("group-recent.svg"))); |
3694 | + categories.add (cat); |
3695 | + |
3696 | + cat = new Unity.Category ("recent", _("Recent apps"), |
3697 | + new FileIcon (icon_dir.get_child ("group-apps.svg"))); |
3698 | + categories.add (cat); |
3699 | + |
3700 | + cat = new Unity.Category ("installed", _("Installed"), |
3701 | + new FileIcon (icon_dir.get_child ("group-installed.svg"))); |
3702 | + categories.add (cat); |
3703 | + |
3704 | + cat = new Unity.Category ("more", _("More suggestions"), |
3705 | + new FileIcon (icon_dir.get_child ("group-treat-yourself.svg"))); |
3706 | + categories.add (cat); |
3707 | + |
3708 | + return categories; |
3709 | + } |
3710 | + |
3711 | + public override Unity.Schema get_schema () |
3712 | + { |
3713 | + var schema = new Unity.Schema (); |
3714 | + schema.add_field ("is_scope", "u", Schema.FieldType.OPTIONAL); |
3715 | + return schema; |
3716 | + } |
3717 | + |
3718 | + public override string normalize_search_query (string search_query) |
3719 | + { |
3720 | + return search_query.strip (); |
3721 | + } |
3722 | + |
3723 | + public override Unity.ScopeSearchBase create_search_for_query (Unity.SearchContext search_context) |
3724 | + { |
3725 | + return new ScopesSearch (this, search_context); |
3726 | + } |
3727 | + |
3728 | + public override Unity.ResultPreviewer create_previewer (Unity.ScopeResult result, Unity.SearchMetadata metadata) |
3729 | + { |
3730 | + return new ScopesResultPreviewer (this, result, metadata); |
3731 | + } |
3732 | + |
3733 | + public override Unity.ActivationResponse? activate (Unity.ScopeResult result, Unity.SearchMetadata metadata, string? action_id) |
3734 | + { |
3735 | + var scope_id = result.uri.substring (8); |
3736 | + if (action_id == "enable-scope") |
3737 | + { |
3738 | + enable_scope (scope_id); |
3739 | + } |
3740 | + else if (action_id == "disable-scope") |
3741 | + { |
3742 | + disable_scope (scope_id); |
3743 | + } |
3744 | + /* Make a preview */ |
3745 | + var preview = create_previewer (result, metadata).run (); |
3746 | + if (preview is Unity.Preview) |
3747 | + { |
3748 | + return new Unity.ActivationResponse.with_preview (preview as Unity.Preview); |
3749 | + } |
3750 | + else |
3751 | + { |
3752 | + warning ("Failed to generate preview for %s", result.uri); |
3753 | + return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); |
3754 | + } |
3755 | + } |
3756 | + |
3757 | + public static Icon? get_default_icon () |
3758 | + { |
3759 | + try |
3760 | + { |
3761 | + return Icon.new_for_string (GENERIC_SCOPE_ICON); |
3762 | + } |
3763 | + catch (Error err) |
3764 | + { |
3765 | + warning ("%s", err.message); |
3766 | + } |
3767 | + return null; |
3768 | + } |
3769 | + } |
3770 | + |
3771 | + private class ScopesSearch : Unity.ScopeSearchBase |
3772 | + { |
3773 | + private ScopesScope scope; |
3774 | + |
3775 | + public ScopesSearch (ScopesScope scope, Unity.SearchContext search_context) |
3776 | + { |
3777 | + this.scope = scope; |
3778 | + this.search_context = search_context; |
3779 | + } |
3780 | + |
3781 | + public override void run () |
3782 | + { |
3783 | + if (this.scope.searcher == null) |
3784 | + return; |
3785 | + |
3786 | + var context = this.search_context; |
3787 | + // Don't add results to global searches |
3788 | + if (context.search_type == SearchType.GLOBAL) |
3789 | + return; |
3790 | + |
3791 | + var filter = context.filter_state.get_filter_by_id ("type") as OptionsFilter; |
3792 | + if (filter != null && filter.filtering && |
3793 | + !filter.get_option ("scopes").active) |
3794 | + return; |
3795 | + |
3796 | + add_local_results (context.search_query, context.result_set); |
3797 | + add_remote_results (context.search_query, context.result_set); |
3798 | + } |
3799 | + |
3800 | + private void add_local_results (string search_query, Unity.ResultSet result_set) |
3801 | + { |
3802 | + bool has_search = !Utils.is_search_empty (search_query); |
3803 | + var pkg_search_string = XapianUtils.prepare_pkg_search_string ( |
3804 | + search_query, null); |
3805 | + |
3806 | + var search_result = this.scope.searcher.search ( |
3807 | + pkg_search_string, 0, Unity.Package.SearchType.PREFIX, |
3808 | + has_search ? |
3809 | + Unity.Package.Sort.BY_RELEVANCY : Unity.Package.Sort.BY_NAME); |
3810 | + |
3811 | + foreach (unowned Unity.Package.PackageInfo info in search_result.results) |
3812 | + { |
3813 | + /* Don't include master scopes in the search results. This is |
3814 | + * performed after deduping so the master scopes don't just |
3815 | + * move to hte "available" category. */ |
3816 | + if (info.is_master_scope) |
3817 | + continue; |
3818 | + |
3819 | + var result = Unity.ScopeResult (); |
3820 | + result.uri = @"scope://$(info.desktop_file)"; |
3821 | + result.category = info.desktop_file in scope.disabled_scope_ids ? |
3822 | + Category.AVAILABLE : Category.INSTALLED; |
3823 | + result.result_type = Unity.ResultType.DEFAULT; |
3824 | + result.mimetype = "application/x-unity-scope"; |
3825 | + result.title = info.application_name; |
3826 | + if (result.title == null) |
3827 | + result.title = ""; |
3828 | + result.comment = ""; |
3829 | + result.dnd_uri = ""; |
3830 | + result.metadata = new HashTable<string, Variant> (str_hash, str_equal); |
3831 | + result.metadata["is_scope"] = new Variant.uint32 (1); |
3832 | + |
3833 | + var icon_hint = info.icon; |
3834 | + try |
3835 | + { |
3836 | + Icon base_icon = icon_hint == null || icon_hint == "" ? |
3837 | + ScopesScope.get_default_icon () : Icon.new_for_string (icon_hint); |
3838 | + var anno_icon = new AnnotatedIcon (base_icon); |
3839 | + anno_icon.size_hint = IconSizeHint.SMALL; |
3840 | + icon_hint = anno_icon.to_string (); |
3841 | + } |
3842 | + catch (Error err) |
3843 | + { |
3844 | + icon_hint = ""; |
3845 | + } |
3846 | + result.icon_hint = icon_hint; |
3847 | + |
3848 | + result_set.add_result (result); |
3849 | + } |
3850 | + } |
3851 | + |
3852 | + private void add_remote_results (string search_query, Unity.ResultSet result_set) |
3853 | + { |
3854 | + var results = Utils.search_index ( |
3855 | + scope.scopes_index, scope.analyzer, search_query); |
3856 | + foreach (var iter in results) |
3857 | + { |
3858 | + var result = Unity.ScopeResult (); |
3859 | + unowned string scope_id = |
3860 | + scope.remote_scopes_model.get_string (iter, RemoteScopesColumn.SCOPE_ID); |
3861 | + result.uri = @"scope://$(scope_id)"; |
3862 | + result.category = scope_id in scope.disabled_scope_ids ? |
3863 | + Category.AVAILABLE : Category.INSTALLED; |
3864 | + result.result_type = Unity.ResultType.DEFAULT; |
3865 | + result.mimetype = "application/x-unity-scope"; |
3866 | + result.title = |
3867 | + scope.remote_scopes_model.get_string (iter, RemoteScopesColumn.NAME); |
3868 | + result.comment = ""; |
3869 | + result.dnd_uri = ""; |
3870 | + result.metadata = new HashTable<string, Variant> (str_hash, str_equal); |
3871 | + result.metadata["is_scope"] = new Variant.uint32 (1); |
3872 | + |
3873 | + var icon_hint = |
3874 | + scope.remote_scopes_model.get_string (iter, RemoteScopesColumn.ICON_HINT); |
3875 | + try |
3876 | + { |
3877 | + Icon base_icon = icon_hint == null || icon_hint == "" ? |
3878 | + ScopesScope.get_default_icon () : Icon.new_for_string (icon_hint); |
3879 | + var anno_icon = new AnnotatedIcon (base_icon); |
3880 | + anno_icon.size_hint = IconSizeHint.SMALL; |
3881 | + icon_hint = anno_icon.to_string (); |
3882 | + } |
3883 | + catch (Error err) |
3884 | + { |
3885 | + icon_hint = ""; |
3886 | + } |
3887 | + result.icon_hint = icon_hint; |
3888 | + |
3889 | + result_set.add_result (result); |
3890 | + } |
3891 | + } |
3892 | + } |
3893 | + |
3894 | + private class ScopesResultPreviewer : Unity.ResultPreviewer |
3895 | + { |
3896 | + private ScopesScope scope; |
3897 | + |
3898 | + public ScopesResultPreviewer (ScopesScope scope, Unity.ScopeResult result, Unity.SearchMetadata metadata) |
3899 | + { |
3900 | + this.scope = scope; |
3901 | + set_scope_result (result); |
3902 | + set_search_metadata (metadata); |
3903 | + } |
3904 | + |
3905 | + public override Unity.AbstractPreview? run () |
3906 | + { |
3907 | + var ml = new MainLoop (); |
3908 | + Unity.AbstractPreview? preview = null; |
3909 | + run_async ((obj, p) => |
3910 | + { |
3911 | + preview = p; |
3912 | + ml.quit (); |
3913 | + }); |
3914 | + ml.run (); |
3915 | + return preview; |
3916 | + } |
3917 | + |
3918 | + public override void run_async (Unity.AbstractPreviewCallback callback) |
3919 | + { |
3920 | + make_preview.begin ((obj, res) => |
3921 | + { |
3922 | + var preview = make_preview.end (res); |
3923 | + callback (this, preview); |
3924 | + }); |
3925 | + } |
3926 | + |
3927 | + private async Unity.AbstractPreview? make_preview () |
3928 | + { |
3929 | + Unity.ApplicationPreview? preview = null; |
3930 | + var scope_id = result.uri.substring (8); |
3931 | + bool scope_disabled = scope_id in scope.disabled_scope_ids; |
3932 | + |
3933 | + // figure out if the scope is remote |
3934 | + bool is_remote_scope = false; |
3935 | + var info = scope.searcher.get_by_desktop_file (scope_id); |
3936 | + |
3937 | + Dee.ModelIter found_iter = null; |
3938 | + if (info == null) |
3939 | + { |
3940 | + var iter = scope.remote_scopes_model.get_first_iter (); |
3941 | + var end_iter = scope.remote_scopes_model.get_last_iter (); |
3942 | + while (iter != end_iter) |
3943 | + { |
3944 | + if (scope.remote_scopes_model.get_string (iter, 0) == scope_id) |
3945 | + { |
3946 | + is_remote_scope = true; |
3947 | + found_iter = iter; |
3948 | + break; |
3949 | + } |
3950 | + iter = scope.remote_scopes_model.next (iter); |
3951 | + } |
3952 | + } |
3953 | + |
3954 | + if (is_remote_scope) |
3955 | + { |
3956 | + var name = scope.remote_scopes_model.get_string (found_iter, 1); |
3957 | + if (name == null || name == "") |
3958 | + name = scope.remote_scopes_model.get_string (found_iter, 0); |
3959 | + var description = scope.remote_scopes_model.get_string (found_iter, 2); |
3960 | + if (description != null) |
3961 | + description = Markup.escape_text(description); |
3962 | + Icon? icon = null; |
3963 | + Icon? screenshot = null; |
3964 | + var icon_hint = scope.remote_scopes_model.get_string (found_iter, 3); |
3965 | + var screenshot_url = scope.remote_scopes_model.get_string (found_iter, 4); |
3966 | + try |
3967 | + { |
3968 | + if (icon_hint != "") icon = Icon.new_for_string (icon_hint); |
3969 | + if (screenshot_url != "") screenshot = Icon.new_for_string (screenshot_url); |
3970 | + } |
3971 | + catch (Error err) |
3972 | + { |
3973 | + warning ("%s", err.message); |
3974 | + } |
3975 | + |
3976 | + if (icon == null) icon = ScopesScope.get_default_icon (); |
3977 | + |
3978 | + preview = new Unity.ApplicationPreview (name, |
3979 | + "", |
3980 | + description, |
3981 | + icon, |
3982 | + screenshot); |
3983 | + } |
3984 | + else if (info != null) |
3985 | + { |
3986 | + var name = info.application_name; |
3987 | + if (name == null || name == "") |
3988 | + name = info.desktop_file; |
3989 | + var subtitle = ""; |
3990 | + var description = info.description; |
3991 | + if (description != null) |
3992 | + description = Markup.escape_text(description); |
3993 | + Icon? icon = null; |
3994 | + Icon? screenshot = null; |
3995 | + try |
3996 | + { |
3997 | + if (info.icon != null && info.icon != "") |
3998 | + icon = Icon.new_for_string (info.icon); |
3999 | + } |
4000 | + catch (Error err) |
4001 | + { |
4002 | + warning ("%s", err.message); |
4003 | + } |
4004 | + if (icon == null) |
4005 | + icon = ScopesScope.get_default_icon (); |
4006 | + |
4007 | + string version, screenshot_uri; |
4008 | + if (yield scope.appscope.get_version_and_screenshot ( |
4009 | + scope_id, out version, out screenshot_uri)) |
4010 | + { |
4011 | + if (version != "") |
4012 | + subtitle = _("Version %s").printf (version); |
4013 | + if (screenshot_uri != null) |
4014 | + { |
4015 | + File scr_file = File.new_for_uri (screenshot_uri); |
4016 | + screenshot = new FileIcon (scr_file); |
4017 | + } |
4018 | + } |
4019 | + |
4020 | + preview = new Unity.ApplicationPreview (name, |
4021 | + subtitle, |
4022 | + description, |
4023 | + icon, |
4024 | + screenshot); |
4025 | + } |
4026 | + if (preview != null && scope_id != "home.scope" && |
4027 | + scope_id != "applications/applications.scope" && |
4028 | + scope_id != "applications/scopes.scope") |
4029 | + { |
4030 | + PreviewAction action; |
4031 | + preview.set_rating(-1.0f, 0); |
4032 | + if (scope_disabled) |
4033 | + { |
4034 | + action = new Unity.PreviewAction ("enable-scope", _("Enable"), null); |
4035 | + } |
4036 | + else |
4037 | + { |
4038 | + action = new Unity.PreviewAction ("disable-scope", _("Disable"), null); |
4039 | + } |
4040 | + preview.add_action (action); |
4041 | + } |
4042 | + return preview; |
4043 | + } |
4044 | + } |
4045 | +} |
4046 | |
4047 | === modified file 'src/software-center-data-cache.vala' |
4048 | --- src/software-center-data-cache.vala 2012-10-04 16:15:09 +0000 |
4049 | +++ src/software-center-data-cache.vala 2013-08-06 08:19:29 +0000 |
4050 | @@ -43,7 +43,7 @@ |
4051 | return false; |
4052 | } |
4053 | |
4054 | - public override SoftwareCenterData.AppInfo?[] get_items_for_category (string category_name) throws Error |
4055 | + public override async SoftwareCenterData.AppInfo?[] get_items_for_category (string category_name) throws Error |
4056 | { |
4057 | int64 last_update = 0; |
4058 | if (category_items_last_update.contains (category_name)) |
4059 | @@ -55,7 +55,7 @@ |
4060 | { |
4061 | category_items_last_update[category_name] = last_update; |
4062 | |
4063 | - var data = base.get_items_for_category (category_name); |
4064 | + var data = yield base.get_items_for_category (category_name); |
4065 | var results = new Gee.ArrayList<SoftwareCenterData.AppInfo?> (); |
4066 | foreach (var item in data) |
4067 | { |
4068 | @@ -68,4 +68,4 @@ |
4069 | } |
4070 | } |
4071 | |
4072 | -} |
4073 | \ No newline at end of file |
4074 | +} |
4075 | |
4076 | === modified file 'src/software-center-data-provider.vala' |
4077 | --- src/software-center-data-provider.vala 2012-10-04 16:15:09 +0000 |
4078 | +++ src/software-center-data-provider.vala 2013-08-06 08:19:29 +0000 |
4079 | @@ -24,10 +24,10 @@ |
4080 | [DBus (name = "com.ubuntu.SoftwareCenterDataProvider")] |
4081 | public interface SoftwareCenterDataProviderService: GLib.Object |
4082 | { |
4083 | - public abstract HashTable<string, Variant> get_app_details (string appname, string pkgname) throws Error; |
4084 | - public abstract string[] get_available_categories () throws Error; |
4085 | - public abstract string[] get_available_subcategories () throws Error; |
4086 | - public abstract SoftwareCenterData.AppInfo?[] get_items_for_category (string category_name) throws Error; |
4087 | + public abstract async HashTable<string, Variant> get_app_details (string appname, string pkgname) throws Error; |
4088 | + public abstract async string[] get_available_categories () throws Error; |
4089 | + public abstract async string[] get_available_subcategories () throws Error; |
4090 | + public abstract async SoftwareCenterData.AppInfo?[] get_items_for_category (string category_name) throws Error; |
4091 | } |
4092 | |
4093 | public class SoftwareCenterDataProviderProxy: GLib.Object |
4094 | @@ -37,9 +37,9 @@ |
4095 | Bus.watch_name (BusType.SESSION, SCDP_DBUS_NAME, BusNameWatcherFlags.NONE, null, on_sc_dbus_name_vanished); |
4096 | } |
4097 | |
4098 | - public void connect_to () throws Error |
4099 | + private async void connect_to () throws Error |
4100 | { |
4101 | - _service = Bus.get_proxy_sync (BusType.SESSION, SCDP_DBUS_NAME, SCDP_DBUS_PATH); |
4102 | + _service = yield Bus.get_proxy (BusType.SESSION, SCDP_DBUS_NAME, SCDP_DBUS_PATH); |
4103 | } |
4104 | |
4105 | private void on_sc_dbus_name_vanished (DBusConnection conn, string name) |
4106 | @@ -47,22 +47,22 @@ |
4107 | _service = null; |
4108 | } |
4109 | |
4110 | - public SoftwareCenterData.AppDetailsData get_app_details (string appname, string pkgname) throws Error |
4111 | + public async SoftwareCenterData.AppDetailsData get_app_details (string appname, string pkgname) throws Error |
4112 | { |
4113 | if (_service == null) |
4114 | - connect_to (); |
4115 | + yield connect_to (); |
4116 | |
4117 | - HashTable<string, Variant> data = _service.get_app_details (appname, pkgname); |
4118 | + HashTable<string, Variant> data = yield _service.get_app_details (appname, pkgname); |
4119 | var details = new SoftwareCenterData.AppDetailsData (data); |
4120 | return details; |
4121 | } |
4122 | |
4123 | - public virtual SoftwareCenterData.AppInfo?[] get_items_for_category (string category_name) throws Error |
4124 | + public virtual async SoftwareCenterData.AppInfo?[] get_items_for_category (string category_name) throws Error |
4125 | { |
4126 | if (_service == null) |
4127 | - connect_to (); |
4128 | + yield connect_to (); |
4129 | |
4130 | - SoftwareCenterData.AppInfo?[] data = _service.get_items_for_category (category_name); |
4131 | + SoftwareCenterData.AppInfo?[] data = yield _service.get_items_for_category (category_name); |
4132 | return data; |
4133 | } |
4134 | |
4135 | |
4136 | === modified file 'tests/unit/software-center-data-provider-mock.vala' |
4137 | --- tests/unit/software-center-data-provider-mock.vala 2012-10-09 16:38:12 +0000 |
4138 | +++ tests/unit/software-center-data-provider-mock.vala 2013-08-06 08:19:29 +0000 |
4139 | @@ -45,9 +45,9 @@ |
4140 | data[category].clear (); |
4141 | } |
4142 | |
4143 | - public virtual SoftwareCenterData.AppInfo?[] get_items_for_category (string category_name) throws Error |
4144 | + public virtual async SoftwareCenterData.AppInfo?[] get_items_for_category (string category_name) throws Error |
4145 | { |
4146 | return data[category_name].to_array (); |
4147 | } |
4148 | } |
4149 | -} |
4150 | \ No newline at end of file |
4151 | +} |
4152 | |
4153 | === modified file 'tests/unit/test-software-center-data-cache.vala' |
4154 | --- tests/unit/test-software-center-data-cache.vala 2012-10-08 15:48:54 +0000 |
4155 | +++ tests/unit/test-software-center-data-cache.vala 2013-08-06 08:19:29 +0000 |
4156 | @@ -40,6 +40,37 @@ |
4157 | return false; |
4158 | } |
4159 | |
4160 | + internal static bool run_with_timeout (MainLoop ml, uint timeout_ms = 5000) |
4161 | + { |
4162 | + bool timeout_reached = false; |
4163 | + var t_id = Timeout.add (timeout_ms, () => |
4164 | + { |
4165 | + timeout_reached = true; |
4166 | + debug ("Timeout reached"); |
4167 | + ml.quit (); |
4168 | + return false; |
4169 | + }); |
4170 | + |
4171 | + ml.run (); |
4172 | + |
4173 | + if (!timeout_reached) Source.remove (t_id); |
4174 | + |
4175 | + return !timeout_reached; |
4176 | + } |
4177 | + |
4178 | + internal static SoftwareCenterData.AppInfo?[] get_items (SoftwareCenterDataCache cache, string category) |
4179 | + { |
4180 | + var ml = new MainLoop (); |
4181 | + SoftwareCenterData.AppInfo?[] items = null; |
4182 | + cache.get_items_for_category.begin (category, (obj, result) => |
4183 | + { |
4184 | + items = cache.get_items_for_category.end (result); |
4185 | + ml.quit (); |
4186 | + }); |
4187 | + assert (run_with_timeout (ml)); |
4188 | + return items; |
4189 | + } |
4190 | + |
4191 | internal static void test_category_cache () |
4192 | { |
4193 | var cache = new SoftwareCenterDataCache (1); // lifetime of cache items = 1s |
4194 | @@ -48,7 +79,7 @@ |
4195 | cache.mock_add (TOP_RATED, "Evince", "evince"); |
4196 | |
4197 | // at this point items are fetched by cache and kept for 1s |
4198 | - var items = cache.get_items_for_category (TOP_RATED); |
4199 | + var items = get_items (cache, TOP_RATED); |
4200 | assert (items.length == 1); |
4201 | |
4202 | // remove & add new items to the mock, but get_items_for_category should still |
4203 | @@ -57,7 +88,7 @@ |
4204 | cache.mock_add (TOP_RATED, "Blender", "blender"); |
4205 | cache.mock_add (TOP_RATED, "Firefox", "firefox"); |
4206 | cache.mock_add (TOP_RATED, "Thunderbird", "thunderbird"); |
4207 | - var items_2 = cache.get_items_for_category (TOP_RATED); |
4208 | + var items_2 = get_items (cache, TOP_RATED); |
4209 | assert (items_2.length == 1); |
4210 | |
4211 | assert (has_app (items_2, "Evince", "evince") == true); |
4212 | @@ -66,10 +97,10 @@ |
4213 | GLib.Thread.usleep (2 * 1000000); |
4214 | |
4215 | // cache will fetch new items now. |
4216 | - var items_3 = cache.get_items_for_category (TOP_RATED); |
4217 | + var items_3 = get_items (cache, TOP_RATED); |
4218 | assert (items_3.length == 3); |
4219 | assert (has_app (items_3, "Blender", "blender") == true); |
4220 | assert (has_app (items_3, "Firefox", "firefox") == true); |
4221 | assert (has_app (items_3, "Thunderbird", "thunderbird") == true); |
4222 | } |
4223 | -} |
4224 | \ No newline at end of file |
4225 | +} |
FAILED: Continuous integration, rev:367 /code.launchpad .net/~jamesh/ unity-lens- applications/ split-scope/ +merge/ 175982/ +edit-commit- message
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http:// jenkins. qa.ubuntu. com/job/ unity-lens- applications- ci/22/ jenkins. qa.ubuntu. com/job/ unity-lens- applications- saucy-amd64- ci/11/console jenkins. qa.ubuntu. com/job/ unity-lens- applications- saucy-armhf- ci/11/console jenkins. qa.ubuntu. com/job/ unity-lens- applications- saucy-i386- ci/11/console
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ unity-lens- applications- ci/22/rebuild
http://