Merge lp:~jamesh/unity-lens-friends/use-scope-loader into lp:unity-lens-friends

Proposed by James Henstridge
Status: Merged
Approved by: Ken VanDine
Approved revision: 76
Merged at revision: 66
Proposed branch: lp:~jamesh/unity-lens-friends/use-scope-loader
Merge into: lp:unity-lens-friends
Diff against target: 848 lines (+260/-280)
8 files modified
configure.ac (+2/-1)
data/Makefile.am (+9/-5)
data/social.scope.in.in (+3/-0)
data/unity-lens-friends.service.in (+1/-1)
debian/rules (+1/-0)
src/Makefile.am (+16/-13)
src/daemon.vala (+210/-179)
src/main.vala (+18/-81)
To merge this branch: bzr merge lp:~jamesh/unity-lens-friends/use-scope-loader
Reviewer Review Type Date Requested Status
Ken VanDine Approve
PS Jenkins bot (community) continuous-integration Approve
Michal Hruby (community) Needs Fixing
Review via email: mp+173878@code.launchpad.net

Commit message

Convert the scope over to the new Unity.AbstractScope API, and run it through the new scope loader.

Description of the change

Switch scope over to using the new scope loader. This will allow the friends scope to be run in-process with other scopes.

To achieve this, I needed to port the scope over to the new Unity.AbstractScope API. In doing so, I've lost two features:

 * no ability to toggle scope visibility.
 * no ability to do live update of results.

(These are not available in the new API).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ken VanDine (ken-vandine) wrote :

You can go ahead and remove the commented out code for the features that aren't available in the API, assuming those features aren't planned in the near future. Please bump the libunity depends to reflect current the required version?

review: Needs Fixing
Revision history for this message
Michal Hruby (mhr3) wrote :

Could you optimize the make_preview() method? It's currently linearly walking the entire model, and since we have extensible schema now it should be easy to save a key which will allow sub-linear search (afterall there's an in-memory model sorted by timestamp). Also it's reading fields from the model that are already present in the ScopeResult.

review: Needs Fixing
71. By James Henstridge

Remove commented out code that was incompatible with new scope API.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
James Henstridge (jamesh) wrote :

mhr3: I had started on that method originally (as visible in the branch history).

I had difficulty making it work together with the handling of the "like"/"unlike" preview actions, where it responds by asking for a new preview. I found I couldn't rely on the state in the Unity.ScopeResult on repeated activations, so reverted back to the original preview logic.

72. By James Henstridge

Merge from trunk, fixing conflict

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ken VanDine (ken-vandine) wrote :

Is it still necessary to ask for a new preview to see the button state change? I always felt request a new preview just to change the text of the action to be a hack.

Revision history for this message
Ken VanDine (ken-vandine) wrote :

the Module set in social.scope has the wrong path. The .so is getting installed in libdir (multiarch) but the Module path is set to /usr/lib/unity/unity-scope-friends.so.

Can you either update it to install the .so in the expected path or fix the .scope file to point to the correct location.

review: Needs Fixing
73. By James Henstridge

Reinstate result invalidation code now that it is suported by libunity's
new API.

74. By James Henstridge

Substitute @libdir@ in the scope file, so the paths are correct when
configured with a custom library dir (e.g. for multi-arch).

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ken VanDine (ken-vandine) wrote :

It's not creating social.scope, I think you need:

-scope_DATA = $(scope_in_files:.scope.in.in=.scope)
+scope_DATA = $(scope_in_in_files:.scope.in.in=.scope)

Also it is installing the .la in the package as well.

review: Needs Fixing
75. By James Henstridge

Fix up definition of scope_DATA.

76. By James Henstridge

Don't include the .la file in the package.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ken VanDine (ken-vandine) wrote :

Looks good, thanks!

review: Approve

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 2013-07-11 16:35:41 +0000
3+++ configure.ac 2013-07-26 10:35:05 +0000
4@@ -2,6 +2,8 @@
5 AM_INIT_AUTOMAKE
6 AC_CONFIG_HEADERS([config.h])
7
8+LT_INIT
9+
10 AM_PROG_VALAC([0.17])
11 AC_PROG_CC
12
13@@ -55,7 +57,6 @@
14 Makefile
15 src/Makefile
16 data/Makefile
17- data/social.scope.in
18 data/icons/Makefile
19 data/icons/hicolor/Makefile
20 data/icons/hicolor/scalable/places/Makefile
21
22=== modified file 'data/Makefile.am'
23--- data/Makefile.am 2013-03-22 07:50:01 +0000
24+++ data/Makefile.am 2013-07-26 10:35:05 +0000
25@@ -6,11 +6,14 @@
26 dbus_services_DATA = $(service_in_files:.service.in=.service)
27
28 %.service: %.service.in
29- sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
30+ sed -e "s|\@bindir\@|$(bindir)|" $< > $@
31
32-scope_in_files = social.scope.in
33+scope_in_in_files = social.scope.in.in
34 scopedir = $(SCOPESDIR)
35-scope_DATA = $(scope_in_files:.scope.in=.scope)
36+scope_DATA = $(scope_in_in_files:.scope.in.in=.scope)
37+
38+%.scope.in: %.scope.in.in
39+ sed -e "s|\@prefix\@|$(prefix)|" -e "s|\@libdir\@|$(libdir)|" $< > $@
40
41 @INTLTOOL_SCOPE_RULE@
42
43@@ -18,9 +21,10 @@
44
45 EXTRA_DIST = \
46 $(scope_DATA) \
47- $(scope_in_files) \
48+ $(scope_in_in_files) \
49 $(service_in_files)
50
51 CLEANFILES = \
52 $(dbus_services_DATA) \
53- $(scope_DATA)
54+ $(scope_DATA) \
55+ $(scope_in_in_files:.scope.in.in=.scope.in)
56
57=== modified file 'data/social.scope.in.in'
58--- data/social.scope.in.in 2013-05-28 13:51:14 +0000
59+++ data/social.scope.in.in 2013-07-26 10:35:05 +0000
60@@ -7,6 +7,9 @@
61 _SearchHint=Search social network messages
62 Shortcut=g
63 Type=social
64+RequiredMetadata=account_id[t];status_id[s]
65+Module=@libdir@/unity/unity-scope-friends.so
66+ModuleType=C
67
68 #
69 # Make translation work with Ubuntu's special handling of keyfiles
70
71=== modified file 'data/unity-lens-friends.service.in'
72--- data/unity-lens-friends.service.in 2013-03-22 16:41:04 +0000
73+++ data/unity-lens-friends.service.in 2013-07-26 10:35:05 +0000
74@@ -1,3 +1,3 @@
75 [D-BUS Service]
76 Name=com.canonical.Unity.Scope.Friends
77-Exec=@libexecdir@/unity-lens-friends
78+Exec=@bindir@/unity-scope-loader social.scope
79
80=== modified file 'debian/rules'
81--- debian/rules 2013-04-22 11:04:16 +0000
82+++ debian/rules 2013-07-26 10:35:05 +0000
83@@ -11,3 +11,4 @@
84
85 override_dh_install:
86 dh_install --fail-missing
87+ rm -f debian/unity-lens-friends/usr/lib/*/unity/*.la
88
89=== modified file 'src/Makefile.am'
90--- src/Makefile.am 2013-07-11 17:07:55 +0000
91+++ src/Makefile.am 2013-07-26 10:35:05 +0000
92@@ -4,10 +4,12 @@
93
94 DATADIR = $(datadir)
95
96-libexec_PROGRAMS = \
97- unity-lens-friends
98-
99-unity_lens_friends_CPPFLAGS = \
100+unitylibdir = $(libdir)/unity
101+
102+unitylib_LTLIBRARIES = \
103+ unity-scope-friends.la
104+
105+AM_CPPFLAGS = \
106 -DDATADIR=\"$(DATADIR)\" \
107 -DPREFIX=\"$(PREFIX)\" \
108 -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
109@@ -15,7 +17,7 @@
110 $(BASE_CFLAGS) \
111 -I$(top_srcdir)/src
112
113-unity_lens_friends_VALAFLAGS = \
114+unity_scope_friends_la_VALAFLAGS = \
115 -C \
116 -X -I$(top_srcdir)/src \
117 --vapidir $(top_srcdir)/vapi \
118@@ -29,31 +31,32 @@
119 --basedir=. \
120 $(MAINTAINER_VALAFLAGS)
121
122-unity_lens_friends_LDADD = \
123+unity_scope_friends_la_LDFLAGS = -shared -module -avoid-version -no-undefined
124+unity_scope_friends_la_LIBADD = \
125 $(BASE_LIBS) \
126 $(NULL)
127
128-unity_lens_friends_VALASOURCES = \
129+unity_scope_friends_la_VALASOURCES = \
130 daemon.vala \
131 main.vala \
132 schemas.vala \
133 $(NULL)
134
135-unity_lens_friends_SOURCES = \
136- $(unity_lens_friends_VALASOURCES:.vala=.c) \
137+unity_scope_friends_la_SOURCES = \
138+ $(unity_scope_friends_la_VALASOURCES:.vala=.c) \
139 $(NULL)
140
141-BUILT_SOURCES += unity_lens_friends.vala.stamp \
142+BUILT_SOURCES += unity_scope_friends.vala.stamp \
143 $(NULL)
144
145 EXTRA_DIST += \
146- unity_lens_friends.vala.stamp \
147+ unity_scope_friends.vala.stamp \
148 $(unity_lens_friends_VALASOURCES) \
149 $(BUILT_SOURCES) \
150 $(NULL)
151
152-unity_lens_friends.vala.stamp: $(unity_lens_friends_VALASOURCES)
153- $(AM_V_GEN) $(VALAC) $(unity_lens_friends_VALAFLAGS) $^
154+unity_scope_friends.vala.stamp: $(unity_scope_friends_la_VALASOURCES)
155+ $(AM_V_GEN) $(VALAC) $(unity_scope_friends_la_VALAFLAGS) $^
156 touch unity_lens_friends.vala.stamp
157
158 CLEANFILES += \
159
160=== modified file 'src/daemon.vala'
161--- src/daemon.vala 2013-07-11 16:57:58 +0000
162+++ src/daemon.vala 2013-07-26 10:35:05 +0000
163@@ -1,5 +1,5 @@
164 /*
165- * Copyright (C) 2011 Canonical Ltd.
166+ * Copyright (C) 2011-13 Canonical Ltd.
167 *
168 * This program is free software: you can redistribute it and/or modify it
169 * under the terms of the GNU General Public License version 3, as published
170@@ -30,14 +30,13 @@
171 const string STRIP_LINKS = """</?a[^>]*>""";
172
173 /**
174- * The Daemon class implements all of the logic for the place.
175+ * The Scope class implements all of the logic for the place.
176 *
177 */
178- public class Daemon : GLib.Object
179+ public class FriendsScope : Unity.AbstractScope
180 {
181- private Unity.DeprecatedScope scope;
182- private Unity.PreferencesManager preferences = Unity.PreferencesManager.get_default ();
183- private unowned Dee.ResourceManager resources;
184+ private Unity.FilterSet? filters;
185+
186 private Dee.Model? _model = null;
187 private Dee.SharedModel? _streams_model = null;
188 private Dee.Filter _sort_filter;
189@@ -52,23 +51,9 @@
190 private Ag.Manager _account_manager;
191 private bool _has_accounts = false;
192 private HashMap<string,Variant> featureMap;
193- private Variant empty_asv;
194
195- construct
196+ public FriendsScope ()
197 {
198- empty_asv = new Variant.array (VariantType.VARDICT.element (), {});
199- scope = new Unity.DeprecatedScope("/com/canonical/unity/scope/friends", "friends.scope");
200- scope.search_in_global = false;
201- scope.search_hint = _("Search social network messages");
202- scope.visible = false;
203- try
204- {
205- scope.export ();
206- } catch (GLib.IOError e)
207- {
208- warning ("failed to export lens: %s", e.message);
209- }
210-
211 featureMap = new HashMap<string,Variant> ();
212
213 // Check for accounts
214@@ -81,11 +66,10 @@
215 if (accts.length () > 0 && !_has_accounts)
216 {
217 _has_accounts = true;
218- setup ();
219+ setup_friends ();
220 }
221 else if (accts.length () == 0)
222 {
223- scope.visible = false;
224 _has_accounts = false;
225 }
226 else
227@@ -95,28 +79,14 @@
228 });
229 }
230
231-
232- void setup ()
233- {
234- setup_friends ();
235-
236- scope.visible = true;
237- scope.preview_uri.connect (preview);
238-
239- /* Listen for filter changes */
240- scope.generate_search_key.connect ((lens_search) =>
241- {
242- return lens_search.search_string.strip ();
243- });
244- scope.search_changed.connect ((search, search_type, cancellable) =>
245- {
246- dispatch_search.begin (search, search_type, cancellable);
247- });
248-
249- preferences.notify["remote-content-search"].connect ((obj, pspec) =>
250- {
251- scope.queue_search_changed (SearchType.DEFAULT);
252- });
253+ public override string get_group_name ()
254+ {
255+ return BUS_NAME;
256+ }
257+
258+ public override string get_unique_name ()
259+ {
260+ return "/com/canonical/unity/scope/friends";
261 }
262
263 private void map_account_features ()
264@@ -142,15 +112,12 @@
265 if (accts.length() > 0)
266 {
267 _has_accounts = true;
268- setup ();
269+ setup_friends ();
270 }
271 }
272
273 private void setup_friends ()
274 {
275- populate_categories ();
276- populate_filters ();
277-
278 Intl.setlocale(LocaleCategory.COLLATE, "C");
279
280 _streams_model = new Dee.SharedModel ("com.canonical.Friends.Streams");
281@@ -159,13 +126,13 @@
282 {
283 _streams_model.notify["synchronized"].connect (on_synchronized);
284 _streams_model.row_added.connect ((source) => {
285- scope.queue_search_changed (SearchType.DEFAULT);
286+ results_invalidated (SearchType.DEFAULT);
287 });
288 _streams_model.row_removed.connect ((source) => {
289- scope.queue_search_changed (SearchType.DEFAULT);
290+ results_invalidated (SearchType.DEFAULT);
291 });
292 _streams_model.row_changed.connect ((source) => {
293- scope.queue_search_changed (SearchType.DEFAULT);
294+ results_invalidated (SearchType.DEFAULT);
295 });
296 }
297 }
298@@ -203,10 +170,12 @@
299 _index = new Dee.TreeIndex (_model, _analyzer, reader);
300 }
301
302- private void populate_filters ()
303+ public override Unity.FilterSet get_filters ()
304 {
305- var filters = new Unity.FilterSet ();
306+ if (filters != null)
307+ return filters;
308
309+ filters = new Unity.FilterSet ();
310 /* Stream filter */
311 {
312 var filter = new CheckOptionFilter ("stream", _("Stream"));
313@@ -230,18 +199,17 @@
314 filters.add (filter);
315 }
316
317- scope.filters = filters;
318-
319 _account_manager.account_created.connect ((id) => {
320 debug ("ACCOUNT ADDED");
321 var _acct = _account_manager.get_account ((Ag.AccountId)id);
322- var filter = scope.filters.get_filter_by_id ("account_id") as CheckOptionFilter;
323+ var filter = filters.get_filter_by_id ("account_id") as CheckOptionFilter;
324 create_account_filter_option (filter, _acct);
325 map_account_features ();
326 });
327
328 /* FIXME: we need a way to remove an option or remove and re-add the
329 * filter on account deletion */
330+ return filters;
331 }
332
333 private Unity.CheckOptionFilter create_account_filter ()
334@@ -274,8 +242,7 @@
335 filter.add_option (_acct.id.to_string(), _provider + "/" + _name, icon);
336 }
337
338-
339- private void populate_categories ()
340+ public override Unity.CategorySet get_categories ()
341 {
342 var categories = new Unity.CategorySet ();
343 Icon icon;
344@@ -315,51 +282,33 @@
345 cat.content_type = CategoryContentType.SOCIAL;
346 categories.add (cat);
347
348- scope.categories = categories;
349- }
350-
351-
352- private bool is_empty_search (DeprecatedScopeSearch search)
353- {
354- return search.search_string.strip () == "";
355- }
356-
357- private async void dispatch_search (DeprecatedScopeSearch search, SearchType search_type, GLib.Cancellable cancellable)
358- {
359- /**
360- * only perform the request if the user has not disabled
361- * online/commercial suggestions. That will hide the category as well.
362- */
363- if (preferences.remote_content_search != Unity.PreferencesManager.RemoteContent.ALL)
364- {
365- search.results_model.clear ();
366- search.finished ();
367- return;
368- }
369-
370- // FIXME: no results for home screen of the dash?
371- if (search_type == SearchType.GLOBAL && is_empty_search (search))
372- {
373- search.finished ();
374- return;
375- }
376-
377- update_results_model (search, null);
378- debug ("%u results", search.results_model.get_n_rows ());
379- search.finished ();
380- }
381-
382- /* Generic method to update a results model. We do it like this to minimize
383- * code dup between updating the global- and the entry results model */
384- private void update_results_model (DeprecatedScopeSearch search,
385- Categories? category)
386- {
387- debug ("%u TOTAL ROWS", _model.get_n_rows ());
388+ return categories;
389+ }
390+
391+ public override Unity.Schema get_schema ()
392+ {
393+ var schema = new Unity.Schema ();
394+ schema.add_field ("account_id", "t", Unity.Schema.FieldType.REQUIRED);
395+ schema.add_field ("status_id", "s", Unity.Schema.FieldType.REQUIRED);
396+ return schema;
397+ }
398+
399+ public override string normalize_search_query (string search_query)
400+ {
401+ return search_query.strip ();
402+ }
403+
404+ public override Unity.ScopeSearchBase create_search_for_query (Unity.SearchContext search_context)
405+ {
406+ return new FriendsSearch (this, search_context);
407+ }
408+
409+ public void perform_search (Unity.SearchContext context)
410+ {
411 unowned Dee.ModelIter iter, end;
412
413- var results_model = search.results_model;
414 var stream_ids = new Gee.ArrayList<string> ();
415- var filter = search.get_filter("stream") as CheckOptionFilter;
416+ var filter = context.filter_state.get_filter_by_id ("stream") as CheckOptionFilter;
417 if (filter.filtering)
418 {
419 foreach (Unity.FilterOption option in filter.options)
420@@ -372,7 +321,7 @@
421 }
422
423 var account_ids = new Gee.ArrayList<string> ();
424- filter = search.get_filter ("account_id") as CheckOptionFilter;
425+ filter = context.filter_state.get_filter_by_id ("account_id") as CheckOptionFilter;
426 if (filter.filtering)
427 {
428 foreach (Unity.FilterOption option in filter.options)
429@@ -384,8 +333,6 @@
430 }
431 }
432
433- results_model.clear ();
434-
435 if (_model == null)
436 setup_friends ();
437
438@@ -397,7 +344,7 @@
439
440 var term_list = Object.new (typeof (Dee.TermList)) as Dee.TermList;
441 // search only the folded terms, FIXME: is that a good idea?
442- _analyzer.tokenize (_ascii_filter.apply (search.search_string), term_list);
443+ _analyzer.tokenize (_ascii_filter.apply (context.search_query), term_list);
444
445 var matches = new Sequence<Dee.ModelIter> ();
446 for (uint i = 0; i < term_list.num_terms (); i++)
447@@ -438,7 +385,7 @@
448
449 if (matches_filters (_model, iter, stream_ids, account_ids))
450 {
451- add_result (_model, iter, results_model);
452+ add_result (_model, iter, context.result_set);
453 }
454
455 match_iter = match_iter.next ();
456@@ -451,7 +398,7 @@
457 {
458 if (matches_filters (_model, iter, stream_ids, account_ids))
459 {
460- add_result (_model, iter, results_model);
461+ add_result (_model, iter, context.result_set);
462 }
463 iter = _model.next (iter);
464 }
465@@ -484,51 +431,51 @@
466 return account_match && stream_match;
467 }
468
469- private void add_result (Dee.Model model, Dee.ModelIter iter, Dee.Model results_model)
470+ private void add_result (Dee.Model model, Dee.ModelIter iter, Unity.ResultSet result_set)
471 {
472- Categories group = Categories.MESSAGES;
473- string _img_uri = null;
474+ var result = Unity.ScopeResult ();
475
476- unowned string stream_id =
477+ result.uri = model.get_string (iter, StreamModelColumn.URL);
478+ unowned string stream_id =
479 model.get_string (iter, StreamModelColumn.STREAM);
480- switch (stream_id)
481- {
482- case "messages": group = Categories.MESSAGES; break;
483- case "mentions":
484- if (model.get_bool (iter, StreamModelColumn.FROM_ME))
485- group = Categories.MESSAGES;
486- else
487- group = Categories.REPLIES;
488- break;
489- case "images": group = Categories.IMAGES; break;
490- case "videos": group = Categories.VIDEOS; break;
491- case "links": group = Categories.LINKS; break;
492- case "private": group = Categories.PRIVATE; break;
493- case "public": group = Categories.PUBLIC; break;
494- }
495-
496- string uri = model.get_string (iter, StreamModelColumn.URL);
497 string _icon_uri = model.get_string (iter, StreamModelColumn.ICON_URI);
498 if (stream_id == "images")
499- _img_uri = model.get_string (iter, StreamModelColumn.LINK_PICTURE);
500+ result.icon_hint = model.get_string (iter, StreamModelColumn.LINK_PICTURE);
501 else if (stream_id == "videos")
502 {
503- _img_uri = model.get_string (iter, StreamModelColumn.LINK_PICTURE);
504- if (_img_uri.length < 1)
505- _img_uri = get_avatar_path (_icon_uri);
506+ result.icon_hint = model.get_string (iter, StreamModelColumn.LINK_PICTURE);
507+ if (result.icon_hint.length < 1)
508+ result.icon_hint = get_avatar_path (_icon_uri);
509 }
510 else
511- _img_uri = get_avatar_path (_icon_uri);
512-
513- results_model.append (uri,
514- _img_uri,
515- group,
516- ResultType.PERSONAL,
517- "text/html",
518- GLib.Markup.escape_text (_model.get_string(iter, StreamModelColumn.SENDER)),
519- sanitize_message (_model.get_string(iter, StreamModelColumn.MESSAGE)),
520- uri,
521- empty_asv);
522+ result.icon_hint = get_avatar_path (_icon_uri);
523+
524+ result.category = Categories.MESSAGES;
525+ switch (stream_id)
526+ {
527+ case "messages": result.category = Categories.MESSAGES; break;
528+ case "mentions":
529+ if (model.get_bool (iter, StreamModelColumn.FROM_ME))
530+ result.category = Categories.MESSAGES;
531+ else
532+ result.category = Categories.REPLIES;
533+ break;
534+ case "images": result.category = Categories.IMAGES; break;
535+ case "videos": result.category = Categories.VIDEOS; break;
536+ case "links": result.category = Categories.LINKS; break;
537+ case "private": result.category = Categories.PRIVATE; break;
538+ case "public": result.category = Categories.PUBLIC; break;
539+ }
540+ result.result_type = ResultType.PERSONAL;
541+ result.mimetype = "text/html";
542+ result.title = GLib.Markup.escape_text (_model.get_string(iter, StreamModelColumn.SENDER));
543+ result.comment = sanitize_message (_model.get_string(iter, StreamModelColumn.MESSAGE));
544+ result.dnd_uri = result.uri;
545+ result.metadata = new HashTable<string,Variant> (str_hash, str_equal);
546+ result.metadata["account_id"] = new Variant.uint64 (model.get_uint64(iter, StreamModelColumn.ACCOUNT_ID));
547+ result.metadata["status_id"] = new Variant.string (model.get_string (iter, StreamModelColumn.MESSAGE_ID));
548+
549+ result_set.add_result (result);
550 }
551
552 private string get_avatar_path (string uri)
553@@ -552,7 +499,12 @@
554 return _avatar_cache_image;
555 }
556
557- public Unity.Preview preview (string uri)
558+ public override Unity.ResultPreviewer create_previewer (Unity.ScopeResult result, Unity.SearchMetadata metadata)
559+ {
560+ return new FriendsResultPreviewer (this, result, metadata);
561+ }
562+
563+ public Unity.Preview make_preview (string uri)
564 {
565 debug ("Previewing: %s", uri);
566 Unity.SocialPreview preview = null;
567@@ -632,21 +584,6 @@
568 var view_action = new Unity.PreviewAction ("view", _("View"), icon);
569 preview.add_action (view_action);
570
571- view_action.activated.connect ((source) => {
572- try
573- {
574- if (GLib.AppInfo.launch_default_for_uri (uri, null))
575- {
576- return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH);
577- }
578- }
579- catch (GLib.Error e)
580- {
581- warning ("Failed to launch default application for uri '%s': %s", uri, e.message);
582- }
583- return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED);
584- });
585-
586 if ("retweet" in _features)
587 {
588 if (_account_service == "twitter")
589@@ -655,14 +592,6 @@
590 retweet_str = _("Share");
591 var retweet_action = new Unity.PreviewAction ("retweet", retweet_str, icon);
592 preview.add_action (retweet_action);
593- retweet_action.activated.connect ((source) => {
594- Idle.add (() => {
595- var dispatcher = new Friends.Dispatcher ();
596- dispatcher.retweet (_account_id, _status_id);
597- return false;
598- });
599- return new Unity.ActivationResponse (Unity.HandledType.SHOW_PREVIEW);
600- });
601 }
602
603 bool from_me = model.get_bool (iter, StreamModelColumn.FROM_ME);
604@@ -682,16 +611,6 @@
605 like_str = liked ? _("Unlike") : _("Like");
606 var like_action = new Unity.PreviewAction ("like", like_str, icon);
607 preview.add_action (like_action);
608- like_action.activated.connect ((source) => {
609- Idle.add (() => {
610- var dispatcher = new Friends.Dispatcher ();
611- var ret = liked ? dispatcher.unlike (_account_id, _status_id) : dispatcher.like (_account_id, _status_id);
612- return false;
613- });
614- model.set_value (iter, StreamModelColumn.LIKED, !liked);
615- Unity.Preview new_preview = this.preview (uri);
616- return new Unity.ActivationResponse.with_preview (new_preview);
617- });
618 }
619
620 return preview;
621@@ -739,6 +658,118 @@
622 return null;
623 }
624
625-
626- } /* End Daemon class */
627+ public override Unity.ActivationResponse? activate (Unity.ScopeResult result, Unity.SearchMetadata metadata, string? action_id)
628+ {
629+ uint _account_id = (uint)result.metadata["account_id"].get_uint64();
630+ var _status_id = result.metadata["status_id"].get_string();
631+ if (action_id == "retweet")
632+ {
633+ Idle.add (() => {
634+ var dispatcher = new Friends.Dispatcher ();
635+ dispatcher.retweet (_account_id, _status_id);
636+ return false;
637+ });
638+ return new Unity.ActivationResponse (Unity.HandledType.SHOW_PREVIEW);
639+ }
640+ else if (action_id == "like")
641+ {
642+ var old_liked = toggle_liked (result.uri);
643+ Idle.add (() => {
644+ var dispatcher = new Friends.Dispatcher ();
645+ if (old_liked)
646+ {
647+ dispatcher.unlike (_account_id, _status_id);
648+ }
649+ else
650+ {
651+ dispatcher.like (_account_id, _status_id);
652+ }
653+ return false;
654+ });
655+ var new_preview = make_preview (result.uri);
656+ return new Unity.ActivationResponse.with_preview (new_preview);
657+ }
658+ else
659+ {
660+ // Default action, or "view"
661+ return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED);
662+ }
663+ }
664+
665+ public bool toggle_liked (string uri)
666+ {
667+ var model = _streams_model;
668+ var iter = model.get_first_iter ();
669+ var end = model.get_last_iter ();
670+ while (iter != end)
671+ {
672+ var row_uri = model.get_string (iter, StreamModelColumn.URL);
673+ if (row_uri == uri)
674+ {
675+ var liked = model.get_bool (iter, StreamModelColumn.LIKED);
676+ model.set_value (iter, StreamModelColumn.LIKED, !liked);
677+ return liked;
678+ }
679+ iter = model.next (iter);
680+ }
681+ return false;
682+ }
683+ }
684+
685+ public class FriendsSearch : Unity.ScopeSearchBase
686+ {
687+ private FriendsScope scope;
688+
689+ public FriendsSearch(FriendsScope scope, Unity.SearchContext search_context)
690+ {
691+ this.scope = scope;
692+ this.search_context = search_context;
693+ }
694+
695+ public override void run ()
696+ {
697+ // FIXME: no results for home screen of the dash?
698+ var context = this.search_context;
699+ if (context.search_type == SearchType.GLOBAL &&
700+ context.search_query.strip () == "")
701+ {
702+ return;
703+ }
704+
705+ this.scope.perform_search (context);
706+ }
707+
708+ public override void run_async (Unity.ScopeSearchBaseCallback callback)
709+ {
710+ // Run the search in the main thread, to avoid
711+ // synchronisation problems.
712+ run ();
713+ callback (this);
714+ }
715+ }
716+
717+ public class FriendsResultPreviewer : Unity.ResultPreviewer
718+ {
719+ private FriendsScope scope;
720+
721+ public FriendsResultPreviewer (FriendsScope scope, Unity.ScopeResult result, Unity.SearchMetadata metadata)
722+ {
723+ this.scope = scope;
724+ set_scope_result (result);
725+ set_search_metadata (metadata);
726+ }
727+
728+ public override Unity.AbstractPreview? run ()
729+ {
730+ return scope.make_preview (result.uri);
731+ }
732+
733+ public override void run_async (Unity.AbstractPreviewCallback callback)
734+ {
735+ // Build preview in the main thread, to avoid
736+ // synchronisation problems.
737+ var preview = run ();
738+ callback (this, preview);
739+ }
740+ }
741 } /* end Friends namespace */
742
743=== modified file 'src/main.vala'
744--- src/main.vala 2013-03-20 08:37:09 +0000
745+++ src/main.vala 2013-07-26 10:35:05 +0000
746@@ -21,84 +21,21 @@
747 using GLib;
748 using Config;
749
750-namespace UnityFriends {
751-
752- static Application? app = null;
753- static Daemon? daemon = null;
754-
755- /* Check if a given well known DBus is owned.
756- * WARNING: This does sync IO! */
757- public static bool dbus_name_has_owner (string name)
758- {
759- try {
760- bool has_owner;
761- DBusConnection bus = Bus.get_sync (BusType.SESSION);
762- Variant result = bus.call_sync ("org.freedesktop.DBus",
763- "/org/freedesktop/dbus",
764- "org.freedesktop.DBus",
765- "NameHasOwner",
766- new Variant ("(s)", name),
767- new VariantType ("(b)"),
768- DBusCallFlags.NO_AUTO_START,
769- -1);
770- result.get ("(b)", out has_owner);
771- return has_owner;
772- } catch (Error e) {
773- warning ("Unable to decide if '%s' is running: %s", name, e.message);
774- }
775-
776- return false;
777- }
778-
779- public static int main (string[] args)
780- {
781- /* Sort up locale to get translations but also sorting and
782- * punctuation right */
783- GLib.Intl.textdomain (Config.PACKAGE);
784- GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALE_DIR);
785- GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8");
786- GLib.Intl.setlocale (GLib.LocaleCategory.ALL, "");
787-
788- /* Make sure the desktop appinfos are picked up correctly */
789- DesktopAppInfo.set_desktop_env ("GNOME");
790-
791- /* Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=640714
792- * GApplication.register() call owns our DBus name in a sync manner
793- * making it race against GDBus' worker thread to export our
794- * objects on the bus before/after owning our name and receiving
795- * method calls on our objects (which may not yet be up!)*/
796- if (dbus_name_has_owner (BUS_NAME))
797- {
798- print ("Another instance of the Unity Friends Daemon " +
799- "already appears to be running.\nBailing out.\n");
800- return 2;
801- }
802-
803- /* Now register our DBus objects *before* acquiring the name!
804- * See above for reasons */
805- daemon = new Daemon ();
806-
807- /* Use GApplication directly for single instance app functionality */
808- app = new Application (BUS_NAME, ApplicationFlags.IS_SERVICE);
809- try {
810- app.register ();
811- } catch (Error e) {
812- /* FIXME: We get this error if another daemon is already running,
813- * but it uses a generic error so we can't detect this reliably... */
814- print ("Failed to start files daemon: %s\n", e.message);
815- return 1;
816- }
817-
818- if (app.get_is_remote ())
819- {
820- print ("Another instance of the Unity Friends Daemon " +
821- "already appears to be running.\nBailing out.\n");
822- return 2;
823- }
824-
825- /* Holding the app makes sure the GApplication doesn't exit */
826- app.hold();
827- return app.run ();
828- }
829-
830-} /* namespace */
831+public int unity_scope_module_get_version ()
832+{
833+ return Unity.SCOPE_API_VERSION;
834+}
835+
836+public List<Unity.AbstractScope> unity_scope_module_load_scopes () throws Error
837+{
838+ /* Sort up locale to get translations but also sorting and
839+ * punctuation right */
840+ GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALE_DIR);
841+ GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8");
842+ GLib.Intl.setlocale (GLib.LocaleCategory.ALL, "");
843+
844+ List<Unity.AbstractScope> scopes = null;
845+ var scope = new UnityFriends.FriendsScope ();
846+ scopes.append (scope);
847+ return scopes;
848+}

Subscribers

People subscribed via source and target branches