2 from Plugins.Plugin import PluginDescriptor
4 from Components.config import config, ConfigSubsection, ConfigSet
5 from Screens import PluginBrowser
6 from Screens.MessageBox import MessageBox
7 from Screens.ChoiceBox import ChoiceBox
8 from Components.PluginComponent import PluginComponent, plugins
9 from Components.PluginList import PluginEntryComponent
10 from Tools.Directories import resolveFilename, fileExists, SCOPE_SKIN_IMAGE, SCOPE_PLUGINS
12 from Components.ActionMap import ActionMap, NumberActionMap
13 from operator import attrgetter # python 2.5+
15 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
17 from enigma import eListboxPythonMultiContent, gFont
18 from Tools.LoadPixmap import LoadPixmap
20 from xml.etree.cElementTree import parse as cet_parse
22 from xml.etree.cElementTree import ParseError
23 except ImportError, ie:
24 ParseError = SyntaxError
26 from shutil import copyfile, Error
28 XML_CONFIG = "/etc/enigma2/pluginsort.xml"
30 def SelectedPluginEntryComponent(plugin):
31 if plugin.icon is None:
32 png = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/plugin.png"))
38 MultiContentEntryText(pos=(120, 5), size=(320, 25), font=0, text=plugin.name, backcolor_sel=1234566),
39 MultiContentEntryText(pos=(120, 26), size=(320, 17), font=1, text=plugin.description, backcolor_sel=1234566),
40 MultiContentEntryPixmapAlphaTest(pos=(10, 5), size=(100, 40), png = png, backcolor_sel=1234566)
44 pdict = PluginDescriptor.__dict__
46 if where.startswith('WHERE_'):
47 WHEREMAP[where] = pdict[where]
49 reverse = lambda map: dict(zip(map.values(), map.keys()))
57 if not fileExists(XML_CONFIG):
61 config = cet_parse(XML_CONFIG).getroot()
62 except ParseError, pe:
64 print "[PluginSort] Parse Error occured in configuration, backing it up and starting from scratch!"
66 copyfile(XML_CONFIG, "/etc/enigma2/pluginsort.xml.%d" % (int(time()),))
68 print "[PluginSort] Uh oh, failed to create the backup... I hope you have one anyway :D"
71 for wheresection in config.findall('where'):
72 where = wheresection.get('type')
73 whereid = WHEREMAP.get(where, None)
74 whereplugins = wheresection.findall('plugin')
75 if not whereid or not whereplugins:
76 print "[PluginSort] Ignoring section %s because of invalid id (%s) or no plugins" % (where, repr(whereid))
79 for plugin in whereplugins:
80 name = plugin.get('name')
82 weight = int(plugin.get('weight'))
83 except ValueError, ve:
84 print "[PluginSort] Invalid weight of %s received for plugin %s, ignoring" % (repr(plugin.get('weight')), repr(name))
86 self.plugins.setdefault(whereid, {})[name] = weight
89 list = ['<?xml version="1.0" ?>\n<pluginsort>\n\n']
93 idmap = reverse(WHEREMAP)
94 for key in self.plugins.keys():
95 whereplugins = self.plugins.get(key, None)
100 extend((' <where type="', str(where), '">\n'))
101 for key, value in whereplugins.iteritems():
102 extend((' <plugin name="', str(key), '" weight="', str(value), '" />\n'))
103 append((' </where>\n'))
104 append('\n</pluginsort>\n')
106 file = open(XML_CONFIG, 'w')
107 file.writelines(list)
110 def get(self, plugin):
111 for x in plugin.where:
112 whereplugins = self.plugins.get(x, None)
113 weight = whereplugins and whereplugins.get(plugin.name, None)
114 if weight is not None:
118 def set(self, plugin):
119 for x in plugin.where:
120 whereplugins = self.plugins.get(x, None)
122 whereplugins[plugin.name] = plugin.weight
124 self.plugins[x] = {plugin.name: plugin.weight}
126 pluginWeights = PluginWeights()
128 def PluginComponent_addPlugin(self, plugin, *args, **kwargs):
129 newWeight = pluginWeights.get(plugin)
130 print "[PluginSort] Setting weight of %s from %d to %d" % (plugin.name, plugin.weight, newWeight)
131 plugin.weight = newWeight
132 PluginComponent.pluginSorter_baseAddPlugin(self, plugin, *args, **kwargs)
134 OriginalPluginBrowser = PluginBrowser.PluginBrowser
135 class SortingPluginBrowser(OriginalPluginBrowser):
136 def __init__(self, *args, **kwargs):
137 OriginalPluginBrowser.__init__(self, *args, **kwargs)
138 self.skinName = ["SortingPluginBrowser", "PluginBrowser"]
140 self.movemode = False
143 self["MenuActions"] = ActionMap(["MenuActions"],
145 "menu": self.openMenu,
148 self["ColorActions"] = ActionMap(["ColorActions"],
150 "green": self.toggleMoveMode,
153 self["ColorActions"].setEnabled(False)
155 self["WizardActions"] = ActionMap(["WizardActions"],
163 # TODO: allow to select first 10 plugins by number (1-9, 0)
164 self["NumberActions"] = NumberActionMap(["NumberActions"],
166 "1": self.keyNumberGlobal,
167 "2": self.keyNumberGlobal,
168 "3": self.keyNumberGlobal,
169 "4": self.keyNumberGlobal,
170 "5": self.keyNumberGlobal,
171 "6": self.keyNumberGlobal,
172 "7": self.keyNumberGlobal,
173 "8": self.keyNumberGlobal,
174 "9": self.keyNumberGlobal,
175 "0": self.keyNumberGlobal,
179 def keyNumberGlobal(self, number):
180 if not self.movemode:
181 realnumber = (number - 1) % 10
182 if realnumber < len(self.list):
183 self["list"].moveToIndex(realnumber)
186 def close(self, *args, **kwargs):
188 self.toggleMoveMode()
189 OriginalPluginBrowser.close(self, *args, **kwargs)
191 # copied from PluginBrowser because we redo pretty much anything :-)
192 def updateList(self):
193 self.pluginlist = plugins.getPlugins(PluginDescriptor.WHERE_PLUGINMENU)
194 self.pluginlist.sort(key=attrgetter('weight', 'name')) # sort first by weight, then by name; we get pretty much a weight sorted but otherwise random list
195 self.list = [PluginEntryComponent(plugin) for plugin in self.pluginlist]
196 self["list"].l.setList(self.list)
197 if fileExists(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/plugin.py")):
198 # TRANSLATORS: leaving this empty is encouraged to not cause any confusion (this string was taken directly from the standard PluginBrowser)
199 self["red"].setText(_("Manage extensions"))
200 self["green"].setText(_("Sort") if not self.movemode else _("End Sort"))
201 self["SoftwareActions"].setEnabled(True)
202 self["PluginDownloadActions"].setEnabled(False)
203 self["ColorActions"].setEnabled(True)
205 # TRANSLATORS: leaving this empty is encouraged to not cause any confusion (this string was taken directly from the standard PluginBrowser)
206 self["red"].setText(_("Remove Plugins"))
207 # TRANSLATORS: leaving this empty is encouraged to not cause any confusion (this string was taken directly from the standard PluginBrowser)
208 self["green"].setText(_("Download Plugins"))
209 self["SoftwareActions"].setEnabled(False)
210 self["PluginDownloadActions"].setEnabled(True)
211 self["ColorActions"].setEnabled(False)
213 def doMove(self, func):
214 if self.selected != -1:
215 oldpos = self["list"].getSelectedIndex()
217 entry = self.list.pop(oldpos)
218 newpos = self["list"].getSelectedIndex()
219 self.list.insert(newpos, entry)
220 self["list"].l.setList(self.list)
225 self.doMove(self["list"].pageUp)
228 self.doMove(self["list"].pageDown)
231 self.doMove(self["list"].up)
234 self.doMove(self["list"].down)
237 selected = self.selected
238 if not self.movemode:
239 OriginalPluginBrowser.save(self)
241 Len = len(self.pluginlist)
242 newpos = self["list"].getSelectedIndex()
243 entry = self.pluginlist[selected]
244 self.pluginlist.remove(entry)
245 self.pluginlist.insert(newpos, entry)
247 # we moved up, increase weight of plugins after us
248 if newpos < selected:
249 print "[PluginSort]", entry.name, "moved up"
251 # since we moved up, there has to be an entry after this one
252 diff = abs(self.pluginlist[i].weight - self.pluginlist[newpos].weight) + 1
253 print "[PluginSort] Using weight from %d (%d) and %d (%d) to calculate diff (%d)" % (i, self.pluginlist[i].weight, newpos, self.pluginlist[newpos].weight, diff)
255 print "[PluginSort] INCREASE WEIGHT OF", self.pluginlist[i].name, "BY", diff
256 self.pluginlist[i].weight += diff
258 # we moved down, decrease weight of plugins before us
259 elif newpos > selected:
260 print "[PluginSort]", entry.name, "moved down"
262 # since we moved up, there has to be an entry before this one
263 diff = abs(self.pluginlist[newpos].weight - self.pluginlist[i].weight) + 1
264 print "[PluginSort] Using weight from %d (%d) and %d (%d) to calculate diff (%d)" % (newpos, self.pluginlist[newpos].weight, i, self.pluginlist[i].weight, diff)
266 print "[PluginSort] DECREASE WEIGHT OF", self.pluginlist[i].name, "BY", diff
267 self.pluginlist[i].weight -= diff
270 print "[PluginSort]", entry.name, "did not move (%d to %d)?" % (selected, newpos)
272 self.list = [PluginEntryComponent(plugin) for plugin in self.pluginlist]
273 print "[PluginSort] NEW LIST:", [(plugin.name, plugin.weight) for plugin in self.pluginlist]
274 self["list"].l.setList(self.list)
277 self.selected = self["list"].getSelectedIndex()
278 self.list[self.selected] = SelectedPluginEntryComponent(self.pluginlist[self.selected])
279 self["list"].l.setList(self.list)
282 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/PluginHider/plugin.py")):
284 (_("hide selected plugin"), self.hidePlugin),
285 (_("disable move mode") if self.movemode else _("enable move mode"), self.toggleMoveMode),
287 self.session.openWithCallback(
293 self.toggleMoveMode()
295 def menuCallback(self, ret):
298 def hidePlugin(self):
300 from Plugins.Extensions.PluginHider.plugin import hidePlugin
302 self.session.open(MessageBox, _("Unable to load PluginHider"), MessageBox.TYPE_ERROR)
304 hidePlugin(self["list"].l.getCurrentSelection()[0])
306 # we were actually in move mode, so save the current position
307 if self.selected != -1:
311 def toggleMoveMode(self):
313 if self.selected != -1:
315 if fileExists(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/plugin.py")):
316 self["green"].setText(_("Sort"))
318 for plugin in self.pluginlist:
319 pluginWeights.set(plugin)
322 if fileExists(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/plugin.py")):
323 self["green"].setText(_("End Sort"))
324 self.movemode = not self.movemode
326 def autostart(reason, *args, **kwargs):
328 PluginComponent.pluginSorter_baseAddPlugin = PluginComponent.addPlugin
329 PluginComponent.addPlugin = PluginComponent_addPlugin
331 # "fix" weight of plugins already added to list, future ones will be fixed automatically
332 for plugin in plugins.getPlugins(PluginDescriptor.WHERE_PLUGINMENU):
333 # enigma2 older than 3.3.2011 does not know plugin weights, so default them to 0 manually
335 newWeight = pluginWeights.get(plugin)
336 except AttributeError, ae:
339 PluginDescriptor.weight = 0
340 print "[PluginSort] Introduced weight attribute to PluginDescriptor for old enigma2 (this message may show multiple times)"
342 print "[PluginSort] Fixing weight for %s (was %d, now %d)" % (plugin.name, plugin.weight, newWeight)
343 plugin.weight = newWeight
345 PluginBrowser.PluginBrowser = SortingPluginBrowser
347 PluginComponent.addPlugin = PluginComponent.pluginSorter_baseAddPlugin
348 PluginBrowser.PluginBrowser = OriginalPluginBrowser
350 def Plugins(**kwargs):
353 where=PluginDescriptor.WHERE_AUTOSTART,
355 needsRestart=False, # TODO: check this!