Merge lp:~stolowski/unity-lens-applications/fuzzy-public-content into lp:unity-lens-applications

Proposed by Paweł Stołowski
Status: Superseded
Proposed branch: lp:~stolowski/unity-lens-applications/fuzzy-public-content
Merge into: lp:unity-lens-applications
Diff against target: 2478 lines (+1085/-330) (has conflicts)
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 (+12/-5)
data/Makefile.am (+2/-2)
data/unity-scope-applications.service.in (+1/-1)
debian/changelog (+55/-0)
debian/control (+1/-1)
po/POTFILES.in (+2/-2)
src/Makefile.am (+2/-1)
src/config.vala.in (+3/-1)
src/daemon.vala (+620/-153)
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)
Text conflict in debian/changelog
Text conflict in src/daemon.vala
To merge this branch: bzr merge lp:~stolowski/unity-lens-applications/fuzzy-public-content
Reviewer Review Type Date Requested Status
Unity Team Pending
Review via email: mp+162976@code.launchpad.net

This proposal has been superseded by a proposal from 2013-05-08.

Commit message

Mark results of libcolumbus fuzzy searches as 'default' content (as opposed to 'personal'). Also mark apps returned by software-center-dbus-provider as 'default', unless they were purchased before.

Description of the change

Mark results of libcolumbus fuzzy searches as 'default' content (as opposed to 'personal'). Also mark apps returned by software-center-dbus-provider as 'default', unless they were purchased before.

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

Fixed conditional to treat all apps from category other than AVAILABLE as personal content, as this also includes
more categories than just INSTALLED (e.g. RECENT apps).

Unmerged revisions

348. By Paweł Stołowski

Fixed conditional to treat all apps from category other than AVAILABLE as personal content, as this also includes
more categories than just INSTALLED (e.g. RECENT apps).

347. By Paweł Stołowski

Mark results of libcolumbus fuzzy searches as 'default' content (as opposed to 'personal'). Also mark apps returned by software-center-dbus-provider
as 'default', unless they were purchased before.

346. By PS Jenkins bot

Releasing 6.10.0daily13.05.07ubuntu.unity.experimental.certified-0ubuntu1 to ubuntu.

Approved by PS Jenkins bot.

345. By Michal Hruby

Remove an old icon used by the commands scope.

Approved by Pawel Stolowski, PS Jenkins bot.

344. By James Henstridge

Add screenshots to local scope previews with information obtained from the software center, escape markup characters in descriptions and don't allow the user to disable the applications scope. Fixes: https://bugs.launchpad.net/bugs/1174453, https://bugs.launchpad.net/bugs/1174910.

Approved by PS Jenkins bot, Michal Hruby.

343. By James Henstridge

Various scope search result fixes: show local scopes on empty search, filter out master scopes from results and move disabled local scopes to available category. Fixes: https://bugs.launchpad.net/bugs/1174464.

Approved by Michal Hruby, PS Jenkins bot.

342. By Michal Hruby

Update the scope description.

Approved by Pawel Stolowski, PS Jenkins bot.

341. By James Henstridge

Index local scopes and include them in the search results. Fixes: https://bugs.launchpad.net/bugs/1170713.

Approved by Michal Hruby, PS Jenkins bot.

340. By Andrea Azzarone

Don't show the ratings widget for remote scope. Fixes: https://bugs.launchpad.net/bugs/1170715.

Approved by Michal Hruby, PS Jenkins bot.

339. By PS Jenkins bot

Releasing 6.10.0daily13.04.23ubuntu.unity.experimental.certified-0ubuntu1 to ubuntu.

Approved by PS Jenkins bot.

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

Subscribers

People subscribed via source and target branches