All pastes #1916126 Raw Edit

Mine

public python v1 · immutable
#1916126 ·published 2010-08-13 01:39 UTC
rendered paste body
=== modified file 'xlgui/__init__.py'--- xlgui/__init__.py	2010-07-26 20:26:14 +0000+++ xlgui/__init__.py	2010-08-10 22:30:44 +0000@@ -61,7 +61,7 @@             @param exaile: The Exaile instance         """         from xlgui import icons, main, panel, tray, progress-        from xlgui.panel import collection, radio, playlists, files+        from xlgui.panel import collection, radio, playlists, files, playlist2          gtk.gdk.set_program_class("Exaile") @@ -102,8 +102,9 @@         self.panels['playlists'] = playlists.PlaylistsPanel(self.main.window,             exaile.playlists, exaile.smart_playlists, exaile.collection)         self.panels['files'] = files.FilesPanel(self.main.window, exaile.collection)+        self.panels['test'] = playlist2.TestPanel(self.main.window) -        for panel in ('collection', 'radio', 'playlists', 'files'):+        for panel in ('collection', 'radio', 'playlists', 'files', 'test'):             self.add_panel(*self.panels[panel].get_panel())          # add the device panels=== added file 'xlgui/panel/playlist2.py'--- xlgui/panel/playlist2.py	1970-01-01 00:00:00 +0000+++ xlgui/panel/playlist2.py	2010-08-13 01:29:37 +0000@@ -0,0 +1,352 @@+# Copyright (C) 2008-2010 Adam Olsen+#+# This program is free software; you can redistribute it and/or modify+# it under the terms of the GNU General Public License as published by+# the Free Software Foundation; either version 2, or (at your option)+# any later version.+#+# This program is distributed in the hope that it will be useful,+# but WITHOUT ANY WARRANTY; without even the implied warranty of+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+# GNU General Public License for more details.+#+# You should have received a copy of the GNU General Public License+# along with this program; if not, write to the Free Software+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.+#+#+# The developers of the Exaile media player hereby grant permission+# for non-GPL compatible GStreamer and Exaile plugins to be used and+# distributed together with GStreamer and Exaile. This permission is+# above and beyond the permissions granted by the GPL license by which+# Exaile is covered. If you modify this code, you may extend this+# exception to your version of the code, but you are not obligated to+# do so. If you do not wish to do so, delete this exception statement+# from your version.++import glib+import gobject+import gtk+import pango++from xl import common, settings++from xlgui import panel, icons++import logging+logger = logging.getLogger(__name__)++class BaseFolderTreeNode(gobject.GObject):+    __gsignals__ = {+        'activated': (+            gobject.SIGNAL_RUN_LAST,+            gobject.TYPE_NONE,+            tuple()+        ),+        'children-changed': (+            gobject.SIGNAL_RUN_LAST,+            gobject.TYPE_NONE,+            tuple()+        ),+        'icon-changed': (+            gobject.SIGNAL_RUN_LAST,+            gobject.TYPE_NONE,+            (gtk.gdk.Pixbuf,)+        ),+        'title-changed': (+            gobject.SIGNAL_RUN_LAST,+            gobject.TYPE_NONE,+            (str,)+        )+    }++    def __init__(self, icon, title, data):+        """+            icon: icon for the node+            title: display name for the node+            data: arbitrary data associated with the node+        """+        gobject.GObject.__init__(self)+        self._icon = icon+        self._title = title+        self.data = data+        self.draggable = False+        self.expand_by_default = False+        self.context_menu = None++    def get_icon(self):+        return self._icon++    def set_icon(self, icon):+        self._icon = icon+        self.emit('icon-changed', icon)++    icon = property(get_icon, set_icon)++    def get_title(self):+        return self._title++    def set_title(self, title):+        self._title = title+        self.emit('title-changed', title)++    title = property(get_title, set_title)++    def has_children(self):+        raise NotImplementedError++    def get_children(self):+        raise NotImplementedError++    def activate(self):+        self.emit('activated')++class FolderTreeNode(BaseFolderTreeNode):+    def __init__(self, icon, title, data):+        BaseFolderTreeNode.__init__(self, icon, title, data)+        self._children = []++    def append_child(self, child):+        self._children.append(child)+        self.emit('children-changed')++    def insert_child(self, index, child):+        self._children.insert(index, child)+        self.emit('children-changed')++    def remove_child(self, child):+        self._children.remove(child)+        self.emit('children-changed')++    def has_children(self):+        return len(self._children) > 0++    def get_children(self):+        return self._children[:]++class TracksFolderTreeNode(FolderTreeNode):+    def __init__(self, icon, title, data):+        FolderTreeNode.__init__(self, icon, title, data)++    def get_tracks(self):+        raise NotImplementedError++class FolderTree(gtk.TreeView):+    store_columns = (gtk.gdk.Pixbuf, gobject.TYPE_STRING, object)+    def __init__(self):+        gtk.TreeView.__init__(self)+        self.model = None+        self.root = FolderTreeNode(None, None, None)+        self.rebuild()++        pb = gtk.CellRendererPixbuf()+        cell = gtk.CellRendererText()+        if settings.get_option('gui/ellipsize_text_in_panels', False):+            cell.set_property('ellipsize-set', True)+            cell.set_property('ellipsize', pango.ELLIPSIZE_END)+        col = gtk.TreeViewColumn('Text')+        col.pack_start(pb, False)+        col.pack_start(cell, True)+        col.set_attributes(pb, pixbuf=0)+        col.set_attributes(cell, text=1)+        self.append_column(col)++        self.set_headers_visible(False)+        self.connect('row-expanded', self.on_row_expanded)+        self.connect('row-activated', self.on_row_activated)++        gobject.add_emission_hook(BaseFolderTreeNode, 'title-changed',+                self.on_node_title_changed)+        gobject.add_emission_hook(BaseFolderTreeNode, 'icon-changed',+                self.on_node_icon_changed)+        gobject.add_emission_hook(BaseFolderTreeNode, 'children-changed',+                self.on_node_children_changed)++        self.loading_icon = icons.MANAGER.pixbuf_from_icon_name('reload')+        self.error_icon = icons.MANAGER.pixbuf_from_icon_name('error')++    def rebuild(self):+        """+            Rebuilds the TreeStore from the root node.+        """+        model = gtk.TreeStore(*self.store_columns)+        self.model = model+        self.set_model(model)+        self._add_children(model, self.root, None)+++    def _add_children(self, model, node, parent):+        """+            Adds the children of the given node to the TreeStore,+            removing placeholders as needed.++            Generally should not be called directly - on_row_expanded+            will call this automatically as needed.+        """+        if parent:+            parent_path = model.get_path(parent)+        else:+            parent_path = None+        current_child = model.iter_children(parent)+        if current_child is not None:+            if model.get_value(current_child, 2) is not None:+                raise Exception, "Cannot add children to node %s: it already has children." % repr(node)+            model.set_value(current_child, 0, self.loading_icon)+            model.set_value(current_child, 1, "Loading...")+            model.set_value(current_child, 2, "Dummy string so we don't load twice")++        self.__add_children_getter(model, node, parent_path)++    @common.threaded+    def __add_children_getter(self, model, node, parent_path):+        try:+            children = node.get_children()+        except:+            # TODO: allow get_children() to raise a particular type of+            # exception that contains a message we can display to the+            # user, in the same place where Loading... is.+            common.log_exception(log=logger)+            if parent_path:+                parent = model.get_iter(parent_path)+            else:+                parent = None+            current_child = model.iter_children(parent)+            if current_child is not None:+                model.set_value(current_child, 0, self.error_icon)+                model.set_value(current_child, 1, "Failed to load.")+                model.set_value(current_child, 2, None)+        else:+            glib.idle_add(self.__add_children, model, node, parent_path, children)++    def __add_children(self, model, node, parent_path, children):+        if parent_path:+            parent = model.get_iter(parent_path)+        else:+            parent = None+        prev = None+        remove_first = False+        current_child = model.iter_children(parent)+        if current_child is not None:+            prev = current_child+            remove_first = True+        for child in children:+            iter = model.insert_after(parent, prev,+                    row=[child.icon, child.title, child])+            if child.has_children():+                model.insert(iter, 0, row=[None, "<FIXME>", None])+            if child.expand_by_default:+                self.expand_row(model.get_path(iter), open_all=False)+            prev = iter+        if remove_first:+            model.remove(model.iter_children(parent))++    def __get_node_iter_filter(self, model, path, iter, res):+        if model.get_value(iter, 2) == res[0]:+            res.append(iter)+            return True++    # FIXME: this is really inefficient - if we keep parent refs in+    # Nodes, then we can fix this to walk up the Node tree, then back+    # down the Gtk tree, which should be rather faster on large trees.+    def get_node_iter(self, node):+        res = [node]+        self.model.foreach(self.__get_node_iter_filter, res)+        if len(res) == 2:+            return res[1]+        return None++    def on_node_title_changed(self, node, title):+        iter = self.get_node_iter(node)+        if iter:+            self.model.set_value(iter, 1, title)+        return True++    def on_node_icon_changed(self, node, icon):+        iter = self.get_node_iter(node)+        if iter:+            self.model.set_value(iter, 0, icon)+        return True++    def on_node_children_changed(self, node):+        if node == self.root:+            self.rebuild()+        iter = self.get_node_iter(node)+        if iter:+            child = self.model.iter_children(iter)+            if child is not None:+                while self.model.remove(child):+                    pass+            self._add_children(self.model, node, iter)+        return True++    def on_row_expanded(self, view, iter, path):+        """+            Called when a row is expanded in the tree, used to lazily+            load additional nodes into the tree.+        """+        model = self.model+        child = model.iter_children(iter)+        if model.get_value(child, 2) == None:+            parent_node = model.get_value(iter, 2)+            self._add_children(model, parent_node, iter)++    def on_row_activated(self, view, path, column):+        """+            Called when a row is activated in the tree.+        """+        iter = self.model.get_iter(path)+        node = self.model.get_value(iter, 2)+        if not isinstance(node, BaseFolderTreeNode):+            return+        node.activate()+++class TestPanel(panel.Panel):+    ui_info = ('playlists_panel.ui', 'PlaylistsPanelWindow')+    def __init__(self, parent):+        panel.Panel.__init__(self, parent)+        self.box = self.builder.get_object('playlists_box')+        self.tree = FolderTree()+++        foldericon = icons.MANAGER.pixbuf_from_icon_name('folder')+        documenticon = icons.MANAGER.pixbuf_from_icon_name('txt')+        t1 = FolderTreeNode(foldericon, "This is a test", None)+        t1.expand_by_default = True+        t1c1 = FolderTreeNode(documenticon, "Activate test.", None)+        def activate_cb(node):+            node.title = "Passed!"+            node.icon = icons.MANAGER.pixbuf_from_icon_name('gtk-apply')+            if node.get_children():+                node.remove_child(node.get_children()[0])+            else:+                t1c1c1 = FolderTreeNode(documenticon, "Passed!", None)+                node.append_child(t1c1c1)+        t1c1.connect('activated', activate_cb)+        t1.append_child(t1c1)+        t1c2 = FolderTreeNode(foldericon, "Asyncronous Load Test", None)+        t1c2c1 = FolderTreeNode(documenticon, "Passed!", None)+        t1c2.append_child(t1c2c1)+        getter = t1c2.get_children+        def delayed_getter(*args):+            import time+            time.sleep(2)+            return getter(*args)+        t1c2.get_children = delayed_getter+        t1.append_child(t1c2)+        t1c3 = FolderTreeNode(foldericon, "Load Failure Test", None)+        t1c3c1 = FolderTreeNode(documenticon, "Failed!", None)+        t1c3.append_child(t1c3c1)+        def error_getter(*args):+            raise ValueError, "Test!"+        t1c3.get_children = error_getter+        t1.append_child(t1c3)+        self.tree.root.append_child(t1)+        t2 = FolderTreeNode(documenticon, "Test part 2", None)+        self.tree.root.append_child(t2)+++        self.box.pack_start(self.tree)+        self.box.show_all()++