Broke menu, navbar, statusbar, panel.notebook, and panel.welcome out of gui
[hooke.git] / hooke / ui / gui / __init__.py
index e4c966b991dbf8be425361d765e45a156e13e3be..69f5a61588a7c698ba9bdbbb45f3a43942b722ae 100644 (file)
@@ -31,145 +31,24 @@ from ...command import CommandExit, Exit, Command, Argument, StoreValue
 from ...config import Setting\r
 from ...interaction import Request, BooleanRequest, ReloadUserInterfaceConfig\r
 from ...ui import UserInterface, CommandMessage\r
+from . import menu as menu\r
+from . import navbar as navbar\r
 from . import panel as panel\r
 from . import prettyformat as prettyformat\r
-\r
-\r
-class Notebook (aui.AuiNotebook):\r
-    def __init__(self, *args, **kwargs):\r
-        super(Notebook, self).__init__(*args, **kwargs)\r
-        self.SetArtProvider(aui.AuiDefaultTabArt())\r
-        #uncomment if we find a nice icon\r
-        #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))\r
-        self.AddPage(self._welcome_window(), 'Welcome')\r
-\r
-    def _welcome_window(self):\r
-        #TODO: move into panel.welcome\r
-        ctrl = wx.html.HtmlWindow(parent=self, size=wx.Size(400, 300))\r
-        lines = [\r
-            '<h1>Welcome to Hooke</h1>',\r
-            '<h3>Features</h3>',\r
-            '<ul>',\r
-            '<li>View, annotate, measure force files</li>',\r
-            '<li>Worm-like chain fit of force peaks</li>',\r
-            '<li>Automatic convolution-based filtering of empty files</li>',\r
-            '<li>Automatic fit and measurement of multiple force peaks</li>',\r
-            '<li>Handles force-clamp force experiments (experimental)</li>',\r
-            '<li>It is extensible through plugins and drivers</li>',\r
-            '</ul>',\r
-            '<p>See the <a href="%s">DocumentationIndex</a>'\r
-            % 'http://code.google.com/p/hooke/wiki/DocumentationIndex',\r
-            'for more information</p>',\r
-            ]\r
-        ctrl.SetPage('\n'.join(lines))\r
-        return ctrl\r
-\r
-\r
-class NavBar (wx.ToolBar):\r
-    def __init__(self, *args, **kwargs):\r
-        super(NavBar, self).__init__(*args, **kwargs)\r
-        self.SetToolBitmapSize(wx.Size(16,16))\r
-        self._c = {\r
-            'previous': self.AddLabelTool(\r
-                id=wx.ID_PREVIEW_PREVIOUS,\r
-                label='Previous',\r
-                bitmap=wx.ArtProvider_GetBitmap(\r
-                    wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16)),\r
-                shortHelp='Previous curve'),\r
-            'next': self.AddLabelTool(\r
-                id=wx.ID_PREVIEW_NEXT,\r
-                label='Next',\r
-                bitmap=wx.ArtProvider_GetBitmap(\r
-                    wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16)),\r
-                shortHelp='Next curve'),\r
-            }\r
-        self.Realize()\r
-\r
-\r
-class FileMenu (wx.Menu):\r
-    def __init__(self, *args, **kwargs):\r
-        super(FileMenu, self).__init__(*args, **kwargs)\r
-        self._c = {'exit': self.Append(wx.ID_EXIT)}\r
-\r
-\r
-class ViewMenu (wx.Menu):\r
-    def __init__(self, *args, **kwargs):\r
-        super(ViewMenu, self).__init__(*args, **kwargs)\r
-        self._c = {\r
-            'folders': self.AppendCheckItem(id=wx.ID_ANY, text='Folders\tF5'),\r
-            'playlist': self.AppendCheckItem(\r
-                id=wx.ID_ANY, text='Playlists\tF6'),\r
-            'commands': self.AppendCheckItem(\r
-                id=wx.ID_ANY, text='Commands\tF7'),\r
-            'assistant': self.AppendCheckItem(\r
-                id=wx.ID_ANY, text='Assistant\tF9'),\r
-            'properties': self.AppendCheckItem(\r
-                id=wx.ID_ANY, text='Properties\tF8'),\r
-            'results': self.AppendCheckItem(id=wx.ID_ANY, text='Results\tF10'),\r
-            'output': self.AppendCheckItem(id=wx.ID_ANY, text='Output\tF11'),\r
-            'note': self.AppendCheckItem(id=wx.ID_ANY, text='Note\tF12'),\r
-            }\r
-        for item in self._c.values():\r
-            item.Check()\r
-\r
-\r
-class PerspectiveMenu (wx.Menu):\r
-    def __init__(self, *args, **kwargs):\r
-        super(PerspectiveMenu, self).__init__(*args, **kwargs)\r
-        self._c = {}\r
-\r
-    def update(self, perspectives, selected, callback):\r
-        """Rebuild the perspectives menu.\r
-        """\r
-        for item in self.GetMenuItems():\r
-            self.UnBind(item)\r
-            self.DeleteItem(item)\r
-        self._c = {\r
-            'save': self.Append(id=wx.ID_ANY, text='Save Perspective'),\r
-            'delete': self.Append(id=wx.ID_ANY, text='Delete Perspective'),\r
-            }\r
-        self.AppendSeparator()\r
-        for label in perspectives:\r
-            self._c[label] = self.AppendRadioItem(id=wx.ID_ANY, text=label)\r
-            self.Bind(wx.EVT_MENU, callback, self._c[label])\r
-            if label == selected:\r
-                self._c[label].Check(True)\r
-            \r
-\r
-class HelpMenu (wx.Menu):\r
-    def __init__(self, *args, **kwargs):\r
-        super(HelpMenu, self).__init__(*args, **kwargs)\r
-        self._c = {'about':self.Append(id=wx.ID_ABOUT)}\r
-\r
-\r
-class MenuBar (wx.MenuBar):\r
-    def __init__(self, *args, **kwargs):\r
-        super(MenuBar, self).__init__(*args, **kwargs)\r
-        self._c = {\r
-            'file': FileMenu(),\r
-            'view': ViewMenu(),\r
-            'perspective': PerspectiveMenu(),\r
-            'help': HelpMenu(),\r
-            }\r
-        self.Append(self._c['file'], 'File')\r
-        self.Append(self._c['view'], 'View')\r
-        self.Append(self._c['perspective'], 'Perspective')\r
-        self.Append(self._c['help'], 'Help')\r
-\r
-\r
-class StatusBar (wx.StatusBar):\r
-    def __init__(self, *args, **kwargs):\r
-        super(StatusBar, self).__init__(*args, **kwargs)\r
-        self.SetStatusWidths([-2, -3])\r
-        self.SetStatusText('Ready', 0)\r
-        self.SetStatusText(u'Welcome to Hooke (version %s)' % version(), 1)\r
+from . import statusbar as statusbar\r
 \r
 \r
 class HookeFrame (wx.Frame):\r
-    def __init__(self, gui, commands, *args, **kwargs):\r
+    """The main Hooke-interface window.\r
+\r
+    \r
+    """\r
+    def __init__(self, gui, commands, inqueue, outqueue, *args, **kwargs):\r
         super(HookeFrame, self).__init__(*args, **kwargs)\r
         self.gui = gui\r
         self.commands = commands\r
+        self.inqueue = inqueue\r
+        self.outqueue = outqueue\r
         self._perspectives = {}  # {name: perspective_str}\r
         self._c = {}\r
 \r
@@ -196,11 +75,14 @@ class HookeFrame (wx.Frame):
 \r
         # Create the menubar after the panes so that the default\r
         # perspective is created with all panes open\r
-        self._c['menu bar'] = MenuBar(\r
+        self._c['menu bar'] = menu.MenuBar(\r
+            callbacks={},\r
             )\r
         self.SetMenuBar(self._c['menu bar'])\r
 \r
-        self._c['status bar'] = StatusBar(self, style=wx.ST_SIZEGRIP)\r
+        self._c['status bar'] = statubar.StatusBar(\r
+            parent=self,\r
+            style=wx.ST_SIZEGRIP)\r
 \r
         self._update_perspectives()\r
         self._bind_events()\r
@@ -226,7 +108,13 @@ class HookeFrame (wx.Frame):
                     style=wx.DIRCTRL_SHOW_FILTERS,\r
                     filter=self.gui.config['folders-filters'],\r
                     defaultFilter=int(self.gui.config['folders-filter-index'])), 'left'),  #HACK: config should convert\r
-            ('playlists', panel.playlist.Playlist(self), 'left'),\r
+            ('playlists', panel.playlist.Playlist(\r
+                    config=self.gui.config,\r
+                    callbacks={},\r
+                    parent=self,\r
+                    style=wx.WANTS_CHARS|wx.NO_BORDER,\r
+                    # WANTS_CHARS so the panel doesn't eat the Return key.\r
+                    size=(160, 200)), 'left'),\r
             ('note', panel.note.Note(self), 'left'),\r
             ('notebook', Notebook(\r
                     parent=self,\r
@@ -237,6 +125,12 @@ class HookeFrame (wx.Frame):
             ('commands', panel.commands.Commands(\r
                     commands=self.commands,\r
                     selected=self.gui.config['selected command'],\r
+                    callbacks={\r
+                        'execute': self.execute_command,\r
+                        'select_plugin': self.select_plugin,\r
+                        'select_command': self.select_command,\r
+#                        'selection_changed': self.panelProperties.select(self, method, command),  #SelectedTreeItem = selected_item,\r
+                        },\r
                     parent=self,\r
                     style=wx.WANTS_CHARS|wx.NO_BORDER,\r
                     # WANTS_CHARS so the panel doesn't eat the Return key.\r
@@ -273,7 +167,13 @@ class HookeFrame (wx.Frame):
         self._c['manager'].AddPane(panel, info)\r
 \r
     def _setup_toolbars(self):\r
-        self._c['navbar'] = NavBar(self, style=wx.TB_FLAT | wx.TB_NODIVIDER)\r
+        self._c['navbar'] = navbar.NavBar(\r
+            callbacks={\r
+                'next': self._next_curve,\r
+                'previous': self._previous_curve,\r
+                },\r
+            parent=self,\r
+            style=wx.TB_FLAT | wx.TB_NODIVIDER)\r
 \r
         self._c['manager'].AddPane(\r
             self._c['navbar'],\r
@@ -301,19 +201,11 @@ class HookeFrame (wx.Frame):
         self.Bind(wx.EVT_MENU, self._on_delete_perspective,\r
                   self._c['menu bar']._c['perspective']._c['delete'])\r
 \r
-        self.Bind(wx.EVT_TOOL, self._on_next, self._c['navbar']._c['next'])\r
-        self.Bind(wx.EVT_TOOL, self._on_previous,self._c['navbar']._c['previous'])\r
-\r
         treeCtrl = self._c['folders'].GetTreeCtrl()\r
         treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self._on_dir_ctrl_left_double_click)\r
         \r
+        # TODO: playlist callbacks\r
         return # TODO: cleanup\r
-        self._c['playlists'].PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)\r
-        self._c['playlists'].PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)\r
-        #commands tree\r
-        evtmgr.eventManager.Register(self.OnExecute, wx.EVT_BUTTON, self._c['commands'].ExecuteButton)\r
-        evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self._c['commands']._c['tree'])\r
-        evtmgr.eventManager.Register(self.OnTreeCtrlItemActivated, wx.EVT_TREE_ITEM_ACTIVATED, self._c['commands']._c['tree'])\r
         evtmgr.eventManager.Register(self.OnUpdateNote, wx.EVT_BUTTON, self.panelNote.UpdateButton)\r
         #property editor\r
         self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)\r
@@ -323,16 +215,16 @@ class HookeFrame (wx.Frame):
     def _GetActiveFileIndex(self):\r
         lib.playlist.Playlist = self.GetActivePlaylist()\r
         #get the selected item from the tree\r
-        selected_item = self._c['playlists'].PlaylistsTree.GetSelection()\r
+        selected_item = self._c['playlists']._c['tree'].GetSelection()\r
         #test if a playlist or a curve was double-clicked\r
-        if self._c['playlists'].PlaylistsTree.ItemHasChildren(selected_item):\r
+        if self._c['playlists']._c['tree'].ItemHasChildren(selected_item):\r
             return -1\r
         else:\r
             count = 0\r
-            selected_item = self._c['playlists'].PlaylistsTree.GetPrevSibling(selected_item)\r
+            selected_item = self._c['playlists']._c['tree'].GetPrevSibling(selected_item)\r
             while selected_item.IsOk():\r
                 count += 1\r
-                selected_item = self._c['playlists'].PlaylistsTree.GetPrevSibling(selected_item)\r
+                selected_item = self._c['playlists']._c['tree'].GetPrevSibling(selected_item)\r
             return count\r
 \r
     def _GetPlaylistTab(self, name):\r
@@ -341,14 +233,6 @@ class HookeFrame (wx.Frame):
                 return index\r
         return -1\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
-\r
     def _restore_perspective(self, name):\r
         # TODO: cleanup\r
         self.gui.config['active perspective'] = name  # TODO: push to engine's Hooke\r
@@ -365,11 +249,45 @@ class HookeFrame (wx.Frame):
         perspectivesFile.write(perspective)\r
         perspectivesFile.close()\r
 \r
-    def AddPlaylist(self, playlist=None, name='Untitled'):\r
-        if playlist and playlist.count > 0:\r
-            playlist.name = self._GetUniquePlaylistName(name)\r
-            playlist.reset()\r
-            self.AddToPlaylists(playlist)\r
+    def execute_command(self, _class, method, command, args):\r
+        self.cmd.inqueue.put(CommandMessage(command, args))\r
+        while True:\r
+            msg = self.cmd.outqueue.get()\r
+            if isinstance(msg, Exit):\r
+                return True\r
+            elif isinstance(msg, CommandExit):\r
+                self.cmd.stdout.write(msg.__class__.__name__+'\n')\r
+                self.cmd.stdout.write(str(msg).rstrip()+'\n')\r
+                break\r
+            elif isinstance(msg, ReloadUserInterfaceConfig):\r
+                self.cmd.ui.reload_config(msg.config)\r
+                continue\r
+            elif isinstance(msg, Request):\r
+                self._handle_request(msg)\r
+                continue\r
+            self.cmd.stdout.write(str(msg).rstrip()+'\n')\r
+                #TODO: run the command\r
+                #command = ''.join(['self.do_', item_text, '()'])\r
+                #self.AppendToOutput(command + '\n')\r
+                #exec(command)\r
+\r
+    def select_plugin(self, _class, method, plugin):\r
+        for option in config[section]:\r
+            properties.append([option, config[section][option]])\r
+\r
+    def select_command(self, _class, method, command):\r
+        self.select_plugin(command.plugin)\r
+        plugin = self.GetItemText(selected_item)\r
+        if plugin != 'core':\r
+            doc_string = eval('plugins.' + plugin + '.' + plugin + 'Commands.__doc__')\r
+        else:\r
+            doc_string = 'The module "core" contains Hooke core functionality'\r
+        if doc_string is not None:\r
+            self.panelAssistant.ChangeValue(doc_string)\r
+        else:\r
+            self.panelAssistant.ChangeValue('')\r
+        panel.propertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)\r
+        self.gui.config['selected command'] = command\r
 \r
     def AddPlaylistFromFiles(self, files=[], name='Untitled'):\r
         if files:\r
@@ -381,34 +299,6 @@ class HookeFrame (wx.Frame):
             playlist.reset()\r
             self.AddTayliss(playlist)\r
 \r
-    def AddToPlaylists(self, playlist):\r
-        if playlist.count > 0:\r
-            #setup the playlist in the Playlist tree\r
-            tree_root = self._c['playlists'].PlaylistsTree.GetRootItem()\r
-            playlist_root = self._c['playlists'].PlaylistsTree.AppendItem(tree_root, playlist.name, 0)\r
-            #add all files to the Playlist tree\r
-#            files = {}\r
-            hide_curve_extension = self.GetBoolFromConfig('core', 'preferences', 'hide_curve_extension')\r
-            for index, file_to_add in enumerate(playlist.files):\r
-                #optionally remove the extension from the name of the curve\r
-                if hide_curve_extension:\r
-                    file_to_add.name = lh.remove_extension(file_to_add.name)\r
-                file_ID = self._c['playlists'].PlaylistsTree.AppendItem(playlist_root, file_to_add.name, 1)\r
-                if index == playlist.index:\r
-                    self._c['playlists'].PlaylistsTree.SelectItem(file_ID)\r
-            playlist.reset()\r
-            #create the plot tab and add playlist to the dictionary\r
-            plotPanel = panel.plot.PlotPanel(self, ID_FirstPlot + len(self.playlists))\r
-            notebook_tab = self._c['notebook'].AddPage(plotPanel, playlist.name, True)\r
-            #tab_index = self._c['notebook'].GetSelection()\r
-            playlist.figure = plotPanel.get_figure()\r
-            self.playlists[playlist.name] = playlist\r
-            #self.playlists[playlist.name] = [playlist, figure]\r
-            self._c['playlists'].PlaylistsTree.Expand(playlist_root)\r
-            self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
-            self.UpdateNote()\r
-            self.UpdatePlot()\r
-\r
     def AppendToOutput(self, text):\r
         self.panelOutput.AppendText(''.join([text, '\n']))\r
 \r
@@ -430,20 +320,6 @@ class HookeFrame (wx.Frame):
                     manipulated_plot = plotmanipulator.method(manipulated_plot, plot_file)\r
             return manipulated_plot\r
 \r
-    def DeleteFromPlaylists(self, name):\r
-        if name in self.playlists:\r
-            del self.playlists[name]\r
-        tree_root = self._c['playlists'].PlaylistsTree.GetRootItem()\r
-        item, cookie = self._c['playlists'].PlaylistsTree.GetFirstChild(tree_root)\r
-        while item.IsOk():\r
-            playlist_name = self._c['playlists'].PlaylistsTree.GetItemText(item)\r
-            if playlist_name == name:\r
-                try:\r
-                    self._c['playlists'].PlaylistsTree.Delete(item)\r
-                except:\r
-                    pass\r
-            item = self._c['playlists'].PlaylistsTree.GetNextSibling(item)\r
-\r
     def GetActiveFigure(self):\r
         playlist_name = self.GetActivePlaylistName()\r
         figure = self.playlists[playlist_name].figure\r
@@ -457,24 +333,6 @@ class HookeFrame (wx.Frame):
             return playlist.get_active_file()\r
         return None\r
 \r
-    def GetActivePlaylist(self):\r
-        playlist_name = self.GetActivePlaylistName()\r
-        if playlist_name in self.playlists:\r
-            return self.playlists[playlist_name]\r
-        return None\r
-\r
-    def GetActivePlaylistName(self):\r
-        #get the selected item from the tree\r
-        selected_item = self._c['playlists'].PlaylistsTree.GetSelection()\r
-        #test if a playlist or a curve was double-clicked\r
-        if self._c['playlists'].PlaylistsTree.ItemHasChildren(selected_item):\r
-            playlist_item = selected_item\r
-        else:\r
-            #get the name of the playlist\r
-            playlist_item = self._c['playlists'].PlaylistsTree.GetItemParent(selected_item)\r
-        #now we have a playlist\r
-        return self._c['playlists'].PlaylistsTree.GetItemText(playlist_item)\r
-\r
     def GetActivePlot(self):\r
         playlist = self.GetActivePlaylist()\r
         if playlist is not None:\r
@@ -687,19 +545,7 @@ class HookeFrame (wx.Frame):
     def _on_erase_background(self, event):\r
         event.Skip()\r
 \r
-    def OnExecute(self, event):\r
-        item = self._c['commands']._c['tree'].GetSelection()\r
-        if item.IsOk():\r
-            if not self._c['commands']._c['tree'].ItemHasChildren(item):\r
-                item_text = self._c['commands']._c['tree'].GetItemText(item)\r
-                command = ''.join(['self.do_', item_text, '()'])\r
-                #self.AppendToOutput(command + '\n')\r
-                exec(command)\r
-\r
-    def OnExit(self, event):\r
-        self.Close()\r
-\r
-    def _on_next(self, event):\r
+    def _next_curve(self, *args):\r
         '''\r
         NEXT\r
         Go to the next curve in the playlist.\r
@@ -707,78 +553,28 @@ class HookeFrame (wx.Frame):
         -----\r
         Syntax: next, n\r
         '''\r
-        selected_item = self._c['playlists'].PlaylistsTree.GetSelection()\r
-        if self._c['playlists'].PlaylistsTree.ItemHasChildren(selected_item):\r
+        selected_item = self._c['playlists']._c['tree'].GetSelection()\r
+        if self._c['playlists']._c['tree'].ItemHasChildren(selected_item):\r
             #GetFirstChild returns a tuple\r
             #we only need the first element\r
-            next_item = self._c['playlists'].PlaylistsTree.GetFirstChild(selected_item)[0]\r
+            next_item = self._c['playlists']._c['tree'].GetFirstChild(selected_item)[0]\r
         else:\r
-            next_item = self._c['playlists'].PlaylistsTree.GetNextSibling(selected_item)\r
+            next_item = self._c['playlists']._c['tree'].GetNextSibling(selected_item)\r
             if not next_item.IsOk():\r
-                parent_item = self._c['playlists'].PlaylistsTree.GetItemParent(selected_item)\r
+                parent_item = self._c['playlists']._c['tree'].GetItemParent(selected_item)\r
                 #GetFirstChild returns a tuple\r
                 #we only need the first element\r
-                next_item = self._c['playlists'].PlaylistsTree.GetFirstChild(parent_item)[0]\r
-        self._c['playlists'].PlaylistsTree.SelectItem(next_item, True)\r
-        if not self._c['playlists'].PlaylistsTree.ItemHasChildren(selected_item):\r
+                next_item = self._c['playlists']._c['tree'].GetFirstChild(parent_item)[0]\r
+        self._c['playlists']._c['tree'].SelectItem(next_item, True)\r
+        if not self._c['playlists']._c['tree'].ItemHasChildren(selected_item):\r
             playlist = self.GetActivePlaylist()\r
             if playlist.count > 1:\r
                 playlist.next()\r
-                self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
+                self._c['status bar'].set_playlist(playlist)\r
                 self.UpdateNote()\r
                 self.UpdatePlot()\r
 \r
-    def _on_notebook_page_close(self, event):\r
-        ctrl = event.GetEventObject()\r
-        playlist_name = ctrl.GetPageText(ctrl._curpage)\r
-        self.DeleteFromPlaylists(playlist_name)\r
-\r
-    def OnPaneClose(self, event):\r
-        event.Skip()\r
-\r
-    def OnPlaylistsLeftDclick(self, event):\r
-        if self._c['playlists'].PlaylistsTree.Count > 0:\r
-            playlist_name = self.GetActivePlaylistName()\r
-            #if that playlist already exists\r
-            #we check if it is the active playlist (ie selected in panelPlaylists)\r
-            #and switch to it if necessary\r
-            if playlist_name in self.playlists:\r
-                index = self._c['notebook'].GetSelection()\r
-                current_playlist = self._c['notebook'].GetPageText(index)\r
-                if current_playlist != playlist_name:\r
-                    index = self._GetPlaylistTab(playlist_name)\r
-                    self._c['notebook'].SetSelection(index)\r
-                #if a curve was double-clicked\r
-                item = self._c['playlists'].PlaylistsTree.GetSelection()\r
-                if not self._c['playlists'].PlaylistsTree.ItemHasChildren(item):\r
-                    index = self._GetActiveFileIndex()\r
-                else:\r
-                    index = 0\r
-                if index >= 0:\r
-                    playlist = self.GetActivePlaylist()\r
-                    playlist.index = index\r
-                    self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
-                    self.UpdateNote()\r
-                    self.UpdatePlot()\r
-            #if you uncomment the following line, the tree will collapse/expand as well\r
-            #event.Skip()\r
-\r
-    def OnPlaylistsLeftDown(self, event):\r
-        hit_item, hit_flags = self._c['playlists'].PlaylistsTree.HitTest(event.GetPosition())\r
-        if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:\r
-            self._c['playlists'].PlaylistsTree.SelectItem(hit_item)\r
-            playlist_name = self.GetActivePlaylistName()\r
-            playlist = self.GetActivePlaylist()\r
-            #if a curve was clicked\r
-            item = self._c['playlists'].PlaylistsTree.GetSelection()\r
-            if not self._c['playlists'].PlaylistsTree.ItemHasChildren(item):\r
-                index = self._GetActiveFileIndex()\r
-                if index >= 0:\r
-                    playlist.index = index\r
-            self.playlists[playlist_name] = playlist\r
-        event.Skip()\r
-\r
-    def _on_previous(self, event):\r
+    def _previous_curve(self, *args):\r
         '''\r
         PREVIOUS\r
         Go to the previous curve in the playlist.\r
@@ -789,22 +585,30 @@ class HookeFrame (wx.Frame):
         #playlist = self.playlists[self.GetActivePlaylistName()][0]\r
         #select the previous curve and tell the user if we wrapped around\r
         #self.AppendToOutput(playlist.previous())\r
-        selected_item = self._c['playlists'].PlaylistsTree.GetSelection()\r
-        if self._c['playlists'].PlaylistsTree.ItemHasChildren(selected_item):\r
-            previous_item = self._c['playlists'].PlaylistsTree.GetLastChild(selected_item)\r
+        selected_item = self._c['playlists']._c['tree'].GetSelection()\r
+        if self._c['playlists']._c['tree'].ItemHasChildren(selected_item):\r
+            previous_item = self._c['playlists']._c['tree'].GetLastChild(selected_item)\r
         else:\r
-            previous_item = self._c['playlists'].PlaylistsTree.GetPrevSibling(selected_item)\r
+            previous_item = self._c['playlists']._c['tree'].GetPrevSibling(selected_item)\r
             if not previous_item.IsOk():\r
-                parent_item = self._c['playlists'].PlaylistsTree.GetItemParent(selected_item)\r
-                previous_item = self._c['playlists'].PlaylistsTree.GetLastChild(parent_item)\r
-        self._c['playlists'].PlaylistsTree.SelectItem(previous_item, True)\r
+                parent_item = self._c['playlists']._c['tree'].GetItemParent(selected_item)\r
+                previous_item = self._c['playlists']._c['tree'].GetLastChild(parent_item)\r
+        self._c['playlists']._c['tree'].SelectItem(previous_item, True)\r
         playlist = self.GetActivePlaylist()\r
         if playlist.count > 1:\r
             playlist.previous()\r
-            self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
+            self._c['status bar'].set_playlist(playlist)\r
             self.UpdateNote()\r
             self.UpdatePlot()\r
 \r
+    def _on_notebook_page_close(self, event):\r
+        ctrl = event.GetEventObject()\r
+        playlist_name = ctrl.GetPageText(ctrl._curpage)\r
+        self.DeleteFromPlaylists(playlist_name)\r
+\r
+    def OnPaneClose(self, event):\r
+        event.Skip()\r
+\r
     def OnPropGridChanged (self, event):\r
         prop = event.GetProperty()\r
         if prop:\r
@@ -829,48 +633,6 @@ class HookeFrame (wx.Frame):
     def _on_size(self, event):\r
         event.Skip()\r
 \r
-    def OnTreeCtrlCommandsSelectionChanged(self, event):\r
-        selected_item = event.GetItem()\r
-        if selected_item is not None:\r
-            plugin = ''\r
-            section = ''\r
-            #deregister/register the listener to avoid infinite loop\r
-            evtmgr.eventManager.DeregisterListener(self.OnTreeCtrlCommandsSelectionChanged)\r
-            self._c['commands']._c['tree'].SelectItem(selected_item)\r
-            evtmgr.eventManager.Register(self.OnTreeCtrlCommandsSelectionChanged, wx.EVT_TREE_SEL_CHANGED, self._c['commands']._c['tree'])\r
-            self.panelProperties.SelectedTreeItem = selected_item\r
-            #if a command was clicked\r
-            properties = []\r
-            if not self._c['commands']._c['tree'].ItemHasChildren(selected_item):\r
-                item_plugin = self._c['commands']._c['tree'].GetItemParent(selected_item)\r
-                plugin = self._c['commands']._c['tree'].GetItemText(item_plugin)\r
-                if self.gui.config.has_key(plugin):\r
-                    #config = self._c['commands']._c['tree'].GetPyData(item_plugin)\r
-                    config = self.gui.config[plugin]\r
-                    section = self._c['commands']._c['tree'].GetItemText(selected_item)\r
-                    #display docstring in help window\r
-                    doc_string = eval('self.do_' + section + '.__doc__')\r
-                    if section in config:\r
-                        for option in config[section]:\r
-                            properties.append([option, config[section][option]])\r
-            else:\r
-                plugin = self._c['commands']._c['tree'].GetItemText(selected_item)\r
-                if plugin != 'core':\r
-                    doc_string = eval('plugins.' + plugin + '.' + plugin + 'Commands.__doc__')\r
-                else:\r
-                    doc_string = 'The module "core" contains Hooke core functionality'\r
-            if doc_string is not None:\r
-                self.panelAssistant.ChangeValue(doc_string)\r
-            else:\r
-                self.panelAssistant.ChangeValue('')\r
-            panel.propertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)\r
-            #save the currently selected command/plugin to the config file\r
-            self.gui.config['command']['command'] = section\r
-            self.gui.config['command']['plugin'] = plugin\r
-\r
-    def OnTreeCtrlItemActivated(self, event):\r
-        self.OnExecute(event)\r
-\r
     def OnUpdateNote(self, event):\r
         '''\r
         Saves the note to the active file.\r
@@ -1033,7 +795,7 @@ class HookeFrame (wx.Frame):
         playlist = self.GetActivePlaylist()\r
         if playlist is not None:\r
             if playlist.index >= 0:\r
-                self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
+                self._c['status bar'].set_playlist(playlist)\r
                 self.UpdateNote()\r
                 self.UpdatePlot()\r
 \r
@@ -1123,9 +885,43 @@ class HookeFrame (wx.Frame):
         #refresh the plot\r
         figure.canvas.draw()\r
 \r
+    def _on_curve_select(self, playlist, curve):\r
+        #create the plot tab and add playlist to the dictionary\r
+        plotPanel = panel.plot.PlotPanel(self, ID_FirstPlot + len(self.playlists))\r
+        notebook_tab = self._c['notebook'].AddPage(plotPanel, playlist.name, True)\r
+        #tab_index = self._c['notebook'].GetSelection()\r
+        playlist.figure = plotPanel.get_figure()\r
+        self.playlists[playlist.name] = playlist\r
+        #self.playlists[playlist.name] = [playlist, figure]\r
+        self._c['status bar'].set_playlist(playlist)\r
+        self.UpdateNote()\r
+        self.UpdatePlot()\r
+\r
 \r
+    def _on_playlist_left_doubleclick(self):\r
+        index = self._c['notebook'].GetSelection()\r
+        current_playlist = self._c['notebook'].GetPageText(index)\r
+        if current_playlist != playlist_name:\r
+            index = self._GetPlaylistTab(playlist_name)\r
+            self._c['notebook'].SetSelection(index)\r
+        self._c['status bar'].set_playlist(playlist)\r
+        self.UpdateNote()\r
+        self.UpdatePlot()\r
+\r
+    def _on_playlist_delete(self, playlist):\r
+        notebook = self.Parent.plotNotebook\r
+        index = self.Parent._GetPlaylistTab(playlist.name)\r
+        notebook.SetSelection(index)\r
+        notebook.DeletePage(notebook.GetSelection())\r
+        self.Parent.DeleteFromPlaylists(playlist_name)\r
+        \r
 \r
 class HookeApp (wx.App):\r
+    """A :class:`wx.App` wrapper around :class:`HookeFrame`.\r
+\r
+    Tosses up a splash screen and then loads :class:`HookeFrame` in\r
+    its own window.\r
+    """\r
     def __init__(self, gui, commands, inqueue, outqueue, *args, **kwargs):\r
         self.gui = gui\r
         self.commands = commands\r
@@ -1154,7 +950,8 @@ class HookeApp (wx.App):
 \r
         self._c = {\r
             'frame': HookeFrame(\r
-                self.gui, self.commands, parent=None, title='Hooke',\r
+                self.gui, self.commands, self.inqueue, self.outqueue,\r
+                parent=None, title='Hooke',\r
                 pos=(left, top), size=(width, height),\r
                 style=wx.DEFAULT_FRAME_STYLE|wx.SUNKEN_BORDER|wx.CLIP_CHILDREN),\r
             }\r
@@ -1181,9 +978,6 @@ class HookeApp (wx.App):
                 sleepFactor = 1.2\r
                 time.sleep(sleepFactor * duration / 1000)\r
 \r
-    def OnExit(self):\r
-        return True\r
-\r
 \r
 class GUI (UserInterface):\r
     """wxWindows graphical user interface.\r
@@ -1216,6 +1010,9 @@ class GUI (UserInterface):
             Setting(section=self.setting_section, option='perspective path',\r
                     value=os.path.join('resources', 'gui', 'perspective'),\r
                     help='Directory containing perspective files.'), # TODO: allow colon separated list, like $PATH.\r
+            Setting(section=self.setting_section, option='hide extensions',\r
+                    value=False,\r
+                    help='Hide file extensions when displaying names.'),\r
             Setting(section=self.setting_section, option='folders-workdir',\r
                     value='.',\r
                     help='This should probably go...'),\r