~ubuntu-branches/ubuntu/karmic/quodlibet/karmic

« back to all changes in this revision

Viewing changes to quodlibet/plugins/songsmenu.py

  • Committer: Bazaar Package Importer
  • Author(s): Luca Falavigna
  • Date: 2009-01-30 23:55:34 UTC
  • mfrom: (1.1.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20090130235534-l4e72ulw0vqfo17w
Tags: 2.0-1ubuntu1
* Merge from Debian experimental (LP: #276856), remaining Ubuntu changes:
  + debian/patches/40-use-music-profile.patch:
    - Use the "Music and Movies" pipeline per default.
* Refresh the above patch for new upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2006 Joe Wreschnig
 
2
#
 
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
 
6
#
 
7
# $Id: songsmenu.py 4330 2008-09-14 03:19:26Z piman $
 
8
 
 
9
import gtk
 
10
 
 
11
from quodlibet import qltk
 
12
 
 
13
from traceback import print_exc
 
14
 
 
15
from quodlibet.plugins import ListWrapper, Manager
 
16
 
 
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:
 
22
    
 
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)
 
29
 
 
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.
 
35
 
 
36
    The single_ variant is only called if a single song/album is selected.
 
37
 
 
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.
 
40
 
 
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).
 
43
 
 
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)
 
48
 
 
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.
 
53
 
 
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).
 
56
    """
 
57
 
 
58
    plugin_single_song = None
 
59
    plugin_song = None
 
60
    plugin_songs = None
 
61
    plugin_single_album = None
 
62
    plugin_album = None
 
63
    plugin_albums = None
 
64
 
 
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)))
 
71
 
 
72
    def plugin_handles(self, songs):
 
73
        return True
 
74
 
 
75
class SongsMenuPlugins(Manager):
 
76
    Kinds = [SongsMenuPlugin]
 
77
 
 
78
    def Menu(self, library, parent, songs):
 
79
        songs = ListWrapper(songs)
 
80
        parent = qltk.get_top_parent(parent)
 
81
 
 
82
        albums = {}
 
83
        for song in songs:
 
84
            key = song.album_key
 
85
            if key not in albums:
 
86
                albums[key] = []
 
87
            albums[key].append(song)
 
88
 
 
89
        albums = albums.values()
 
90
        map(list.sort, albums)
 
91
 
 
92
        attrs = ['plugin_song', 'plugin_songs',
 
93
                 'plugin_album', 'plugin_albums']
 
94
            
 
95
        if len(songs) == 1: attrs.append('plugin_single_song')
 
96
        if len(albums) == 1: attrs.append('plugin_single_album')
 
97
 
 
98
        items = []
 
99
        kinds = self.find_subclasses(SongsMenuPlugin)
 
100
        kinds.sort(key=lambda plugin: plugin.PLUGIN_ID)
 
101
        for Kind in kinds:
 
102
            usable = max([callable(getattr(Kind, s)) for s in attrs])
 
103
            if usable:
 
104
                try: items.append(Kind(songs))
 
105
                except: print_exc()
 
106
 
 
107
        if items:
 
108
            menu = gtk.Menu()
 
109
            for item in items:
 
110
                try:
 
111
                    menu.append(item)
 
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)
 
117
                    else:
 
118
                        item.connect('activate', self.__handle, *args)
 
119
                except:
 
120
                    print_exc()
 
121
                    item.destroy()
 
122
 
 
123
        else: menu = None
 
124
        return menu
 
125
 
 
126
    def __handle(self, plugin, library, parent, songs, albums):
 
127
        plugin.plugin_window = parent
 
128
 
 
129
        try:
 
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()
 
133
                else:
 
134
                    if ret: return
 
135
            if callable(plugin.plugin_song):
 
136
                try: ret = map(plugin.plugin_song, songs)
 
137
                except Exception: print_exc()
 
138
                else:
 
139
                    if max(ret): return
 
140
            if callable(plugin.plugin_songs):
 
141
                try: ret = plugin.plugin_songs(songs)
 
142
                except Exception: print_exc()
 
143
                else:
 
144
                    if ret: return
 
145
 
 
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()
 
149
                else:
 
150
                    if ret: return
 
151
            if callable(plugin.plugin_album):
 
152
                try: ret = map(plugin.plugin_album, albums)
 
153
                except Exception: print_exc()
 
154
                else:
 
155
                    if max(ret): return
 
156
            if callable(plugin.plugin_albums):
 
157
                try: ret = plugin.plugin_albums(albums)
 
158
                except Exception: print_exc()
 
159
                else:
 
160
                    if ret: return
 
161
 
 
162
        finally:
 
163
            del(plugin.plugin_window)
 
164
            self._check_change(library, parent, filter(None, songs))