1
# -*- coding: utf-8 -*-
2
# Copyright 2005 Joe Wreschnig
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License version 2 as
6
# published by the Free Software Foundation
8
# $Id: editing.py 4330 2008-09-14 03:19:26Z piman $
12
from quodlibet.plugins import Manager
14
class RenameFilesPlugin(object):
15
"""Plugins of this type must subclass a GTK widget. They will be
16
packed into the RenameFiles pane (currently a ScrolledWindow hidden
17
with an expander, but that might change).
19
The 'filter' function will be called with the song's original filename
20
as a string (probably in the local filesystem encoding) and the proposed
21
new filename as a unicode object. It should return an
22
appropriate-transformed filename, still as a unicode object.
24
The plugin must provide either a 'changed' or 'preview'. 'preview'
25
causes the entire display to be re-previewed. 'changed' causes the
26
Preview button to made sensitive, and Save to be disabled.
28
If the 'active' attribute is false, the filter will not be called.
29
This is particularly useful for gtk.CheckButtons.
31
The '_order' attribute decides the sort order of the plugin. The
32
default filters have orders between 1 and 2. Plugins have order 0 by
33
default. Plugins with equal orders are sorted by class name."""
38
def filter(self, original_filename, value): return value
39
def filter_list(self, origs, names): return map(self.filter, origs, names)
41
def __cmp__(self, other):
42
return (cmp(self._order, other._order) or
43
cmp(type(self).__name__, type(other).__name__))
45
class TagsFromPathPlugin(object):
46
"""Plugins of this type must subclass a GTK widget. They will be
47
packed into the TagsFromPath pane (currently a ScrolledWindow hidden
48
with an expander, but that might change).
50
The 'filter' function will be called with the tag and proposed value
51
as a unicode object. It should return an appropriate-transformed
52
filename, still as a unicode object.
54
If you need to work on a set of filenames at once, you should
55
instead overload the 'filter_list' function, which takes two lists;
56
one of original filenames, the other of proposed new filenames.
57
The default filter_list function calls filter on each item.
59
The plugin must provide either a 'changed' or 'preview'. 'preview'
60
causes the entire display to be re-previewed. 'changed' causes the
61
Preview button to made sensitive, and Save to be disabled.
63
If the 'active' attribute is false, the filter will not be called.
64
This is particularly useful for gtk.CheckButtons.
66
The '_order' attribute decides the sort order of the plugin. The
67
default filters have orders between 1 and 2. Plugins have order 0 by
68
default. Plugins with equal orders are sorted by class name."""
73
def filter(self, tag, value): return value
75
def __cmp__(self, other):
76
return (cmp(self._order, other._order) or
77
cmp(type(self).__name__, type(other).__name__))
79
class EditTagsPlugin(gtk.ImageMenuItem):
80
"""Plugins of this type are subclasses of gtk.ImageMenuItem.
81
They will be added to the context menu of the EditTags tree view.
83
The 'tags' attribute is a list of tags this plugin should appear on,
84
or false if it should appear for all tags. This must be a class
85
attribute, as it is checked before instantiation.
87
The 'needs' attribute is a list of tags that must be editable in
88
the currently selected songs for the plugin to be sensitive.
90
The constructor is called with the tag and value for that row. This
91
can be used to set the sensitivity of the menu item, or change its
94
When clicked, the 'activated' function is called on the object,
95
again with the tag name and value. It should return a list of
96
(tag, value) tuples to replace the previous tag/value with.
98
The '_order' attribute decides the sort order of the plugin. The
99
default items have orders between 0 and 1. Plugins have order 2.0 by
100
default. Plugins with equal orders are sorted by class name.
102
How to Handle Submenus
103
----------------------
104
If the menu item is given a submenu, magic happens. In particular,
105
the callbacks are proxied to the submenu's items, and are attached,
106
via connect_object, to make sure activated is called on the original
109
So, to handle submenus,
110
1. Make a submenu and attach it to your menu item.2
111
2. To each item in the submenu, attach its 'activate' signal to
112
something appropriate to prepare the original item's
114
3. Because that method will be called after the subitem is
115
clicked on, and your activate handler runs.
122
def activated(self, tag, value): return [(tag, value)]
124
def connect(self, signal, callback, *args, **kwargs):
125
if self.get_submenu():
126
for item in self.get_submenu().get_children():
127
item.connect_object(signal, callback, self, *args, **kwargs)
129
super(EditTagsPlugin, self).connect(
130
signal, callback, *args, **kwargs)
132
class EditingPlugins(Manager):
133
Kinds = [EditTagsPlugin, RenameFilesPlugin, TagsFromPathPlugin]
135
def RenamePlugins(self):
136
return super(EditingPlugins, self).find_subclasses(RenameFilesPlugin)
138
def TagsFromPathPlugins(self):
139
return super(EditingPlugins, self).find_subclasses(TagsFromPathPlugin)
141
def EditTagsPlugins(self):
142
return super(EditingPlugins, self).find_subclasses(EditTagsPlugin)