Merge lp:~stolowski/unity-lens-applications/unity-lens-applications.formatting-fixes into lp:unity-lens-applications

Proposed by Paweł Stołowski
Status: Superseded
Proposed branch: lp:~stolowski/unity-lens-applications/unity-lens-applications.formatting-fixes
Merge into: lp:unity-lens-applications
Diff against target: 2167 lines (+1039/-460)
9 files modified
configure.ac (+3/-3)
src/Makefile.am (+5/-2)
src/aptd-client.vala (+97/-0)
src/daemon.vala (+523/-232)
src/launcher-client.vala (+45/-0)
src/software-center-data-provider.vala (+115/-0)
src/unity-package-search.cc (+237/-213)
src/unity-package-search.h (+12/-9)
vapi/unity-package-search.vapi (+2/-1)
To merge this branch: bzr merge lp:~stolowski/unity-lens-applications/unity-lens-applications.formatting-fixes
Reviewer Review Type Date Requested Status
Michal Hruby Pending
Review via email: mp+118791@code.launchpad.net

Description of the change

Formatting fixes.

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

Merged previews branch.

314. By Paweł Stołowski

Some more formatting fixes.

315. By Paweł Stołowski

Merged trunk.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure.ac'
2--- configure.ac 2012-04-27 07:39:53 +0000
3+++ configure.ac 2012-08-08 16:35:21 +0000
4@@ -103,9 +103,9 @@
5 # look for dbus service dir
6 #############################################
7 if test "x$with_localinstall" = "xyes"; then
8- DBUSSERVICEDIR="${datadir}/dbus-1/services/"
9+ DBUSSERVICEDIR="${datadir}/dbus-1/services/"
10 else
11- DBUSSERVICEDIR=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1`
12+ DBUSSERVICEDIR=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1`
13 fi
14 AC_SUBST(DBUSSERVICEDIR)
15
16@@ -140,7 +140,7 @@
17
18 Prefix : ${prefix}
19 Sysconfdir : ${sysconfdir}
20-
21+
22 Local install : ${with_localinstall}
23
24 Extra CFlags : ${CPPFLAGS} $MAINTAINER_CFLAGS
25
26=== modified file 'src/Makefile.am'
27--- src/Makefile.am 2012-01-24 11:30:08 +0000
28+++ src/Makefile.am 2012-08-08 16:35:21 +0000
29@@ -19,7 +19,7 @@
30 -I$(srcdir) \
31 -g
32
33-# Note that we require the GLIB_2_22 flag for Valac because libzeitgeist
34+# Note that we require glib 2.26 because libzeitgeist
35 # expects us to use g_ptr_array_unref instead of g_ptr_array_free.
36 unity_applications_daemon_VALAFLAGS = \
37 -C \
38@@ -35,7 +35,7 @@
39 --pkg libgnome-menu \
40 --pkg unity-package-search \
41 --pkg unity-ratings-db \
42- --define=GLIB_2_22 \
43+ --target-glib=2.26 \
44 $(MAINTAINER_VALAFLAGS)
45
46 unity_package_search_libs = -lxapian -lstdc++
47@@ -54,10 +54,13 @@
48 app-watcher.vala \
49 config.vala \
50 daemon.vala \
51+ launcher-client.vala \
52 main.vala \
53 runner.vala \
54 schemas.vala \
55 utils.vala \
56+ aptd-client.vala \
57+ software-center-data-provider.vala \
58 $(NULL)
59
60 unity-package-search.o : $(srcdir)/unity-package-search.cc $(srcdir)/unity-package-search.h
61
62=== added file 'src/aptd-client.vala'
63--- src/aptd-client.vala 1970-01-01 00:00:00 +0000
64+++ src/aptd-client.vala 2012-08-08 16:35:21 +0000
65@@ -0,0 +1,97 @@
66+/*
67+ * Copyright (C) 2012 Canonical Ltd
68+ *
69+ * This program is free software: you can redistribute it and/or modify
70+ * it under the terms of the GNU General Public License version 3 as
71+ * published by the Free Software Foundation.
72+ *
73+ * This program is distributed in the hope that it will be useful,
74+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
75+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
76+ * GNU General Public License for more details.
77+ *
78+ * You should have received a copy of the GNU General Public License
79+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
80+ *
81+ * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
82+ */
83+
84+namespace Unity.ApplicationsLens {
85+
86+ static const string APTD_DBUS_NAME = "org.debian.apt";
87+ static const string APTD_DBUS_PATH = "/org/debian/apt";
88+
89+ /**
90+ * Expose a subset of org.debian.apt interfaces -- only what's needed by applications lens.
91+ */
92+ [DBus (name = "org.debian.apt")]
93+ public interface AptdService: GLib.Object
94+ {
95+ public abstract async string install_packages (string[] packages) throws IOError;
96+ public abstract async string remove_packages (string[] packages) throws IOError;
97+ public abstract async void quit () throws IOError;
98+ }
99+
100+ [DBus (name = "org.debian.apt.transaction")]
101+ public interface AptdTransactionService: GLib.Object
102+ {
103+ public abstract void run () throws IOError;
104+ public abstract void simulate () throws IOError;
105+ public abstract void cancel () throws IOError;
106+ public signal void finished (string exit_state);
107+ }
108+
109+ public class AptdProxy: GLib.Object
110+ {
111+ public void connect_to_aptd () throws IOError
112+ {
113+ _aptd_service = Bus.get_proxy_sync (BusType.SYSTEM, APTD_DBUS_NAME, APTD_DBUS_PATH);
114+ }
115+
116+ public async string install_packages (string[] packages)
117+ {
118+ string res = yield _aptd_service.install_packages (packages);
119+ return res;
120+ }
121+
122+ public async string remove_packages (string[] packages)
123+ {
124+ string res = yield _aptd_service.remove_packages (packages);
125+ return res;
126+ }
127+
128+ public async void quit ()
129+ {
130+ yield _aptd_service.quit ();
131+ }
132+
133+ private AptdService _aptd_service;
134+ }
135+
136+ public class AptdTransactionProxy: GLib.Object
137+ {
138+ public signal void finished (string transaction_id);
139+
140+ public void connect_to_aptd (string transaction_id) throws IOError
141+ {
142+ _aptd_service = Bus.get_proxy_sync (BusType.SYSTEM, APTD_DBUS_NAME, transaction_id);
143+ _aptd_service.finished.connect ((exit_state) =>
144+ {
145+ debug ("aptd transaction finished: %s\n", exit_state);
146+ finished (transaction_id);
147+ });
148+ }
149+
150+ public void simulate ()
151+ {
152+ _aptd_service.simulate ();
153+ }
154+
155+ public void run ()
156+ {
157+ _aptd_service.run ();
158+ }
159+
160+ private AptdTransactionService _aptd_service;
161+ }
162+}
163
164=== modified file 'src/daemon.vala'
165--- src/daemon.vala 2012-04-23 13:58:42 +0000
166+++ src/daemon.vala 2012-08-08 16:35:21 +0000
167@@ -27,6 +27,7 @@
168 namespace Unity.ApplicationsLens {
169
170 const string ICON_PATH = Config.DATADIR + "/icons/unity-icon-theme/places/svg/";
171+ const string ICON_APP_INSTALL_PATH = Config.DATADIR + "/app-install/icons";
172
173 public class Daemon : GLib.Object
174 {
175@@ -41,13 +42,33 @@
176 * the Xapian index from the Software Center */
177 private Unity.Package.Searcher? pkgsearcher;
178 public Unity.Package.Searcher appsearcher;
179-
180+
181 /* Read the app ratings dumped by the Software Center */
182 private Unity.Ratings.Database? ratings;
183
184 private Unity.Lens lens;
185 private Unity.Scope scope;
186-
187+
188+ /* Support aptd dbus interface; created when application install/remove was requested by preview action */
189+ private AptdProxy aptdclient;
190+ private AptdTransactionProxy aptd_transaction;
191+
192+ /* Maps demangled names to mangled names expected by S-C xapian DB (e.g. kde4-KCharSelect.desktop -> kde4__KCharSelect.desktop).
193+ There are very few apps that need this and we only store mappings when needed, so it takes very little memory.
194+ */
195+ private HashTable<string, string> mangled_desktop_ids;
196+
197+ /* Used for adding launcher icon on app installation from the preview */
198+ private LauncherProxy launcherservice;
199+
200+ /* Desktop file & icon name for unity-install:// install candidate displayed in last preview; we store
201+ * them here to avoid extra query for app details if app install action is activated */
202+ private string preview_installable_desktop_file;
203+ private string preview_installable_icon_file;
204+
205+ /* SoftwareCenter data provider used for app preview details */
206+ private SoftwareCenterDataProviderProxy sc_data_provider;
207+
208 private Unity.ApplicationsLens.Runner runner;
209
210 /* Keep references to the FilterOptions for sources */
211@@ -60,14 +81,14 @@
212
213 private Gee.List<string> image_extensions;
214 private HashTable<string,Icon> file_icon_cache;
215-
216+
217 /* Monitor the favorite apps in the launcher, so we can filter them
218 * out of the results for Recent Apps */
219 private Unity.LauncherFavorites favorite_apps;
220 private AppWatcher app_watcher;
221
222 private PtrArray zg_templates;
223-
224+
225 /* Gnome menu structure - also used to check whether apps are installed */
226 private uint app_menu_changed_reindex_timeout = 0;
227 private GMenu.Tree app_menu = null;
228@@ -95,7 +116,7 @@
229 monitor.events_inserted.connect (mark_dirty);
230 monitor.events_deleted.connect (mark_dirty);
231 log.install_monitor (monitor);
232-
233+
234 popularity_map = new HashMap<string, int> ();
235 popularities_dirty = true;
236 // refresh the popularities every now and then
237@@ -111,17 +132,17 @@
238
239 pkgsearcher = new Unity.Package.Searcher ();
240 if (pkgsearcher == null)
241- {
242- critical ("Failed to load Software Center index. 'Apps Available for Download' will not be listed");
243- }
244-
245+ {
246+ critical ("Failed to load Software Center index. 'Apps Available for Download' will not be listed");
247+ }
248+
249 try {
250 ratings = new Unity.Ratings.Database ();
251 } catch (FileError e) {
252 warning (e.message);
253 ratings = null;
254 }
255-
256+
257 /* Image file extensions in order of popularity */
258 image_extensions = new Gee.ArrayList<string> ();
259 image_extensions.add ("png");
260@@ -135,6 +156,7 @@
261 build_app_menu_index ();
262
263 file_icon_cache = new HashTable<string,Icon>(str_hash, str_equal);
264+ mangled_desktop_ids = new HashTable<string, string>(str_hash, str_equal);
265
266 scope = new Unity.Scope ("/com/canonical/unity/scope/applications");
267 //scope.icon = @"$(Config.PREFIX)/share/unity/themes/applications.png";
268@@ -158,7 +180,7 @@
269 {
270 dispatch_search (lens_search, search_type, cancellable);
271 });
272-
273+
274 /* Re-do the search if the filters changed */
275 scope.filters_changed.connect (() =>
276 {
277@@ -172,13 +194,14 @@
278 });
279
280 scope.activate_uri.connect (activate);
281+ scope.preview_uri.connect (preview);
282
283 /* Listen for changes in the installed applications */
284 AppInfoManager.get_default().changed.connect (mark_dirty);
285-
286+
287 /* Now start the RunEntry */
288 runner = new Unity.ApplicationsLens.Runner (this);
289-
290+
291 try {
292 uri_regex = new Regex ("^[a-z]+:.+$");
293 mountable_regex = new Regex ("((ftp|ssh|sftp|smb|dav)://).+");
294@@ -186,14 +209,16 @@
295 uri_regex = null;
296 critical ("Failed to compile URI regex. URL launching will be disabled");
297 }
298-
299+
300 favorite_apps = Unity.LauncherFavorites.get_default ();
301 favorite_apps.changed.connect(mark_dirty);
302
303 app_watcher = new AppWatcher ();
304 app_watcher.running_applications_changed.connect (mark_dirty);
305
306-
307+ aptdclient = new AptdProxy ();
308+ launcherservice = new LauncherProxy ();
309+
310 lens = new Unity.Lens ("/com/canonical/unity/lens/applications", "applications");
311 lens.search_hint = _("Search Applications");
312 lens.visible = true;
313@@ -253,9 +278,9 @@
314 var cat = new Unity.Category (_("Recently Used"),
315 new FileIcon (icon_dir.get_child ("group-recent.svg")));
316 categories.append (cat);
317-
318+
319 cat = new Unity.Category (_("Recent Apps"),
320- new FileIcon (icon_dir.get_child ("group-apps.svg")));
321+ new FileIcon (icon_dir.get_child ("group-apps.svg")));
322 categories.append (cat);
323
324 cat = new Unity.Category (_("Installed"),
325@@ -265,11 +290,11 @@
326 cat = new Unity.Category (_("Apps Available for Download"),
327 new FileIcon (icon_dir.get_child ("group-downloads.svg")));
328 categories.append (cat);
329-
330+
331 cat = new Unity.Category (_("Applications"),
332 new FileIcon (icon_dir.get_child ("group-apps.svg")));
333 categories.append (cat);
334-
335+
336 lens.categories = categories;
337 }
338
339@@ -281,7 +306,7 @@
340 {
341 var filter = new CheckOptionFilter ("type", _("Type"));
342 filter.sort_type = Unity.OptionsFilter.SortType.DISPLAY_NAME;
343-
344+
345 filter.add_option ("accessories", _("Accessories"));
346 filter.add_option ("education", _("Education"));
347 filter.add_option ("game", _("Games"));
348@@ -321,38 +346,37 @@
349 return usc_apps_option.active;
350 return true;
351 }
352-
353+
354 /* Load xdg menu info and build a Xapian index over it.
355 * Do throttled re-index if the menu changes */
356 private bool build_app_menu_index ()
357- {
358+ {
359 if (app_menu == null)
360- {
361- debug ("Building initial application menu");
362+ {
363+ debug ("Building initial application menu");
364
365- /* We need INCLUDE_NODISPLAY to employ proper de-duping between
366- * the Installed and Availabale categorys. If a NoDisplay app is installed,
367- * eg. Evince, it wont otherwise be in the menu index, only in the
368- * S-C index - thus show up in the Available category */
369- app_menu = GMenu.Tree.lookup ("unity-lens-applications.menu",
370- GMenu.TreeFlags.INCLUDE_NODISPLAY);
371-
372- app_menu.add_monitor ((menu) => {
373+ /* We need INCLUDE_NODISPLAY to employ proper de-duping between
374+ * the Installed and Availabale categorys. If a NoDisplay app is installed,
375+ * eg. Evince, it wont otherwise be in the menu index, only in the
376+ * S-C index - thus show up in the Available category */
377+ app_menu = GMenu.Tree.lookup ("unity-lens-applications.menu",
378+ GMenu.TreeFlags.INCLUDE_NODISPLAY);
379+
380+ app_menu.add_monitor ((menu) => {
381 /* Reschedule the timeout if we already have one. The menu tree triggers
382 * many change events during app installation. This way we wait the full
383 * delay *after* the last change event has triggered */
384 if (app_menu_changed_reindex_timeout != 0)
385 Source.remove (app_menu_changed_reindex_timeout);
386-
387- app_menu_changed_reindex_timeout =
388- Timeout.add_seconds (5, build_app_menu_index_and_result_models);
389+
390+ app_menu_changed_reindex_timeout = Timeout.add_seconds (5, build_app_menu_index_and_result_models);
391 });
392- }
393-
394+ }
395+
396 debug ("Indexing application menu");
397 appsearcher = new Unity.Package.Searcher.for_menu (app_menu);
398 app_menu_changed_reindex_timeout = 0;
399-
400+
401 return false;
402 }
403
404@@ -363,7 +387,7 @@
405 private bool build_app_menu_index_and_result_models ()
406 {
407 build_app_menu_index ();
408-
409+
410 mark_dirty ();
411
412 return false;
413@@ -391,15 +415,15 @@
414 OptionsFilter? options)
415 {
416 string s = search_string.strip ();
417-
418+
419 if (!s.has_suffix ("*") && s != "")
420 s = s + "*";
421-
422+
423 if (s != "")
424 s = @"app:($s)";
425 else
426 return extract_type_query (options);
427-
428+
429 if (options == null || !options.filtering)
430 return s;
431 else
432@@ -448,13 +472,13 @@
433 * to finish, to prevent flicker. */
434
435 debug ("Searching for: %s", search.search_string);
436-
437+
438 var filter = scope.get_filter ("type") as OptionsFilter;
439
440 string pkg_search_string = prepare_pkg_search_string (search, filter);
441
442 bool has_search = !Utils.is_search_empty (search);
443-
444+
445 Timer timer = new Timer ();
446
447 var transaction = new Dee.Transaction (model);
448@@ -513,8 +537,7 @@
449 // no need to bother
450 return;
451 } catch (GLib.Error e) {
452- warning ("Error performing search '%s': %s",
453- search.search_string, e.message);
454+ warning ("Error performing search '%s': %s", search.search_string, e.message);
455 }
456 }
457
458@@ -556,7 +579,7 @@
459
460 search.finished ();
461 }
462-
463+
464 private async void update_global_search (Unity.LensSearch search,
465 Cancellable cancellable)
466 {
467@@ -564,17 +587,17 @@
468 * In global search, with a non-empty search string, we collate all
469 * hits under one Applications category
470 */
471-
472+
473 if (Utils.is_search_empty (search))
474- {
475- yield update_global_without_search (search, cancellable);
476- return;
477- }
478-
479+ {
480+ yield update_global_without_search (search, cancellable);
481+ return;
482+ }
483+
484 var model = search.results_model;
485-
486+
487 model.clear ();
488-
489+
490 var search_string = prepare_pkg_search_string (search, null);
491 Set<string> installed_uris = new HashSet<string> ();
492 Set<string> available_uris = new HashSet<string> ();
493@@ -585,18 +608,18 @@
494 resort_pkg_search_results (appresults);
495 add_pkg_search_result (appresults, installed_uris, available_uris, model,
496 Category.APPLICATIONS);
497-
498+
499 timer.stop ();
500 debug ("Global search listed %i Installed apps in %fms for query: %s",
501 appresults.num_hits, timer.elapsed ()*1000, search_string);
502-
503+
504 /* Allow new searches once we enter an idle again.
505 * We don't do it directly from here as that could mean we start
506 * changing the model even before we had flushed out current changes
507 */
508 search.finished ();
509 }
510-
511+
512 private async void update_global_without_search (Unity.LensSearch search,
513 Cancellable cancellable)
514 {
515@@ -605,7 +628,7 @@
516 * Excluding apps with icons in the launcher (be they running or faves)
517 */
518 var model = search.results_model;
519-
520+
521 Timer timer = new Timer ();
522
523 if (local_apps_active () && display_recent_apps)
524@@ -678,103 +701,122 @@
525
526 return result == null ? "NOT category:XYZ" : "(%s)".printf (result);
527 }
528-
529+
530 private string prepare_pkg_search_string (Unity.LensSearch search,
531 OptionsFilter? options)
532 {
533 if (Utils.is_search_empty (search))
534- {
535- if (options == null || !options.filtering)
536- return "type:Application";
537- else
538- return "type:Application AND " + extract_type_query (options);
539- }
540+ {
541+ if (options == null || !options.filtering)
542+ return "type:Application";
543+ else
544+ return "type:Application AND " + extract_type_query (options);
545+ }
546 else
547+ {
548+ var s = search.search_string;
549+
550+ s = s.strip ();
551+
552+ /* The Xapian query parser seems to handle hyphens in a special way,
553+ * namely that it forces the joined tokens into a phrase query
554+ * no matter if it appears as the last word in a query and we have
555+ * the PARTIAL flag set on the query parser. This makes 'd-f' not
556+ * match 'd-feet' etc. */
557+ s = s.delimit ("-", ' ');
558+
559+ if (options == null || !options.filtering)
560+ return "type:Application AND " + s;
561+ else
562+ return "type:Application AND %s AND %s".printf (extract_type_query (options), s);
563+ }
564+ }
565+
566+ /**
567+ * Find app icon in DATADIR/app-install/icons, based on package name
568+ * trying all supported image extensions.
569+ */
570+ public string find_app_install_icon_path (string pkgname)
571+ {
572+ string icon = @"$ICON_APP_INSTALL_PATH/$preview_installable_icon_file.";
573+ string[] exts = {"png", "xpm"};
574+ foreach (string ext in exts)
575+ {
576+ string path = icon + ext;
577+ if (FileUtils.test (path, FileTest.EXISTS))
578 {
579- var s = search.search_string;
580-
581- s = s.strip ();
582-
583- /* The Xapian query parser seems to handle hyphens in a special way,
584- * namely that it forces the joined tokens into a phrase query
585- * no matter if it appears as the last word in a query and we have
586- * the PARTIAL flag set on the query parser. This makes 'd-f' not
587- * match 'd-feet' etc. */
588- s = s.delimit ("-", ' ');
589-
590- if (options == null || !options.filtering)
591- return "type:Application AND " + s;
592- else
593- return "type:Application AND %s AND %s".printf (extract_type_query (options), s);
594+ return path;
595 }
596+ }
597+ return "";
598 }
599-
600+
601 public Icon find_pkg_icon (Unity.Package.PackageInfo pkginfo)
602 {
603 string desktop_id = Path.get_basename (pkginfo.desktop_file);
604 bool installed = AppInfoManager.get_default().lookup (desktop_id) != null;
605-
606+
607 /* If the app is already installed we should be able to pull the
608 * icon from the theme */
609 if (installed)
610 return new ThemedIcon (pkginfo.icon);
611-
612+
613 /* App is not installed - we need to find the right icon in the bowels
614 * of the software center */
615 if (pkginfo.icon.has_prefix ("/"))
616- {
617- return new FileIcon (File.new_for_path (pkginfo.icon));
618- }
619+ {
620+ return new FileIcon (File.new_for_path (pkginfo.icon));
621+ }
622 else
623- {
624- Icon icon = file_icon_cache.lookup (pkginfo.icon);
625-
626- if (icon != null)
627- return icon;
628-
629- /* If the icon name contains a . it probably already have a
630- * type postfix - so test icon name directly */
631- string path;
632- if ("." in pkginfo.icon)
633- {
634- path = @"$(Config.DATADIR)/app-install/icons/$(pkginfo.icon)";
635- if (FileUtils.test (path, FileTest.EXISTS))
636- {
637- icon = new FileIcon (File.new_for_path (path));
638- file_icon_cache.insert (pkginfo.icon, icon);
639- return icon;
640- }
641- /* Try also software center cache dir */
642- path = Path.build_filename (Environment.get_user_cache_dir (),
643- "software-center",
644- "icons",
645- pkginfo.icon);
646- if (FileUtils.test (path, FileTest.EXISTS))
647- {
648- icon = new FileIcon (File.new_for_path (path));
649- file_icon_cache.insert (pkginfo.icon, icon);
650- return icon;
651- }
652- }
653-
654- /* Now try appending all the image extensions we know */
655- foreach (var ext in image_extensions)
656- {
657- path = @"$(Config.DATADIR)/app-install/icons/$(pkginfo.icon).$(ext)";
658- if (FileUtils.test (path, FileTest.EXISTS))
659- {
660- /* Got it! Cache the icon path and return the icon */
661- icon = new FileIcon (File.new_for_path (path));
662- file_icon_cache.insert (pkginfo.icon, icon);
663- return icon;
664- }
665- }
666- }
667-
668+ {
669+ Icon icon = file_icon_cache.lookup (pkginfo.icon);
670+
671+ if (icon != null)
672+ return icon;
673+
674+ /* If the icon name contains a . it probably already have a
675+ * type postfix - so test icon name directly */
676+ string path;
677+ if ("." in pkginfo.icon)
678+ {
679+ path = @"$(Config.DATADIR)/app-install/icons/$(pkginfo.icon)";
680+ if (FileUtils.test (path, FileTest.EXISTS))
681+ {
682+ icon = new FileIcon (File.new_for_path (path));
683+ file_icon_cache.insert (pkginfo.icon, icon);
684+ return icon;
685+ }
686+ /* Try also software center cache dir */
687+ path = Path.build_filename (Environment.get_user_cache_dir (),
688+ "software-center",
689+ "icons",
690+ pkginfo.icon);
691+ if (FileUtils.test (path, FileTest.EXISTS))
692+ {
693+ icon = new FileIcon (File.new_for_path (path));
694+ file_icon_cache.insert (pkginfo.icon, icon);
695+ return icon;
696+ }
697+ }
698+
699+ /* Now try appending all the image extensions we know */
700+ foreach (var ext in image_extensions)
701+ {
702+ path = @"$(Config.DATADIR)/app-install/icons/$(pkginfo.icon).$(ext)";
703+ if (FileUtils.test (path, FileTest.EXISTS))
704+ {
705+ /* Got it! Cache the icon path and return the icon */
706+ icon = new FileIcon (File.new_for_path (path));
707+ file_icon_cache.insert (pkginfo.icon, icon);
708+ return icon;
709+ }
710+ }
711+ }
712+
713 /* Cache the fact that we couldn't find this icon */
714 var icon = new ThemedIcon ("applications-other");
715 file_icon_cache.insert (pkginfo.icon, icon);
716-
717+
718 return icon;
719 }
720
721@@ -791,7 +833,14 @@
722 desktop_id = desktop_id[colon_pos+1:desktop_id.length];
723 /* well it's still not real desktop_id, S-C converts slashes to "__"
724 * if the desktop file is in /usr/share/applications */
725- desktop_id = desktop_id.replace ("__", "-");
726+ string demangled_desktop_id = desktop_id.replace ("__", "-");
727+
728+ // store demangled name -> mangled name mapping
729+ if (desktop_id != demangled_desktop_id)
730+ {
731+ mangled_desktop_ids.replace (demangled_desktop_id, desktop_id);
732+ }
733+ desktop_id = demangled_desktop_id;
734 }
735
736 return desktop_id;
737@@ -824,7 +873,7 @@
738 return rel_b - rel_a; // we want higher relevancy first
739 });
740 }
741-
742+
743 private void add_pkg_search_result (Unity.Package.SearchResult results,
744 Set<string> installed_uris,
745 Set<string> available_uris,
746@@ -838,29 +887,29 @@
747
748 foreach (var pkginfo in results.results)
749 {
750- if (pkginfo.desktop_file == null)
751+ if (pkginfo.desktop_file == null)
752 continue;
753-
754+
755 string desktop_id = extract_desktop_id (pkginfo.desktop_file,
756- category == Category.AVAILABLE);
757+ category == Category.AVAILABLE);
758 string full_path;
759
760 AppInfo? app = appmanager.lookup (desktop_id);
761 full_path = appmanager.get_path (desktop_id);
762-
763+
764 /* De-dupe by 'application://foo.desktop' URI. Also note that we need
765 * to de-dupe before we chuck out NoDisplay app infos, otherwise they'd
766 * show up from alternate sources */
767 string uri = @"application://$(desktop_id)";
768 if (uri in installed_uris || uri in available_uris)
769 continue;
770-
771+
772 /* Extract basic metadata and register de-dupe keys */
773 string display_name;
774 string comment;
775 switch (category)
776 {
777- case Category.INSTALLED:
778+ case Category.INSTALLED:
779 case Category.APPLICATIONS:
780 installed_uris.add (uri);
781 display_name = app.get_display_name ();
782@@ -874,52 +923,52 @@
783 default:
784 warning (@"Illegal category for package search $(category)");
785 continue;
786- }
787-
788+ }
789+
790 /* We can only chuck out NoDisplay and OnlyShowIn app infos after
791 * we have registered a de-dupe key for them - which is done in the
792 * switch block above) */
793 if (app != null && !app.should_show ())
794 continue;
795-
796+
797 if (category == Category.AVAILABLE)
798+ {
799+ /* If we have an available item, which is not a dupe, but is
800+ * installed anyway, we weed it out here, because it's probably
801+ * left out from the Installed section because of some rule in the
802+ * .menu file */
803+ if (app != null)
804+ continue;
805+
806+ /* Filter by app rating in Software Center if enabled */
807+ if (ratings != null && ratings_filter.rating > 0.00001)
808 {
809- /* If we have an available item, which is not a dupe, but is
810- * installed anyway, we weed it out here, because it's probably
811- * left out from the Installed section because of some rule in the
812- * .menu file */
813- if (app != null)
814+ Unity.Ratings.Result result;
815+ if (ratings.query (pkginfo.package_name, out result))
816+ {
817+ if (result.average_rating < ratings_filter.rating * 5 - 0.2)
818+ continue;
819+ }
820+ else
821 continue;
822-
823- /* Filter by app rating in Software Center if enabled */
824- if (ratings != null && ratings_filter.rating > 0.00001)
825- {
826- Unity.Ratings.Result result;
827- if (ratings.query (pkginfo.package_name, out result))
828- {
829- if (result.average_rating < ratings_filter.rating * 5 - 0.2)
830- continue;
831- }
832- else
833- continue;
834- }
835-
836- /* Apps that are not installed, ie. in the Available category
837- * use the 'unity-install://pkgname/Full App Name' URI scheme,
838- * but only use that after we've de-duped the results.
839- * But only change the URI *after* we've de-duped the results! */
840- uri = @"unity-install://$(pkginfo.package_name)/$(pkginfo.application_name)";
841- available_uris.add (uri);
842 }
843-
844+
845+ /* Apps that are not installed, ie. in the Available category
846+ * use the 'unity-install://pkgname/Full App Name' URI scheme,
847+ * but only use that after we've de-duped the results.
848+ * But only change the URI *after* we've de-duped the results! */
849+ uri = @"unity-install://$(pkginfo.package_name)/$(pkginfo.application_name)";
850+ available_uris.add (uri);
851+ }
852+
853 Icon icon = find_pkg_icon (pkginfo);
854-
855+
856 model.append (uri, icon.to_string (),
857 category,"application/x-desktop",
858 display_name != null ? display_name : "",
859 comment != null ? comment : "",
860- full_path != null ? "file://" + full_path : "");
861-
862+ full_path != null ? "file://" + full_path : "");
863+
864 /* Stop if we added the number of items requested */
865 n_added++;
866 if (max_add > 0 && n_added >= max_add)
867@@ -927,6 +976,248 @@
868 }
869 }
870
871+ private async void call_install_packages (string package_name, out string tid)
872+ {
873+ tid = yield aptdclient.install_packages ({package_name});
874+ }
875+
876+ private async void call_remove_packages (string package_name, out string tid)
877+ {
878+ tid = yield aptdclient.remove_packages ({package_name});
879+ }
880+
881+ private Unity.ActivationResponse app_preview_launch (string uri)
882+ {
883+ return activate (uri);
884+ }
885+
886+ /**
887+ * Handler for free apps installation.
888+ * Triggers package installation via apt-daemon DBus service
889+ */
890+ private Unity.ActivationResponse app_preview_install (string uri)
891+ {
892+ if (uri.has_prefix ("unity-install://"))
893+ {
894+ string app = uri.substring (16); // trim "unity-install://"
895+ string[] parts = app.split ("/");
896+
897+ if (parts.length > 1)
898+ {
899+ string pkgname = parts[0];
900+ string appname = parts[1];
901+ aptdclient.connect_to_aptd ();
902+ call_install_packages.begin (pkgname, (obj, res) =>
903+ {
904+ try {
905+ string tid;
906+ call_install_packages.end (res, out tid);
907+ debug ("transaction started: %s, pkg: %s\n", tid, pkgname);
908+ aptd_transaction = new AptdTransactionProxy ();
909+ aptd_transaction.connect_to_aptd (tid);
910+ aptd_transaction.simulate ();
911+ aptd_transaction.run ();
912+
913+ launcherservice.connect_to_launcher ();
914+ string desktop_file = preview_installable_desktop_file;
915+ string icon = find_app_install_icon_path (preview_installable_icon_file);
916+
917+ launcherservice.add_launcher_item_from_position (appname, icon, 0, 0, 32, desktop_file, tid);
918+ }
919+ catch (IOError e)
920+ {
921+ warning (@"Package '$(pkgname)' installation failed: $(e.message)");
922+ }
923+ });
924+ }
925+ else
926+ {
927+ warning (@"Bad install uri: '%s'", uri);
928+ }
929+ }
930+ else
931+ {
932+ warning (@"Can't handle '%s' in app_preview_install handler", uri);
933+ return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
934+ }
935+ return new Unity.ActivationResponse(Unity.HandledType.SHOW_DASH);
936+ }
937+
938+ private Unity.ActivationResponse app_preview_install_commercial (string uri)
939+ {
940+ return activate (uri); //this will just launch Software-Center
941+ }
942+
943+ private Unity.ActivationResponse app_preview_buy (string uri)
944+ {
945+ return activate (uri); //this will just launch Software-Center
946+ }
947+
948+ private Unity.ActivationResponse app_preview_uninstall (string uri)
949+ {
950+ if (uri.has_prefix ("application://"))
951+ {
952+ string desktopfile = uri.substring (14); // trim "application://"
953+
954+ // de-mangle desktop file names back to what S-C expects
955+ if (mangled_desktop_ids.contains (desktopfile))
956+ {
957+ desktopfile = mangled_desktop_ids.get (desktopfile);
958+ }
959+
960+ var pkginfo = pkgsearcher.get_by_desktop_file (desktopfile);
961+
962+ if (pkginfo != null && pkginfo.package_name != null)
963+ {
964+ aptdclient.connect_to_aptd ();
965+ call_remove_packages.begin (pkginfo.package_name, (obj, res) =>
966+ {
967+ try {
968+ string tid;
969+ call_remove_packages.end (res, out tid);
970+ debug ("transaction started: %s, pkg: %s\n", tid, pkginfo.package_name);
971+ aptd_transaction = new AptdTransactionProxy ();
972+ aptd_transaction.connect_to_aptd (tid);
973+ aptd_transaction.simulate ();
974+ aptd_transaction.run ();
975+ }
976+ catch (IOError e)
977+ {
978+ warning (@"Package '$(pkginfo.package_name)' removal failed: $(e.message)");
979+ }
980+ });
981+ return new Unity.ActivationResponse(Unity.HandledType.SHOW_DASH);
982+ }
983+ else
984+ {
985+ warning (@"Cannot find package info for $uri");
986+ }
987+ }
988+ warning (@"Can't handle '%s' in app_preview_uninstall handler", uri);
989+ return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
990+ }
991+
992+ public Unity.Preview preview (string uri)
993+ {
994+ Unity.ApplicationPreview? preview = null;
995+
996+ string pkgname = "";
997+ string appname = "";
998+
999+ if (uri.has_prefix ("application://") || uri.has_prefix ("unity-install://"))
1000+ {
1001+ if (uri.has_prefix ("application://"))
1002+ {
1003+ string desktopfile = uri.substring (14); //remove "application://" prefix
1004+
1005+ // de-mangle desktop file names back to what S-C expects
1006+ if (mangled_desktop_ids.contains (desktopfile))
1007+ {
1008+ desktopfile = mangled_desktop_ids.get (desktopfile);
1009+ }
1010+
1011+ Unity.Package.PackageInfo pkginfo = pkgsearcher.get_by_desktop_file (desktopfile);
1012+ if (pkginfo != null)
1013+ {
1014+ appname = pkginfo.application_name;
1015+ pkgname = pkginfo.package_name;
1016+ }
1017+ }
1018+ else // unity-install
1019+ {
1020+ string app = uri.substring (16); //remove "unity-install://" prefix
1021+ string[] parts = app.split ("/");
1022+ if (parts.length > 1)
1023+ {
1024+ pkgname = parts[0];
1025+ appname = parts[1];
1026+ }
1027+ }
1028+
1029+ if (pkgname != "")
1030+ {
1031+ try {
1032+ sc_data_provider = new SoftwareCenterDataProviderProxy ();
1033+ sc_data_provider.connect_to ();
1034+
1035+ debug ("Requesting pkg info: %s, %s\n", pkgname, appname);
1036+ sc_data_provider.get_app_details (appname, pkgname);
1037+
1038+ Icon icon = new GLib.ThemedIcon (sc_data_provider.icon);
1039+ Icon? screenshot = null;
1040+
1041+ if (sc_data_provider.screenshot != null)
1042+ {
1043+ File scr_file = File.new_for_uri (sc_data_provider.screenshot);
1044+ screenshot = new FileIcon (scr_file);
1045+ }
1046+
1047+ preview = new Unity.ApplicationPreview (sc_data_provider.name, sc_data_provider.summary, sc_data_provider.description, icon, screenshot);
1048+ preview.license = sc_data_provider.license;
1049+
1050+ if (ratings !=null)
1051+ {
1052+ Unity.Ratings.Result result;
1053+ ratings.query (pkgname, out result);
1054+ preview.set_rating (result.average_rating, result.total_rating);
1055+ }
1056+
1057+ preview.add_info (new InfoHint.with_variant ("size", _("Size"), null, new GLib.Variant.int64 (sc_data_provider.size)));
1058+ preview.add_info (new InfoHint ("hardware-requirements", _("Hardware requirements"), null, sc_data_provider.hardware_requirements));
1059+ preview.add_info (new InfoHint ("version", _("Version"), null, sc_data_provider.version));
1060+ preview.add_info (new InfoHint ("website", _("Website"), null, sc_data_provider.website));
1061+ preview.add_info (new InfoHint ("price", _("Price"), null, sc_data_provider.price));
1062+
1063+ if (uri.has_prefix ("unity-install://")) // application needs to be purchased/installed
1064+ {
1065+ // uninstalled and not purchased before
1066+ if (sc_data_provider.pkg_state == SoftwareCenterDataProviderProxy.PackageState.NEEDS_PURCHASE)
1067+ {
1068+ var buy_action = new Unity.PreviewAction ("buy", _("Buy"), null);
1069+ buy_action.activated.connect (app_preview_buy);
1070+ preview.add_action (buy_action);
1071+ }
1072+ else // uninstalled, purchased before
1073+ {
1074+ var install_action = new Unity.PreviewAction ("install", _("Install"), null);
1075+ if (sc_data_provider.price != "")
1076+ {
1077+ install_action.activated.connect (app_preview_install_commercial);
1078+ }
1079+ else
1080+ {
1081+ install_action.activated.connect (app_preview_install);
1082+ }
1083+ preview.add_action (install_action);
1084+ }
1085+ }
1086+ else // application is already installed
1087+ {
1088+ preview.add_info (new InfoHint ("date-installed", _("Installed on"), null, sc_data_provider.installation_date));
1089+ var launch_action = new Unity.PreviewAction ("launch", _("Launch"), null);
1090+ launch_action.activated.connect (app_preview_launch);
1091+ preview.add_action (launch_action);
1092+ var uninstall_action = new Unity.PreviewAction ("uninstall", _("Uninstall"), null);
1093+ uninstall_action.activated.connect (app_preview_uninstall);
1094+ preview.add_action (uninstall_action);
1095+ }
1096+
1097+ preview_installable_desktop_file = sc_data_provider.desktop_file;
1098+ preview_installable_icon_file = sc_data_provider.icon;
1099+ }
1100+ catch (IOError e)
1101+ {
1102+ warning ("Failed to get package details for '%s': %s", uri, e.message);
1103+ }
1104+ }
1105+ else
1106+ {
1107+ warning ("pksearcher: no data for '%s'", uri);
1108+ }
1109+ }
1110+ return preview;
1111+ }
1112+
1113 /**
1114 * Override of the default activation handler. The apps lens daemon
1115 * can handle activation of installable apps using the Software Center
1116@@ -936,79 +1227,79 @@
1117 string[] args;
1118 string exec_or_dir = null;
1119 if (uri.has_prefix ("unity-install://"))
1120- {
1121- unowned string pkg = uri.offset (16); // strip off "unity-install://" prefix
1122- debug ("Installing: %s", pkg);
1123- args = new string[2];
1124- args[0] = "software-center";
1125- args[1] = pkg;
1126- }
1127+ {
1128+ unowned string pkg = uri.offset (16); // strip off "unity-install://" prefix
1129+ debug ("Installing: %s", pkg);
1130+ args = new string[2];
1131+ args[0] = "software-center";
1132+ args[1] = pkg;
1133+ }
1134 else if (uri.has_prefix ("unity-runner://"))
1135- {
1136- string orig;
1137- orig = uri.offset (15);
1138- if (orig.has_prefix("\\\\"))
1139- orig = orig.replace ("\\\\","smb://");
1140- if (uri_regex != null && uri_regex.match (orig)) {
1141- try {
1142- /* this code ensures that a file manager will be used
1143- * if uri it's a remote location that should be mounted */
1144- if (mountable_regex.match (orig)) {
1145- var muris = new GLib.List<string>();
1146- muris.prepend (orig);
1147- var file_manager = AppInfo.get_default_for_type("inode/directory", true);
1148- file_manager.launch_uris(muris,null);
1149- } else {
1150+ {
1151+ string orig;
1152+ orig = uri.offset (15);
1153+ if (orig.has_prefix("\\\\"))
1154+ orig = orig.replace ("\\\\","smb://");
1155+ if (uri_regex != null && uri_regex.match (orig)) {
1156+ try {
1157+ /* this code ensures that a file manager will be used
1158+ * if uri it's a remote location that should be mounted */
1159+ if (mountable_regex.match (orig)) {
1160+ var muris = new GLib.List<string>();
1161+ muris.prepend (orig);
1162+ var file_manager = AppInfo.get_default_for_type("inode/directory", true);
1163+ file_manager.launch_uris(muris,null);
1164+ } else {
1165 AppInfo.launch_default_for_uri (orig, null);
1166- }
1167- } catch (GLib.Error error) {
1168- warning ("Failed to launch URI %s", orig);
1169- return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
1170 }
1171- return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH);
1172-
1173- } else {
1174- exec_or_dir = Utils.subst_tilde (orig);
1175- args = exec_or_dir.split (" ", 0);
1176- for (int i = 0; i < args.length; i++)
1177- args[i] = Utils.subst_tilde (args[i]);
1178+ } catch (GLib.Error error) {
1179+ warning ("Failed to launch URI %s", orig);
1180+ return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
1181 }
1182- this.runner.add_history (orig);
1183+ return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH);
1184+
1185+ } else {
1186+ exec_or_dir = Utils.subst_tilde (orig);
1187+ args = exec_or_dir.split (" ", 0);
1188+ for (int i = 0; i < args.length; i++)
1189+ args[i] = Utils.subst_tilde (args[i]);
1190 }
1191+ this.runner.add_history (orig);
1192+ }
1193 else
1194- {
1195- /* Activation of standard application:// uris */
1196-
1197- /* Make sure fresh install learns quickly */
1198- if (popularity_map.size <= 5) popularities_dirty = true;
1199-
1200- return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
1201- }
1202+ {
1203+ /* Activation of standard application:// uris */
1204+
1205+ /* Make sure fresh install learns quickly */
1206+ if (popularity_map.size <= 5) popularities_dirty = true;
1207+
1208+ return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
1209+ }
1210
1211 if ((exec_or_dir != null) && FileUtils.test (exec_or_dir, FileTest.IS_DIR))
1212 {
1213 try {
1214- AppInfo.launch_default_for_uri ("file://" + exec_or_dir, null);
1215+ AppInfo.launch_default_for_uri ("file://" + exec_or_dir, null);
1216 } catch (GLib.Error err) {
1217- warning ("Failed to open current folder '%s' in file manager: %s",
1218- exec_or_dir, err.message);
1219- return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
1220+ warning ("Failed to open current folder '%s' in file manager: %s",
1221+ exec_or_dir, err.message);
1222+ return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
1223 }
1224 }
1225 else
1226 {
1227- try {
1228- unowned string home_dir = GLib.Environment.get_home_dir ();
1229- Process.spawn_async (home_dir, args, null, SpawnFlags.SEARCH_PATH, null, null);
1230- } catch (SpawnError e) {
1231- warning ("Failed to spawn software-center or direct URI activation '%s': %s",
1232- uri, e.message);
1233- return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
1234- }
1235+ try {
1236+ unowned string home_dir = GLib.Environment.get_home_dir ();
1237+ Process.spawn_async (home_dir, args, null, SpawnFlags.SEARCH_PATH, null, null);
1238+ } catch (SpawnError e) {
1239+ warning ("Failed to spawn software-center or direct URI activation '%s': %s",
1240+ uri, e.message);
1241+ return new Unity.ActivationResponse(Unity.HandledType.NOT_HANDLED);
1242+ }
1243 }
1244
1245 return new Unity.ActivationResponse(Unity.HandledType.HIDE_DASH);
1246-
1247+
1248 }
1249
1250 /* Appends the subject URIs from a set of Zeitgeist.Events to our Dee.Model
1251@@ -1026,7 +1317,7 @@
1252 string? app_uri = null;
1253 if (ev.num_subjects () > 0)
1254 app_uri = ev.get_subject (0).get_uri ();
1255-
1256+
1257 if (app_uri == null)
1258 {
1259 warning ("Unexpected event without subject");
1260@@ -1035,7 +1326,7 @@
1261
1262 /* Assert that we indeed have a known application as actor */
1263 string desktop_id = Utils.get_desktop_id_for_actor (app_uri);
1264-
1265+
1266 /* Discard Recently Used apps that are in the launcher */
1267 if ((category_id == Category.RECENT ||
1268 category_id == Category.RECENT_APPS) &&
1269@@ -1049,7 +1340,7 @@
1270
1271 if (app == null)
1272 continue;
1273-
1274+
1275 if (!app.should_show ())
1276 continue;
1277
1278@@ -1073,7 +1364,7 @@
1279 app.get_description (), full_uri);
1280 }
1281 }
1282-
1283+
1284 } /* END: class Daemon */
1285
1286 } /* namespace */
1287
1288=== added file 'src/launcher-client.vala'
1289--- src/launcher-client.vala 1970-01-01 00:00:00 +0000
1290+++ src/launcher-client.vala 2012-08-08 16:35:21 +0000
1291@@ -0,0 +1,45 @@
1292+/*
1293+ * Copyright (C) 2012 Canonical Ltd
1294+ *
1295+ * This program is free software: you can redistribute it and/or modify
1296+ * it under the terms of the GNU General Public License version 3 as
1297+ * published by the Free Software Foundation.
1298+ *
1299+ * This program is distributed in the hope that it will be useful,
1300+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1301+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1302+ * GNU General Public License for more details.
1303+ *
1304+ * You should have received a copy of the GNU General Public License
1305+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1306+ *
1307+ * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
1308+ */
1309+
1310+namespace Unity.ApplicationsLens {
1311+
1312+ static const string LAUNCHER_DBUS_NAME = "com.canonical.Unity.Launcher";
1313+ static const string LAUNCHER_DBUS_PATH = "/com/canonical/Unity/Launcher";
1314+
1315+ [DBus (name = "com.canonical.Unity.Launcher")]
1316+ public interface LauncherService: GLib.Object
1317+ {
1318+ public abstract async void add_launcher_item_from_position (string title, string icon, int icon_x, int icon_y, int icon_size, string desktop_file, string aptdaemon_task) throws IOError;
1319+ }
1320+
1321+ public class LauncherProxy: GLib.Object
1322+ {
1323+ public void connect_to_launcher ()
1324+ {
1325+ _launcher_service = Bus.get_proxy_sync (BusType.SESSION, LAUNCHER_DBUS_NAME, LAUNCHER_DBUS_PATH);
1326+ }
1327+
1328+ public async void add_launcher_item_from_position (string title, string icon, int icon_x, int icon_y, int icon_size, string desktop_file, string aptdaemon_task) throws IOError
1329+ {
1330+ yield _launcher_service.add_launcher_item_from_position (title, icon, icon_x, icon_y, icon_size, desktop_file, aptdaemon_task);
1331+ }
1332+
1333+ private LauncherService _launcher_service;
1334+ }
1335+
1336+}
1337\ No newline at end of file
1338
1339=== added file 'src/software-center-data-provider.vala'
1340--- src/software-center-data-provider.vala 1970-01-01 00:00:00 +0000
1341+++ src/software-center-data-provider.vala 2012-08-08 16:35:21 +0000
1342@@ -0,0 +1,115 @@
1343+/*
1344+ * Copyright (C) 2012 Canonical Ltd
1345+ *
1346+ * This program is free software: you can redistribute it and/or modify
1347+ * it under the terms of the GNU General Public License version 3 as
1348+ * published by the Free Software Foundation.
1349+ *
1350+ * This program is distributed in the hope that it will be useful,
1351+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1352+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1353+ * GNU General Public License for more details.
1354+ *
1355+ * You should have received a copy of the GNU General Public License
1356+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1357+ *
1358+ * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
1359+ */
1360+
1361+namespace Unity.ApplicationsLens {
1362+
1363+ static const string SCDP_DBUS_NAME = "com.ubuntu.SoftwareCenterDataProvider";
1364+ static const string SCDP_DBUS_PATH = "/com/ubuntu/SoftwareCenterDataProvider";
1365+
1366+ [DBus (name = "com.ubuntu.SoftwareCenterDataProvider")]
1367+ public interface SoftwareCenterDataProviderService: GLib.Object
1368+ {
1369+ public abstract HashTable<string, Variant> get_app_details (string appname, string pkgname) throws IOError;
1370+ }
1371+
1372+ public class SoftwareCenterDataProviderProxy: GLib.Object
1373+ {
1374+ public enum PackageState
1375+ {
1376+ UNINSTALLED,
1377+ INSTALLED,
1378+ NEEDS_PURCHASE,
1379+ UNKNOWN
1380+ }
1381+
1382+ public string name { get; set; }
1383+ public string summary { get; set; }
1384+ public string description { get; set; }
1385+ public string version { get; set; }
1386+ public string screenshot { get; set; }
1387+ public string desktop_file { get; set; }
1388+ public string license { get; set; }
1389+ public string icon { get; set; }
1390+ public string price { get; set; }
1391+ public PackageState pkg_state { get; set; }
1392+ public string installation_date { get; set; }
1393+ public string website { get; set; }
1394+ public int64 size { get; set; }
1395+ public string hardware_requirements { get; set; }
1396+
1397+ public void connect_to () throws IOError
1398+ {
1399+ _service = Bus.get_proxy_sync (BusType.SESSION, SCDP_DBUS_NAME, SCDP_DBUS_PATH);
1400+ }
1401+
1402+ public HashTable<string, Variant> get_app_details (string appname, string pkgname)
1403+ {
1404+ HashTable<string, Variant> data = _service.get_app_details (appname, pkgname);
1405+
1406+ name = data.get ("name").get_string ();
1407+ summary = data.get ("summary").get_string ();
1408+ description = data.get ("description").get_string ();
1409+ version = data.get ("version").get_string ();
1410+ desktop_file = data.get ("desktop_file").get_string ();
1411+ license = data.get ("license").get_string ();
1412+ icon = data.get ("icon_file_name").get_string ();
1413+ price = data.get ("price").get_string ();
1414+ installation_date = data.get ("installation_date").get_string ();
1415+ website = data.get ("website").get_string ();
1416+ hardware_requirements = data.get ("hardware_requirements").get_string ();
1417+ size = int64.parse (data.get ("size").get_string ());
1418+
1419+ var state = data.get ("pkg_state").get_string ();
1420+
1421+ if (state == "installed")
1422+ {
1423+ pkg_state = PackageState.INSTALLED;
1424+ }
1425+ else if (state == "uninstalled")
1426+ {
1427+ pkg_state = PackageState.UNINSTALLED;
1428+ }
1429+ else if (state == "needs_purchase")
1430+ {
1431+ pkg_state = PackageState.NEEDS_PURCHASE;
1432+ }
1433+ else
1434+ {
1435+ pkg_state = PackageState.UNKNOWN;
1436+ }
1437+
1438+ screenshot = null;
1439+
1440+ if (data.contains ("screenshots"))
1441+ {
1442+ var screenshot_var = data.get ("screenshots").get_child_value (0).lookup_value ("large_image_url", VariantType.STRING);
1443+ if (screenshot_var != null)
1444+ {
1445+ screenshot = screenshot_var.get_string ();
1446+ }
1447+ }
1448+ if (screenshot == null)
1449+ {
1450+ screenshot = "";
1451+ }
1452+ return data;
1453+ }
1454+
1455+ private SoftwareCenterDataProviderService _service;
1456+ }
1457+}
1458
1459=== modified file 'src/unity-package-search.cc'
1460--- src/unity-package-search.cc 2012-03-27 10:49:37 +0000
1461+++ src/unity-package-search.cc 2012-08-08 16:35:21 +0000
1462@@ -1,6 +1,6 @@
1463 /*
1464 * Copyright (C) 2010 Canonical Ltd
1465- *
1466+ *
1467 * This program is free software: you can redistribute it and/or modify
1468 * it under the terms of the GNU General Public License version 3 as
1469 * published by the Free Software Foundation.
1470@@ -50,12 +50,15 @@
1471 #define XAPIAN_VALUE_SUMMARY 177
1472 #define XAPIAN_VALUE_ARCHIVE_CHANNEL 178
1473 #define XAPIAN_VALUE_DESKTOP_FILE 179
1474+#define XAPIAN_VALUE_SCREENSHOT_URLS 185
1475+#define XAPIAN_VALUE_DESCRIPTION 188
1476+#define XAPIAN_VALUE_VERSION_INFO 198
1477
1478 #include "unity-package-search.h"
1479
1480 extern "C"
1481 {
1482-extern gchar* unity_applications_lens_utils_preprocess_string (const gchar* input);
1483+ extern gchar* unity_applications_lens_utils_preprocess_string (const gchar* input);
1484 }
1485
1486 struct _UnityPackageSearcher
1487@@ -71,17 +74,17 @@
1488 class LocaleKeyMaker : public Xapian::KeyMaker
1489 {
1490 private:
1491- Xapian::valueno value;
1492-
1493+ Xapian::valueno value;
1494+
1495 public:
1496-
1497+
1498 LocaleKeyMaker (Xapian::valueno value)
1499 {
1500 this->value = value;
1501 }
1502-
1503+
1504 virtual ~LocaleKeyMaker() { }
1505-
1506+
1507 virtual std::string operator() (const Xapian::Document &doc) const
1508 {
1509 string val = doc.get_value (value);
1510@@ -89,40 +92,40 @@
1511 string col_key_mm = col_key_c;
1512 g_free (col_key_c);
1513 return col_key_mm;
1514- }
1515+ }
1516 };
1517
1518 /* Do generic searcher setup */
1519 static void
1520 init_searcher (UnityPackageSearcher *searcher)
1521 {
1522- // Activate Xapian CJK support
1523+ // Activate Xapian CJK support
1524 setenv("XAPIAN_CJK_NGRAM", "1", 1);
1525
1526- Xapian::Database db = *searcher->db;
1527+ Xapian::Database db = *searcher->db;
1528
1529 // Start an enquire session
1530 Xapian::Enquire *enquire = new Xapian::Enquire (db);
1531- //enquire->set_sort_by_value (XAPIAN_VALUE_APPNAME, FALSE);
1532+ enquire->set_sort_by_value (XAPIAN_VALUE_APPNAME, FALSE);
1533 searcher->enquire = enquire;
1534
1535 // Make sure we respect sorting rules for the current locale
1536 searcher->sorter = new LocaleKeyMaker (XAPIAN_VALUE_APPNAME);
1537 enquire->set_sort_by_key (searcher->sorter, FALSE);
1538
1539- // Create query parser
1540- Xapian::QueryParser *query_parser = new Xapian::QueryParser ();
1541- query_parser->add_prefix ("section", "AE");
1542- query_parser->add_prefix ("type", "AT");
1543- query_parser->add_prefix ("category", "AC");
1544- query_parser->add_prefix ("name", "AA");
1545- query_parser->add_prefix ("pkgname", "AP");
1546- query_parser->add_prefix ("exec", "XX");
1547- query_parser->add_prefix ("keyword", "KW");
1548- query_parser->set_default_op (Xapian::Query::OP_AND);
1549- query_parser->set_database (db);
1550- searcher->query_parser = query_parser;
1551-
1552+ // Create query parser
1553+ Xapian::QueryParser *query_parser = new Xapian::QueryParser ();
1554+ query_parser->add_prefix ("section", "AE");
1555+ query_parser->add_prefix ("type", "AT");
1556+ query_parser->add_prefix ("category", "AC");
1557+ query_parser->add_prefix ("name", "AA");
1558+ query_parser->add_prefix ("pkgname", "AP");
1559+ query_parser->add_prefix ("exec", "XX");
1560+ query_parser->add_prefix ("keyword", "KW");
1561+ query_parser->set_default_op (Xapian::Query::OP_AND);
1562+ query_parser->set_database (db);
1563+ searcher->query_parser = query_parser;
1564+
1565 // Init random number generator from 32 lowest bits of system time
1566 searcher->random = g_rand_new_with_seed (g_get_monotonic_time ());
1567 }
1568@@ -141,110 +144,103 @@
1569 gchar *dum1, *dum2, *dum3;
1570 gint i, len;
1571
1572- g_return_if_fail (db != NULL);
1573- g_return_if_fail (indexer != NULL);
1574- g_return_if_fail (item != NULL);
1575-
1576- switch (gmenu_tree_item_get_type (item))
1577- {
1578- case GMENU_TREE_ITEM_INVALID:
1579- return;
1580- case GMENU_TREE_ITEM_DIRECTORY:
1581- /* Recurse into directory */
1582- iter = gmenu_tree_directory_get_contents (GMENU_TREE_DIRECTORY (item));
1583- for (; iter != NULL; iter = iter->next)
1584- {
1585- index_menu_item (db, indexer, GMENU_TREE_ITEM (iter->data));
1586- }
1587- break;
1588- case GMENU_TREE_ITEM_ENTRY:
1589- /* Add this entry to the index */
1590- entry = GMENU_TREE_ENTRY (item);
1591-
1592- /* Store relevant values */
1593- if (gmenu_tree_entry_get_display_name (entry))
1594- doc.add_value (XAPIAN_VALUE_APPNAME,
1595- gmenu_tree_entry_get_display_name (entry));
1596- if (gmenu_tree_entry_get_icon (entry))
1597- doc.add_value (XAPIAN_VALUE_ICON,
1598- gmenu_tree_entry_get_icon (entry));
1599+ g_return_if_fail (db != NULL);
1600+ g_return_if_fail (indexer != NULL);
1601+ g_return_if_fail (item != NULL);
1602+
1603+ switch (gmenu_tree_item_get_type (item))
1604+ {
1605+ case GMENU_TREE_ITEM_INVALID:
1606+ return;
1607+ case GMENU_TREE_ITEM_DIRECTORY:
1608+ /* Recurse into directory */
1609+ iter = gmenu_tree_directory_get_contents (GMENU_TREE_DIRECTORY (item));
1610+ for (; iter != NULL; iter = iter->next)
1611+ {
1612+ index_menu_item (db, indexer, GMENU_TREE_ITEM (iter->data));
1613+ }
1614+ break;
1615+ case GMENU_TREE_ITEM_ENTRY:
1616+ /* Add this entry to the index */
1617+ entry = GMENU_TREE_ENTRY (item);
1618+
1619+ /* Store relevant values */
1620+ if (gmenu_tree_entry_get_display_name (entry))
1621+ doc.add_value (XAPIAN_VALUE_APPNAME, gmenu_tree_entry_get_display_name (entry));
1622+ if (gmenu_tree_entry_get_icon (entry))
1623+ doc.add_value (XAPIAN_VALUE_ICON, gmenu_tree_entry_get_icon (entry));
1624 if (gmenu_tree_entry_get_desktop_file_id (entry))
1625- doc.add_value (XAPIAN_VALUE_DESKTOP_FILE,
1626- gmenu_tree_entry_get_desktop_file_id (entry));
1627-
1628- /* Index full text data */
1629- indexer->set_document(doc);
1630+ doc.add_value (XAPIAN_VALUE_DESKTOP_FILE, gmenu_tree_entry_get_desktop_file_id (entry));
1631+
1632+ /* Index full text data */
1633+ indexer->set_document(doc);
1634 if (gmenu_tree_entry_get_display_name (entry))
1635 {
1636- dum1 = unity_applications_lens_utils_preprocess_string (
1637- gmenu_tree_entry_get_display_name (entry));
1638+ dum1 = unity_applications_lens_utils_preprocess_string (gmenu_tree_entry_get_display_name (entry));
1639 indexer->index_text(dum1, 5);
1640 g_free (dum1);
1641 }
1642 if (gmenu_tree_entry_get_name (entry))
1643 {
1644- dum1 = unity_applications_lens_utils_preprocess_string (
1645- gmenu_tree_entry_get_name (entry));
1646+ dum1 = unity_applications_lens_utils_preprocess_string (gmenu_tree_entry_get_name (entry));
1647 indexer->index_text(dum1, 5);
1648 g_free (dum1);
1649 }
1650 if (gmenu_tree_entry_get_comment (entry))
1651 {
1652- dum1 = unity_applications_lens_utils_preprocess_string (
1653- gmenu_tree_entry_get_comment (entry));
1654- indexer->index_text (dum1, 0);
1655- g_free (dum1);
1656- }
1657-
1658- /* Index the XDG categories */
1659- appman = unity_app_info_manager_get_default ();
1660- cats = unity_app_info_manager_get_categories (appman, // const return
1661- gmenu_tree_entry_get_desktop_file_id (entry),
1662- &len);
1663-
1664- /* Note: Wine apps and app launchers created with Alacarte commonly
1665- * don't have any category metadata, so they'll only show up under
1666- * All Applications */
1667- for (i = 0; i < len; i++)
1668- {
1669- dum1 = g_ascii_strdown (cats[i], -1);
1670- dum2 = g_strconcat ("AC", dum1, NULL);
1671- doc.add_term (dum2);
1672- g_free (dum1);
1673- g_free (dum2);
1674- }
1675-
1676- /* Index Keywords*/
1677- keywords = unity_app_info_manager_get_keywords (appman, // const return
1678- gmenu_tree_entry_get_desktop_file_id (entry),
1679- &len);
1680- for (i = 0; i < len; i++)
1681- {
1682- dum1 = unity_applications_lens_utils_preprocess_string (keywords[i]);
1683- indexer->index_text (dum1, 0);
1684- indexer->index_text (dum1, 0, "KW");
1685- g_free (dum1);
1686- }
1687-
1688-
1689- g_object_unref (appman);
1690-
1691- /* Always assume Type=Application for items in a menu... */
1692- doc.add_term ("ATapplication");
1693-
1694- /* Index application names */
1695- dum1 = (gchar *) gmenu_tree_entry_get_display_name (entry); // const
1696- dum2 = g_strconcat ("AA", dum1, NULL);
1697+ dum1 = unity_applications_lens_utils_preprocess_string (gmenu_tree_entry_get_comment (entry));
1698+ indexer->index_text (dum1, 0);
1699+ g_free (dum1);
1700+ }
1701+
1702+ /* Index the XDG categories */
1703+ appman = unity_app_info_manager_get_default ();
1704+ cats = unity_app_info_manager_get_categories (appman, // const return
1705+ gmenu_tree_entry_get_desktop_file_id (entry),
1706+ &len);
1707+
1708+ /* Note: Wine apps and app launchers created with Alacarte commonly
1709+ * don't have any category metadata, so they'll only show up under
1710+ * All Applications */
1711+ for (i = 0; i < len; i++)
1712+ {
1713+ dum1 = g_ascii_strdown (cats[i], -1);
1714+ dum2 = g_strconcat ("AC", dum1, NULL);
1715+ doc.add_term (dum2);
1716+ g_free (dum1);
1717+ g_free (dum2);
1718+ }
1719+
1720+ /* Index Keywords*/
1721+ keywords = unity_app_info_manager_get_keywords (appman, // const return
1722+ gmenu_tree_entry_get_desktop_file_id (entry),
1723+ &len);
1724+ for (i = 0; i < len; i++)
1725+ {
1726+ dum1 = unity_applications_lens_utils_preprocess_string (keywords[i]);
1727+ indexer->index_text (dum1, 0);
1728+ indexer->index_text (dum1, 0, "KW");
1729+ g_free (dum1);
1730+ }
1731+
1732+ g_object_unref (appman);
1733+
1734+ /* Always assume Type=Application for items in a menu... */
1735+ doc.add_term ("ATapplication");
1736+
1737+ /* Index application names */
1738+ dum1 = (gchar *) gmenu_tree_entry_get_display_name (entry); // const
1739+ dum2 = g_strconcat ("AA", dum1, NULL);
1740 doc.add_term (dum2);
1741 g_free (dum2);
1742-
1743+
1744 dum1 = (gchar *) gmenu_tree_entry_get_name (entry); // const
1745- dum2 = g_strconcat ("AA", dum1, NULL);
1746+ dum2 = g_strconcat ("AA", dum1, NULL);
1747 doc.add_term (dum2);
1748 g_free (dum2);
1749
1750 /* Index executable name, change - in _ for exec */
1751- dum1 = g_strdup (gmenu_tree_entry_get_exec (entry)); // alloc
1752+ dum1 = g_strdup (gmenu_tree_entry_get_exec (entry)); // alloc
1753 if (dum1) {
1754 dum2 = strstr (dum1, " "); // const
1755 dum2 == NULL ? : *dum2 = '\0'; // const
1756@@ -256,25 +252,23 @@
1757 doc.add_term (dum2);
1758 g_free (dum2);
1759 dum2 = g_strconcat ("XX", dum1, NULL); // alloc
1760- doc.add_term (dum2);
1761+ doc.add_term (dum2);
1762 g_free (dum2);
1763- g_free (dum1);
1764- }
1765-
1766+ g_free (dum1);
1767+ }
1768+
1769 db->add_document(doc);
1770- break;
1771- case GMENU_TREE_ITEM_SEPARATOR:
1772- case GMENU_TREE_ITEM_HEADER:
1773- case GMENU_TREE_ITEM_ALIAS:
1774- break;
1775- default:
1776- g_warning ("Unexpected GMenuTreeItemType %u",
1777- gmenu_tree_item_get_type (item));
1778- return;
1779+ break;
1780+ case GMENU_TREE_ITEM_SEPARATOR:
1781+ case GMENU_TREE_ITEM_HEADER:
1782+ case GMENU_TREE_ITEM_ALIAS:
1783+ break;
1784+ default:
1785+ g_warning ("Unexpected GMenuTreeItemType %u", gmenu_tree_item_get_type (item));
1786+ return;
1787 }
1788
1789 // Add the document to the database.
1790-
1791 }
1792
1793 /* Create a searcher that searches in a menu tree. The menu tree
1794@@ -289,16 +283,16 @@
1795 db = new Xapian::WritableDatabase ();
1796 searcher->db = db;
1797 searcher->db->add_database (Xapian::InMemory::open ());
1798-
1799+
1800 init_searcher (searcher);
1801-
1802+
1803 /* Index the menu recursively */
1804 Xapian::TermGenerator *indexer = new Xapian::TermGenerator ();
1805 index_menu_item (db, indexer,
1806 GMENU_TREE_ITEM (gmenu_tree_get_root_directory (menu)));
1807 delete indexer;
1808 db->flush ();
1809-
1810+
1811 return searcher;
1812 }
1813
1814@@ -325,14 +319,14 @@
1815 agent = g_strdup_printf("%s/software-center/software-center-agent.db",
1816 g_get_user_cache_dir());
1817 if (g_file_test(agent, G_FILE_TEST_IS_DIR))
1818- searcher->db->add_database (Xapian::Database (agent));
1819+ searcher->db->add_database (Xapian::Database (agent));
1820 g_free(agent);
1821 } catch(const Xapian::Error &error) {
1822 cerr << "Error loading agent indexes: " << error.get_msg() << endl;
1823 }
1824
1825 init_searcher (searcher);
1826-
1827+
1828 return searcher;
1829 }
1830
1831@@ -373,13 +367,17 @@
1832 static void _free_package_info (gpointer pkg)
1833 {
1834 UnityPackageInfo *pkginfo = (UnityPackageInfo*) pkg;
1835+ unity_package_package_info_free (pkginfo);
1836+}
1837
1838+void unity_package_package_info_free (UnityPackageInfo *pkginfo)
1839+{
1840 g_free (pkginfo->package_name);
1841 g_free (pkginfo->application_name);
1842 g_free (pkginfo->desktop_file);
1843 g_free (pkginfo->icon);
1844
1845- g_slice_free (UnityPackageInfo, pkg);
1846+ g_slice_free (UnityPackageInfo, pkginfo);
1847 }
1848
1849 UnityPackageSearchResult*
1850@@ -432,7 +430,7 @@
1851 searcher->enquire->set_sort_by_relevance ();
1852 break;
1853 }
1854-
1855+
1856 result = g_slice_new0 (UnityPackageSearchResult);
1857 try
1858 {
1859@@ -440,29 +438,25 @@
1860 searcher->enquire->set_collapse_key(XAPIAN_VALUE_DESKTOP_FILE);
1861 max_hits = (max_hits != 0 ? max_hits : searcher->db->get_doccount ());
1862 searcher->enquire->set_query(query);
1863- Xapian::MSet matches =
1864- searcher->enquire->get_mset(0, max_hits);
1865+ Xapian::MSet matches = searcher->enquire->get_mset(0, max_hits);
1866
1867 // Retrieve the results, note that we build the result->results
1868 // list in reverse order and then reverse it before we return it
1869 result->num_hits = matches.get_matches_estimated ();
1870- for (Xapian::MSetIterator i = matches.begin();
1871- i != matches.end();
1872- ++i)
1873- {
1874- try
1875- {
1876- Xapian::Document doc = i.get_document();
1877- UnityPackageInfo *pkginfo = _pkginfo_from_document (doc);
1878- pkginfo->relevancy = i.get_percent ();
1879- result->results = g_slist_prepend (result->results, pkginfo);
1880- }
1881- catch (Xapian::Error e)
1882- {
1883- g_warning ("Unable to read document from result set: %s",
1884- e.get_msg().c_str());
1885- }
1886- }
1887+ for (Xapian::MSetIterator i = matches.begin(); i != matches.end(); ++i)
1888+ {
1889+ try
1890+ {
1891+ Xapian::Document doc = i.get_document();
1892+ UnityPackageInfo *pkginfo = _pkginfo_from_document (doc);
1893+ pkginfo->relevancy = i.get_percent ();
1894+ result->results = g_slist_prepend (result->results, pkginfo);
1895+ }
1896+ catch (Xapian::Error e)
1897+ {
1898+ g_warning ("Unable to read document from result set: %s", e.get_msg().c_str());
1899+ }
1900+ }
1901
1902 result->results = g_slist_reverse (result->results);
1903 }
1904@@ -470,7 +464,7 @@
1905 {
1906 g_warning ("Error running query '%s': %s", search_string, e.get_msg().c_str());
1907 }
1908-
1909+
1910 return result;
1911 }
1912
1913@@ -486,79 +480,78 @@
1914 g_return_val_if_fail (searcher != NULL, NULL);
1915
1916 result = g_slice_new0 (UnityPackageSearchResult);
1917- result->num_hits = n_apps;
1918+ result->num_hits = n_apps;
1919 lastdocid = searcher->db->get_lastdocid ();
1920-
1921+
1922 /* Since we really just pick random apps we may end up with dupes */
1923 unique = g_hash_table_new (g_str_hash, g_str_equal);
1924-
1925+
1926 /* When looking for random apps we check up to twice the number of
1927 * requested apps in order to try and avoid dupes. This is a sloppy
1928 * check, but works well enough in practice */
1929 if (filter_query == NULL)
1930+ {
1931+ g_debug ("RANDOM");
1932+ for (i = 0, n_unique = 0; i < n_apps*2, n_unique < n_apps; i++)
1933 {
1934- g_debug ("RANDOM");
1935- for (i = 0, n_unique = 0; i < n_apps*2, n_unique < n_apps; i++)
1936- {
1937- Xapian::Document doc;
1938- try
1939- {
1940- doc = searcher->db->get_document (
1941- g_rand_int_range (searcher->random, 1, lastdocid));
1942- UnityPackageInfo *pkginfo = _pkginfo_from_document (doc);
1943- if (g_hash_table_lookup_extended (unique, pkginfo->package_name, NULL, NULL))
1944- {
1945- _free_package_info (pkginfo);
1946- }
1947- else
1948- {
1949- g_hash_table_insert (unique, pkginfo->package_name, NULL);
1950- result->results = g_slist_prepend (result->results, pkginfo);
1951- n_unique++;
1952- }
1953- }
1954- catch (Xapian::Error e)
1955- {
1956- g_warning ("Error getting random apps: %s", e.get_msg().c_str());
1957- continue;
1958- }
1959- }
1960+ Xapian::Document doc;
1961+ try
1962+ {
1963+ doc = searcher->db->get_document (
1964+ g_rand_int_range (searcher->random, 1, lastdocid));
1965+ UnityPackageInfo *pkginfo = _pkginfo_from_document (doc);
1966+ if (g_hash_table_lookup_extended (unique, pkginfo->package_name, NULL, NULL))
1967+ {
1968+ _free_package_info (pkginfo);
1969+ }
1970+ else
1971+ {
1972+ g_hash_table_insert (unique, pkginfo->package_name, NULL);
1973+ result->results = g_slist_prepend (result->results, pkginfo);
1974+ n_unique++;
1975+ }
1976+ }
1977+ catch (Xapian::Error e)
1978+ {
1979+ g_warning ("Error getting random apps: %s", e.get_msg().c_str());
1980+ continue;
1981+ }
1982 }
1983+ }
1984 else
1985- {
1986- g_debug ("FILTER %s", filter_query);
1987- Xapian::Query query;
1988- try
1989- {
1990- query = searcher->query_parser->parse_query (filter_query, QUERY_PARSER_FILTER_FLAGS);
1991- searcher->enquire->set_sort_by_relevance ();
1992- searcher->enquire->set_query(query);
1993- Xapian::MSet matches = searcher->enquire->get_mset(0, searcher->db->get_doccount ());
1994- for (i = 0, n_unique = 0; i < n_apps*4, n_unique < n_apps; i++)
1995- {
1996- docid = g_rand_int_range (searcher->random, 0, matches.size ());
1997- Xapian::MSetIterator iter = matches[docid];
1998- Xapian::Document doc = iter.get_document ();
1999- UnityPackageInfo *pkginfo = _pkginfo_from_document (doc);
2000- if (g_hash_table_lookup_extended (unique, pkginfo->package_name, NULL, NULL))
2001- {
2002- _free_package_info (pkginfo);
2003- }
2004- else
2005- {
2006- g_hash_table_insert (unique, pkginfo->package_name, NULL);
2007- result->results = g_slist_prepend (result->results, pkginfo);
2008- n_unique++;
2009- }
2010- }
2011- }
2012- catch (Xapian::Error e)
2013- {
2014- g_debug ("Error getting random apps for query '%s': %s",
2015- filter_query, e.get_msg().c_str());
2016- return g_slice_new0 (UnityPackageSearchResult);
2017- }
2018- }
2019+ {
2020+ g_debug ("FILTER %s", filter_query);
2021+ Xapian::Query query;
2022+ try
2023+ {
2024+ query = searcher->query_parser->parse_query (filter_query, QUERY_PARSER_FILTER_FLAGS);
2025+ searcher->enquire->set_sort_by_relevance ();
2026+ searcher->enquire->set_query(query);
2027+ Xapian::MSet matches = searcher->enquire->get_mset(0, searcher->db->get_doccount ());
2028+ for (i = 0, n_unique = 0; i < n_apps*4, n_unique < n_apps; i++)
2029+ {
2030+ docid = g_rand_int_range (searcher->random, 0, matches.size ());
2031+ Xapian::MSetIterator iter = matches[docid];
2032+ Xapian::Document doc = iter.get_document ();
2033+ UnityPackageInfo *pkginfo = _pkginfo_from_document (doc);
2034+ if (g_hash_table_lookup_extended (unique, pkginfo->package_name, NULL, NULL))
2035+ {
2036+ _free_package_info (pkginfo);
2037+ }
2038+ else
2039+ {
2040+ g_hash_table_insert (unique, pkginfo->package_name, NULL);
2041+ result->results = g_slist_prepend (result->results, pkginfo);
2042+ n_unique++;
2043+ }
2044+ }
2045+ }
2046+ catch (Xapian::Error e)
2047+ {
2048+ g_debug ("Error getting random apps for query '%s': %s", filter_query, e.get_msg().c_str());
2049+ return g_slice_new0 (UnityPackageSearchResult);
2050+ }
2051+ }
2052
2053 g_hash_table_unref (unique);
2054
2055@@ -566,6 +559,38 @@
2056 return result;
2057 }
2058
2059+UnityPackageInfo *
2060+unity_package_searcher_get_by_desktop_file (UnityPackageSearcher *searcher, const gchar *desktop_file)
2061+{
2062+ g_return_val_if_fail (searcher != NULL, NULL);
2063+
2064+ UnityPackageInfo *pkginfo = NULL;
2065+
2066+ Xapian::PostingIterator it = searcher->db->postlist_begin ("");
2067+ Xapian::PostingIterator end_it = searcher->db->postlist_end ("");
2068+
2069+ const string query = desktop_file;
2070+
2071+ while (it != end_it)
2072+ {
2073+ Xapian::Document doc = searcher->db->get_document (*it);
2074+ string value = doc.get_value (XAPIAN_VALUE_DESKTOP_FILE);
2075+
2076+ size_t sep = value.find (':');
2077+ if (sep != string::npos)
2078+ {
2079+ if (value.compare (sep+1, value.size() - sep, query) == 0)
2080+ {
2081+ pkginfo = _pkginfo_from_document (doc);
2082+ return pkginfo;
2083+ }
2084+ }
2085+ ++it;
2086+ }
2087+
2088+ return pkginfo;
2089+}
2090+
2091 void
2092 unity_package_search_result_free (UnityPackageSearchResult *result)
2093 {
2094@@ -574,4 +599,3 @@
2095 g_slist_free_full (result->results, _free_package_info);
2096 g_slice_free (UnityPackageSearchResult, result);
2097 }
2098-
2099
2100=== modified file 'src/unity-package-search.h'
2101--- src/unity-package-search.h 2012-03-09 17:44:30 +0000
2102+++ src/unity-package-search.h 2012-08-08 16:35:21 +0000
2103@@ -36,23 +36,23 @@
2104
2105 typedef struct
2106 {
2107- GSList *results;
2108- gint num_hits;
2109+ GSList *results;
2110+ gint num_hits;
2111 } UnityPackageSearchResult;
2112
2113 typedef struct
2114 {
2115- gchar *package_name;
2116- gchar *application_name;
2117- gchar *desktop_file;
2118- gchar *icon;
2119- gint relevancy;
2120+ gchar *package_name;
2121+ gchar *application_name;
2122+ gchar *desktop_file;
2123+ gchar *icon;
2124+ gint relevancy;
2125 } UnityPackageInfo;
2126
2127 #ifdef __cplusplus
2128 extern "C" {
2129 #endif
2130-
2131+
2132 UnityPackageSearcher* unity_package_searcher_new ();
2133
2134 UnityPackageSearcher* unity_package_searcher_new_for_menu (GMenuTree *menu);
2135@@ -71,7 +71,10 @@
2136
2137 void unity_package_search_result_free (UnityPackageSearchResult *result);
2138
2139+void unity_package_package_info_free (UnityPackageInfo *pkginfo);
2140+
2141+UnityPackageInfo* unity_package_searcher_get_by_desktop_file (UnityPackageSearcher *searcher, const gchar *desktop_file);
2142+
2143 #ifdef __cplusplus
2144 }
2145 #endif
2146-
2147
2148=== modified file 'vapi/unity-package-search.vapi'
2149--- vapi/unity-package-search.vapi 2012-03-09 17:44:30 +0000
2150+++ vapi/unity-package-search.vapi 2012-08-08 16:35:21 +0000
2151@@ -27,6 +27,7 @@
2152 public Searcher.for_menu(GMenu.Tree menu);
2153 public SearchResult search (string search_string, uint max_hits, Unity.Package.SearchType search_type, Unity.Package.Sort sort);
2154 public SearchResult get_random_apps (string? filter_query, uint n_apps);
2155+ public PackageInfo? get_by_desktop_file (string desktop_file);
2156 }
2157
2158 [Compact]
2159@@ -37,7 +38,7 @@
2160 }
2161
2162 [Compact]
2163- [CCode (cname = "UnityPackageInfo", cheader_filename = "unity-package-search.h")]
2164+ [CCode (cname = "UnityPackageInfo", free_function = "unity_package_package_info_free", cheader_filename = "unity-package-search.h")]
2165 public class PackageInfo {
2166 public string package_name;
2167 public string application_name;

Subscribers

People subscribed via source and target branches