Merge lp:~stolowski/unity-lens-applications/merge-smartscopes2 into lp:unity-lens-applications

Proposed by Paweł Stołowski
Status: Merged
Approved by: Łukasz Zemczak
Approved revision: 343
Merged at revision: 338
Proposed branch: lp:~stolowski/unity-lens-applications/merge-smartscopes2
Merge into: lp:unity-lens-applications
Diff against target: 2525 lines (+1036/-436)
24 files modified
Makefile.am (+6/-11)
applications.lens.in.in (+0/-12)
applications.scope.in.in (+13/-0)
commands.scope.in.in (+6/-4)
configure.ac (+14/-7)
data/Makefile.am (+2/-2)
data/unity-scope-applications.service.in (+1/-1)
debian/changelog (+6/-0)
debian/control (+2/-2)
po/POTFILES.in (+2/-2)
src/Makefile.am (+2/-1)
src/config.vala.in (+3/-1)
src/daemon.vala (+617/-256)
src/main.vala (+4/-4)
src/runner.vala (+99/-112)
src/schemas.vala (+10/-0)
src/unity-package-search.cc (+127/-7)
src/unity-package-search.h (+7/-2)
src/utils.vala (+93/-0)
src/xapian-utils.vala (+7/-5)
tests/unit/Makefile.am (+2/-0)
tests/unit/test-xapian-utils.vala (+7/-7)
vapi/unity-package-search.deps (+1/-0)
vapi/unity-package-search.vapi (+5/-0)
To merge this branch: bzr merge lp:~stolowski/unity-lens-applications/merge-smartscopes2
Reviewer Review Type Date Requested Status
Łukasz Zemczak Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+164230@code.launchpad.net

Commit message

Merge new scope API changes (100 scopes feature).

Description of the change

Merge new scope API changes (100 scopes feature).
Note: needs libunity 7.0.0 to land (should happen very soon).

To post a comment you must log in.
341. By Paweł Stołowski

Fixed required versions of libunity-dev and unity.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
342. By Paweł Stołowski

Bumped version once more and updated changelog.

343. By Paweł Stołowski

Version should actually change 7.0.0...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Łukasz Zemczak (sil2100) wrote :

Looks good, but it's late, soo... Anyway, approved!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile.am'
2--- Makefile.am 2013-02-15 10:11:07 +0000
3+++ Makefile.am 2013-05-16 21:22:23 +0000
4@@ -4,18 +4,14 @@
5 #
6 # Install the applications.lens file
7 #
8-lens_in_files = applications.lens.in
9-lensdir = $(datadir)/unity/lenses/applications
10-lens_DATA = $(lens_in_files:.lens.in=.lens)
11-
12-cmdlens_in_files = commands.lens.in
13-cmdlensdir = $(datadir)/unity/lenses/commands
14-cmdlens_DATA = $(cmdlens_in_files:.lens.in=.lens)
15+scope_in_files = applications.scope.in commands.scope.in
16+scopedir = $(datadir)/unity/scopes
17+scope_DATA = $(scope_in_files:.scope.in=.scope)
18
19 icondir = $(datadir)/unity/themes
20 icon_DATA = applications.png
21
22-@INTLTOOL_LENS_RULE@
23+@INTLTOOL_SCOPE_RULE@
24
25 DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall
26
27@@ -37,7 +33,7 @@
28 EXTRA_DIST = \
29 applications.png \
30 autogen.sh \
31- $(lens_in_files) \
32+ $(scope_in_files) \
33 AUTHORS \
34 COPYING \
35 HACKING \
36@@ -50,8 +46,7 @@
37 $(NULL)
38
39 CLEANFILES = \
40- $(lens_DATA) \
41- $(cmdlens_DATA) \
42+ $(scope_DATA) \
43 $(NULL)
44
45 include $(top_srcdir)/Makefile.am.coverage
46
47=== removed file 'applications.lens.in.in'
48--- applications.lens.in.in 2012-07-04 16:05:38 +0000
49+++ applications.lens.in.in 1970-01-01 00:00:00 +0000
50@@ -1,12 +0,0 @@
51-[Lens]
52-DBusName=com.canonical.Unity.Lens.Applications
53-DBusPath=/com/canonical/unity/lens/applications
54-_Name=Applications
55-Icon=@prefix@/share/unity/6/lens-nav-app.svg
56-Description=
57-_SearchHint=Search Applications
58-Shortcut=a
59-
60-[Desktop Entry]
61-X-Ubuntu-Gettext-Domain=unity-lens-applications
62-
63
64=== added file 'applications.scope.in.in'
65--- applications.scope.in.in 1970-01-01 00:00:00 +0000
66+++ applications.scope.in.in 2013-05-16 21:22:23 +0000
67@@ -0,0 +1,13 @@
68+[Scope]
69+DBusName=com.canonical.Unity.Scope.Applications
70+DBusPath=/com/canonical/unity/scope/applications
71+Icon=@prefix@/share/unity/icons/lens-nav-app.svg
72+_Name=Applications
73+_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.
74+Type=applications
75+_SearchHint=Search Applications
76+Shortcut=a
77+
78+[Desktop Entry]
79+X-Ubuntu-Gettext-Domain=unity-lens-applications
80+
81
82=== renamed file 'commands.lens.in.in' => 'commands.scope.in.in'
83--- commands.lens.in.in 2011-08-08 22:05:14 +0000
84+++ commands.scope.in.in 2013-05-16 21:22:23 +0000
85@@ -1,8 +1,10 @@
86-[Lens]
87-DBusName=com.canonical.Unity.Lens.Applications
88-DBusPath=/com/canonical/unity/lens/commands
89+[Scope]
90+DBusName=com.canonical.Unity.Scope.Applications
91+DBusPath=/com/canonical/unity/scope/commands
92 _Name=Commands
93-Icon=@prefix@/share/unity/themes/applications.png
94+_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.
95+Icon=
96+Type=commands
97 _SearchHint=Run a command
98 Visible=false
99
100
101=== modified file 'configure.ac'
102--- configure.ac 2013-03-01 15:11:29 +0000
103+++ configure.ac 2013-05-16 21:22:23 +0000
104@@ -1,4 +1,4 @@
105-AC_INIT(unity-lens-applications, 6.8.0, https://launchpad.net/unity-lens-applications)
106+AC_INIT(unity-lens-applications, 7.0.0, https://launchpad.net/unity-lens-applications)
107 AC_COPYRIGHT([Copyright 2010-2012 Canonical])
108 AM_INIT_AUTOMAKE([1.11])
109
110@@ -47,10 +47,10 @@
111 AC_DEFINE_UNQUOTED(PREFIXDIR, "${PREFIX}",[Prefix directory])
112
113 ######################################################
114-# intltool rule for generating translated .lens file
115+# intltool rule for generating translated .scope file
116 ######################################################
117-INTLTOOL_LENS_RULE='%.lens: %.lens.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@'
118-AC_SUBST(INTLTOOL_LENS_RULE)
119+INTLTOOL_SCOPE_RULE='%.scope: %.scope.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@'
120+AC_SUBST(INTLTOOL_SCOPE_RULE)
121
122 ###########################
123 # gcov coverage reporting
124@@ -76,7 +76,8 @@
125 dee-1.0 >= 0.5.16
126 zeitgeist-1.0 >= 0.3.8
127 libcolumbus0 >= 0.4.0
128- unity >= 6.7.0
129+ unity >= 7.0.0
130+ unity-protocol-private
131 libgnome-menu-3.0 >= 3.6.0)
132
133 AC_SUBST(LENS_DAEMON_CFLAGS)
134@@ -138,6 +139,12 @@
135 AS_AC_EXPAND(DATADIR, $datarootdir)
136 AC_SUBST(DATADIR)
137
138+#####################################################
139+# Look for protocol-private lib dir
140+#####################################################
141+PROTOCOLPRIVATELIBDIR="`$PKG_CONFIG --variable=libdir unity-protocol-private`/libunity"
142+AC_SUBST(PROTOCOLPRIVATELIBDIR)
143+
144 #############################################
145 # look for dbus service dir
146 #############################################
147@@ -159,8 +166,8 @@
148 #############################################
149 AC_CONFIG_FILES([
150 Makefile
151- applications.lens.in
152- commands.lens.in
153+ applications.scope.in
154+ commands.scope.in
155 data/com.canonical.Unity.AppsLens.gschema.xml.in
156 data/Makefile
157 data/X-Unity-All-Applications.directory
158
159=== modified file 'data/Makefile.am'
160--- data/Makefile.am 2012-11-08 04:49:06 +0000
161+++ data/Makefile.am 2013-05-16 21:22:23 +0000
162@@ -2,7 +2,7 @@
163 # DBus service files
164 #############################################################
165 dbus_servicesdir = $(DBUSSERVICEDIR)
166-service_in_files = unity-lens-applications.service.in
167+service_in_files = unity-scope-applications.service.in
168 dbus_services_DATA = $(service_in_files:.service.in=.service)
169
170 %.service: %.service.in
171@@ -39,6 +39,6 @@
172 $(menu_in_files)
173
174 CLEANFILES = \
175- unity-lens-applications.service \
176+ unity-scope-applications.service \
177 $(gsettings_SCHEMAS)
178
179
180=== renamed file 'data/unity-lens-applications.service.in' => 'data/unity-scope-applications.service.in'
181--- data/unity-lens-applications.service.in 2012-11-08 04:49:06 +0000
182+++ data/unity-scope-applications.service.in 2013-05-16 21:22:23 +0000
183@@ -1,3 +1,3 @@
184 [D-BUS Service]
185-Name=com.canonical.Unity.Lens.Applications
186+Name=com.canonical.Unity.Scope.Applications
187 Exec=@pkglibexecdir@/unity-applications-daemon
188
189=== modified file 'debian/changelog'
190--- debian/changelog 2013-05-01 21:58:08 +0000
191+++ debian/changelog 2013-05-16 21:22:23 +0000
192@@ -1,3 +1,9 @@
193+unity-lens-applications (7.0.0-0ubuntu1) UNRELEASED; urgency=low
194+
195+ * New upstream release.
196+
197+ -- Pawel Stolowski <pawel.stolowski@ubuntu.com> Thu, 16 May 2013 23:12:47 +0200
198+
199 unity-lens-applications (6.10.0daily13.05.01.1ubuntu.unity.next-0ubuntu1) raring; urgency=low
200
201 [ Sebastien Bacher ]
202
203=== modified file 'debian/control'
204--- debian/control 2013-04-12 12:21:04 +0000
205+++ debian/control 2013-05-16 21:22:23 +0000
206@@ -11,7 +11,7 @@
207 libgee-dev,
208 libdee-dev (>= 0.5.16),
209 libzeitgeist-dev (>= 0.3.8),
210- libunity-dev (>= 6.8.0),
211+ libunity-dev (>= 7.0.0),
212 libgnome-menu-3-dev,
213 dh-autoreconf,
214 libxapian-dev,
215@@ -28,7 +28,7 @@
216 Architecture: any
217 Depends: ${shlibs:Depends},
218 ${misc:Depends},
219- unity-common (>= 4.4.0)
220+ unity-common (>= 7.0.0)
221 Breaks: unity (<< 6.0.0),
222 Description: Application lens for unity
223 This package contains the "application" lens which can be used
224
225=== modified file 'po/POTFILES.in'
226--- po/POTFILES.in 2011-08-04 10:07:39 +0000
227+++ po/POTFILES.in 2013-05-16 21:22:23 +0000
228@@ -4,5 +4,5 @@
229 src/utils.vala
230 src/main.vala
231 data/X-Unity-All-Applications.directory.in
232-[type: gettext/ini]applications.lens.in.in
233-[type: gettext/ini]commands.lens.in.in
234+[type: gettext/ini]applications.scope.in.in
235+[type: gettext/ini]commands.scope.in.in
236
237=== modified file 'src/Makefile.am'
238--- src/Makefile.am 2013-02-22 12:05:05 +0000
239+++ src/Makefile.am 2013-05-16 21:22:23 +0000
240@@ -35,6 +35,7 @@
241 --pkg dee-1.0 \
242 --pkg zeitgeist-1.0 \
243 --pkg unity \
244+ --pkg unity-protocol \
245 --pkg gee-1.0 \
246 --pkg libgnome-menu-3.0 \
247 --pkg unity-package-search \
248@@ -54,7 +55,7 @@
249 unity-ratings-db.o \
250 $(NULL)
251
252-unity_applications_daemon_LDFLAGS = $(COVERAGE_LDFLAGS)
253+unity_applications_daemon_LDFLAGS = -rpath $(PROTOCOLPRIVATELIBDIR) $(COVERAGE_LDFLAGS)
254
255 unity_applications_daemon_VALASOURCES = \
256 app-watcher.vala \
257
258=== modified file 'src/config.vala.in'
259--- src/config.vala.in 2010-06-22 07:36:19 +0000
260+++ src/config.vala.in 2013-05-16 21:22:23 +0000
261@@ -4,6 +4,8 @@
262
263 const string DATADIR = "@DATADIR@";
264
265+ const string PKGDATADIR = "@DATADIR@/unity";
266+
267 const string BINDIR = "@prefix@/bin";
268
269 const string LOCALEDIR = "@DATADIR@/locale";
270@@ -11,4 +13,4 @@
271 const string PACKAGE = "@PACKAGE@";
272
273 const string VERSION = "@VERSION@";
274-}
275\ No newline at end of file
276+}
277
278=== modified file 'src/daemon.vala'
279--- src/daemon.vala 2013-04-22 17:45:49 +0000
280+++ src/daemon.vala 2013-05-16 21:22:23 +0000
281@@ -42,9 +42,13 @@
282
283 const string ICON_PATH = Config.DATADIR + "/icons/unity-icon-theme/places/svg/";
284 const string GENERIC_APP_ICON = "applications-other";
285+ const string GENERIC_SCOPE_ICON = ICON_PATH + "service-generic.svg";
286+
287+ const string LIBUNITY_SCHEMA = "com.canonical.Unity.Lenses";
288
289 public class Daemon : GLib.Object
290 {
291+ private Variant empty_asv;
292 private Zeitgeist.Log log;
293 private Zeitgeist.Index zg_index;
294 private Zeitgeist.Monitor monitor;
295@@ -55,14 +59,14 @@
296 /* The searcher for online material may be null if it fails to load
297 * the Xapian index from the Software Center */
298 private Unity.Package.Searcher? pkgsearcher;
299+ private Unity.Package.Searcher? scopesearcher;
300 public Unity.Package.Searcher appsearcher;
301
302 /* Read the app ratings dumped by the Software Center */
303 private bool ratings_db_initialized = false;
304 private Unity.Ratings.Database? ratings = null;
305
306- private Unity.Lens lens;
307- private Unity.Scope scope;
308+ private Unity.DeprecatedScope scope;
309
310 /* Support aptd dbus interface; created when application install/remove was requested by preview action */
311 private AptdProxy aptdclient;
312@@ -107,6 +111,7 @@
313 private Regex mountable_regex;
314
315 private Settings gp_settings;
316+ private HashTable<unowned string, unowned string> disabled_scope_ids;
317
318 private const string DISPLAY_RECENT_APPS_KEY = "display-recent-apps";
319 private const string DISPLAY_AVAILABLE_APPS_KEY = "display-available-apps";
320@@ -116,6 +121,9 @@
321 public bool force_small_icons_for_suggestions { get; set; default = true; }
322
323 private PurchaseInfoHelper purchase_info = null;
324+ private Dee.Model remote_scopes_model;
325+ private Dee.Index scopes_index;
326+ private Dee.Analyzer analyzer;
327
328 construct
329 {
330@@ -123,6 +131,7 @@
331
332 log = new Zeitgeist.Log();
333 zg_index = new Zeitgeist.Index();
334+ empty_asv = new Variant.array (new VariantType ("{sv}"), {});
335 monitor = new Zeitgeist.Monitor (new Zeitgeist.TimeRange.from_now (),
336 zg_templates);
337 monitor.events_inserted.connect (mark_dirty);
338@@ -159,14 +168,17 @@
339 image_extensions.add ("jpg");
340
341 build_app_menu_index ();
342+ build_scope_index.begin ();
343
344 file_icon_cache = new HashTable<string,Icon>(str_hash, str_equal);
345 sc_mangler = new SoftwareCenterUtils.MangledDesktopFileLookup ();
346
347- scope = new Unity.Scope ("/com/canonical/unity/scope/applications");
348- scope.provides_personal_content = true;
349- //scope.icon = @"$(Config.PREFIX)/share/unity/themes/applications.png";
350+ scope = new Unity.DeprecatedScope ("/com/canonical/unity/scope/applications",
351+ "applications");
352
353+ scope.search_hint = _("Search Applications");
354+ scope.search_in_global = true;
355+ // scope.sources_display_name = _("Sources");
356 // TRANSLATORS: Please make sure this string is short enough to fit
357 // into the filter button
358 local_apps_option = scope.sources.add_option ("local", _("Local Apps"));
359@@ -177,6 +189,10 @@
360 usc_apps_option = scope.sources.add_option ("usc", _("Software Center"));
361 }
362
363+ populate_categories ();
364+ populate_filters();
365+ //scope.icon = @"$(Config.PREFIX)/share/unity/themes/applications.png";
366+
367 scope.generate_search_key.connect ((lens_search) =>
368 {
369 return lens_search.search_string.strip ();
370@@ -184,16 +200,10 @@
371 /* Listen for changes to the lens scope search */
372 scope.search_changed.connect ((lens_search, search_type, cancellable) =>
373 {
374- dispatch_search (lens_search, search_type, cancellable);
375- });
376-
377- /* Re-do the search if the filters changed */
378- scope.filters_changed.connect (() =>
379- {
380- scope.queue_search_changed (SearchType.DEFAULT);
381- });
382-
383- /* And also if the sources change */
384+ dispatch_search.begin (lens_search, search_type, cancellable);
385+ });
386+
387+ /* Re-do the search if the sources change */
388 scope.active_sources_changed.connect (() =>
389 {
390 scope.queue_search_changed (SearchType.DEFAULT);
391@@ -225,15 +235,35 @@
392 aptdclient = new AptdProxy ();
393 launcherservice = new LauncherProxy ();
394
395- lens = new Unity.Lens ("/com/canonical/unity/lens/applications", "applications");
396- lens.search_hint = _("Search Applications");
397- lens.visible = true;
398- lens.search_in_global = true;
399- lens.sources_display_name = _("Sources");
400- populate_categories ();
401- populate_filters();
402- lens.add_local_scope (scope);
403- lens.export ();
404+ scope.export ();
405+
406+ remote_scopes_model = new Dee.SharedModel ("com.canonical.Unity.SmartScopes.RemoteScopesModel");
407+ remote_scopes_model.set_schema ("s", "s", "s", "s", "s", "as");
408+
409+ scopes_index = Utils.prepare_index (remote_scopes_model,
410+ RemoteScopesColumn.NAME,
411+ (model, iter) =>
412+ {
413+ unowned string name = model.get_string (iter, RemoteScopesColumn.NAME);
414+ return "%s\n%s".printf (_("scope"), name);
415+ }, out analyzer);
416+
417+ disabled_scope_ids = new HashTable<unowned string, unowned string> (str_hash, str_equal);
418+ update_disabled_scopes_hash ();
419+
420+ var pref_man = PreferencesManager.get_default ();
421+ pref_man.notify["disabled-scopes"].connect (update_disabled_scopes_hash);
422+ }
423+
424+ private void update_disabled_scopes_hash ()
425+ {
426+ disabled_scope_ids.remove_all ();
427+ var pref_man = PreferencesManager.get_default ();
428+ foreach (unowned string scope_id in pref_man.disabled_scopes)
429+ {
430+ // using HashTable as a set (optimized in glib when done like this)
431+ disabled_scope_ids[scope_id] = scope_id;
432+ }
433 }
434
435 private void init_ratings_db ()
436@@ -251,9 +281,9 @@
437 ratings_db_initialized = true;
438 }
439
440- private async void dispatch_search (LensSearch lens_search,
441+ private async void dispatch_search (DeprecatedScopeSearch search,
442 SearchType search_type,
443- Cancellable cancellable)
444+ GLib.Cancellable cancellable)
445 {
446 if (popularities_dirty)
447 {
448@@ -265,43 +295,45 @@
449 }
450
451 if (search_type == SearchType.DEFAULT)
452- yield update_scope_search (lens_search, cancellable);
453+ yield update_scope_search (search, cancellable);
454 else
455- yield update_global_search (lens_search, cancellable);
456+ yield update_global_search (search, cancellable);
457+
458+ search.finished ();
459 }
460
461 private void populate_categories ()
462 {
463- GLib.List<Unity.Category> categories = new GLib.List<Unity.Category> ();
464+ Unity.CategorySet categories = new Unity.CategorySet ();
465 File icon_dir = File.new_for_path (ICON_PATH);
466
467- var cat = new Unity.Category (_("Applications"),
468+ var cat = new Unity.Category ("apps", _("Applications"),
469 new FileIcon (icon_dir.get_child ("group-apps.svg")));
470- categories.append (cat);
471+ categories.add (cat);
472
473- cat = new Unity.Category (_("Recently Used"),
474+ cat = new Unity.Category ("recently-used", _("Recently Used"),
475 new FileIcon (icon_dir.get_child ("group-recent.svg")));
476- categories.append (cat);
477+ categories.add (cat);
478
479- cat = new Unity.Category (_("Recent Apps"),
480+ cat = new Unity.Category ("recent", _("Recent Apps"),
481 new FileIcon (icon_dir.get_child ("group-apps.svg")));
482- categories.append (cat);
483+ categories.add (cat);
484
485- cat = new Unity.Category (_("Installed"),
486+ cat = new Unity.Category ("installed", _("Installed"),
487 new FileIcon (icon_dir.get_child ("group-installed.svg")));
488- categories.append (cat);
489+ categories.add (cat);
490
491- cat = new Unity.Category (_("More suggestions"),
492+ cat = new Unity.Category ("more", _("More suggestions"),
493 new FileIcon (icon_dir.get_child ("group-treat-yourself.svg")),
494 Unity.CategoryRenderer.FLOW);
495- categories.append (cat);
496+ categories.add (cat);
497
498- lens.categories = categories;
499+ scope.categories = categories;
500 }
501
502 private void populate_filters()
503 {
504- GLib.List<Unity.Filter> filters = new GLib.List<Unity.Filter> ();
505+ Unity.FilterSet filters = new Unity.FilterSet ();
506
507 /* Type filter */
508 {
509@@ -320,25 +352,28 @@
510 filter.add_option ("accessibility", _("Accessibility"));
511 filter.add_option ("developer", _("Developer"));
512 filter.add_option ("science-and-engineering", _("Science & Engineering"));
513+ filter.add_option ("scopes", _("Search plugins"));
514 filter.add_option ("system", _("System"));
515
516- filters.append (filter);
517+ filters.add (filter);
518 }
519
520- lens.filters = filters;
521+ scope.filters = filters;
522 }
523
524- private bool local_apps_active ()
525+ private bool local_apps_active (DeprecatedScopeSearch search)
526 {
527- if (scope.sources.filtering && local_apps_option != null)
528- return local_apps_option.active;
529+ var filter = search.get_filter (scope.sources.id) as Unity.OptionsFilter;
530+ if (filter.filtering && local_apps_option != null)
531+ return filter.get_option (local_apps_option.id).active;
532 return true;
533 }
534
535- private bool usc_apps_active ()
536+ private bool usc_apps_active (DeprecatedScopeSearch search)
537 {
538- if (scope.sources.filtering && usc_apps_option != null)
539- return usc_apps_option.active;
540+ var filter = search.get_filter (scope.sources.id) as Unity.OptionsFilter;
541+ if (filter.filtering && usc_apps_option != null)
542+ return filter.get_option (usc_apps_option.id).active;
543 return true;
544 }
545
546@@ -401,6 +436,15 @@
547 return false;
548 }
549
550+ private async void build_scope_index ()
551+ {
552+ var scope_registry = yield Unity.Protocol.ScopeRegistry.find_scopes (
553+ Config.PKGDATADIR + "/scopes");
554+
555+ debug ("Indexing scopes");
556+ scopesearcher = new Unity.Package.Searcher.for_scopes (scope_registry);
557+ }
558+
559 private void populate_zg_templates ()
560 {
561 /* Create a template that activation of applications */
562@@ -430,7 +474,7 @@
563 zg_templates,
564 StorageState.ANY,
565 256,
566- ResultType.MOST_POPULAR_SUBJECTS,
567+ Zeitgeist.ResultType.MOST_POPULAR_SUBJECTS,
568 null);
569
570 // most popular apps must have high value, so unknown apps (where
571@@ -461,21 +505,22 @@
572 return app == null;
573 }
574
575- private async void update_scope_search (Unity.LensSearch search,
576- Cancellable cancellable)
577+ private async void update_scope_search (DeprecatedScopeSearch search,
578+ GLib.Cancellable cancellable)
579 {
580 var model = search.results_model;
581 /* We'll clear the model once we finish waiting for the dbus-call
582 * to finish, to prevent flicker. */
583
584- debug ("Searching for: %s", search.search_string);
585-
586- var filter = scope.get_filter ("type") as OptionsFilter;
587-
588- string pkg_search_string = XapianUtils.prepare_pkg_search_string (search.search_string, filter);
589-
590- bool has_filter = (filter != null && filter.filtering);
591- bool has_search = !Utils.is_search_empty (search.search_string);
592+ var search_string = search.search_string.strip ();
593+ debug ("Searching for: %s", search_string);
594+
595+ var type_filter = search.get_filter ("type") as OptionsFilter;
596+
597+ string pkg_search_string = XapianUtils.prepare_pkg_search_string (search_string, type_filter);
598+
599+ bool has_filter = (type_filter != null && type_filter.filtering);
600+ bool has_search = !Utils.is_search_empty (search_string);
601
602 Timer timer = new Timer ();
603
604@@ -494,7 +539,7 @@
605 has_search ?
606 Unity.Package.Sort.BY_RELEVANCY :
607 Unity.Package.Sort.BY_NAME);
608- if (local_apps_active ())
609+ if (local_apps_active (search))
610 {
611 if (has_search) resort_pkg_search_results (appresults);
612 add_pkg_search_result (appresults, installed_uris, available_uris,
613@@ -505,7 +550,7 @@
614 debug ("Entry search listed %i Installed apps in %fms for query: %s",
615 appresults.num_hits, timer.elapsed ()*1000, pkg_search_string);
616
617- if (local_apps_active () && display_recent_apps)
618+ if (local_apps_active (search) && display_recent_apps)
619 {
620 try
621 {
622@@ -513,7 +558,7 @@
623 /* Ignore the search string, we want to keep displaying the same apps
624 * in the recent category and just filter out those that don't match
625 * the search query */
626- var zg_search_string = XapianUtils.prepare_zg_search_string ("", filter);
627+ var zg_search_string = XapianUtils.prepare_zg_search_string ("", type_filter);
628
629 var results = yield zg_index.search (zg_search_string,
630 new Zeitgeist.TimeRange.anytime(),
631@@ -535,16 +580,23 @@
632 // no need to bother
633 return;
634 } catch (GLib.Error e) {
635- warning ("Error performing search '%s': %s", search.search_string, e.message);
636+ warning ("Error performing search '%s': %s", search_string, e.message);
637 }
638 }
639
640 transaction.commit ();
641
642+ // add scopes (if filter is active)
643+ if (!has_filter || type_filter.get_option ("scopes").active)
644+ {
645+ add_local_scopes_results (search_string, model, installed_uris);
646+ add_remote_scopes_results (search_string, model);
647+ }
648+
649 purchase_info = new PurchaseInfoHelper ();
650
651 /* If we don't have a search we display 6 random apps */
652- if (usc_apps_active () && display_available_apps && pkgsearcher != null)
653+ if (usc_apps_active (search) && display_available_apps && pkgsearcher != null)
654 {
655 if (has_search)
656 {
657@@ -561,7 +613,7 @@
658 else if (has_filter) /* Empty search string + active filters should get lots of results from selected categories */
659 {
660 timer.start ();
661- string? filter_query = XapianUtils.prepare_pkg_search_string (search.search_string, filter);
662+ string? filter_query = XapianUtils.prepare_pkg_search_string (search_string, type_filter);
663
664 var pkgresults = pkgsearcher.get_apps (filter_query, MAX_APP_FOR_DOWNLOAD_FOR_EMPTY_QUERY, filter_cb);
665 purchase_info.from_pkgresults (pkgresults);
666@@ -610,19 +662,18 @@
667 search.set_reply_hint ("no-results-hint",
668 _("Sorry, there are no applications that match your search."));
669 }
670-
671- search.finished ();
672 }
673
674- private async void update_global_search (Unity.LensSearch search,
675- Cancellable cancellable)
676+ private async void update_global_search (DeprecatedScopeSearch search,
677+ GLib.Cancellable cancellable)
678 {
679 /*
680 * In global search, with a non-empty search string, we collate all
681 * hits under one Applications category
682 */
683+ var search_string = search.search_string.strip ();
684
685- if (Utils.is_search_empty (search.search_string))
686+ if (Utils.is_search_empty (search_string))
687 {
688 yield update_global_without_search (search, cancellable);
689 return;
690@@ -632,11 +683,11 @@
691
692 model.clear ();
693
694- var search_string = XapianUtils.prepare_pkg_search_string (search.search_string, null);
695+ var pkg_search_string = XapianUtils.prepare_pkg_search_string (search_string, null);
696 Set<string> installed_uris = new HashSet<string> ();
697 Set<string> available_uris = new HashSet<string> ();
698 var timer = new Timer ();
699- var appresults = appsearcher.search (search_string, 0,
700+ var appresults = appsearcher.search (pkg_search_string, 0,
701 Unity.Package.SearchType.PREFIX,
702 Unity.Package.Sort.BY_RELEVANCY);
703 resort_pkg_search_results (appresults);
704@@ -645,17 +696,11 @@
705
706 timer.stop ();
707 debug ("Global search listed %i Installed apps in %fms for query: %s",
708- appresults.num_hits, timer.elapsed ()*1000, search_string);
709-
710- /* Allow new searches once we enter an idle again.
711- * We don't do it directly from here as that could mean we start
712- * changing the model even before we had flushed out current changes
713- */
714- search.finished ();
715+ appresults.num_hits, timer.elapsed ()*1000, pkg_search_string);
716 }
717
718- private async void update_global_without_search (Unity.LensSearch search,
719- Cancellable cancellable)
720+ private async void update_global_without_search (DeprecatedScopeSearch search,
721+ GLib.Cancellable cancellable)
722 {
723 /*
724 * In global search, with an empty search string, we show just Recent Apps
725@@ -665,12 +710,12 @@
726
727 Timer timer = new Timer ();
728
729- if (local_apps_active () && display_recent_apps)
730+ if (local_apps_active (search) && display_recent_apps)
731 {
732 try
733 {
734- var zg_search_string = XapianUtils.prepare_zg_search_string (search.search_string,
735- null);
736+ var zg_search_string = XapianUtils.prepare_zg_search_string ("",
737+ null);
738
739 var time_range = new Zeitgeist.TimeRange.anytime ();
740 var results = yield log.find_events (time_range,
741@@ -697,8 +742,6 @@
742 search.search_string, e.message);
743 }
744 }
745-
746- search.finished ();
747 }
748
749 public Icon find_pkg_icon (string? desktop_file, string icon_name)
750@@ -871,10 +914,12 @@
751
752 model.append (uri, icon_obj,
753 category,
754+ pinfo != null && pinfo.paid ? Unity.ResultType.PERSONAL : Unity.ResultType.DEFAULT,
755 "application/x-desktop",
756 app.application_name,
757 "", //comment
758- "file://" + app.desktop_file);
759+ "file://" + app.desktop_file,
760+ empty_asv);
761 duplicates_lookup.add (uri);
762 i++;
763 if (i == max_results)
764@@ -968,11 +1013,16 @@
765 icon_str = icon.to_string ();
766 }
767
768+ var result_type = (category != Category.AVAILABLE && !results.fuzzy_search) ?
769+ Unity.ResultType.PERSONAL : Unity.ResultType.DEFAULT;
770 model.append (uri, icon_str,
771- category,"application/x-desktop",
772+ category,
773+ result_type,
774+ "application/x-desktop",
775 display_name != null ? display_name : "",
776 comment != null ? comment : "",
777- full_path != null ? "file://" + full_path : "");
778+ full_path != null ? "file://" + full_path : "",
779+ empty_asv);
780
781 /* Stop if we added the number of items requested */
782 n_added++;
783@@ -981,6 +1031,124 @@
784 }
785 }
786
787+ private static Icon? get_default_scope_icon ()
788+ {
789+ try
790+ {
791+ return Icon.new_for_string (GENERIC_SCOPE_ICON);
792+ }
793+ catch (Error err)
794+ {
795+ warning ("%s", err.message);
796+ }
797+ return null;
798+ }
799+
800+ private void add_local_scopes_results (string search_string,
801+ Dee.Model model,
802+ Set<string> installed_uris)
803+ {
804+ // If the local scopes Xapian index hasn't been built, return.
805+ if (scopesearcher == null)
806+ return;
807+
808+ bool has_search = !Utils.is_search_empty (search_string);
809+ var pkg_search_string = XapianUtils.prepare_pkg_search_string (
810+ search_string, null);
811+
812+ var results = scopesearcher.search (pkg_search_string, 0,
813+ Unity.Package.SearchType.PREFIX,
814+ has_search ?
815+ Unity.Package.Sort.BY_RELEVANCY :
816+ Unity.Package.Sort.BY_NAME);
817+
818+ foreach (unowned Unity.Package.PackageInfo info in results.results)
819+ {
820+ /* De-dupe by "application://foo.scope", since that is what is
821+ * used for software-center results. */
822+ string dedup_key = @"application://$(info.desktop_file)";
823+ if (dedup_key in installed_uris)
824+ continue;
825+ installed_uris.add(dedup_key);
826+
827+ /* Don't include master scopes in the search results. This is
828+ * performed after deduping so the master scopes don't just
829+ * move to hte "available" category. */
830+ if (info.is_master_scope)
831+ continue;
832+
833+ var uri = @"scope://$(info.desktop_file)";
834+ var name = info.application_name;
835+ var icon_hint = info.icon;
836+ var category = info.desktop_file in disabled_scope_ids ?
837+ Category.AVAILABLE : Category.INSTALLED;
838+
839+ try
840+ {
841+ Icon base_icon = icon_hint == null || icon_hint == "" ?
842+ get_default_scope_icon () : Icon.new_for_string (icon_hint);
843+ var anno_icon = new AnnotatedIcon (base_icon);
844+ anno_icon.size_hint = IconSizeHint.SMALL;
845+ icon_hint = anno_icon.to_string ();
846+ }
847+ catch (Error err)
848+ {
849+ icon_hint = "";
850+ }
851+
852+ model.append (uri,
853+ icon_hint,
854+ category,
855+ ResultType.DEFAULT,
856+ "application/x-unity-scope",
857+ name != "" ? name : uri,
858+ "",
859+ "", // dnd_uri ?!
860+ empty_asv);
861+ }
862+ }
863+
864+ private void add_remote_scopes_results (string search_string,
865+ Dee.Model model)
866+ {
867+ var results = Utils.search_index (scopes_index, analyzer, search_string);
868+ foreach (var iter in results)
869+ {
870+ unowned string scope_id =
871+ remote_scopes_model.get_string (iter, RemoteScopesColumn.SCOPE_ID);
872+ var uri = @"scope://$(scope_id)";
873+ var name =
874+ remote_scopes_model.get_string (iter, RemoteScopesColumn.NAME);
875+ var icon_hint =
876+ remote_scopes_model.get_string (iter, RemoteScopesColumn.ICON_HINT);
877+
878+ try
879+ {
880+ Icon base_icon = icon_hint == null || icon_hint == "" ?
881+ get_default_scope_icon () : Icon.new_for_string (icon_hint);
882+ var anno_icon = new AnnotatedIcon (base_icon);
883+ anno_icon.size_hint = IconSizeHint.SMALL;
884+ icon_hint = anno_icon.to_string ();
885+ }
886+ catch (Error err)
887+ {
888+ icon_hint = "";
889+ }
890+
891+ var category = scope_id in disabled_scope_ids ?
892+ Category.AVAILABLE : Category.INSTALLED;
893+
894+ model.append (uri,
895+ icon_hint,
896+ category,
897+ ResultType.DEFAULT,
898+ "application/x-unity-scope",
899+ name != "" ? name : uri,
900+ "",
901+ "", // dnd_uri ?!
902+ empty_asv);
903+ }
904+ }
905 private async void call_install_packages (string package_name, out string tid) throws IOError
906 {
907 tid = yield aptdclient.install_packages ({package_name});
908@@ -1141,181 +1309,371 @@
909
910 public Unity.Preview preview (string uri)
911 {
912+ Unity.Preview? preview = null;
913+ bool installed = uri.has_prefix ("application://");
914+ bool is_scope = uri.has_prefix ("scope://");
915+
916+ if (installed || uri.has_prefix ("unity-install://"))
917+ {
918+ preview = make_app_preview (uri, installed);
919+ }
920+ else if (is_scope)
921+ {
922+ preview = make_scope_preview (uri);
923+ }
924+ return preview;
925+ }
926+
927+ private SoftwareCenterData.AppDetailsData get_app_details (string appname, string pkgname) throws Error
928+ {
929+ if (sc_data_provider == null)
930+ {
931+ sc_data_provider = new SoftwareCenterDataCache (TOP_RATED_ITEMS_CACHE_LIFETIME);
932+ sc_data_provider.connect_to ();
933+ }
934+
935+ debug ("Requesting pkg info: %s, %s\n", pkgname, appname);
936+ return sc_data_provider.get_app_details (appname, pkgname);
937+ }
938+
939+ private Unity.Preview make_app_preview (string uri, bool installed)
940+ {
941 Unity.ApplicationPreview? preview = null;
942-
943+ string desktopfile = null;
944 string pkgname = "";
945 string appname = "";
946- bool installed = uri.has_prefix ("application://");
947-
948- if (installed || uri.has_prefix ("unity-install://"))
949- {
950- string desktopfile = null;
951- if (installed)
952- {
953- desktopfile = uri.substring (14); //remove "application://" prefix
954-
955- // de-mangle desktop file names back to what S-C expects
956- if (sc_mangler.contains (desktopfile))
957- {
958- desktopfile = sc_mangler.get (desktopfile);
959- }
960-
961- Unity.Package.PackageInfo pkginfo = pkgsearcher.get_by_desktop_file (desktopfile);
962- if (pkginfo != null)
963- {
964- appname = pkginfo.application_name;
965- pkgname = pkginfo.package_name;
966- }
967- }
968- else // unity-install
969- {
970- string app = uri.substring (16); //remove "unity-install://" prefix
971- string[] parts = app.split ("/");
972- if (parts.length > 1)
973- {
974- pkgname = parts[0];
975- appname = parts[1];
976- }
977- }
978-
979- if (pkgname != "")
980- {
981+
982+ if (installed)
983+ {
984+ desktopfile = uri.substring (14); //remove "application://" prefix
985+
986+ // de-mangle desktop file names back to what S-C expects
987+ if (sc_mangler.contains (desktopfile))
988+ {
989+ desktopfile = sc_mangler.get (desktopfile);
990+ }
991+
992+ Unity.Package.PackageInfo pkginfo = pkgsearcher.get_by_desktop_file (desktopfile);
993+ if (pkginfo != null)
994+ {
995+ appname = pkginfo.application_name;
996+ pkgname = pkginfo.package_name;
997+ }
998+ }
999+ else // unity-install
1000+ {
1001+ string app = uri.substring (16); //remove "unity-install://" prefix
1002+ string[] parts = app.split ("/");
1003+ if (parts.length > 1)
1004+ {
1005+ pkgname = parts[0];
1006+ appname = parts[1];
1007+ }
1008+ }
1009+
1010+ if (pkgname != "")
1011+ {
1012+ try {
1013+ var details = get_app_details (appname, pkgname);
1014+
1015+ Icon? icon = null;
1016+ if (installed)
1017+ icon = new GLib.ThemedIcon (details.icon);
1018+ else
1019+ {
1020+ icon = find_pkg_icon (null, details.icon);
1021+ if (icon.to_string () == GENERIC_APP_ICON && details.icon_url != null && details.icon_url != "")
1022+ {
1023+ icon = new GLib.FileIcon (File.new_for_uri (details.icon_url));
1024+ }
1025+ }
1026+
1027+ Icon? screenshot = null;
1028+
1029+ if (details.screenshot != null)
1030+ {
1031+ File scr_file = File.new_for_uri (details.screenshot);
1032+ screenshot = new FileIcon (scr_file);
1033+ }
1034+
1035+ string subtitle = "";
1036+ if (details.version != "")
1037+ subtitle = _("Version %s").printf (details.version);
1038+ if (details.size > 0)
1039+ {
1040+ if (subtitle != "")
1041+ subtitle += ", ";
1042+ subtitle += (_("Size %s")).printf (GLib.format_size (details.size));
1043+ }
1044+ preview = new Unity.ApplicationPreview (details.name, subtitle, details.description, icon, screenshot);
1045+ preview.license = details.license;
1046+
1047+ init_ratings_db ();
1048+ if (ratings != null)
1049+ {
1050+ Unity.Ratings.Result result;
1051+ ratings.query (pkgname, out result);
1052+ preview.set_rating (result.average_rating / 5.0f, result.total_rating);
1053+ }
1054+
1055+ if (details.hardware_requirements != "")
1056+ preview.add_info (new InfoHint ("hardware-requirements", _("Hardware requirements"), null, details.hardware_requirements));
1057+
1058+ if (uri.has_prefix ("unity-install://")) // application needs to be purchased/installed
1059+ {
1060+ // uninstalled and not purchased before
1061+ if (details.pkg_state == SoftwareCenterData.PackageState.NEEDS_PURCHASE)
1062+ {
1063+ var buy_action = new Unity.PreviewAction ("buy", _("Buy"), null);
1064+ if (details.price != null && details.price != "")
1065+ {
1066+ buy_action.extra_text = details.price;
1067+ }
1068+
1069+ buy_action.activated.connect (start_software_center);
1070+ preview.add_action (buy_action);
1071+ }
1072+ else // uninstalled, purchased before
1073+ {
1074+
1075+ Unity.PreviewAction install_action = null;
1076+ if (details.raw_price == null || details.raw_price == "")
1077+ {
1078+ install_action = new Unity.PreviewAction ("install", _("Free Download"), null);
1079+ install_action.activated.connect (app_preview_install);
1080+ }
1081+ else
1082+ {
1083+ install_action = new Unity.PreviewAction ("install", _("Install"), null);
1084+ install_action.activated.connect (start_software_center);
1085+ }
1086+ preview.add_action (install_action);
1087+ }
1088+
1089+ if (details.website != null && details.website != "")
1090+ {
1091+ preview_developer_website = details.website;
1092+ var website_action = new Unity.PreviewAction ("website", _("Developer Site"), null);
1093+ website_action.activated.connect (app_preview_website);
1094+ preview.add_action (website_action);
1095+ }
1096+ }
1097+ else // application is already installed
1098+ {
1099+ preview.add_info (new InfoHint ("date-installed", _("Installed on"), null, details.installation_date));
1100+ var launch_action = new Unity.PreviewAction ("launch", _("Launch"), null);
1101+ preview.add_action (launch_action);
1102+ if (!details.is_desktop_dependency)
1103+ {
1104+ var uninstall_action = new Unity.PreviewAction ("uninstall", _("Uninstall"), null);
1105+ uninstall_action.activated.connect (app_preview_uninstall);
1106+ preview.add_action (uninstall_action);
1107+ }
1108+ }
1109+
1110+ preview_installable_desktop_file = details.desktop_file;
1111+ preview_installable_icon_file = details.icon;
1112+ }
1113+ catch (Error e)
1114+ {
1115+ warning ("Failed to get package details for '%s': %s", uri, e.message);
1116+ preview = null;
1117+ }
1118+ }
1119+
1120+ // xapian db doesn't know this .desktop file or S-C dbus data provider fails,
1121+ // fallback to DesktopAppInfo (based on installed .desktop file) if available
1122+ if (preview == null && desktopfile != null)
1123+ {
1124+ var app_info = new DesktopAppInfo (desktopfile);
1125+ if (app_info != null)
1126+ {
1127+ preview = new Unity.ApplicationPreview (app_info.get_display_name (), "", app_info.get_description () ?? "", app_info.get_icon (), null);
1128+ var launch_action = new Unity.PreviewAction ("launch", _("Launch"), null);
1129+ preview.add_action (launch_action);
1130+ }
1131+ }
1132+
1133+ if (preview == null)
1134+ {
1135+ warning ("No pksearcher nor desktop app info for '%s'", uri);
1136+ }
1137+ return preview;
1138+ }
1139+
1140+ private Unity.Preview make_scope_preview(string uri)
1141+ {
1142+ Unity.ApplicationPreview? preview = null;
1143+ var scope_id = uri.substring (8);
1144+ bool scope_disabled = scope_id in disabled_scope_ids;
1145+
1146+ // figure out if the scope is remote
1147+ bool is_remote_scope = false;
1148+ var info = scopesearcher.get_by_desktop_file (scope_id);
1149+
1150+ Dee.ModelIter found_iter = null;
1151+ if (info == null)
1152+ {
1153+ var iter = remote_scopes_model.get_first_iter ();
1154+ var end_iter = remote_scopes_model.get_last_iter ();
1155+ while (iter != end_iter)
1156+ {
1157+ if (remote_scopes_model.get_string (iter, 0) == scope_id)
1158+ {
1159+ is_remote_scope = true;
1160+ found_iter = iter;
1161+ break;
1162+ }
1163+ iter = remote_scopes_model.next (iter);
1164+ }
1165+ }
1166+
1167+ if (is_remote_scope)
1168+ {
1169+ var name = remote_scopes_model.get_string (found_iter, 1);
1170+ if (name == null || name == "")
1171+ name = remote_scopes_model.get_string (found_iter, 0);
1172+ var description = remote_scopes_model.get_string (found_iter, 2);
1173+ if (description != null)
1174+ description = Markup.escape_text(description);
1175+ Icon? icon = null;
1176+ Icon? screenshot = null;
1177+ var icon_hint = remote_scopes_model.get_string (found_iter, 3);
1178+ var screenshot_url = remote_scopes_model.get_string (found_iter, 4);
1179+ try
1180+ {
1181+ if (icon_hint != "") icon = Icon.new_for_string (icon_hint);
1182+ if (screenshot_url != "") screenshot = Icon.new_for_string (screenshot_url);
1183+ }
1184+ catch (Error err)
1185+ {
1186+ warning ("%s", err.message);
1187+ }
1188+
1189+ if (icon == null) icon = get_default_scope_icon ();
1190+
1191+ preview = new Unity.ApplicationPreview (name,
1192+ "",
1193+ description,
1194+ icon,
1195+ screenshot);
1196+ }
1197+ else if (info != null)
1198+ {
1199+ var name = info.application_name;
1200+ if (name == null || name == "")
1201+ name = info.desktop_file;
1202+ var subtitle = "";
1203+ var description = info.description;
1204+ if (description != null)
1205+ description = Markup.escape_text(description);
1206+ Icon? icon = null;
1207+ Icon? screenshot = null;
1208+ try
1209+ {
1210+ if (info.icon != null && info.icon != "")
1211+ icon = Icon.new_for_string (info.icon);
1212+ }
1213+ catch (Error err)
1214+ {
1215+ warning ("%s", err.message);
1216+ }
1217+ if (icon == null)
1218+ icon = get_default_scope_icon ();
1219+
1220+ // de-mangle desktop file names back to what S-C expects
1221+ string mangled_id;
1222+ if (sc_mangler.contains (scope_id))
1223+ {
1224+ mangled_id = sc_mangler.get (scope_id);
1225+ }
1226+ else
1227+ {
1228+ mangled_id = scope_id;
1229+ }
1230+
1231+ Unity.Package.PackageInfo pkginfo = pkgsearcher.get_by_desktop_file (mangled_id);
1232+ if (pkginfo != null)
1233+ {
1234+ SoftwareCenterData.AppDetailsData? details;
1235 try {
1236- if (sc_data_provider == null)
1237- {
1238- sc_data_provider = new SoftwareCenterDataCache (TOP_RATED_ITEMS_CACHE_LIFETIME);
1239- sc_data_provider.connect_to ();
1240- }
1241-
1242- debug ("Requesting pkg info: %s, %s\n", pkgname, appname);
1243- var details = sc_data_provider.get_app_details (appname, pkgname);
1244-
1245- Icon? icon = null;
1246- if (installed)
1247- icon = new GLib.ThemedIcon (details.icon);
1248- else
1249- {
1250- icon = find_pkg_icon (null, details.icon);
1251- if (icon.to_string () == GENERIC_APP_ICON && details.icon_url != null && details.icon_url != "")
1252- {
1253- icon = new GLib.FileIcon (File.new_for_uri (details.icon_url));
1254- }
1255- }
1256-
1257- Icon? screenshot = null;
1258-
1259+ details = get_app_details(pkginfo.application_name,
1260+ pkginfo.package_name);
1261+ } catch (Error e) {
1262+ details = null;
1263+ }
1264+ if (details != null) {
1265+ if (details.version != "")
1266+ subtitle = _("Version %s").printf (details.version);
1267 if (details.screenshot != null)
1268 {
1269 File scr_file = File.new_for_uri (details.screenshot);
1270 screenshot = new FileIcon (scr_file);
1271 }
1272-
1273- string subtitle = "";
1274- if (details.version != "")
1275- subtitle = _("Version %s").printf (details.version);
1276- if (details.size > 0)
1277- {
1278- if (subtitle != "")
1279- subtitle += ", ";
1280- subtitle += (_("Size %s")).printf (GLib.format_size (details.size));
1281- }
1282- preview = new Unity.ApplicationPreview (details.name, subtitle, details.description, icon, screenshot);
1283- preview.license = details.license;
1284-
1285- init_ratings_db ();
1286- if (ratings != null)
1287- {
1288- Unity.Ratings.Result result;
1289- ratings.query (pkgname, out result);
1290- preview.set_rating (result.average_rating / 5.0f, result.total_rating);
1291- }
1292-
1293- if (details.hardware_requirements != "")
1294- preview.add_info (new InfoHint ("hardware-requirements", _("Hardware requirements"), null, details.hardware_requirements));
1295-
1296- if (uri.has_prefix ("unity-install://")) // application needs to be purchased/installed
1297- {
1298- // uninstalled and not purchased before
1299- if (details.pkg_state == SoftwareCenterData.PackageState.NEEDS_PURCHASE)
1300- {
1301- var buy_action = new Unity.PreviewAction ("buy", _("Buy"), null);
1302- if (details.price != null && details.price != "")
1303- {
1304- buy_action.extra_text = details.price;
1305- }
1306-
1307- buy_action.activated.connect (start_software_center);
1308- preview.add_action (buy_action);
1309- }
1310- else // uninstalled, purchased before
1311- {
1312-
1313- Unity.PreviewAction install_action = null;
1314- if (details.raw_price == null || details.raw_price == "")
1315- {
1316- install_action = new Unity.PreviewAction ("install", _("Free Download"), null);
1317- install_action.activated.connect (app_preview_install);
1318- }
1319- else
1320- {
1321- install_action = new Unity.PreviewAction ("install", _("Install"), null);
1322- install_action.activated.connect (start_software_center);
1323- }
1324- preview.add_action (install_action);
1325- }
1326-
1327- if (details.website != null && details.website != "")
1328- {
1329- preview_developer_website = details.website;
1330- var website_action = new Unity.PreviewAction ("website", _("Developer Site"), null);
1331- website_action.activated.connect (app_preview_website);
1332- preview.add_action (website_action);
1333- }
1334- }
1335- else // application is already installed
1336- {
1337- preview.add_info (new InfoHint ("date-installed", _("Installed on"), null, details.installation_date));
1338- var launch_action = new Unity.PreviewAction ("launch", _("Launch"), null);
1339- preview.add_action (launch_action);
1340- if (!details.is_desktop_dependency)
1341- {
1342- var uninstall_action = new Unity.PreviewAction ("uninstall", _("Uninstall"), null);
1343- uninstall_action.activated.connect (app_preview_uninstall);
1344- preview.add_action (uninstall_action);
1345- }
1346- }
1347-
1348- preview_installable_desktop_file = details.desktop_file;
1349- preview_installable_icon_file = details.icon;
1350- }
1351- catch (Error e)
1352- {
1353- warning ("Failed to get package details for '%s': %s", uri, e.message);
1354- preview = null;
1355- }
1356- }
1357-
1358- // xapian db doesn't know this .desktop file or S-C dbus data provider fails,
1359- // fallback to DesktopAppInfo (based on installed .desktop file) if available
1360- if (preview == null && desktopfile != null)
1361- {
1362- var app_info = new DesktopAppInfo (desktopfile);
1363- if (app_info != null)
1364- {
1365- preview = new Unity.ApplicationPreview (app_info.get_display_name (), "", app_info.get_description () ?? "", app_info.get_icon (), null);
1366- var launch_action = new Unity.PreviewAction ("launch", _("Launch"), null);
1367- preview.add_action (launch_action);
1368- }
1369- }
1370-
1371- if (preview == null)
1372- {
1373- warning ("No pksearcher nor desktop app info for '%s'", uri);
1374- }
1375+ }
1376+ }
1377+
1378+ preview = new Unity.ApplicationPreview (name,
1379+ subtitle,
1380+ description,
1381+ icon,
1382+ screenshot);
1383+ }
1384+ if (preview != null && scope_id != "home.scope" &&
1385+ scope_id != "applications.scope")
1386+ {
1387+ PreviewAction action;
1388+ preview.set_rating(-1.0f, 0);
1389+ if (scope_disabled)
1390+ {
1391+ action = new Unity.PreviewAction ("enable-scope", _("Enable"), null);
1392+ action.activated.connect (() =>
1393+ {
1394+ enable_scope (scope_id);
1395+ return new ActivationResponse.with_preview (this.preview (uri));
1396+ });
1397+ }
1398+ else
1399+ {
1400+ action = new Unity.PreviewAction ("disable-scope", _("Disable"), null);
1401+ action.activated.connect (() =>
1402+ {
1403+ disable_scope (scope_id);
1404+ return new ActivationResponse.with_preview (this.preview (uri));
1405+ });
1406+ }
1407+ preview.add_action (action);
1408 }
1409 return preview;
1410 }
1411
1412+ private void disable_scope (string scope_id)
1413+ {
1414+ if (scope_id in disabled_scope_ids) return;
1415+
1416+ var pref_man = PreferencesManager.get_default ();
1417+ var disabled_scopes = pref_man.disabled_scopes;
1418+ disabled_scopes += scope_id;
1419+
1420+ var settings = new Settings (LIBUNITY_SCHEMA);
1421+ settings.set_strv ("disabled-scopes", disabled_scopes);
1422+ }
1423+
1424+ private void enable_scope (string scope_id)
1425+ {
1426+ if (!(scope_id in disabled_scope_ids)) return;
1427+
1428+ var pref_man = PreferencesManager.get_default ();
1429+ string[] disabled_scopes = {};
1430+ foreach (unowned string disabled_scope_id in pref_man.disabled_scopes)
1431+ {
1432+ if (disabled_scope_id != scope_id)
1433+ disabled_scopes += disabled_scope_id;
1434+ }
1435+
1436+ var settings = new Settings (LIBUNITY_SCHEMA);
1437+ settings.set_strv ("disabled-scopes", disabled_scopes);
1438+ }
1439 /**
1440 * Override of the default activation handler. The apps lens daemon
1441 * can handle activation of installable apps using the Software Center
1442@@ -1324,7 +1682,7 @@
1443 {
1444 string[] args;
1445 string exec_or_dir = null;
1446- if (uri.has_prefix ("unity-install://"))
1447+ if (uri.has_prefix ("unity-install://") || uri.has_prefix ("scope://"))
1448 {
1449 var prv = preview (uri);
1450 if (prv != null)
1451@@ -1334,7 +1692,7 @@
1452 else
1453 {
1454 warning ("Failed to generate preview for %s", uri);
1455- return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
1456+ return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED);
1457 }
1458 }
1459 else if (uri.has_prefix ("unity-runner://"))
1460@@ -1462,9 +1820,12 @@
1461 string full_path = appmanager.get_path (desktop_id);
1462 string full_uri = full_path != null ? "file://" + full_path : app_uri;
1463
1464- results.append (app_uri, app.get_icon().to_string(), category_id,
1465+ results.append (app_uri, app.get_icon().to_string(),
1466+ category_id, Unity.ResultType.DEFAULT,
1467 "application/x-desktop", app.get_display_name (),
1468- sanitize_binary_name (app.get_executable ()), full_uri);
1469+ sanitize_binary_name (app.get_executable ()),
1470+ full_uri,
1471+ empty_asv);
1472 }
1473 }
1474
1475
1476=== modified file 'src/main.vala'
1477--- src/main.vala 2011-10-06 12:25:59 +0000
1478+++ src/main.vala 2013-05-16 21:22:23 +0000
1479@@ -22,6 +22,7 @@
1480
1481 namespace Unity.ApplicationsLens {
1482
1483+ const string DBUS_NAME = "com.canonical.Unity.Scope.Applications";
1484 static Application? app = null;
1485 static Daemon? daemon = null;
1486
1487@@ -71,7 +72,7 @@
1488 * making it race against GDBus' worker thread to export our
1489 * objects on the bus before/after owning our name and receiving
1490 * method calls on our objects (which may not yet be up!)*/
1491- if (dbus_name_has_owner ("com.canonical.Unity.Lens.Applications"))
1492+ if (dbus_name_has_owner (DBUS_NAME))
1493 {
1494 print ("Another instance of the Unity Applications Daemon " +
1495 "already appears to be running.\nBailing out.\n");
1496@@ -83,8 +84,7 @@
1497 daemon = new Daemon ();
1498
1499 /* Use GApplication directly for single instance app functionality */
1500- app = new Application ("com.canonical.Unity.Lens.Applications",
1501- ApplicationFlags.IS_SERVICE);
1502+ app = new Application (DBUS_NAME, ApplicationFlags.IS_SERVICE);
1503 try {
1504 app.register ();
1505 } catch (Error e) {
1506@@ -93,7 +93,7 @@
1507 print ("Failed to start applications daemon: %s\n", e.message);
1508 return 1;
1509 }
1510-
1511+
1512 if (app.get_is_remote ())
1513 {
1514 print ("Another instance of the Unity Applications Daemon " +
1515
1516=== modified file 'src/runner.vala'
1517--- src/runner.vala 2012-11-05 19:18:41 +0000
1518+++ src/runner.vala 2013-05-16 21:22:23 +0000
1519@@ -27,7 +27,7 @@
1520 public string name;
1521 public string exec;
1522 public Icon icon;
1523-
1524+
1525 public AboutEntry (string name, string exec, Icon icon)
1526 {
1527 this.name = name;
1528@@ -37,30 +37,33 @@
1529 }
1530
1531 public class Runner: GLib.Object
1532- {
1533-
1534- private Unity.ApplicationsLens.Daemon daemon;
1535+ {
1536+
1537+ private Unity.ApplicationsLens.Daemon daemon;
1538 private const string BUS_NAME_PREFIX = "com.canonical.Unity.ApplicationsLens.Runner";
1539-
1540+
1541 /* for now, load the same keys as gnome-panel */
1542 private const string HISTORY_KEY = "history";
1543 private const int MAX_HISTORY = 10;
1544-
1545- public Unity.Lens lens;
1546- public Unity.Scope scope;
1547+
1548+ public Unity.DeprecatedScope scope;
1549
1550 private Gee.HashMap<string,AboutEntry> about_entries;
1551 private Gee.List<string> history;
1552 private ExecSearcher exec_searcher = new ExecSearcher ();
1553
1554 private Settings gp_settings;
1555-
1556+
1557 public Runner (Unity.ApplicationsLens.Daemon daemon)
1558 {
1559 /* First create scope */
1560- scope = new Unity.Scope ("/com/canonical/unity/scope/commands");
1561+ scope = new Unity.DeprecatedScope ("/com/canonical/unity/scope/commands",
1562+ "commands");
1563 scope.search_in_global = false;
1564-
1565+ scope.search_hint = _("Run a command");
1566+ scope.visible = false;
1567+ populate_categories ();
1568+
1569 /* Listen for changes to the lens scope search */
1570 scope.search_changed.connect ((lens_search, search_type, cancellable) =>
1571 {
1572@@ -73,56 +76,53 @@
1573 return lens_search.search_string.strip ();
1574 });
1575
1576- // Listen for when the lens is hidden/shown by the Unity Shell
1577- scope.notify["active"].connect(() =>
1578- {
1579- if (scope.active)
1580- {
1581- scope.queue_search_changed (SearchType.DEFAULT);
1582- }
1583- });
1584-
1585 scope.activate_uri.connect (daemon.activate);
1586
1587- /* Now create Lens */
1588- lens = new Unity.Lens ("/com/canonical/unity/lens/commands", "commands");
1589- lens.search_hint = _("Run a command");
1590- lens.visible = false;
1591- lens.search_in_global = false;
1592- populate_categories ();
1593- //populate_filters();
1594- lens.add_local_scope (scope);
1595- lens.export ();
1596-
1597 /* create the private about entries */
1598 about_entries = new Gee.HashMap<string,AboutEntry> ();
1599 load_about_entries ();
1600-
1601+
1602 this.gp_settings = new Settings ("com.canonical.Unity.Runner");
1603 history = new Gee.ArrayList<string> ();
1604 load_history ();
1605-
1606+
1607 this.daemon = daemon;
1608-
1609+
1610+ scope.export ();
1611 }
1612
1613 private void populate_categories ()
1614 {
1615- var categories = new GLib.List<Unity.Category> ();
1616+ var categories = new Unity.CategorySet ();
1617 var icon_dir = File.new_for_path (ICON_PATH);
1618- var cat = new Unity.Category (_("Results"),
1619+ var cat = new Unity.Category ("results", _("Results"),
1620 new FileIcon (icon_dir.get_child ("group-installed.svg")));
1621- categories.append (cat);
1622+ categories.add (cat);
1623
1624- cat = new Unity.Category (_("History"),
1625+ cat = new Unity.Category ("history", _("History"),
1626 new FileIcon (icon_dir.get_child ("group-available.svg")));
1627
1628- categories.append (cat);
1629-
1630- lens.categories = categories;
1631- }
1632-
1633- private async void update_search (LensSearch search)
1634+ categories.add (cat);
1635+
1636+ scope.categories = categories;
1637+ }
1638+
1639+ private void add_result (Dee.Model model, string uri, string icon_hint,
1640+ uint category_id, string mimetype, string title)
1641+ {
1642+ Variant row_data[9];
1643+ model.append_row (
1644+ model.build_named_row_static (row_data, "uri", uri,
1645+ "icon_hint", icon_hint,
1646+ "category", category_id,
1647+ "result_type", ResultType.DEFAULT,
1648+ "mimetype", mimetype,
1649+ "title", title,
1650+ "comment", "",
1651+ "dnd_uri", uri));
1652+ }
1653+
1654+ private async void update_search (DeprecatedScopeSearch search)
1655 {
1656 var model = search.results_model;
1657 var executables_match = new Gee.ArrayList<string> ();
1658@@ -132,19 +132,17 @@
1659 var search_string = search.search_string;
1660 bool has_search = !Utils.is_search_empty (search.search_string);
1661
1662- string uri;
1663+ string uri;
1664 Icon icon;
1665 string mimetype;
1666 string display_name;
1667 var category_id = RunnerCategory.HISTORY;
1668
1669 foreach (var command in this.history)
1670- {
1671- display_name = get_icon_uri_and_mimetype (command, out icon, out uri, out mimetype);
1672- model.append (uri, icon.to_string (),
1673- category_id, mimetype,
1674- display_name,
1675- null);
1676+ {
1677+ display_name = get_icon_uri_and_mimetype (command, out icon, out uri, out mimetype);
1678+ add_result (model, uri, icon.to_string (), category_id, mimetype,
1679+ display_name);
1680 }
1681
1682 if (!has_search)
1683@@ -154,34 +152,30 @@
1684 }
1685
1686 Timer timer = new Timer ();
1687-
1688+
1689 /* no easter egg in unity */
1690 if (search_string == "free the fish")
1691 {
1692- uri = "no-easter-egg";
1693+ uri = "about:blank";
1694 string commenteaster = _("There is no easter egg in Unity");
1695 icon = new ThemedIcon ("gnome-panel-fish");
1696- model.append (uri, icon.to_string (),
1697- 0, "no-mime",
1698- commenteaster,
1699- null);
1700+ add_result (model, uri, icon.to_string (), 0, "text/plain",
1701+ commenteaster);
1702 search.finished ();
1703 return;
1704 }
1705 else if (search_string == "gegls from outer space")
1706 {
1707- uri = "really-no-easter-egg";
1708+ uri = "about:blank";
1709 string commentnoeaster = _("Still no easter egg in Unity");
1710 icon = new ThemedIcon ("gnome-panel-fish");
1711- model.append (uri, icon.to_string (),
1712- 0, "no-mime",
1713- commentnoeaster,
1714- null);
1715+ add_result (model, uri, icon.to_string (), 0, "text/plain",
1716+ commentnoeaster);
1717 search.finished ();
1718 return;
1719-
1720+
1721 }
1722-
1723+
1724 /* manual seek with directory and executables result */
1725 if (search_string.has_prefix ("/") || search_string.has_prefix ("~"))
1726 {
1727@@ -189,7 +183,7 @@
1728 var search_dirname = Path.get_dirname (search_string);
1729 var directory = File.new_for_path (search_dirname);
1730 var search_dirname_in_path = false;
1731-
1732+
1733 /* strip path_directory if in executable in path */
1734 foreach (var path_directory in Environment.get_variable ("PATH").split(":"))
1735 {
1736@@ -208,7 +202,7 @@
1737 var subelem_info = iterator.next_file ();
1738 if (subelem_info == null)
1739 break;
1740-
1741+
1742 var complete_path = Path.build_filename (search_dirname, subelem_info.get_name ());
1743 if (complete_path.has_prefix (search_string))
1744 {
1745@@ -241,10 +235,10 @@
1746 executables_match.add (matching_exec);
1747 }
1748 }
1749-
1750+
1751 executables_match.sort ();
1752 dirs_match.sort ();
1753-
1754+
1755 category_id = RunnerCategory.RESULTS;
1756
1757 // populate results
1758@@ -252,45 +246,38 @@
1759 if ((executables_match.size == 0) && (dirs_match.size == 0))
1760 {
1761 display_name = get_icon_uri_and_mimetype (search_string, out icon, out uri, out mimetype);
1762- model.append (uri.strip (), icon.to_string (),
1763- category_id, mimetype,
1764- display_name,
1765- null);
1766+ add_result (model, uri.strip (), icon.to_string (),
1767+ category_id, mimetype, display_name);
1768 }
1769-
1770+
1771 // 2. add possible directories (we don't store them)
1772 mimetype = "inode/directory";
1773 icon = ContentType.get_icon (mimetype);
1774 foreach (var dir in dirs_match)
1775 {
1776 uri = @"unity-runner://$(dir)";
1777- model.append (uri, icon.to_string (),
1778- category_id, mimetype,
1779- dir,
1780- null);
1781+ add_result (model, uri, icon.to_string (),
1782+ category_id, mimetype, dir);
1783 }
1784-
1785+
1786 // 3. add available exec
1787 foreach (var final_exec in executables_match)
1788 {
1789 // TODO: try to match to a desktop file for the icon
1790 uri = @"unity-runner://$(final_exec)";
1791- display_name = get_icon_uri_and_mimetype (final_exec, out icon, out uri, out mimetype);
1792-
1793- model.append (uri, icon.to_string (),
1794- category_id, mimetype,
1795- display_name,
1796- null);
1797+ display_name = get_icon_uri_and_mimetype (final_exec, out icon, out uri, out mimetype);
1798+ add_result (model, uri, icon.to_string (),
1799+ category_id, mimetype, display_name);
1800 }
1801-
1802+
1803 timer.stop ();
1804 debug ("Entry search listed %i dir matches and %i exec matches in %fms for search: %s",
1805 dirs_match.size, executables_match.size, timer.elapsed ()*1000, search_string);
1806-
1807+
1808
1809 search.finished ();
1810 }
1811-
1812+
1813 private class ExecSearcher: Object
1814 {
1815 public ExecSearcher ()
1816@@ -378,13 +365,13 @@
1817 return matching;
1818 }
1819 }
1820-
1821+
1822 private string get_icon_uri_and_mimetype (string exec_string, out Icon? icon, out string? uri, out string? mimetype)
1823 {
1824-
1825+
1826 AboutEntry? entry = null;
1827-
1828- mimetype = "application/x-unity-run";
1829+
1830+ mimetype = "application/x-unity-run";
1831 entry = about_entries[exec_string];
1832 if (entry != null)
1833 {
1834@@ -395,7 +382,7 @@
1835
1836 uri = @"unity-runner://$(exec_string)";
1837
1838-
1839+
1840 // if it's a folder, show… a folder icone! + right exec
1841 if (FileUtils.test (exec_string, FileTest.IS_DIR))
1842 {
1843@@ -404,7 +391,7 @@
1844 return exec_string;
1845 }
1846
1847- var s = exec_string.delimit ("-", '_').split (" ", 0)[0];
1848+ var s = exec_string.delimit ("-", '_').split (" ", 0)[0];
1849 var appresults = this.daemon.appsearcher.search (@"type:Application AND exec:$s", 0,
1850 Unity.Package.SearchType.EXACT,
1851 Unity.Package.Sort.BY_NAME);
1852@@ -413,19 +400,19 @@
1853
1854 if (pkginfo.desktop_file == null)
1855 continue;
1856-
1857+
1858 // pick the first one
1859 icon = this.daemon.find_pkg_icon (pkginfo.desktop_file, pkginfo.icon);
1860 return exec_string;
1861-
1862+
1863 }
1864-
1865+
1866 // if no result, default icon
1867 icon = new ThemedIcon ("gtk-execute");
1868 return exec_string;
1869-
1870+
1871 }
1872-
1873+
1874
1875 public void add_history (string last_command)
1876 {
1877@@ -434,14 +421,14 @@
1878 var new_history = new Gee.ArrayList<string> ();
1879 var history_store = new string [this.history.size + 1];
1880 int i = 1;
1881-
1882+
1883 new_history.add (last_command);
1884 history_store[0] = last_command;
1885 for (var j = 0; (j < this.history.size) && (i < MAX_HISTORY); j++)
1886 {
1887 if (this.history[j] == last_command)
1888 continue;
1889-
1890+
1891 new_history.add(history[j]);
1892 history_store[i] = history[j];
1893 i++;
1894@@ -450,11 +437,11 @@
1895
1896 // store in gsettings
1897 this.gp_settings.set_strv (HISTORY_KEY, history_store);
1898-
1899+
1900 // force a search to refresh history order (TODO: be more clever in the future)
1901 scope.queue_search_changed (SearchType.DEFAULT);
1902 }
1903-
1904+
1905 private void load_history ()
1906 {
1907 int i = 0;
1908@@ -467,39 +454,39 @@
1909 i++;
1910 }
1911 }
1912-
1913+
1914 private void load_about_entries ()
1915- {
1916+ {
1917 AboutEntry entry;
1918 string name;
1919 string exec;
1920 Icon icon;
1921-
1922+
1923 // first about:config
1924 name = "about:config";
1925 exec = "ccsm -p unityshell";
1926 try {
1927- icon = Icon.new_for_string (@"$(Config.PREFIX)/share/ccsm/icons/hicolor/64x64/apps/plugin-unityshell.png");
1928+ icon = Icon.new_for_string (@"$(Config.PREFIX)/share/ccsm/icons/hicolor/64x64/apps/plugin-unityshell.png");
1929 }
1930 catch (Error err) {
1931 warning ("Can't find unityshell icon: %s", err.message);
1932 icon = new ThemedIcon ("gtk-execute");
1933- }
1934+ }
1935 entry = new AboutEntry (name, exec, icon);
1936-
1937+
1938 about_entries[name] = entry;
1939 about_entries[exec] = entry;
1940-
1941+
1942 // second about:robots
1943 name = "Robots have a plan.";
1944 exec = "firefox about:robots";
1945 entry = new AboutEntry (name, exec, icon = new ThemedIcon ("battery"));
1946-
1947+
1948 about_entries["about:robots"] = entry;
1949 about_entries[exec] = entry;
1950-
1951+
1952 }
1953-
1954+
1955 }
1956-
1957+
1958 }
1959
1960=== modified file 'src/schemas.vala'
1961--- src/schemas.vala 2012-09-13 11:15:00 +0000
1962+++ src/schemas.vala 2013-05-16 21:22:23 +0000
1963@@ -37,6 +37,16 @@
1964 INSTALLED,
1965 AVAILABLE,
1966 }
1967+
1968+ public enum RemoteScopesColumn
1969+ {
1970+ SCOPE_ID,
1971+ NAME,
1972+ DESCRIPTION,
1973+ ICON_HINT,
1974+ SCREENSHOT_URL,
1975+ KEYWORDS
1976+ }
1977
1978 public enum RunnerCategory
1979 {
1980
1981=== modified file 'src/unity-package-search.cc'
1982--- src/unity-package-search.cc 2013-04-15 16:32:36 +0000
1983+++ src/unity-package-search.cc 2013-05-16 21:22:23 +0000
1984@@ -59,6 +59,10 @@
1985 #define XAPIAN_VALUE_EXENAME 294
1986 #define XAPIAN_VALUE_CURRENCY 201
1987
1988+/* this isn't a Software Center slot, but we use it to mark master
1989+ * scopes in the scope index. */
1990+#define XAPIAN_VALUE_MASTERSCOPE 1000
1991+
1992 #include "unity-package-search.h"
1993 #include "columbus.hh"
1994
1995@@ -184,6 +188,10 @@
1996 query_parser->add_prefix ("pkgname", "AP");
1997 query_parser->add_prefix ("exec", "XX");
1998 query_parser->add_prefix ("keyword", "KW");
1999+ query_parser->add_prefix ("pkg_wildcard", "XP");
2000+ query_parser->add_prefix ("pkg_wildcard", "XPM");
2001+ query_parser->add_prefix ("pkg_wildcard", "AP");
2002+ query_parser->add_prefix ("pkg_wildcard", "APM");
2003 query_parser->set_default_op (Xapian::Query::OP_AND);
2004 query_parser->set_database (db);
2005 searcher->query_parser = query_parser;
2006@@ -235,6 +243,9 @@
2007 if (g_app_info_get_display_name (app_info))
2008 doc.add_value (XAPIAN_VALUE_APPNAME, g_app_info_get_display_name (app_info));
2009
2010+ if (g_app_info_get_description (app_info))
2011+ doc.add_value (XAPIAN_VALUE_DESCRIPTION, g_app_info_get_description (app_info));
2012+
2013 if (g_app_info_get_icon (app_info))
2014 {
2015 gchar *icon_str = g_icon_to_string (g_app_info_get_icon (app_info));
2016@@ -379,6 +390,97 @@
2017 return searcher;
2018 }
2019
2020+/* Recursively traverse the set of scopes */
2021+static void
2022+index_scope (Xapian::WritableDatabase *db,
2023+ Xapian::TermGenerator *indexer,
2024+ UnityProtocolScopeRegistryScopeMetadata *info)
2025+{
2026+ Xapian::Document doc;
2027+ char *dum1;
2028+
2029+ if (info->name != NULL)
2030+ doc.add_value (XAPIAN_VALUE_APPNAME, info->name);
2031+ if (info->description != NULL)
2032+ doc.add_value (XAPIAN_VALUE_DESCRIPTION, info->description);
2033+ if (info->icon != NULL)
2034+ doc.add_value (XAPIAN_VALUE_ICON, info->icon);
2035+ if (info->id != NULL)
2036+ doc.add_value (XAPIAN_VALUE_DESKTOP_FILE, info->id);
2037+ if (info->is_master)
2038+ doc.add_value (XAPIAN_VALUE_MASTERSCOPE, "true");
2039+
2040+ indexer->set_document (doc);
2041+ if (info->name != NULL)
2042+ {
2043+ dum1 = unity_applications_lens_utils_preprocess_string (info->name);
2044+ indexer->index_text (dum1, 5);
2045+ g_free (dum1);
2046+ }
2047+ if (info->description != NULL)
2048+ {
2049+ dum1 = unity_applications_lens_utils_preprocess_string (info->description);
2050+ indexer->index_text (dum1, 5);
2051+ g_free (dum1);
2052+ }
2053+ for (GSList *l = info->keywords; l != NULL; l = l->next)
2054+ {
2055+ dum1 = unity_applications_lens_utils_preprocess_string ((char *)l->data);
2056+ indexer->index_text (dum1, 0);
2057+ indexer->index_text (dum1, 0, "KW");
2058+ g_free (dum1);
2059+ }
2060+
2061+ /* Always assume Type=Scope for scopes... */
2062+ doc.add_term ("ATscope");
2063+
2064+ /* Index application name */
2065+ dum1 = g_strconcat ("AA", info->name, NULL);
2066+ doc.add_term (dum1);
2067+ g_free (dum1);
2068+
2069+ db->add_document (doc);
2070+}
2071+
2072+UnityPackageSearcher*
2073+unity_package_searcher_new_for_scopes (UnityProtocolScopeRegistry *scope_registry)
2074+{
2075+ UnityPackageSearcher *searcher;
2076+ Xapian::WritableDatabase *db;
2077+
2078+ searcher = new UnityPackageSearcher;
2079+ db = new Xapian::WritableDatabase ();
2080+ searcher->db = db;
2081+ searcher->db->add_database (Xapian::InMemory::open ());
2082+
2083+ init_searcher (searcher);
2084+
2085+ /* Index the menu recursively */
2086+ searcher->db_merged = false;
2087+ Xapian::TermGenerator *indexer = new Xapian::TermGenerator ();
2088+
2089+ GSList *scopes = unity_protocol_scope_registry_get_scopes (scope_registry);
2090+ for (GSList *l = scopes; l != NULL; l = l->next)
2091+ {
2092+ UnityProtocolScopeRegistryScopeRegistryNode *node = (UnityProtocolScopeRegistryScopeRegistryNode *)l->data;
2093+ index_scope (db, indexer, node->scope_info);
2094+
2095+ // Also index any sub-scopes
2096+ for (GSList *sl = node->sub_scopes; sl != NULL; sl = sl->next)
2097+ {
2098+ UnityProtocolScopeRegistryScopeMetadata *info = (UnityProtocolScopeRegistryScopeMetadata *)sl->data;
2099+ index_scope (db, indexer, info);
2100+ }
2101+ }
2102+ delete indexer;
2103+ db->flush ();
2104+
2105+ searcher->matcher = new Columbus::Matcher();
2106+ buildMatcher(searcher);
2107+
2108+ return searcher;
2109+}
2110+
2111 /* Create a new searcher that searches into the Xapian index
2112 * provided by the Software Center */
2113 UnityPackageSearcher*
2114@@ -448,6 +550,9 @@
2115 string appname = doc.get_value (XAPIAN_VALUE_APPNAME);
2116 pkginfo->application_name = g_strdup (appname.c_str ());
2117
2118+ string description = doc.get_value (XAPIAN_VALUE_DESCRIPTION);
2119+ pkginfo->description = g_strdup (description.c_str ());
2120+
2121 string desktop_file = doc.get_value (XAPIAN_VALUE_DESKTOP_FILE);
2122 pkginfo->desktop_file = g_strdup (desktop_file.c_str ());
2123
2124@@ -465,6 +570,9 @@
2125 const string purchase_date = doc.get_value (XAPIAN_VALUE_PURCHASED_DATE);
2126 pkginfo->needs_purchase = purchase_date.empty ();
2127
2128+ string is_master = doc.get_value (XAPIAN_VALUE_MASTERSCOPE);
2129+ pkginfo->is_master_scope = (is_master == "true");
2130+
2131 return pkginfo;
2132 }
2133
2134@@ -572,6 +680,7 @@
2135 // Retrieve the results, note that we build the result->results
2136 // list in reverse order and then reverse it before we return it
2137 result->num_hits = matches.get_matches_estimated ();
2138+ result->fuzzy_search = false;
2139
2140 for (Xapian::MSetIterator i = matches.begin(); i != matches.end(); ++i)
2141 {
2142@@ -623,6 +732,7 @@
2143 }
2144
2145 result->results = g_slist_reverse (result->results);
2146+ result->fuzzy_search = true;
2147 return result;
2148 }
2149
2150@@ -637,11 +747,12 @@
2151 g_return_val_if_fail (searcher != NULL, NULL);
2152 g_return_val_if_fail (search_string != NULL, NULL);
2153
2154- bool has_category = strstr(search_string, "category:") != NULL;
2155+ bool is_filtered = strstr(search_string, "category:") != NULL
2156+ || strstr(search_string, "pkg_wildcard:") != NULL;
2157 const char *col_query_str = strstr(search_string, "AND");
2158 xap_result = xapian_search(searcher, search_string, max_hits, search_type, sort);
2159 // If Xapian does not find anything, try fuzzy matching.
2160- if(g_slist_length(xap_result->results) == 0 && (!has_category && col_query_str)) {
2161+ if (g_slist_length(xap_result->results) == 0 && (!is_filtered && col_query_str)) {
2162 g_slice_free(UnityPackageSearchResult, xap_result);
2163 col_query_str += 3;
2164 UnityPackageSearchResult *res = libcolumbus_search(searcher, col_query_str);
2165@@ -710,6 +821,7 @@
2166 g_hash_table_unref (unique);
2167
2168 result->num_hits = num_matches;
2169+ result->fuzzy_search = false;
2170 return result;
2171 }
2172
2173@@ -726,6 +838,7 @@
2174
2175 result = g_slice_new0 (UnityPackageSearchResult);
2176 result->num_hits = n_apps;
2177+ result->fuzzy_search = false;
2178 lastdocid = searcher->db->get_lastdocid ();
2179
2180 /* Since we really just pick random apps we may end up with dupes */
2181@@ -867,6 +980,7 @@
2182 }
2183
2184 result->num_hits = num_matches;
2185+ result->fuzzy_search = false;
2186 return result;
2187 }
2188
2189@@ -887,14 +1001,20 @@
2190 Xapian::Document doc = searcher->db->get_document (*it);
2191 string value = doc.get_value (XAPIAN_VALUE_DESKTOP_FILE);
2192
2193+ bool matches = false;
2194 size_t sep = value.find (':');
2195 if (sep != string::npos)
2196 {
2197- if (value.compare (sep+1, value.size() - sep, query) == 0)
2198- {
2199- pkginfo = _pkginfo_from_document (doc);
2200- return pkginfo;
2201- }
2202+ matches = value.compare (sep+1, value.size() - sep, query) == 0;
2203+ }
2204+ else
2205+ {
2206+ matches = value == query;
2207+ }
2208+ if (matches)
2209+ {
2210+ pkginfo = _pkginfo_from_document (doc);
2211+ return pkginfo;
2212 }
2213 ++it;
2214 }
2215
2216=== modified file 'src/unity-package-search.h'
2217--- src/unity-package-search.h 2012-10-09 15:26:49 +0000
2218+++ src/unity-package-search.h 2013-05-16 21:22:23 +0000
2219@@ -19,6 +19,7 @@
2220
2221 #include <glib.h>
2222 #include <gmenu-tree.h>
2223+#include <unity-protocol.h>
2224
2225 typedef struct _UnityPackageSearcher UnityPackageSearcher;
2226
2227@@ -36,19 +37,22 @@
2228
2229 typedef struct
2230 {
2231- GSList *results;
2232- gint num_hits;
2233+ GSList *results;
2234+ gint num_hits;
2235+ gboolean fuzzy_search;
2236 } UnityPackageSearchResult;
2237
2238 typedef struct
2239 {
2240 gchar *package_name;
2241 gchar *application_name;
2242+ gchar *description;
2243 gchar *desktop_file;
2244 gchar *icon;
2245 gchar *price;
2246 gboolean needs_purchase;
2247 gint relevancy;
2248+ gboolean is_master_scope;
2249 } UnityPackageInfo;
2250
2251 typedef gboolean (*AppFilterCallback)(UnityPackageInfo *, void *);
2252@@ -61,6 +65,7 @@
2253 UnityPackageSearcher* unity_package_searcher_new ();
2254
2255 UnityPackageSearcher* unity_package_searcher_new_for_menu (GMenuTree *menu);
2256+UnityPackageSearcher* unity_package_searcher_new_for_scopes (UnityProtocolScopeRegistry *scope_registry);
2257
2258 void unity_package_searcher_free (UnityPackageSearcher *searcher);
2259
2260
2261=== modified file 'src/utils.vala'
2262--- src/utils.vala 2012-10-05 16:17:11 +0000
2263+++ src/utils.vala 2013-05-16 21:22:23 +0000
2264@@ -138,6 +138,99 @@
2265 }
2266 }
2267
2268+ public Dee.Index prepare_index (Dee.Model model,
2269+ uint sort_column,
2270+ owned Dee.ModelReaderFunc reader_func,
2271+ out Dee.Analyzer out_analyzer)
2272+ {
2273+ // reuse the icu_filter
2274+ if (icu_filter == null)
2275+ {
2276+ icu_filter = new Dee.ICUTermFilter.ascii_folder ();
2277+ }
2278+
2279+ var sort_filter = Dee.Filter.new_collator (sort_column);
2280+ var filter_model = new Dee.FilterModel (model, sort_filter);
2281+
2282+ var analyzer = new Dee.TextAnalyzer ();
2283+ analyzer.add_term_filter ((terms_in, terms_out) =>
2284+ {
2285+ for (uint i = 0; i < terms_in.num_terms (); i++)
2286+ {
2287+ unowned string term = terms_in.get_term (i);
2288+ var folded = icu_filter.apply (term);
2289+ terms_out.add_term (term);
2290+ if (folded != term) terms_out.add_term (folded);
2291+ }
2292+ });
2293+ out_analyzer = analyzer;
2294+
2295+ var reader = Dee.ModelReader.new (reader_func);
2296+ return new Dee.TreeIndex (filter_model, analyzer, reader);
2297+ }
2298+
2299+ public SList<Dee.ModelIter> search_index (Dee.Index index,
2300+ Dee.Analyzer analyzer,
2301+ string query)
2302+ {
2303+ if (is_search_empty (query))
2304+ {
2305+ var model = index.get_model ();
2306+ var iter = model.get_first_iter ();
2307+ var end_iter = model.get_last_iter ();
2308+
2309+ var result = new SList<Dee.ModelIter> ();
2310+ while (iter != end_iter)
2311+ {
2312+ result.prepend (iter);
2313+ iter = model.next (iter);
2314+ }
2315+ result.reverse ();
2316+ return result;
2317+ }
2318+
2319+ var term_list = Object.new (typeof (Dee.TermList)) as Dee.TermList;
2320+ analyzer.tokenize (query, term_list);
2321+ var matches = new Sequence<Dee.ModelIter> ();
2322+
2323+ uint num_terms = term_list.num_terms ();
2324+ for (uint i = 0; i < num_terms; i++)
2325+ {
2326+ var rs = index.lookup (term_list.get_term (i),
2327+ i < num_terms - 1 ? Dee.TermMatchFlag.EXACT : Dee.TermMatchFlag.PREFIX);
2328+
2329+ bool first_pass = i == 0;
2330+ CompareDataFunc<Dee.ModelIter> cmp_func = (a, b) =>
2331+ {
2332+ return a == b ? 0 : ((void*) a > (void*) b ? 1 : -1);
2333+ };
2334+ // intersect the results (cause we want to AND the terms)
2335+ var remaining = new Sequence<Dee.ModelIter> ();
2336+ foreach (var item in rs)
2337+ {
2338+ if (first_pass)
2339+ matches.insert_sorted (item, cmp_func);
2340+ else if (matches.lookup (item, cmp_func) != null)
2341+ remaining.insert_sorted (item, cmp_func);
2342+ }
2343+ if (!first_pass) matches = (owned) remaining;
2344+ // final result set empty already?
2345+ if (matches.get_begin_iter () == matches.get_end_iter ()) break;
2346+ }
2347+
2348+ var result = new SList<Dee.ModelIter> ();
2349+ var iter = matches.get_begin_iter ();
2350+ var end_iter = matches.get_end_iter ();
2351+ while (iter != end_iter)
2352+ {
2353+ result.prepend (iter.get ());
2354+ iter = iter.next ();
2355+ }
2356+
2357+ result.reverse ();
2358+ return result;
2359+ }
2360+
2361 /* Substitute tilde character in @s by the home directory.
2362 * Expansion of ~username also works if 'username' is found. */
2363 public string subst_tilde (string s)
2364
2365=== modified file 'src/xapian-utils.vala'
2366--- src/xapian-utils.vala 2012-10-08 13:30:44 +0000
2367+++ src/xapian-utils.vala 2013-05-16 21:22:23 +0000
2368@@ -31,6 +31,7 @@
2369 "accessibility",
2370 "developer",
2371 "science-and-engineering",
2372+ "scopes",
2373 "system"
2374 };
2375
2376@@ -58,6 +59,7 @@
2377 type_queries.insert ("accessibility", "(category:Accessibility AND NOT category:Settings)");
2378 type_queries.insert ("developer", "category:Development"); // FIXME emacs.desktop should be added
2379 type_queries.insert ("science-and-engineering", "(category:Science OR category:Engineering)");
2380+ type_queries.insert ("scopes", "(pkg_wildcard:unity_scope_* OR pkg_wildcard:unity_lens_*)");
2381 type_queries.insert ("system", "(category:System OR category:Security)");
2382 }
2383 }
2384@@ -107,9 +109,9 @@
2385 if (Utils.is_search_empty (search_string))
2386 {
2387 if (options == null || !options.filtering)
2388- return "type:Application";
2389+ return "(type:Application OR type:Scope)";
2390 else
2391- return "type:Application AND " + extract_type_query (options);
2392+ return "(type:Application OR type:Scope) AND " + extract_type_query (options);
2393 }
2394 else
2395 {
2396@@ -125,9 +127,9 @@
2397 s = s.delimit ("-", ' ');
2398
2399 if (options == null || !options.filtering)
2400- return "type:Application AND " + s;
2401+ return "(type:Application OR type:Scope) AND " + s;
2402 else
2403- return "type:Application AND %s AND %s".printf (extract_type_query (options), s);
2404+ return "(type:Application OR type:Scope) AND %s AND %s".printf (extract_type_query (options), s);
2405 }
2406 }
2407-}
2408\ No newline at end of file
2409+}
2410
2411=== modified file 'tests/unit/Makefile.am'
2412--- tests/unit/Makefile.am 2013-03-01 15:41:08 +0000
2413+++ tests/unit/Makefile.am 2013-05-16 21:22:23 +0000
2414@@ -30,6 +30,8 @@
2415 $(test_libs) \
2416 $(UNITY_PACKAGE_SEARCH_LIBS)
2417
2418+LDFLAGS += -rpath $(PROTOCOLPRIVATELIBDIR)
2419+
2420 AM_CPPFLAGS = \
2421 $(LENS_DAEMON_CFLAGS) \
2422 -I$(srcdir) \
2423
2424=== modified file 'tests/unit/test-xapian-utils.vala'
2425--- tests/unit/test-xapian-utils.vala 2012-10-08 08:41:45 +0000
2426+++ tests/unit/test-xapian-utils.vala 2013-05-16 21:22:23 +0000
2427@@ -43,7 +43,7 @@
2428 internal static void test_empty_search_query ()
2429 {
2430 var query = XapianUtils.prepare_pkg_search_string ("", null);
2431- assert (query == "type:Application");
2432+ assert (query == "(type:Application OR type:Scope)");
2433 }
2434
2435 internal static void test_empty_search_with_single_cat_filter ()
2436@@ -54,7 +54,7 @@
2437 filter.add_option ("internet", "Internet", null);
2438
2439 var query = XapianUtils.prepare_pkg_search_string ("", filter);
2440- assert (query == "type:Application AND (category:AudioVideo)");
2441+ assert (query == "(type:Application OR type:Scope) AND (category:AudioVideo)");
2442 }
2443
2444 internal static void test_empty_search_with_multi_cat_filter ()
2445@@ -65,16 +65,16 @@
2446 filter.add_option ("internet", "Internet", null);
2447 filter.add_option ("game", "Game", null).active = true;
2448 var query = XapianUtils.prepare_pkg_search_string ("", filter);
2449- assert (query == "type:Application AND (category:Game OR category:AudioVideo)");
2450+ assert (query == "(type:Application OR type:Scope) AND (category:Game OR category:AudioVideo)");
2451 }
2452
2453 internal static void test_search_query_no_filters ()
2454 {
2455 var query = XapianUtils.prepare_pkg_search_string (" foo ", null);
2456- assert (query == "type:Application AND foo");
2457+ assert (query == "(type:Application OR type:Scope) AND foo");
2458
2459 query = XapianUtils.prepare_pkg_search_string ("foo", null);
2460- assert (query == "type:Application AND foo");
2461+ assert (query == "(type:Application OR type:Scope) AND foo");
2462 }
2463
2464 internal static void test_search_query_with_single_cat_filter ()
2465@@ -84,7 +84,7 @@
2466 filter.add_option ("media", "Media", null);
2467 filter.add_option ("internet", "Internet", null).active = true;
2468 var query = XapianUtils.prepare_pkg_search_string ("foo", filter);
2469- assert (query == "type:Application AND (category:Network) AND foo");
2470+ assert (query == "(type:Application OR type:Scope) AND (category:Network) AND foo");
2471 }
2472
2473 internal static void test_search_query_with_multi_cat_filter ()
2474@@ -95,7 +95,7 @@
2475 filter.add_option ("internet", "Internet", null).active = true;
2476 filter.add_option ("game", "Game", null).active = true;
2477 var query = XapianUtils.prepare_pkg_search_string ("foo", filter);
2478- assert (query == "type:Application AND (category:Game OR category:Network) AND foo");
2479+ assert (query == "(type:Application OR type:Scope) AND (category:Game OR category:Network) AND foo");
2480 }
2481
2482 internal static void test_zg_empty_search_query ()
2483
2484=== modified file 'vapi/unity-package-search.deps'
2485--- vapi/unity-package-search.deps 2012-10-18 12:36:37 +0000
2486+++ vapi/unity-package-search.deps 2013-05-16 21:22:23 +0000
2487@@ -1,2 +1,3 @@
2488 glib-2.0
2489 libgnome-menu-3.0
2490+unity-protocol
2491
2492=== modified file 'vapi/unity-package-search.vapi'
2493--- vapi/unity-package-search.vapi 2012-10-08 15:11:07 +0000
2494+++ vapi/unity-package-search.vapi 2013-05-16 21:22:23 +0000
2495@@ -27,6 +27,8 @@
2496 public Searcher ();
2497 [CCode (cname = "unity_package_searcher_new_for_menu")]
2498 public Searcher.for_menu(GMenu.Tree menu);
2499+ [CCode (cname = "unity_package_searcher_new_for_scopes")]
2500+ public Searcher.for_scopes(Unity.Protocol.ScopeRegistry scope_registry);
2501 public SearchResult search (string search_string, uint max_hits, Unity.Package.SearchType search_type, Unity.Package.Sort sort);
2502 public SearchResult get_random_apps (string? filter_query, uint n_apps);
2503 public SearchResult get_apps (string? filter_query, uint n_apps, AppFilterCallback cb);
2504@@ -41,6 +43,7 @@
2505 public SearchResult ();
2506 public GLib.SList<PackageInfo> results;
2507 public int num_hits;
2508+ public bool fuzzy_search;
2509 }
2510
2511 [Compact]
2512@@ -49,11 +52,13 @@
2513 public PackageInfo ();
2514 public string package_name;
2515 public string application_name;
2516+ public string description;
2517 public string desktop_file;
2518 public string icon;
2519 public string price;
2520 public bool needs_purchase;
2521 public int relevancy;
2522+ public bool is_master_scope;
2523 }
2524 }
2525 }

Subscribers

People subscribed via source and target branches