Several changes while getting 'plot' panel working
[hooke.git] / hooke / ui / gui / panel / playlist.py
index 53eb9980a44251451dd2ce99d3163319a85c46e4..d9c996c6628c62a7a98af39d437fe814af26a5b5 100644 (file)
@@ -28,7 +28,9 @@ class Menu (wx.Menu):
 class Tree (wx.TreeCtrl):\r
     """:class:`wx.TreeCtrl` subclass handling playlist and curve selection.\r
     """\r
-    def __init__(self, config, callbacks, *args, **kwargs):\r
+    def __init__(self, *args, **kwargs):\r
+        self._panel = kwargs['parent']\r
+        self._callbacks = self._panel._callbacks # TODO: CallbackClass.set_callback{,s}()\r
         super(Tree, self).__init__(*args, **kwargs)\r
         imglist = wx.ImageList(width=16, height=16, mask=True, initialCount=2)\r
         imglist.Add(wx.ArtProvider.GetBitmap(\r
@@ -46,12 +48,8 @@ class Tree (wx.TreeCtrl):
             'root': self.AddRoot(text='Playlists', image=self.image['root'])\r
             }\r
         self.Bind(wx.EVT_RIGHT_DOWN, self._on_context_menu)\r
-        self.Bind(wx.EVT_TREE_SEL_CHANGED, self._on_curve_select)\r
-        self.Bind(wx.EVT_LEFT_DOWN, self._on_left_down)\r
-        self.Bind(wx.EVT_LEFT_DCLICK, self._on_left_doubleclick)\r
+        self.Bind(wx.EVT_TREE_SEL_CHANGED, self._on_select)\r
 \r
-        self.config = config\r
-        self._callbacks = callbacks\r
         self._setup_playlists()\r
 \r
     def _setup_playlists(self):\r
@@ -63,13 +61,6 @@ class Tree (wx.TreeCtrl):
         self._id_for_name = {}  # {name: id}\r
         self._name_for_id = {}  # {id: name}\r
 \r
-    def _name(self, name):\r
-        """Cleanup names according to configured preferences.\r
-        """\r
-        if self.config['hide extensions'] == 'True':  # HACK: config should decode\r
-            name,ext = os.path.splitext(name)\r
-        return name\r
-\r
     def _is_curve(self, name):  # name from ._id_for_name / ._name_for_id\r
         """Return `True` if `name` corresponds to a :class:`hooke.curve.Curve`.\r
         """\r
@@ -93,34 +84,8 @@ class Tree (wx.TreeCtrl):
                 return c_id\r
         raise KeyError(_id)\r
 \r
-    def _on_curve_select(self, event):\r
-        """Act on playlist/curve selection.\r
 \r
-        Currently just a hook for a potential callback.\r
-        """\r
-        _id = self.GetSelection()\r
-        name = self._name_for_id[self._canonical_id(_id)]\r
-        if self._is_curve(name):\r
-            playlist = self._playlists[name[0]]\r
-            curve = playlist.current()\r
-            in_callback(self, playlist, curve)\r
-\r
-    def _on_left_down(self, event):\r
-        """Select the clicked-on curve/playlist.\r
-        """ # TODO: dup with _on_curve_select?\r
-        hit_id, hit_flags = self.HitTest(event.GetPosition())\r
-        if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:\r
-            name = self._name_for_id[self._canonical_id(hit_id)]\r
-            if self._is_curve(name):\r
-                self.set_selected_curve(name[0], name[1])\r
-            else:\r
-                self.set_selected_playlist(name)\r
-        event.Skip()\r
-\r
-    def _on_left_doubleclick(self, event):\r
-        pass\r
-        #playlist.index = index\r
-        #event.Skip()\r
+    # Context menu\r
 \r
     def _on_context_menu(self, event):\r
         """Launch a popup :class:`Menu` with per-playlist/curve activities.\r
@@ -132,23 +97,22 @@ class Tree (wx.TreeCtrl):
             self.PopupMenu(menu, event.GetPosition())\r
             menu.Destroy()\r
 \r
-    def _on_delete(self, event):\r
-        """Handler for :class:`Menu`'s `Delete` button.\r
-\r
-        Determines the clicked item and calls the appropriate\r
-        `.delete_*()` method on it.\r
-        """\r
-        #if hasattr(self, '_hit_id'):  # called via ._c['menu']\r
-        _id = self._hit_id\r
-        del(self._hit_id)\r
-        name = self._name_for_id[_id]\r
-        if self._is_curve(name):\r
-            self.delete_curve(playlist_name=name[0], name=name[1])\r
-        else:\r
-            self.delete_playlist(name)\r
+    # Add\r
+    #   add_* called directly by HookeFrame\r
+    #   _add_* called on every addition\r
 \r
     def add_playlist(self, playlist):\r
         """Add a :class:`hooke.playlist.Playlist` to the tree.\r
+\r
+        Calls :meth:`_add_playlist` and triggers a callback.\r
+        """\r
+        self._add_playlist(playlist)\r
+        in_callback(self, playlist)\r
+\r
+    def _add_playlist(self, playlist):\r
+        """Add a class:`hooke.playlist.Playlist` to the tree.\r
+\r
+        No callback triggered.\r
         """\r
         if playlist.name not in self._playlists:\r
             pass\r
@@ -157,36 +121,67 @@ class Tree (wx.TreeCtrl):
         self._playlists[playlist.name] = playlist\r
         p_id = self.AppendItem(\r
             parent=self._c['root'],\r
-            text=self._name(playlist.name),\r
+            text=self._panel._hooke_frame._file_name(playlist.name),\r
             image=self.image['playlist'])\r
         self._id_for_name[playlist.name] = p_id\r
         self._name_for_id[p_id] = playlist.name\r
-\r
-        # temporarily disable any add_curve callbacks\r
-        acc = self._callbacks.get('add_curve', None)\r
-        self._callbacks['add_curve'] = None\r
-\r
         for curve in playlist:\r
-            self.add_curve(playlist.name, curve)\r
-\r
-        # restore the add_curve callback\r
-        self._callbacks['add_curve'] = acc\r
-\r
-        in_callback(self, playlist)\r
+            self._add_curve(playlist.name, curve)\r
 \r
     def add_curve(self, playlist_name, curve):\r
         """Add a :class:`hooke.curve.Curve` to a curently loaded playlist.\r
+\r
+        Calls :meth:`_add_curve` and triggers a callback.\r
+        """\r
+        self._add_curve(playlist_name, curve)\r
+        playlist = self._playlists[playlist_name]\r
+        in_callback(self, playlist, curve)\r
+\r
+    def _add_curve(self, playlist_name, curve):\r
+        """Add a class:`hooke.curve.Curve` to the tree.\r
+\r
+        No callback triggered.\r
         """\r
         p = self._playlists[playlist_name]\r
         if curve not in p:\r
             p.append(curve)\r
         c_id = self.AppendItem(\r
             parent=self._id_for_name[playlist_name],\r
-            text=self._name(curve.name),\r
+            text=self._panel._hooke_frame._file_name(curve.name),\r
             image=self.image['curve'])\r
         self._id_for_name[(p.name, curve.name)] = c_id\r
         self._name_for_id[c_id] = (p.name, curve.name)\r
-        in_callback(self, p, curve)\r
+\r
+    @callback\r
+    def generate_new_playlist(self):\r
+        pass  # TODO\r
+\r
+    def _GetUniquePlaylistName(self, name):  # TODO\r
+        playlist_name = name\r
+        count = 1\r
+        while playlist_name in self.playlists:\r
+            playlist_name = ''.join([name, str(count)])\r
+            count += 1\r
+        return playlist_name\r
+\r
+    # Delete\r
+    #   delete_* called by _on_delete handler (user click) or HookeFrame\r
+    #   _delete_* called on every deletion\r
+\r
+    def _on_delete(self, event):\r
+        """Handler for :class:`Menu`'s `Delete` button.\r
+\r
+        Determines the clicked item and calls the appropriate\r
+        `.delete_*()` method on it.\r
+        """\r
+        #if hasattr(self, '_hit_id'):  # called via ._c['menu']\r
+        _id = self._hit_id\r
+        del(self._hit_id)\r
+        name = self._name_for_id[_id]\r
+        if self._is_curve(name):\r
+            self.delete_curve(playlist_name=name[0], name=name[1])\r
+        else:\r
+            self.delete_playlist(name)\r
 \r
     def delete_playlist(self, name):\r
         """Delete a :class:`hooke.playlist.Playlist` by name.\r
@@ -242,11 +237,17 @@ class Tree (wx.TreeCtrl):
         del(self._name_for_id[_id])\r
         in_callback(self, playlist, curve)\r
 \r
+    # Get selection\r
+\r
     def get_selected_playlist(self):\r
         """Return the selected :class:`hooke.playlist.Playlist`.\r
         """\r
         _id = self.GetSelection()\r
-        name = self._name_for_id[self._canonical_id(_id)]\r
+        try:\r
+            _id = self._canonical_id(_id)\r
+        except KeyError:  # no playlist selected\r
+            return None\r
+        name = self._name_for_id[_id]\r
         if self._is_curve(name):\r
             name = name[0]\r
         return self._playlists[name]\r
@@ -259,58 +260,69 @@ class Tree (wx.TreeCtrl):
         if self._is_curve(name):\r
             p_name,c_name = name\r
             playlist = self._playlists[p_name]\r
-            index = [i for i,c in enumerate(playlist) if c.name == c_name]\r
-            playlist.jump(index)\r
+            c = playlist.current()\r
+            assert c.name == c_name, '%s != %s' % (c.name, c_name)\r
         else:\r
             playlist = self._playlists[name]\r
         return playlist.current()\r
 \r
-    def set_selected_playlist(self, name):\r
-        """Set the selected :class:`hooke.playlist.Playlist` by name.\r
-        """\r
-        playlist = self._playlists[name]\r
-        curve = playlist.current()\r
-        self.set_selected_curve(playlist.name, curve.name)\r
+    # Set selection (via user interaction with this panel)\r
+    #\r
+    # These are hooks for HookeFrame callbacks which will send\r
+    # the results back via 'get curve' calling 'set_selected_curve'.\r
 \r
-    def set_selected_curve(self, playlist_name, name):\r
-        """Set the selected :class:`hooke.curve.Curve` by name.\r
+    def _on_select(self, event):\r
+        """Select the clicked-on curve/playlist.\r
         """\r
-        playlist = self._playlists[playlist.name]\r
-        for i,curve in enumerate(playlist):\r
-            if curve.name == name:\r
-                playlist.jump(i)\r
+        _id = self.GetSelection()\r
+        name = self._name_for_id[self._canonical_id(_id)]\r
+        if self._is_curve(name):\r
+            p_name,c_name = name\r
+            self._on_set_selected_curve(p_name, c_name)\r
+        else:\r
+            self._on_set_selected_playlist(name)\r
+\r
+    def _on_set_selected_playlist(self, name):\r
+        in_callback(self, self._playlists[name])\r
+\r
+    def _on_set_selected_curve(self, playlist_name, name):\r
+        playlist = self._playlists[playlist_name]\r
+        curve = None\r
+        for i,c in enumerate(playlist):\r
+            if c.name == name:\r
+                curve = c\r
                 break\r
-        curve = playlist.current()\r
-        _id = self._id_for_name[(playlist.name, curve.name)]\r
-        self.Expand(self._id_for_name[playlist.name])\r
-        self.SelectItem(_id)\r
-        in_callback(self, playlist, curve) # TODO: dup callback with _on_curve_select\r
+        if curve == None:\r
+            raise ValueError(name)\r
+        in_callback(self, playlist, curve)\r
+        \r
+    # Set selection (from the HookeFrame)\r
 \r
-    @callback\r
-    def generate_new_playlist(self):\r
-        pass\r
+    def set_selected_curve(self, playlist, curve):\r
+        """Make the curve the playlist's current curve.\r
+        """\r
+        print 'expanding', playlist.name\r
+        self.Expand(self._id_for_name[playlist.name])\r
+        self.Unbind(wx.EVT_TREE_SEL_CHANGED)\r
+        print 'selecting', curve.name\r
+        self.SelectItem(self._id_for_name[(playlist.name, curve.name)])\r
+        self.Bind(wx.EVT_TREE_SEL_CHANGED, self._on_select)\r
 \r
-    def _GetUniquePlaylistName(self, name):\r
-        playlist_name = name\r
-        count = 1\r
-        while playlist_name in self.playlists:\r
-            playlist_name = ''.join([name, str(count)])\r
-            count += 1\r
-        return playlist_name\r
+    def update_playlist(self, playlist):\r
+        """Absorb changed `._index`, etc.\r
+        """\r
+        self._playlists[playlist.name] = playlist\r
 \r
 \r
 class Playlist (Panel, wx.Panel):\r
     """:class:`wx.Panel` subclass wrapper for :class:`Tree`.\r
     """\r
-    def __init__(self, config, callbacks, *args, **kwargs):\r
+    def __init__(self, callbacks=None, **kwargs):\r
         # Use the WANTS_CHARS style so the panel doesn't eat the Return key.\r
-        super(Playlist, self).__init__(*args, **kwargs)\r
-        self.name = 'playlist panel'\r
-\r
+        super(Playlist, self).__init__(\r
+            name='playlist', callbacks=callbacks, **kwargs)\r
         self._c = {\r
             'tree': Tree(\r
-                config=config,\r
-                callbacks=callbacks,\r
                 parent=self,\r
                 size=wx.Size(160, 250),\r
                 style=wx.TR_DEFAULT_STYLE | wx.NO_BORDER | wx.TR_HIDE_ROOT),\r