1
# Copyright 2006 Joe Wreschnig
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License version 2 as
5
# published by the Free Software Foundation
7
# $Id: songsmenu.py 3640 2006-07-16 05:51:52Z piman $
13
from traceback import print_exc
15
from plugins import ListWrapper, Manager
17
__all__ = [] # trick out old plugin manager
19
class SongsMenuPlugin(gtk.ImageMenuItem):
20
"""Plugins of this type are subclasses of gtk.ImageMenuItem.
21
They will be added, in alphabetical order, to the "Plugins" menu
22
that appears when songs or lists of songs are right-clicked.
23
They provide one or more of the following instance methods:
25
self.plugin_single_song(song)
26
self.plugin_song(song)
27
self.plugin_songs(songs)
28
self.plugin_single_album(album)
29
self.plugin_album(album)
30
self.plugin_albums(albums)
32
All matching provided callables on a single object are called in the
33
above order if they match until one returns a true value. They are
34
not called with real AudioFile objects, but rather wrappers that
35
automatically detect metadata or disk changes, and save or reload
36
the files as appropriate.
38
The single_ variant is only called if a single song/album is selected.
40
The singular tense is called once for each selected song/album, but the
41
plural tense is called with a list of songs/albums.
43
An album is a list of songs all with the same album, labelid,
44
and/or musicbrainz_albumid tags (like in the Album List).
46
To make your plugin insensitive if unsupported songs are selected,
47
a method that takes a list of songs and returns True or False to set
48
the sensitivity of the menu entry:
49
self.plugin_handles(songs)
51
When these functions are called, the self.plugin_window will be
52
available. This is the gtk.Window the plugin was invoked from. This
53
provides access to two important widgets, self.plugin_window.browser
54
and self.plugin_window.songlist.
56
All of this is managed by the constructor for SongsMenuPlugin, so
57
make sure it gets called if you override it (you shouldn't have to).
60
plugin_single_song = None
63
plugin_single_album = None
67
def __init__(self, songs):
68
super(SongsMenuPlugin, self).__init__(self.PLUGIN_NAME)
69
try: i = gtk.image_new_from_stock(self.PLUGIN_ICON, gtk.ICON_SIZE_MENU)
70
except AttributeError: pass
71
else: self.set_image(i)
72
self.set_sensitive(bool(self.plugin_handles(songs)))
74
def plugin_handles(self, songs):
77
class SongsMenuPlugins(Manager):
78
Kinds = [SongsMenuPlugin]
80
def Menu(self, library, parent, songs):
81
songs = ListWrapper(songs)
82
parent = qltk.get_top_parent(parent)
89
albums[key].append(song)
91
albums = albums.values()
92
map(list.sort, albums)
94
attrs = ['plugin_song', 'plugin_songs',
95
'plugin_album', 'plugin_albums']
97
if len(songs) == 1: attrs.append('plugin_single_song')
98
if len(albums) == 1: attrs.append('plugin_single_album')
101
kinds = self.find_subclasses(SongsMenuPlugin)
102
kinds.sort(key=lambda plugin: plugin.PLUGIN_ID)
104
usable = max([callable(getattr(Kind, s)) for s in attrs])
106
try: items.append(Kind(songs))
114
item.connect('activate', self.__handle,
115
library, parent, songs, albums)
123
def __handle(self, plugin, library, parent, songs, albums):
124
plugin.plugin_window = parent
127
if len(songs) == 1 and callable(plugin.plugin_single_song):
128
try: ret = plugin.plugin_single_song(songs[0])
129
except Exception: print_exc()
132
if callable(plugin.plugin_song):
133
try: ret = map(plugin.plugin_song, songs)
134
except Exception: print_exc()
137
if callable(plugin.plugin_songs):
138
try: ret = plugin.plugin_songs(songs)
139
except Exception: print_exc()
143
if len(albums) == 1 and callable(plugin.plugin_single_album):
144
try: ret = plugin.plugin_single_album(albums[0])
145
except Exception: print_exc()
148
if callable(plugin.plugin_album):
149
try: ret = map(plugin.plugin_album, albums)
150
except Exception: print_exc()
153
if callable(plugin.plugin_albums):
154
try: ret = plugin.plugin_albums(albums)
155
except Exception: print_exc()
160
del(plugin.plugin_window)
161
self._check_change(library, parent, filter(None, songs))