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 4330 2008-09-14 03:19:26Z piman $
11
from quodlibet import qltk
13
from traceback import print_exc
15
from quodlibet.plugins import ListWrapper, Manager
17
class SongsMenuPlugin(gtk.ImageMenuItem):
18
"""Plugins of this type are subclasses of gtk.ImageMenuItem.
19
They will be added, in alphabetical order, to the "Plugins" menu
20
that appears when songs or lists of songs are right-clicked.
21
They provide one or more of the following instance methods:
23
self.plugin_single_song(song)
24
self.plugin_song(song)
25
self.plugin_songs(songs)
26
self.plugin_single_album(album)
27
self.plugin_album(album)
28
self.plugin_albums(albums)
30
All matching provided callables on a single object are called in the
31
above order if they match until one returns a true value. They are
32
not called with real AudioFile objects, but rather wrappers that
33
automatically detect metadata or disk changes, and save or reload
34
the files as appropriate.
36
The single_ variant is only called if a single song/album is selected.
38
The singular tense is called once for each selected song/album, but the
39
plural tense is called with a list of songs/albums.
41
An album is a list of songs all with the same album, labelid,
42
and/or musicbrainz_albumid tags (like in the Album List).
44
To make your plugin insensitive if unsupported songs are selected,
45
a method that takes a list of songs and returns True or False to set
46
the sensitivity of the menu entry:
47
self.plugin_handles(songs)
49
When these functions are called, the self.plugin_window will be
50
available. This is the gtk.Window the plugin was invoked from. This
51
provides access to two important widgets, self.plugin_window.browser
52
and self.plugin_window.songlist.
54
All of this is managed by the constructor for SongsMenuPlugin, so
55
make sure it gets called if you override it (you shouldn't have to).
58
plugin_single_song = None
61
plugin_single_album = None
65
def __init__(self, songs):
66
super(SongsMenuPlugin, self).__init__(self.PLUGIN_NAME)
67
try: i = gtk.image_new_from_stock(self.PLUGIN_ICON, gtk.ICON_SIZE_MENU)
68
except AttributeError: pass
69
else: self.set_image(i)
70
self.set_sensitive(bool(self.plugin_handles(songs)))
72
def plugin_handles(self, songs):
75
class SongsMenuPlugins(Manager):
76
Kinds = [SongsMenuPlugin]
78
def Menu(self, library, parent, songs):
79
songs = ListWrapper(songs)
80
parent = qltk.get_top_parent(parent)
87
albums[key].append(song)
89
albums = albums.values()
90
map(list.sort, albums)
92
attrs = ['plugin_song', 'plugin_songs',
93
'plugin_album', 'plugin_albums']
95
if len(songs) == 1: attrs.append('plugin_single_song')
96
if len(albums) == 1: attrs.append('plugin_single_album')
99
kinds = self.find_subclasses(SongsMenuPlugin)
100
kinds.sort(key=lambda plugin: plugin.PLUGIN_ID)
102
usable = max([callable(getattr(Kind, s)) for s in attrs])
104
try: items.append(Kind(songs))
112
args = (library, parent, songs, albums)
113
if item.get_submenu():
114
for subitem in item.get_submenu().get_children():
115
subitem.connect_object(
116
'activate', self.__handle, item, *args)
118
item.connect('activate', self.__handle, *args)
126
def __handle(self, plugin, library, parent, songs, albums):
127
plugin.plugin_window = parent
130
if len(songs) == 1 and callable(plugin.plugin_single_song):
131
try: ret = plugin.plugin_single_song(songs[0])
132
except Exception: print_exc()
135
if callable(plugin.plugin_song):
136
try: ret = map(plugin.plugin_song, songs)
137
except Exception: print_exc()
140
if callable(plugin.plugin_songs):
141
try: ret = plugin.plugin_songs(songs)
142
except Exception: print_exc()
146
if len(albums) == 1 and callable(plugin.plugin_single_album):
147
try: ret = plugin.plugin_single_album(albums[0])
148
except Exception: print_exc()
151
if callable(plugin.plugin_album):
152
try: ret = map(plugin.plugin_album, albums)
153
except Exception: print_exc()
156
if callable(plugin.plugin_albums):
157
try: ret = plugin.plugin_albums(albums)
158
except Exception: print_exc()
163
del(plugin.plugin_window)
164
self._check_change(library, parent, filter(None, songs))