test/data/vclamp_jpk/README: Document sample versions
[hooke.git] / hooke / ui / gui / panel / playlist.py
index c50da0bd1449763fdffb075464285b6ccfdb9e81..a2e8813d7d3d639d21234dd215a56222308169d0 100644 (file)
@@ -1,21 +1,19 @@
-# Copyright (C) 2010 Massimo Sandal <devicerandom@gmail.com>
-#                    W. Trevor King <wking@drexel.edu>
+# Copyright (C) 2010-2012 W. Trevor King <wking@tremily.us>
 #
 # This file is part of Hooke.
 #
-# Hooke is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# Hooke is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
 #
-# Hooke 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 Lesser General
-# Public License for more details.
+# Hooke 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 Lesser General Public License for more
+# details.
 #
-# You should have received a copy of the GNU Lesser General Public
-# License along with Hooke.  If not, see
-# <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Lesser General Public License
+# along with Hooke.  If not, see <http://www.gnu.org/licenses/>.
 
 """Playlist panel for Hooke.
 
@@ -198,63 +196,57 @@ class Tree (wx.TreeCtrl):
         del(self._hit_id)
         name = self._name_for_id[_id]
         if self._is_curve(name):
-            self.delete_curve(playlist_name=name[0], name=name[1])
+            self._delete_curve(playlist_name=name[0], name=name[1])
         else:
-            self.delete_playlist(name)
+            self._delete_playlist(name)
 
-    def delete_playlist(self, name):
+    def _delete_playlist(self, name):
         """Delete a :class:`hooke.playlist.Playlist` by name.
 
-        Called by the :meth:`_on_delete` handler.
-
-        Removes the playlist and its curves from the tree, then calls
-        :meth:`_delete_playlist`.
+        Called by the :meth:`_on_delete` handler.  Calls the
+        approptiate interface callback.
         """
         _id = self._id_for_name[name]
-        self.Delete(_id)
         playlist = self._playlists[name]
-        self._delete_playlist(playlist)
         in_callback(self, playlist)
 
-    def _delete_playlist(self, playlist):
-        """Adjust name/id caches for the playlist and its curves.
+    def delete_playlist(self, playlist):
+        """Respond to playlist deletion.
 
         Called on *every* playlist deletion.
         """
         self._playlists.pop(playlist.name)
         _id = self._id_for_name.pop(playlist.name)
+        self.Delete(_id)
         del(self._name_for_id[_id])
         for curve in playlist:
             self._delete_curve(playlist, curve)
-        in_callback(self, playlist)
 
-    def delete_curve(self, playlist_name, name):
+    def _delete_curve(self, playlist_name, name):
         """Delete a :class:`hooke.curve.Curve` by name.
 
-        Called by the :meth:`_on_delete` handler.
-
-        Removes the curve from the tree, then calls
-        :meth:`_delete_curve`.
+        Called by the :meth:`_on_delete` handler.  Calls the
+        approptiate interface callback.
         """
         _id = self._id_for_name[(playlist_name, name)]
-        self.Delete(_id)
         playlist = self._playlists[playlist_name]
         curve = None
         for i,c in enumerate(playlist):
             if c.name == name:
                 curve = c
                 break
-        self._delete_curve(playlist, curve)
+        if curve is None:
+            raise ValueError(name)
         in_callback(self, playlist, curve)
 
-    def _delete_curve(self, playlist, curve):
-        """Adjust name/id caches.
+    def delete_curve(self, playlist_name, name):
+        """Respond to curve deletions.
 
-        Called on _every_ curve deletion.
+        Called on *every* curve deletion.
         """
-        _id = self._id_for_name.pop((playlist.name, curve.name))
+        _id = self._id_for_name.pop((playlist_name, name))
+        self.Delete(_id)
         del(self._name_for_id[_id])
-        in_callback(self, playlist, curve)
 
     # Get selection
 
@@ -334,6 +326,30 @@ class Tree (wx.TreeCtrl):
         """Absorb changed `.index()`, etc.
         """
         self._playlists[playlist.name] = playlist
+        cnames = set()
+        for curve in playlist:
+            if (playlist.name, curve.name) not in self._id_for_name:
+                self._add_curve(playlist.name, curve)
+            cnames.add(curve.name)
+        for name in self._id_for_name.keys():
+            if not self._is_curve(name):
+                continue
+            pname,cname = name
+            if pname != playlist.name:
+                continue
+            if cname not in cnames:
+                self.delete_curve(playlist_name=pname, name=cname)
+
+    def is_playlist_loaded(self, playlist):
+        """Return `True` if `playlist` is loaded, `False` otherwise.
+        """
+        return self.is_playlist_name_loaded(playlist.name)
+
+    def is_playlist_name_loaded(self, name):
+        """Return `True` if a playlist named `name` is loaded, `False`
+        otherwise.
+        """
+        return name in self._playlists
 
 
 class Playlist (Panel, wx.Panel):
@@ -359,8 +375,8 @@ class Playlist (Panel, wx.Panel):
         # Following DRY and the LoD.
         for attribute_name in dir(self._c['tree']):
             if (attribute_name.startswith('_')
-                or 'playlist' not in attribute_name
-                or 'curve' not in attribute_name):
+                or ('playlist' not in attribute_name
+                    and 'curve' not in attribute_name)):
                 continue  # not an attribute we're interested in
             attr = getattr(self._c['tree'], attribute_name)
             if hasattr(attr, '__call__'):  # attr is a function / method