Patch GUI to handle 'new playlist' and 'glob curves to playlist'
[hooke.git] / hooke / playlist.py
index 906cb061a7c089220cfb49e07b679dd880c12b8a..f8199d4a5a44429044b690cbfd4ff50b6695f7af 100644 (file)
 
 import copy
 import hashlib
+import os
 import os.path
 import types
 import xml.dom.minidom
 
 from . import curve as curve
 from .compat import minidom as minidom  # dynamically patch xml.sax.minidom
+from .util.itertools import reverse_enumerate
 
 
 class NoteIndexList (list):
@@ -88,13 +90,27 @@ class NoteIndexList (list):
     def items(self, reverse=False):
         """Iterate through `self` calling `_setup_item` on each item
         before yielding.
+
+        Notes
+        -----
+        Updates :attr:`_index` during the iteration so
+        :func:`~hooke.plugin.curve.current_curve_callback` works as
+        expected in :class:`~hooke.command.Command`\s called from
+        :class:`~hooke.plugin.playlist.ApplyCommand`.  After the
+        iteration completes, :attr:`_index` is restored to its
+        original value.
         """
+        index = self._index
         items = self
         if reverse == True:
-            items = reversed(self)
-        for item in items:
+            items = reverse_enumerate(self)
+        else:
+            items = enumerate(self)
+        for i,item in items:
+            self._index = i
             self._setup_item(item)
             yield item
+        self._index = index
 
     def filter(self, keeper_fn=lambda item:True, *args, **kwargs):
         c = copy.deepcopy(self)
@@ -118,21 +134,16 @@ class Playlist (NoteIndexList):
         self.drivers = drivers
         self._loaded = [] # List of loaded curves, see :meth:`._setup_item`.
         self._max_loaded = 100 # curves to hold in memory simultaneously.
-        self._curve_load_args = ()
-        self._curve_load_kwargs = {}
 
-    def append_curve_by_path(self, path, info=None, identify=True):
+    def append_curve_by_path(self, path, info=None, identify=True, hooke=None):
         path = os.path.normpath(path)
         c = curve.Curve(path, info=info)
+        c.set_hooke(hooke)
         if identify == True:
             c.identify(self.drivers)
         self.append(c)
         return c
 
-    def set_curve_load_args(self, *args, **kwargs):
-        self._curve_load_args = args
-        self._curve_load_kwargs = kwargs
-
     def _setup_item(self, curve):
         if curve != None and curve not in self._loaded:
             if curve not in self:
@@ -140,7 +151,7 @@ class Playlist (NoteIndexList):
             if curve.driver == None:
                 c.identify(self.drivers)
             if curve.data == None:
-                curve.load(*self._curve_load_args, **self._curve_load_kwargs)
+                curve.load()
             self._loaded.append(curve)
             if len(self._loaded) > self._max_loaded:
                 oldest = self._loaded.pop(0)
@@ -148,6 +159,8 @@ class Playlist (NoteIndexList):
 
 
 class FilePlaylist (Playlist):
+    """A file-backed :class:`Playlist`.
+    """
     version = '0.1'
 
     def __init__(self, drivers, name=None, path=None):
@@ -231,7 +244,7 @@ class FilePlaylist (Playlist):
 
         Relative paths are interpreted relative to the location of the
         playlist file.
-        
+
         Examples
         --------
 
@@ -272,6 +285,11 @@ class FilePlaylist (Playlist):
                 or not isinstance(value, types.StringTypes)):
                 continue
             root.setAttribute(self._clean_key(key), str(value))
+        if self.path == None:
+            base_path = os.getcwd()
+        else:
+            base_path = os.path.abspath(
+                os.path.expanduser(self.path))
         for curve in self: # save curves and their attributes
             curve_element = doc.createElement('curve')
             root.appendChild(curve_element)
@@ -279,9 +297,7 @@ class FilePlaylist (Playlist):
             if absolute_paths == False:
                 path = os.path.relpath(
                     path,
-                    os.path.dirname(
-                        os.path.abspath(
-                            os.path.expanduser(self.path))))
+                    os.path.dirname(base_path))
             curve_element.setAttribute('path', path)
             for key,value in curve.info.items():
                 if (key in self._ignored_keys
@@ -344,18 +360,23 @@ class FilePlaylist (Playlist):
         doc = xml.dom.minidom.parseString(string)
         self._from_xml_doc(doc, identify=identify)
 
-    def load(self, path=None, identify=True):
+    def load(self, path=None, identify=True, hooke=None):
         """Load a playlist from a file.
         """
         self.set_path(path)
         doc = xml.dom.minidom.parse(self.path)
         self._from_xml_doc(doc, identify=identify)
         self._digest = self.digest()
+        for curve in self:
+            curve.set_hooke(hooke)
 
-    def save(self, path=None):
+    def save(self, path=None, makedirs=True):
         """Saves the playlist in a XML file.
         """
         self.set_path(path)
+        dirname = os.path.dirname(self.path) or '.'
+        if makedirs == True and not os.path.isdir(dirname):
+            os.makedirs(dirname)
         with open(self.path, 'w') as f:
             f.write(self.flatten())
             self._digest = self.digest()