Merge lp:~bidossessi-sodonon/synapse-project/mpc-plugin into lp:synapse-project

Proposed by Bidossessi Sodonon on 2011-05-25
Status: Needs review
Proposed branch: lp:~bidossessi-sodonon/synapse-project/mpc-plugin
Merge into: lp:synapse-project
Diff against target: 667 lines (+641/-0)
3 files modified
src/plugins/Makefile.am (+1/-0)
src/plugins/mpc-plugin.vala (+639/-0)
src/ui/synapse-main.vala (+1/-0)
To merge this branch: bzr merge lp:~bidossessi-sodonon/synapse-project/mpc-plugin
Reviewer Review Type Date Requested Status
Bidossessi Sodonon (community) Resubmit on 2011-05-31
Alberto Aldegheri Needs Fixing on 2011-05-30
Michal Hruby 2011-05-25 Needs Fixing on 2011-05-27
Review via email: mp+62308@code.launchpad.net

This proposal supersedes a proposal from 2011-05-25.

Description of the change

Changed MpcEngine's methods to static to avoid unnecessary instantiation.

To post a comment you must log in.
Alberto Aldegheri (albyrock87) wrote : Posted in a previous version of this proposal

First of all, thank you for your contribution! :)

Some comments:
1)
MpcEngine class is instantiated each time I execute an action.
But MpcEngine doesn't have "attributes" that needs to be instantiated.
So I suggest to turn each method and attribute of MpcEnginge into a static one.
In example:
private static string bin = "mpc"; //this can be also "const"
public static void play_pause ()...

In this way you can call directly:
MpcEngine.play_pause ();

2) what's the meaning of
 >>search the database for a "type" and turn results to uris on the fly<< ?

3) Synapse's configuration capabilities will come asap (mhr3 and I are very busy in this moment)

Bidossessi Sodonon (bidossessi-sodonon) wrote : Posted in a previous version of this proposal

Thanks for your prompt reply.

About 1), blame my lack of programming experience with compiled code. I will update this asap.

Now, for 2), from man mpc:

search <type> <query> [<type> <query>]...
              Searches for songs where all of the given tag <type>s match the given <query>s. Any number of tag type and query combinations can be specified. Possible tag
              types are: artist, album, title, track, name, genre, date, composer, performer, comment, disc, filename, or any (to match any tag).

Here is a sample of the list returned:

[b_sodonon@sysadmin synapse-project]$ mpc search genre world
Ali Farka Toure & Toumani Diabate/In the Heart of the Moon/02. Kala.mp3
Ali Farka Toure & Toumani Diabate/In the Heart of the Moon/03. Mamadou Boutiquier.mp3

The resulting file paths are relative to mpd's "musid_dir" configuration.

what needs to happen is the following (python):

abs_path = music_dir + rel_path
uri = 'file://' + str(urllib.quote(mpd_path, safe='/~'))

I use that code to create uris I feed into zeitgeist.

I have no idea how to do that in vala.
Not to mention that, to enqueue files in mpd, I have to provide relative_path as an argument, which means going from uri to relative path. No idea how I could do that either. So until I become a bit more conversant with vala, I'll stick to the basic functionality.

Once again, thanks for the reply. It feels good be finally giving back to the community. :)

Cheers.

I hope this is the right way to go about updating my merge proposal.
If not, I'm sorry.

Cheers

Alberto Aldegheri (albyrock87) wrote :

:) Do not resubmit the proposal (I made the same mistake the first time I used Launchpad :P).
Just add a comment an I (or mhr3) will come here and look at changes.

I'll look at your merge proposal asap!

443. By Bidossessi Sodonon on 2011-05-26

Forgot to add the "stop" command

444. By Bidossessi Sodonon on 2011-05-26

Forgot some strings to make Stop appear

445. By Bidossessi Sodonon on 2011-05-26

Fixed unreachable catch issues

446. By Bidossessi Sodonon on 2011-05-27

Feature-complete code, as compared to banshee-plugin

Plugin is now feature-complete, using banshee as a feature basis.

It does the following actions:
- start, stop, pause and resume playback
- enqueue a uri in mpd
- play a track, clearing the playlist

I reinstated instantiation in prevision of Synapse's configuration capabilities.
That way, (almost) no refactoring will be needed.

I'm sure the code is not optimized, but, that's my level, for now.

Cheers

447. By Bidossessi Sodonon on 2011-05-27

Fixed the location for this file.

448. By Bidossessi Sodonon on 2011-05-27

Updated the pot file.

Michal Hruby (mhr3) wrote :

A few notes:

- please revert the changes to hybrid-search-plugin
- line 210-217 and others - why do you catch the error, if you only re-throw it?

Other than that it looks ok...

review: Needs Fixing
449. By Bidossessi Sodonon on 2011-05-28

* reverted hybrid-search-plugin to previous code
* stopped catching errors in public methods in mpc-plugin (no real need)
* updated synapse.pot to

> A few notes:
>
> - please revert the changes to hybrid-search-plugin
> - line 210-217 and others - why do you catch the error, if you only re-throw
> it?
>
> Other than that it looks ok...
Well, I kept catching until vala stopped giving me unhandled error messages. They were kind of scary. But I admint there's no real need to do that.

I believe that with 449, this plugin is ready.

Please check if more needs to be done for this plugin.
Thank you.

review: Resubmit
Alberto Aldegheri (albyrock87) wrote :

Please, enter in Synapse's folder, and execute:
bzr revert --revision 440 src/plugins/hybrid-search-plugin.vala

And:

bzr revert --revision 440 po/synapse.pot

(we'll update translations later)

At this point: return path[music_root.length:path.length]; (line 411 of the diff)
Are you sure that music_root is a prefix of path ?

review: Needs Fixing
450. By Bidossessi Sodonon on 2011-05-31

Don't try to play files that are not in MPD's music root (since it will fail anyway).

451. By Bidossessi Sodonon on 2011-05-31

Reverted pot file and hybrid-search-plugin to rev 440.

> Please, enter in Synapse's folder, and execute:
> bzr revert --revision 440 src/plugins/hybrid-search-plugin.vala
>
> And:
>
> bzr revert --revision 440 po/synapse.pot
>
> (we'll update translations later)
>
Done, and sorry about that.
> At this point: return path[music_root.length:path.length]; (line 411 of the
> diff)
> Are you sure that music_root is a prefix of path ?
Got it: files not in mpd's music_root are now handled.

review: Resubmit
Antono Vasiljev (antono) wrote :

Any updates here?

Unmerged revisions

451. By Bidossessi Sodonon on 2011-05-31

Reverted pot file and hybrid-search-plugin to rev 440.

450. By Bidossessi Sodonon on 2011-05-31

Don't try to play files that are not in MPD's music root (since it will fail anyway).

449. By Bidossessi Sodonon on 2011-05-28

* reverted hybrid-search-plugin to previous code
* stopped catching errors in public methods in mpc-plugin (no real need)
* updated synapse.pot to

448. By Bidossessi Sodonon on 2011-05-27

Updated the pot file.

447. By Bidossessi Sodonon on 2011-05-27

Fixed the location for this file.

446. By Bidossessi Sodonon on 2011-05-27

Feature-complete code, as compared to banshee-plugin

445. By Bidossessi Sodonon on 2011-05-26

Fixed unreachable catch issues

444. By Bidossessi Sodonon on 2011-05-26

Forgot some strings to make Stop appear

443. By Bidossessi Sodonon on 2011-05-26

Forgot to add the "stop" command

442. By Bidossessi Sodonon on 2011-05-25

Turned MpcEngine instance method to class methods (static)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/plugins/Makefile.am'
2--- src/plugins/Makefile.am 2011-04-18 14:49:45 +0000
3+++ src/plugins/Makefile.am 2011-05-31 10:31:30 +0000
4@@ -46,6 +46,7 @@
5 hybrid-search-plugin.vala \
6 launchpad-plugin.vala \
7 locate-plugin.vala \
8+ mpc-plugin.vala \
9 opensearch.vala \
10 pastebin-plugin.vala \
11 pidgin-plugin.vala \
12
13=== added file 'src/plugins/mpc-plugin.vala'
14--- src/plugins/mpc-plugin.vala 1970-01-01 00:00:00 +0000
15+++ src/plugins/mpc-plugin.vala 2011-05-31 10:31:30 +0000
16@@ -0,0 +1,639 @@
17+/*
18+ * Copyright (C) 2011 Bidossessi Sodonon <stanislas.sodonon at gmail.com>
19+ *
20+ * This program is free software; you can redistribute it and/or modify
21+ * it under the terms of the GNU General Public License as published by
22+ * the Free Software Foundation; either version 2 of the License, or
23+ * (at your option) any later version.
24+ *
25+ * This program is distributed in the hope that it will be useful,
26+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
27+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28+ * GNU General Public License for more details.
29+ *
30+ * You should have received a copy of the GNU General Public License
31+ * along with this program; if not, write to the Free Software
32+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
33+ *
34+ * Authored by Bidossessi Sodonon <stanislas.sodonon at gmail.com>
35+ *
36+ */
37+
38+namespace Synapse
39+{
40+ public class MpcEngine: Object
41+ {
42+ // our executable string array
43+ private const string bin = "mpc";
44+ // mpd paths are relative to this directory
45+ public string music_root {get;construct;}
46+ // if these default values are given, use them
47+ public string host {get;construct;} // localhost by defaut
48+ public string port {get;construct;} // 6600 by default
49+ public string passwd {get;construct;}// no pasword by default
50+
51+ // constructor
52+ public MpcEngine (string? host = "localhost",
53+ string? port = "6600", string? passwd = null,
54+ string? music_root = null)
55+ {
56+ // mpc is called like this: mpc [-h [_passwd@]_host] [-p _port] <command> [arguments]
57+ Object(host: host, port: port, passwd: passwd, music_root: music_root);
58+ }
59+ construct {
60+ //make sure we find the music_dir if it wasn't given.
61+ music_root = find_music_root(music_root);
62+ }
63+
64+ /* Public operations (pass errors to caller)*/
65+ public void stop () throws IOError
66+ {
67+ run_command("stop");
68+ }
69+ public void play_pause () throws IOError
70+ {
71+ run_command("toggle");
72+ }
73+ public void play_now (string uri) throws IOError
74+ {
75+ string rel_path = path_from_uri(uri);
76+ run_command ("clear");
77+ run_command ("add", rel_path);
78+ run_command ("play");
79+ }
80+ public void enqueue (string uri) throws IOError
81+ {
82+ string rel_path = path_from_uri(uri);
83+ run_command("add", rel_path);
84+ }
85+ public void next () throws IOError
86+ {
87+ run_command("next");
88+ }
89+ public void previous () throws IOError
90+ {
91+ run_command("prev");
92+ }
93+
94+ /* Engine Internals */
95+ // convenience function
96+ protected string turn_abs(string user_path)
97+ {
98+ //this shouldn't affect absolute paths
99+ try
100+ {
101+ MatchInfo info;
102+ Regex regex = new Regex("^~");
103+ string user_dir = Environment.get_home_dir();
104+ if (regex.match(user_path, 0, out info))
105+ {
106+ string abs_path = regex.replace (user_path, -1, 0, user_dir);
107+ //make sure it exists
108+ var u = File.new_for_path(abs_path);
109+ if (u.query_exists ())
110+ {
111+ return abs_path;
112+ }
113+ }
114+ }
115+ // ouch, what happened?
116+ catch (Error e)
117+ {
118+ Utils.Logger.warning (this,"Failed to turn %s to absolute path: %s", user_path, e.message);
119+ }
120+ return user_path;
121+ }
122+
123+ protected File? get_mpd_conf ()
124+ {
125+ // try ~/.mpdconf
126+ string user_conf = turn_abs("~/.mpdconf");
127+ var ufile = File.new_for_path (user_conf);
128+ if (ufile.query_exists ())
129+ {
130+ return ufile;
131+ }
132+ // try /etc/mpd.conf
133+ else
134+ {
135+ Utils.Logger.warning (this,"File '%s' doesn't exist", user_conf);
136+ //not found, trying the other, hoping it's world-readable
137+ var sfile = File.new_for_path ("/etc/mpd.conf");
138+ if (sfile.query_exists ())
139+ {
140+ return sfile;
141+ }
142+ }
143+ Utils.Logger.warning (this,"File '/etc/mpd.conf' doesn't exist");
144+ return null;
145+ }
146+
147+ protected string? find_music_root (string? given_root)
148+ {
149+ try
150+ {
151+ if (given_root != null)
152+ {
153+ var g = File.new_for_path(given_root);
154+ if (g.query_exists ())
155+ {
156+ return given_root;
157+ }
158+ }
159+ // the given path didn't check out
160+ else
161+ {
162+ File file = get_mpd_conf();
163+ if (file != null)
164+ {
165+ var dis = new DataInputStream (file.read ());
166+ string line;
167+ while ((line = dis.read_line (null)) != null)
168+ {
169+ //find the right line
170+ Regex tag = new Regex("^music_directory.*$");
171+ if (tag.match (line))
172+ {
173+ //capture the dir between the quotation marks
174+ MatchInfo info;
175+ Regex dir = new Regex("(?<=\")(.*?)(?=\")");
176+ if (dir.match (line, 0, out info))
177+ {
178+ string found_root = turn_abs(info.fetch(0)) + "/";
179+ //make sure it exists
180+ var r = File.new_for_path(found_root);
181+ if (r.query_exists ())
182+ {
183+ return found_root;
184+ }
185+ }
186+ }
187+ }
188+ }
189+ }
190+ }
191+ catch (Error e)
192+ {
193+ Utils.Logger.warning (this,"Music root not found: %s", e.message);
194+ }
195+ return null;
196+ }
197+
198+ // convert uri to relative path
199+ private string? path_from_uri (string uri) throws IOError
200+ {
201+ try
202+ {
203+ string path = Filename.from_uri(uri);
204+ //make sure it exists
205+ var p = File.new_for_path(path);
206+ if (p.query_exists ())
207+ {
208+ if (music_root != null)
209+ {
210+ if (music_root in path)
211+ {
212+ //strip music_root
213+ return path[music_root.length:path.length];
214+ }
215+ else
216+ {
217+ throw new IOError.NOT_FOUND("File not in mpd's music root");
218+ }
219+ }
220+ else
221+ {
222+ throw new IOError.NOT_FOUND("Music root not found");
223+ }
224+ }
225+ else
226+ {
227+ throw new IOError.NOT_FOUND("File %s not found", path);
228+ }
229+ }
230+ catch (Error e)
231+ {
232+ Utils.Logger.warning (this,"Couldn't convert URI to filename: %s", e.message);
233+ }
234+ return null;
235+ }
236+
237+ // run command
238+ protected void run_command (string command,
239+ string? qtype = null, string? sarg = null) throws IOError
240+ {
241+ Pid pid;
242+ string complete_output = "";
243+ bool error_found = false;
244+ string[] argv = {bin};
245+ if (host != null)
246+ {
247+ var s = new StringBuilder();
248+ if (passwd != null)
249+ {
250+ s.append_printf("%s@", passwd);
251+ }
252+ s.append_printf("%s", host);
253+ argv += "-h";
254+ argv += s.str;
255+ }
256+ if (port != null)
257+ {
258+ argv += "-p";
259+ argv += port;
260+ }
261+ argv += command;
262+ if (qtype != null)
263+ {
264+ argv += qtype;
265+ if (sarg != null)
266+ {
267+ argv += sarg;
268+ }
269+ }
270+
271+ try
272+ {
273+ int read_fd;
274+ // FIXME: probably a better way to do this
275+ Process.spawn_async_with_pipes (null, argv, null,
276+ SpawnFlags.SEARCH_PATH,
277+ null, out pid, null, out read_fd);
278+ UnixInputStream read_stream = new UnixInputStream (read_fd, true);
279+ DataInputStream mpd_output = new DataInputStream (read_stream);
280+
281+ string? line = null;
282+ do
283+ {
284+ line = mpd_output.read_line (null);
285+ if (line != null)
286+ {
287+ complete_output += line;
288+ }
289+ } while (line != null);
290+ // try and catch any 'error' line from mpc
291+ Regex error = new Regex ("error:.*$");
292+ if (error.match (complete_output))
293+ {
294+ error_found = true;
295+ }
296+ }
297+ catch (Error err)
298+ {
299+ Utils.Logger.warning (this,"MPD is not available: %s", err.message);
300+ }
301+ if (error_found == true)
302+ {
303+ throw new IOError.CONNECTION_REFUSED (complete_output);
304+ }
305+ }
306+ }//end of MpcEngine
307+
308+
309+ /* SYNAPSE side (finally :p) */
310+ public class MpcActions: Object, Activatable, ItemProvider, ActionProvider
311+ {
312+ public bool enabled { get; set; default = true; }
313+
314+ public void activate ()
315+ {
316+ }
317+
318+ public void deactivate ()
319+ {
320+ }
321+
322+ static void register_plugin ()
323+ {
324+ DataSink.PluginRegistry.get_default ().register_plugin (
325+ typeof (MpcActions),
326+ _ ("Mpc"),
327+ _ ("Control the Music Player Daemon."),
328+ "mpc",
329+ register_plugin,
330+ Environment.find_program_in_path ("mpc") != null,
331+ _ ("mpc is not installed")
332+ );
333+ }
334+
335+ static construct
336+ {
337+ register_plugin ();
338+ }
339+
340+ private abstract class MpcAction: Object, Match
341+ {
342+ // from Match interface
343+ public string title { get; construct set; }
344+ public string description { get; set; }
345+ public string icon_name { get; construct set; }
346+ public bool has_thumbnail { get; construct set; }
347+ public string thumbnail_path { get; construct set; }
348+ public MatchType match_type { get; construct set; }
349+
350+ public int default_relevancy { get; set; }
351+
352+ public abstract bool valid_for_match (Match match);
353+
354+ public abstract void execute_internal (Match? match);
355+ public void execute (Match? match)
356+ {
357+ execute_internal (match);
358+ }
359+ public virtual int get_relevancy ()
360+ {
361+ return default_relevancy + Match.Score.INCREMENT_LARGE;
362+ }
363+ }
364+
365+ private abstract class MpcControlMatch: Object, Match
366+ {
367+ // for Match interface
368+ public string title { get; construct set; }
369+ public string description { get; set; default = ""; }
370+ public string icon_name { get; construct set; default = ""; }
371+ public bool has_thumbnail { get; construct set; default = false; }
372+ public string thumbnail_path { get; construct set; }
373+ public MatchType match_type { get; construct set; }
374+
375+ public void execute (Match? match)
376+ {
377+ this.do_action ();
378+ }
379+
380+ public abstract void do_action ();
381+
382+ }
383+
384+ /* MATCHES of Type.ACTION */
385+ private class Play: MpcControlMatch
386+ {
387+ public Play ()
388+ {
389+ Object (title: _ ("Play"),
390+ description: _ ("Start playback in MPD"),
391+ icon_name: "media-playback-start", has_thumbnail: false,
392+ match_type: MatchType.ACTION);
393+ }
394+ public override void do_action ()
395+ {
396+ try
397+ {
398+ var player = new MpcEngine ();
399+ player.play_pause ();
400+ }
401+ catch (IOError e) {
402+ Utils.Logger.warning (this,"MPD is not available: %s", e.message);
403+ }
404+ }
405+ }
406+
407+ private class Pause: Play
408+ {
409+ public Pause ()
410+ {
411+ Object (title: _ ("Pause"),
412+ description: _ ("Pause playback in MPD"),
413+ icon_name: "media-playback-pause", has_thumbnail: false,
414+ match_type: MatchType.ACTION);
415+ }
416+ }
417+
418+ private class Next: MpcControlMatch
419+ {
420+ public Next ()
421+ {
422+ Object (title: _ ("Next"),
423+ description: _ ("Plays the next song in MPD's playlist"),
424+ icon_name: "media-skip-forward", has_thumbnail: false,
425+ match_type: MatchType.ACTION);
426+ }
427+
428+ public override void do_action ()
429+ {
430+ try
431+ {
432+ var player = new MpcEngine ();
433+ player.next ();
434+ }
435+ catch (IOError e) {
436+ Utils.Logger.warning (this,"MPD is not available: %s", e.message);
437+ }
438+ }
439+
440+ }
441+ private class Previous: MpcControlMatch
442+ {
443+ public Previous ()
444+ {
445+ Object (title: _ ("Previous"),
446+ description: _ ("Plays the previous song in MPD's playlist"),
447+ icon_name: "media-skip-backward", has_thumbnail: false,
448+ match_type: MatchType.ACTION);
449+ }
450+
451+ public override void do_action ()
452+ {
453+ try
454+ {
455+ var player = new MpcEngine ();
456+ player.previous ();
457+ }
458+ catch (IOError e) {
459+ Utils.Logger.warning (this,"MPD is not available: %s", e.message);
460+ }
461+ }
462+ }
463+
464+ private class Stop: MpcControlMatch
465+ {
466+ public Stop ()
467+ {
468+ Object (title: _ ("Stop"),
469+ description: _ ("Stop playback in MPD"),
470+ icon_name: "media-stop", has_thumbnail: false,
471+ match_type: MatchType.ACTION);
472+ }
473+
474+ public override void do_action ()
475+ {
476+ try
477+ {
478+ var player = new MpcEngine ();
479+ player.stop ();
480+ }
481+ catch (IOError e) {
482+ Utils.Logger.warning (this, "MPD is not available: %s", e.message);
483+ }
484+ }
485+ }
486+
487+ /* ACTIONS FOR Music files */
488+ private class AddToPlaylist: MpcAction
489+ {
490+ public AddToPlaylist ()
491+ {
492+ Object (title: _ ("Enqueue in MPD"),
493+ description: _ ("Add the song to MPD's playlist"),
494+ icon_name: "media-playback-start", has_thumbnail: false,
495+ match_type: MatchType.ACTION,
496+ default_relevancy: Match.Score.AVERAGE);
497+ }
498+
499+ public override void execute_internal (Match? match)
500+ {
501+ return_if_fail (match.match_type == MatchType.GENERIC_URI);
502+ UriMatch uri = match as UriMatch;
503+ return_if_fail ((uri.file_type & QueryFlags.AUDIO) != 0);
504+
505+ try
506+ {
507+ var player = new MpcEngine ();
508+ player.enqueue (uri.uri);
509+ }
510+ catch (IOError e) {
511+ Utils.Logger.warning (this, "MPD is not available: %s", e.message);
512+ }
513+
514+ }
515+
516+ public override bool valid_for_match (Match match)
517+ {
518+ switch (match.match_type)
519+ {
520+ case MatchType.GENERIC_URI:
521+ UriMatch uri = match as UriMatch;
522+ if ((uri.file_type & QueryFlags.AUDIO) != 0)
523+ return true;
524+ else
525+ return false;
526+ default:
527+ return false;
528+ }
529+ }
530+ }
531+ private class PlayNow: MpcAction
532+ {
533+ public PlayNow ()
534+ {
535+ Object (title: _ ("Play in MPD"),
536+ description: _ ("Clears the current playlist and plays the song"),
537+ icon_name: "media-playback-start", has_thumbnail: false,
538+ match_type: MatchType.ACTION,
539+ default_relevancy: Match.Score.ABOVE_AVERAGE);
540+ }
541+
542+ public override void execute_internal (Match? match)
543+ {
544+ return_if_fail (match.match_type == MatchType.GENERIC_URI);
545+ UriMatch uri = match as UriMatch;
546+ return_if_fail ((uri.file_type & QueryFlags.AUDIO) != 0 );
547+
548+ try
549+ {
550+ var player = new MpcEngine ();
551+ player.play_now (uri.uri);
552+ }
553+ catch (IOError e) {
554+ Utils.Logger.warning (this, "MPD is not available: %s", e.message);
555+ }
556+ }
557+
558+ public override bool valid_for_match (Match match)
559+ {
560+ switch (match.match_type)
561+ {
562+ case MatchType.GENERIC_URI:
563+ UriMatch uri = match as UriMatch;
564+ if ((uri.file_type & QueryFlags.AUDIO) != 0)
565+ return true;
566+ else
567+ return false;
568+ default:
569+ return false;
570+ }
571+ }
572+ }
573+
574+ private Gee.List<MpcAction> actions;
575+ private Gee.List<MpcControlMatch> matches;
576+
577+ construct
578+ {
579+ actions = new Gee.ArrayList<MpcAction> ();
580+ matches = new Gee.ArrayList<MpcControlMatch> ();
581+
582+ actions.add (new PlayNow());
583+ actions.add (new AddToPlaylist());
584+
585+ matches.add (new Play ());
586+ matches.add (new Stop ());
587+ matches.add (new Pause ());
588+ matches.add (new Previous ());
589+ matches.add (new Next ());
590+ }
591+
592+ public async ResultSet? search (Query q) throws SearchError
593+ {
594+ // we only search for actions
595+ if (!(QueryFlags.ACTIONS in q.query_type)) return null;
596+
597+ var result = new ResultSet ();
598+
599+ var matchers = Query.get_matchers_for_query (q.query_string, 0,
600+ RegexCompileFlags.OPTIMIZE | RegexCompileFlags.CASELESS);
601+
602+ foreach (var action in matches)
603+ {
604+ foreach (var matcher in matchers)
605+ {
606+ if (matcher.key.match (action.title))
607+ {
608+ result.add (action, matcher.value - Match.Score.INCREMENT_SMALL);
609+ break;
610+ }
611+ }
612+ }
613+
614+ q.check_cancellable ();
615+
616+ return result;
617+ }
618+
619+ public ResultSet? find_for_match (Query query, Match match)
620+ {
621+ bool query_empty = query.query_string == "";
622+ var results = new ResultSet ();
623+
624+ if (query_empty)
625+ {
626+ foreach (var action in actions)
627+ {
628+ if (action.valid_for_match (match))
629+ {
630+ results.add (action, action.get_relevancy ());
631+ }
632+ }
633+ }
634+ else
635+ {
636+ var matchers = Query.get_matchers_for_query (query.query_string, 0,
637+ RegexCompileFlags.OPTIMIZE | RegexCompileFlags.CASELESS);
638+ foreach (var action in actions)
639+ {
640+ if (!action.valid_for_match (match)) continue;
641+ foreach (var matcher in matchers)
642+ {
643+ if (matcher.key.match (action.title))
644+ {
645+ results.add (action, matcher.value);
646+ break;
647+ }
648+ }
649+ }
650+ }
651+
652+ return results;
653+ }
654+ }
655+}
656
657=== modified file 'src/ui/synapse-main.vala'
658--- src/ui/synapse-main.vala 2011-05-05 00:07:47 +0000
659+++ src/ui/synapse-main.vala 2011-05-31 10:31:30 +0000
660@@ -178,6 +178,7 @@
661 // typeof (FileOpPlugin),
662 // typeof (PidginPlugin),
663 // typeof (ChatActions),
664+ typeof (MpcActions),
665 #if HAVE_ZEITGEIST
666 typeof (ZeitgeistPlugin),
667 #endif