1/* 2 * Copyright (C) 2012 Jörn Magens <shuerhaaken@googlemail.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 * 18 * Authored by Jörn Magens <shuerhaaken@googlemail.com> 19 * 20 */ 21 22namespace Synapse 23{ 24 [DBus (name = "org.gtk.xnoise.PlayerEngine")] 25 private interface XnoisePlayerEngine : Object 26 { 27 public const string UNIQUE_NAME = "org.gtk.xnoise.PlayerEngine"; 28 public const string OBJECT_PATH = "/PlayerEngine"; 29 30 public abstract void quit () throws Error; 31 public abstract void raise () throws Error; 32 33 public abstract void next () throws Error; 34 public abstract void previous () throws Error; 35 public abstract void pause () throws Error; 36 public abstract void toggle_playing () throws Error; 37 public abstract void stop () throws Error; 38 public abstract void play () throws Error; 39 public abstract void open_uri (string uri) throws Error; 40 } 41 42 public class XnoiseActions : Object, Activatable, ItemProvider, ActionProvider 43 { 44 public bool enabled { get; set; default = true; } 45 46 public void activate () 47 { 48 } 49 50 public void deactivate () 51 { 52 } 53 54 static void register_plugin () 55 { 56 PluginRegistry.get_default ().register_plugin ( 57 typeof (XnoiseActions), 58 "Xnoise", 59 _("Control Xnoise media player."), 60 "xnoise", 61 register_plugin, 62 Environment.find_program_in_path ("xnoise") != null, 63 _("Xnoise is not installed!") 64 ); 65 } 66 67 static construct 68 { 69 register_plugin (); 70 } 71 72 private abstract class XnoiseAction : Action 73 { 74 public virtual int get_relevancy () 75 { 76 bool xnoise_running = DBusService.get_default ().name_has_owner (XnoisePlayerEngine.UNIQUE_NAME); 77 return xnoise_running ? default_relevancy + MatchScore.INCREMENT_LARGE : default_relevancy; 78 } 79 } 80 81 private abstract class XnoiseControlMatch : ActionMatch 82 { 83 public virtual bool action_available () 84 { 85 return DBusService.get_default ().name_has_owner (XnoisePlayerEngine.UNIQUE_NAME); 86 } 87 } 88 89 /* MATCHES of Type.ACTION */ 90 private class Quit : XnoiseControlMatch 91 { 92 public Quit () 93 { 94 Object (title: _("Quit"), 95 description: _("Quit Xnoise"), 96 icon_name: "gtk-close", 97 has_thumbnail: false); 98 } 99 100 public override void do_action () 101 { 102 try { 103 XnoisePlayerEngine player = Bus.get_proxy_sync (BusType.SESSION, 104 XnoisePlayerEngine.UNIQUE_NAME, 105 XnoisePlayerEngine.OBJECT_PATH); 106 player.quit (); 107 } catch (Error e) { 108 warning ("Xnoise is not available.\n%s", e.message); 109 } 110 } 111 } 112 113 private class Raise : XnoiseControlMatch 114 { 115 public Raise () 116 { 117 Object (title: _("Raise"), 118 description: _("Show Xnoise"), 119 icon_name: "xnoise", 120 has_thumbnail: false); 121 } 122 123 public override void do_action () 124 { 125 try { 126 XnoisePlayerEngine player = Bus.get_proxy_sync (BusType.SESSION, 127 XnoisePlayerEngine.UNIQUE_NAME, 128 XnoisePlayerEngine.OBJECT_PATH); 129 player.raise (); 130 } catch (Error e) { 131 warning ("Xnoise is not available.\n%s", e.message); 132 } 133 } 134 } 135 136 private class Play : XnoiseControlMatch 137 { 138 public Play () 139 { 140 Object (title: _("Play"), 141 description: _("Start playback in Xnoise"), 142 icon_name: "media-playback-start", 143 has_thumbnail: false); 144 } 145 146 public override void do_action () 147 { 148 try { 149 XnoisePlayerEngine player = Bus.get_proxy_sync (BusType.SESSION, 150 XnoisePlayerEngine.UNIQUE_NAME, 151 XnoisePlayerEngine.OBJECT_PATH); 152 player.play (); 153 } catch (Error e) { 154 warning ("Xnoise is not available.\n%s", e.message); 155 } 156 } 157 } 158 159 private class TogglePlaying : XnoiseControlMatch 160 { 161 public TogglePlaying () 162 { 163 Object (title: _("TogglePlaying"), 164 description: _("Start/Pause playback in Xnoise"), 165 icon_name: "media-playback-pause", 166 has_thumbnail: false); 167 } 168 169 public override void do_action () 170 { 171 try { 172 XnoisePlayerEngine player = Bus.get_proxy_sync (BusType.SESSION, 173 XnoisePlayerEngine.UNIQUE_NAME, 174 XnoisePlayerEngine.OBJECT_PATH); 175 player.toggle_playing (); 176 } catch (Error e) { 177 warning ("Xnoise is not available.\n%s", e.message); 178 } 179 } 180 181 public override bool action_available () 182 { 183 return true; 184 } 185 } 186 187 private class Pause : XnoiseControlMatch 188 { 189 public Pause () 190 { 191 Object (title: _("Pause"), 192 description: _("Pause playback in Xnoise"), 193 icon_name: "media-playback-pause", 194 has_thumbnail: false); 195 } 196 197 public override void do_action () 198 { 199 try { 200 XnoisePlayerEngine player = Bus.get_proxy_sync (BusType.SESSION, 201 XnoisePlayerEngine.UNIQUE_NAME, 202 XnoisePlayerEngine.OBJECT_PATH); 203 player.pause (); 204 } catch (Error e) { 205 warning ("Xnoise is not available.\n%s", e.message); 206 } 207 } 208 } 209 210 private class Next : XnoiseControlMatch 211 { 212 public Next () 213 { 214 Object (title: _("Next"), 215 description: _("Plays the next song in Xnoise's playlist"), 216 icon_name: "media-skip-forward", 217 has_thumbnail: false); 218 } 219 220 public override void do_action () 221 { 222 try { 223 XnoisePlayerEngine player = Bus.get_proxy_sync (BusType.SESSION, 224 XnoisePlayerEngine.UNIQUE_NAME, 225 XnoisePlayerEngine.OBJECT_PATH); 226 227 player.next (); 228 } catch (Error e) { 229 warning ("Xnoise is not available.\n%s", e.message); 230 } 231 } 232 } 233 234 private class Previous : XnoiseControlMatch 235 { 236 public Previous () 237 { 238 Object (title: _("Previous"), 239 description: _("Plays the previous song in Xnoise's playlist"), 240 icon_name: "media-skip-backward", 241 has_thumbnail: false); 242 } 243 244 public override void do_action () 245 { 246 try { 247 XnoisePlayerEngine player = Bus.get_proxy_sync (BusType.SESSION, 248 XnoisePlayerEngine.UNIQUE_NAME, 249 XnoisePlayerEngine.OBJECT_PATH); 250 player.previous (); 251 } catch (Error e) { 252 warning ("Xnoise is not available.\n%s", e.message); 253 } 254 } 255 } 256 257 private class Stop : XnoiseControlMatch 258 { 259 public Stop () 260 { 261 Object (title: _("Stop"), 262 description: _("Stops the playback of Xnoise"), 263 icon_name: "media-playback-stop", 264 has_thumbnail: false); 265 } 266 267 public override void do_action () 268 { 269 try { 270 XnoisePlayerEngine player = Bus.get_proxy_sync (BusType.SESSION, 271 XnoisePlayerEngine.UNIQUE_NAME, 272 XnoisePlayerEngine.OBJECT_PATH); 273 player.stop (); 274 } catch (Error e) { 275 warning ("Xnoise is not available.\n%s", e.message); 276 } 277 } 278 } 279 280 /* ACTIONS FOR MP3s */ 281 private class OpenUri : XnoiseAction 282 { 283 public OpenUri () 284 { 285 Object (title: _("Play in Xnoise"), 286 description: _("Queues and plays the song"), 287 icon_name: "media-playback-start", 288 has_thumbnail: false, 289 default_relevancy: MatchScore.ABOVE_AVERAGE 290 ); 291 } 292 293 public override void do_execute (Match match, Match? target = null) 294 { 295 unowned UriMatch? uri = match as UriMatch; 296 return_if_fail (uri != null); 297 return_if_fail ((uri.file_type & QueryFlags.AUDIO) != 0 || 298 (uri.file_type & QueryFlags.VIDEO) != 0); 299 300 try { 301 XnoisePlayerEngine player = Bus.get_proxy_sync (BusType.SESSION, 302 XnoisePlayerEngine.UNIQUE_NAME, 303 XnoisePlayerEngine.OBJECT_PATH); 304 player.open_uri (uri.uri); 305 player.play (); 306 } catch (Error e) { 307 warning ("Xnoise is not available.\n%s", e.message); 308 } 309 } 310 311 public override bool valid_for_match (Match match) 312 { 313 unowned UriMatch? uri_match = match as UriMatch; 314 if (uri_match == null) 315 return false; 316 317 return ((uri_match.file_type & QueryFlags.AUDIO) != 0 || 318 (uri_match.file_type & QueryFlags.VIDEO) != 0); 319 } 320 } 321 322 private Gee.List<XnoiseAction> actions; 323 private Gee.List<XnoiseControlMatch> matches; 324 325 construct 326 { 327 actions = new Gee.ArrayList<XnoiseAction> (); 328 matches = new Gee.ArrayList<XnoiseControlMatch> (); 329 330 actions.add (new OpenUri()); 331 332 matches.add (new Raise ()); 333 matches.add (new Quit ()); 334 335 matches.add (new Play ()); 336 matches.add (new TogglePlaying ()); 337 matches.add (new Pause ()); 338 matches.add (new Stop ()); 339 matches.add (new Previous ()); 340 matches.add (new Next ()); 341 } 342 343 public async ResultSet? search (Query q) throws SearchError 344 { 345 // we only search for actions 346 if (!(QueryFlags.ACTIONS in q.query_type)) return null; 347 348 var result = new ResultSet (); 349 350 var matchers = Query.get_matchers_for_query (q.query_string, 0, 351 RegexCompileFlags.OPTIMIZE | RegexCompileFlags.CASELESS); 352 353 foreach (var action in matches) 354 { 355 if (!action.action_available ()) continue; 356 foreach (var matcher in matchers) 357 { 358 if (matcher.key.match (action.title)) 359 { 360 result.add (action, matcher.value - MatchScore.INCREMENT_SMALL); 361 break; 362 } 363 } 364 } 365 q.check_cancellable (); 366 return result; 367 } 368 369 public ResultSet? find_for_match (ref Query query, Match match) 370 { 371 bool query_empty = query.query_string == ""; 372 var results = new ResultSet (); 373 374 if (query_empty) 375 { 376 foreach (var action in actions) 377 { 378 if (action.valid_for_match (match)) 379 { 380 results.add (action, action.get_relevancy ()); 381 } 382 } 383 } 384 else 385 { 386 var matchers = Query.get_matchers_for_query (query.query_string, 0, 387 RegexCompileFlags.OPTIMIZE | RegexCompileFlags.CASELESS); 388 foreach (var action in actions) 389 { 390 if (!action.valid_for_match (match)) continue; 391 foreach (var matcher in matchers) 392 { 393 if (matcher.key.match (action.title)) 394 { 395 results.add (action, matcher.value); 396 break; 397 } 398 } 399 } 400 } 401 return results; 402 } 403 } 404} 405