Adding support for beremiz extensions to define custom file editors for project files
authorlaurent
Sun, 01 Jul 2012 23:20:19 +0200
changeset 784 a1d970365e41
parent 783 f5cea1a6851e
child 785 a9bdd7c2f063
Adding support for beremiz extensions to define custom file editors for project files
Beremiz.py
ConfigTreeNode.py
ProjectController.py
canfestival/canfestival.py
features.py
images/FILE.png
images/icons.svg
util/FileManagementPanel.py
--- a/Beremiz.py	Thu Jun 28 16:42:07 2012 +0200
+++ b/Beremiz.py	Sun Jul 01 23:20:19 2012 +0200
@@ -605,12 +605,14 @@
         if self.CTR is not None:
             selected = self.TabsOpened.GetSelection()
             if selected >= 0:
-                graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
+                window = self.TabsOpened.GetPage(selected)
+                viewer_is_modified = window.IsModified()
+                is_viewer = isinstance(window, Viewer)
             else:
-                graphic_viewer = False
+                viewer_is_modified = is_viewer = False
             if self.TabsOpened.GetPageCount() > 0:
                 self.FileMenu.Enable(wx.ID_CLOSE, True)
-                if graphic_viewer:
+                if is_viewer:
                     self.FileMenu.Enable(wx.ID_PREVIEW, True)
                     self.FileMenu.Enable(wx.ID_PRINT, True)
                     MenuToolBar.EnableTool(wx.ID_PRINT, True)
@@ -624,7 +626,7 @@
                 self.FileMenu.Enable(wx.ID_PRINT, False)
                 MenuToolBar.EnableTool(wx.ID_PRINT, False)
             self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
-            project_modified = self.CTR.ProjectTestModified()
+            project_modified = self.CTR.ProjectTestModified() or viewer_is_modified
             self.FileMenu.Enable(wx.ID_SAVE, project_modified)
             MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
             self.FileMenu.Enable(wx.ID_SAVEAS, True)
@@ -876,12 +878,20 @@
         self.RefreshAll()
     
     def OnSaveProjectMenu(self, event):
+        selected = self.TabsOpened.GetSelection()
+        if selected != -1:
+            window = self.TabsOpened.GetPage(selected)
+            window.Save()
         if self.CTR is not None:
             self.CTR.SaveProject()
             self.RefreshAll()
             self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)
     
     def OnSaveProjectAsMenu(self, event):
+        selected = self.TabsOpened.GetSelection()
+        if selected != -1:
+            window = self.TabsOpened.GetPage(selected)
+            window.SaveAs()
         if self.CTR is not None:
             self.CTR.SaveProjectAs()
             self.RefreshAll()
--- a/ConfigTreeNode.py	Thu Jun 28 16:42:07 2012 +0200
+++ b/ConfigTreeNode.py	Sun Jul 01 23:20:19 2012 +0200
@@ -408,15 +408,13 @@
     def _OpenView(self, name=None, onlyopened=False):
         if self.EditorType is not None:
             app_frame = self.GetCTRoot().AppFrame
-            if self._View is None:
+            if self._View is None and not onlyopened:
                 
                 self._View = self.EditorType(app_frame.TabsOpened, self, app_frame)
-                
+            
+            if self._View is not None:
                 app_frame.EditProjectElement(self._View, self.CTNName())
             
-            elif onlyopened:
-                app_frame.EditProjectElement(self._View, self.CTNName(), onlyopened)
-            
             return self._View
         return None
 
--- a/ProjectController.py	Thu Jun 28 16:42:07 2012 +0200
+++ b/ProjectController.py	Sun Jul 01 23:20:19 2012 +0200
@@ -939,6 +939,10 @@
     def _OpenProjectFiles(self):
         self._OpenView("Project files")
     
+    _FileEditors = {}
+    def _OpenFileEditor(self, filepath):
+        self._OpenView(filepath)
+    
     def _OpenView(self, name=None, onlyopened=False):
         if name == "IEC code":
             if self._IECCodeView is None:
@@ -955,11 +959,9 @@
                 self._IECCodeView.SetText(text = text)
                 self._IECCodeView.SetIcon(GetBitmap("ST"))
             
+            if self._IECCodeView is not None:
                 self.AppFrame.EditProjectElement(self._IECCodeView, name)
             
-            elif onlyopened:
-                self.AppFrame.EditProjectElement(self._IECCodeView, name, onlyopened)
-            
             return self._IECCodeView
         
         elif name == "IEC raw code":
@@ -972,25 +974,56 @@
                 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS)
                 self._IECRawCodeView.RefreshView()
                 self._IECRawCodeView.SetIcon(GetBitmap("ST"))
-                    
+            
+            if self._IECRawCodeView is not None:
                 self.AppFrame.EditProjectElement(self._IECRawCodeView, name)
             
-            elif onlyopened:
-                self.AppFrame.EditProjectElement(self._IECRawCodeView, name, onlyopened)
-            
             return self._IECRawCodeView
         
         elif name == "Project files":
             if self._ProjectFilesView is None:
                 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True)
                 
+                extensions = []
+                for extension, name, editor in features.file_editors:
+                    if extension not in extensions:
+                        extensions.append(extension)
+                self._ProjectFilesView.SetEditableFileExtensions(extensions)
+                
+            if self._ProjectFilesView is not None:
                 self.AppFrame.EditProjectElement(self._ProjectFilesView, name)
             
-            elif onlyopened:
-                self.AppFrame.EditProjectElement(self._ProjectFilesView, name, onlyopened)
+            return self._ProjectFilesView
+        
+        elif name is not None and os.path.isfile(name):
+            if not self._FileEditors.has_key(name):
+                file_extension = os.path.splitext(name)[1]
+                
+                editors = dict([(editor_name, editor)
+                                for extension, editor_name, editor in features.file_editors
+                                if extension == file_extension])
+                
+                editor_name = None
+                if len(editors) == 1:
+                    editor_name = editors.keys()[0]
+                elif len(editors) > 0:
+                    names = editors.keys()
+                    dialog = wx.SingleChoiceDialog(self.ParentWindow, 
+                          _("Select an editor:"), _("Editor selection"), 
+                          names, wx.OK|wx.CANCEL)
+                    if dialog.ShowModal() == wx.ID_OK:
+                        editor_name = names[dialog.GetSelection()]
+                    dialog.Destroy()
+                
+                if editor_name is not None:
+                    editor = editors[editor_name]()
+                    self._FileEditors[name] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame)
+                    self._FileEditors[name].SetIcon(GetBitmap("FILE"))
+                    
+            if self._FileEditors.has_key(name):
+                self.AppFrame.EditProjectElement(self._FileEditors[name], name)
             
-            return self._ProjectFilesView
-           
+            return self._FileEditors[name]
         else:
             return ConfigTreeNode._OpenView(self, name, onlyopened)
 
@@ -1002,6 +1035,8 @@
             self._IECRawCodeView = None
         if self._ProjectFilesView == view:
             self._ProjectFilesView = None
+        if view in self._FileEditors.values():
+            self._FileEditors.pop(view.GetTagName())
 
     def _Clean(self):
         self._CloseView(self._IECCodeView)
--- a/canfestival/canfestival.py	Thu Jun 28 16:42:07 2012 +0200
+++ b/canfestival/canfestival.py	Sun Jul 01 23:20:19 2012 +0200
@@ -108,8 +108,8 @@
     def GetCanDevice(self):
         return self.CanFestivalSlaveNode.getCan_Device()
 
-    def _OpenView(self):
-        ConfigTreeNode._OpenView(self)
+    def _OpenView(self, name=None, onlyopened=False):
+        ConfigTreeNode._OpenView(self, name, onlyopened)
         if self._View is not None:
             self._View.SetBusId(self.GetCurrentLocation())
         return self._View
@@ -273,10 +273,8 @@
                 manager = MiniNodeManager(self, masterpath, self.CTNFullName() + ".generated_master")
                 self._GeneratedMasterView = MasterViewer(app_frame.TabsOpened, manager, app_frame)
                 
-                app_frame.EditProjectElement(self._GeneratedMasterView, name)
-            
-            elif onlyopened:
-                app_frame.EditProjectElement(self._IECCodeView, name, onlyopened)
+            if self._GeneratedMasterView is not None:
+                app_frame.EditProjectElement(self._IECCodeView, name)
             
             return self._GeneratedMasterView
         else:
--- a/features.py	Thu Jun 28 16:42:07 2012 +0200
+++ b/features.py	Sun Jul 01 23:20:19 2012 +0200
@@ -1,5 +1,6 @@
-libraries = [('Python','py_ext.PythonLibrary'),
-             ('SVGUI','svgui.SVGUILibrary')]
+libraries = [
+    ('Python', 'py_ext.PythonLibrary'),
+    ('SVGUI', 'svgui.SVGUILibrary')]
 
 catalog = [
     ('canfestival', _('CANopen support'), _('Map located variables over CANopen'), 'canfestival.canfestival.RootClass'),
@@ -8,3 +9,4 @@
     ('wxglade_hmi', _('WxGlade GUI'), _('Add a simple WxGlade based GUI.'), 'wxglade_hmi.WxGladeHMI'),
     ('svgui', _('SVGUI'), _('Experimental web based HMI'), 'svgui.SVGUI')]
 
+file_editors = []
Binary file images/FILE.png has changed
--- a/images/icons.svg	Thu Jun 28 16:42:07 2012 +0200
+++ b/images/icons.svg	Sun Jul 01 23:20:19 2012 +0200
@@ -43,9 +43,9 @@
      pagecolor="#ffffff"
      id="base"
      showgrid="false"
-     inkscape:zoom="7.2407738"
-     inkscape:cx="596.13887"
-     inkscape:cy="800.65537"
+     inkscape:zoom="28.963095"
+     inkscape:cx="519.38634"
+     inkscape:cy="721.386"
      inkscape:window-x="0"
      inkscape:window-y="24"
      inkscape:current-layer="svg2"
@@ -87871,6 +87871,164 @@
        id="radialGradient3101"
        xlink:href="#radialGradient8198"
        inkscape:collect="always" />
+    <linearGradient
+       y2="16"
+       x2="15"
+       y1="1"
+       x1="0"
+       gradientTransform="translate(-40,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient17986-08"
+       xlink:href="#linearGradient5175-3-7-9-2-7-1"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5175-3-7-9-2-7-1">
+      <stop
+         style="stop-color:#bdcccd;stop-opacity:1;"
+         offset="0"
+         id="stop5177-6-9-6-1-9-0" />
+      <stop
+         style="stop-color:#7979ff;stop-opacity:1;"
+         offset="1"
+         id="stop5179-73-8-3-1-8-7" />
+    </linearGradient>
+    <linearGradient
+       y2="16"
+       x2="15"
+       y1="1"
+       x1="0"
+       gradientTransform="translate(-40,0)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient18365"
+       xlink:href="#linearGradient5175-3-7-9-2-7-1"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3262-4"
+       y2="16"
+       gradientUnits="userSpaceOnUse"
+       x2="25"
+       gradientTransform="translate(-17.058,0)"
+       x1="25">
+      <stop
+         id="stop3311-1"
+         style="stop-color:#f6f6f6"
+         offset="0" />
+      <stop
+         id="stop3313-5"
+         style="stop-color:#ccc"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3264-6"
+       y2="16.004999"
+       gradientUnits="userSpaceOnUse"
+       x2="21"
+       gradientTransform="translate(-17.058,0)"
+       x1="21">
+      <stop
+         id="stop3399-07"
+         style="stop-color:#aaa"
+         offset="0" />
+      <stop
+         id="stop3401-2"
+         style="stop-color:#8c8c8c"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3260-1"
+       y2="20.895"
+       gradientUnits="userSpaceOnUse"
+       x2="84.639"
+       gradientTransform="matrix(0.096142,0,0,0.096142,1.8469,1.943)"
+       y1="105.1"
+       x1="86.133003">
+      <stop
+         id="stop5130-5-8"
+         style="stop-color:#e5e5e5"
+         offset="0" />
+      <stop
+         id="stop5132-52"
+         style="stop-color:#ababab"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2517-1"
+       y2="47.013"
+       gradientUnits="userSpaceOnUse"
+       x2="25.132"
+       gradientTransform="matrix(0.31429,0,0,0.32593,0.45711,-0.32225)"
+       y1="0.98521"
+       x1="25.132">
+      <stop
+         id="stop3602-3"
+         style="stop-color:#f4f4f4"
+         offset="0" />
+      <stop
+         id="stop3604-1"
+         style="stop-color:#dbdbdb"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2519-4"
+       y2="2.9061999"
+       gradientUnits="userSpaceOnUse"
+       x2="-51.785999"
+       gradientTransform="matrix(0.25379,0,0,0.30502,19.129,-0.68549)"
+       y1="50.785999"
+       x1="-51.785999">
+      <stop
+         id="stop3106-86"
+         style="stop-color:#aaa"
+         offset="0" />
+      <stop
+         id="stop3108-5"
+         style="stop-color:#c8c8c8"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2511-5"
+       y2="46.016998"
+       gradientUnits="userSpaceOnUse"
+       x2="24"
+       gradientTransform="matrix(0.27273,0,0,0.30232,1.4546,0.7442)"
+       y1="2"
+       x1="24">
+      <stop
+         id="stop3213-2"
+         style="stop-color:#fff"
+         offset="0" />
+      <stop
+         id="stop3215-9"
+         style="stop-color:#fff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2507-51"
+       y2="5.4565001"
+       gradientUnits="userSpaceOnUse"
+       x2="36.358002"
+       gradientTransform="matrix(0.3092,0,0,0.37669,0.47615,0.10718)"
+       y1="8.059"
+       x1="32.891998">
+      <stop
+         id="stop8591-03"
+         style="stop-color:#fefefe"
+         offset="0" />
+      <stop
+         id="stop8593-1"
+         style="stop-color:#cbcbcb"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="5.4565001"
+       x2="36.358002"
+       y1="8.059"
+       x1="32.891998"
+       gradientTransform="matrix(0.3092,0,0,0.37669,0.47615,0.10718)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3039-9"
+       xlink:href="#linearGradient2507-51"
+       inkscape:collect="always" />
   </defs>
   <g
      id="g19063"
@@ -91756,7 +91914,7 @@
        y="318.55981"
        x="166.52481"
        id="tspan16195-0"
-       sodipodi:role="line">%% Extension Cfile Pyfile wxGlade SVGUI FOLDER %%</tspan></text>
+       sodipodi:role="line">%% Extension Cfile Pyfile wxGlade SVGUI FOLDER FILE %%</tspan></text>
   <use
      style="display:inline"
      inkscape:label="#use3839"
@@ -93398,4 +93556,64 @@
        sodipodi:nodetypes="cccccccccc"
        inkscape:label="#rect2160" />
   </g>
+  <g
+     transform="matrix(0.83945151,0,0,0.83945151,496.66938,322.90098)"
+     id="layer1-50">
+    <path
+       inkscape:connector-curvature="0"
+       id="path4160-4"
+       style="fill:url(#linearGradient2517-1);stroke:url(#linearGradient2519-4);stroke-width:0.99992001;stroke-linejoin:round"
+       d="M 1.5,0.49997 H 9.9412 C 10.383,0.66176 12.88,2.63277 13.5,3.90907 v 11.591 h -12 v -15 z" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path2435-5"
+       style="opacity:0.6;fill:none;stroke:url(#linearGradient2511-5)"
+       d="m 12.5,4.2151 v 10.285 h -10 v -13 h 7.2363" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path3330-0"
+       style="fill:#c1c1c1;fill-rule:evenodd"
+       d="M 9.2941,0.8409 C 10.142,3.6448 9,5.0341 9,5.0341 c 0,0 1.893,-1.2514 4.171,-0.1023 1.943,0.9798 0.036,-1.008 -0.041,-1.129 C 12.587,2.9553 10.707,1.1697 10.025,0.8726 9.9696,0.84836 9.5814,0.8409 9.2941,0.8409 z" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path4474-14"
+       style="fill:url(#linearGradient3039-9);fill-rule:evenodd"
+       d="m 9.2941,0.8409 c 0.9879,0 0.7059,3.181 0.7059,3.181 0,0 2.272,-0.5007 3.171,0.9099 0.163,0.2556 0.036,-1.008 -0.041,-1.129 C 12.587,2.9553 10.707,1.1697 10.025,0.8726 9.9696,0.84836 9.5814,0.8409 9.2941,0.8409 z" />
+  </g>
+  <g
+     transform="translate(534.91586,321.71842)"
+     style="display:inline"
+     id="FILE"
+     inkscape:export-xdpi="90"
+     inkscape:export-ydpi="90">
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:url(#linearGradient18365);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+       d="m -40,0 0,16 16,0 0,-16 -16,0 z m 1,1 14,0 0,14 -14,0 0,-14 z"
+       id="path3806-1-6-1-9-0"
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:label="#rect2160" />
+  </g>
+  <g
+     transform="matrix(0.39454693,0,0,0.39454693,503.03943,330.01484)"
+     id="layer1-6-4">
+    <g
+       id="g2479-4">
+      <path
+         inkscape:connector-curvature="0"
+         id="path2426-8"
+         style="fill:url(#linearGradient3262-4);stroke:url(#linearGradient3264-6);stroke-linejoin:round;display:block"
+         d="M 6.9375,0.5 C 6.6891,0.5 6.5,0.68908 6.5,0.9375 v 1.25 C 5.9461,2.3297 5.4488,2.5594 4.9688,2.8438 L 4.0625,1.9375 c -0.17566,-0.17566 -0.44934,-0.17566 -0.625,0 l -1.5,1.5 c -0.17566,0.17566 -0.17566,0.44934 0,0.625 L 2.8438,4.9688 C 2.5594,5.4488 2.3297,5.9461 2.1875,6.5 h -1.25 C 0.68908,6.5 0.5,6.6891 0.5,6.9375 v 2.125 c 1e-8,0.24842 0.18908,0.4375 0.4375,0.4375 h 1.25 c 0.1422,0.5539 0.37188,1.0512 0.65625,1.5312 l -0.9063,0.907 c -0.17566,0.17566 -0.17566,0.44934 0,0.625 l 1.5,1.5 c 0.17566,0.17566 0.44934,0.17566 0.625,0 l 0.9063,-0.907 c 0.48,0.285 0.9773,0.514 1.5312,0.656 v 1.25 c 1e-7,0.24842 0.18908,0.4375 0.4375,0.4375 h 2.125 c 0.2484,0 0.4375,-0.189 0.4375,-0.438 v -1.25 c 0.5539,-0.1422 1.0512,-0.37188 1.5312,-0.65625 l 0.90625,0.90625 c 0.17566,0.17566 0.44934,0.17566 0.625,0 l 1.5,-1.5 c 0.17566,-0.17566 0.17566,-0.44934 0,-0.625 l -0.906,-0.906 c 0.285,-0.48 0.514,-0.977 0.656,-1.531 h 1.25 c 0.249,0 0.438,-0.1891 0.438,-0.4375 v -2.125 c 0,-0.2484 -0.189,-0.4375 -0.438,-0.4375 h -1.25 c -0.142,-0.5539 -0.371,-1.0512 -0.656,-1.5312 l 0.906,-0.9063 c 0.17566,-0.17566 0.17566,-0.44934 0,-0.625 l -1.5,-1.5 c -0.17566,-0.17566 -0.44934,-0.17566 -0.625,0 l -0.906,0.9063 c -0.48,-0.2844 -0.977,-0.5141 -1.531,-0.6563 v -1.25 C 9.5004,0.68878 9.3113,0.4997 9.0629,0.4997 H 6.9379 z M 8,6 c 1.104,0 2,0.896 2,2 0,1.104 -0.896,2 -2,2 C 6.896,10 6,9.104 6,8 6,6.896 6.896,6 8,6 z" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path3315-1"
+         style="opacity:0.05"
+         d="M 8,3.4651 C 5.4994,3.4651 3.4651,5.4994 3.4651,8 c 0,2.501 2.0343,4.535 4.5349,4.535 2.501,0 4.535,-2.034 4.535,-4.535 C 12.535,5.4994 10.501,3.4651 8,3.4651 z m 0,2.093 c 1.3479,0 2.4419,1.094 2.4419,2.4419 0,1.3479 -1.094,2.4419 -2.4419,2.4419 -1.3479,0 -2.4419,-1.0941 -2.4419,-2.442 C 5.5581,6.652 6.6521,5.558 8,5.558 z" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path28-7"
+         style="fill:none;stroke:url(#linearGradient3260-1)"
+         d="M 8,4 C 5.7944,4 4,5.7944 4,8 c 0,2.206 1.7944,4 4,4 2.206,0 4,-1.794 4,-4 C 12,5.7944 10.206,4 8,4 z" />
+    </g>
+  </g>
 </svg>
--- a/util/FileManagementPanel.py	Thu Jun 28 16:42:07 2012 +0200
+++ b/util/FileManagementPanel.py	Sun Jul 01 23:20:19 2012 +0200
@@ -268,7 +268,8 @@
         for idx, (name, bitmap, help) in enumerate([
                 ("DeleteButton", "remove_element", _("Remove file from left folder")),
                 ("LeftCopyButton", "LeftCopy", _("Copy file from right folder to left")),
-                ("RightCopyButton", "RightCopy", _("copy file from left folder to right"))]):
+                ("RightCopyButton", "RightCopy", _("copy file from left folder to right")),
+                ("EditButton", "edit", _("Edit file"))]):
             button = wx.lib.buttons.GenBitmapButton(self.Editor, 
                   bitmap=GetBitmap(bitmap), 
                   size=wx.Size(28, 28), style=wx.NO_BORDER)
@@ -311,6 +312,9 @@
         
         self.Controler = controler
         
+        self.EditableFileExtensions = []
+        self.EditButton.Hide()
+        
         self.SetIcon(GetBitmap("FOLDER"))
     
     def __del__(self):
@@ -319,6 +323,11 @@
     def GetTitle(self):
         return self.TagName
     
+    def SetEditableFileExtensions(self, extensions):
+        self.EditableFileExtensions = extensions
+        if len(self.EditableFileExtensions) > 0:
+            self.EditButton.Show()
+    
     def RefreshView(self):
         self.ManagedDir.RefreshTree()
         self.SystemDir.RefreshTree()
@@ -331,6 +340,10 @@
         self.DeleteButton.Enable(os.path.isfile(managed_filepath))
         self.LeftCopyButton.Enable(os.path.isfile(system_filepath))
         self.RightCopyButton.Enable(os.path.isfile(managed_filepath))
+        if len(self.EditableFileExtensions) > 0:
+            self.EditButton.Enable(
+                os.path.isfile(managed_filepath) and
+                os.path.splitext(managed_filepath)[1] in self.EditableFileExtensions)
     
     def OnTreeItemChanged(self, event):
         self.RefreshButtonsState()
@@ -352,6 +365,13 @@
                 self.ManagedDir.RefreshTree()
         event.Skip()
 
+    def OnEditButton(self, event):
+        filepath = self.ManagedDir.GetPath()
+        if (os.path.isfile(filepath) and 
+            os.path.splitext(filepath)[1] in self.EditableFileExtensions):
+            self.Controler._OpenView(filepath)
+        event.Skip()
+        
     def CopyFile(self, src, dst):
         if os.path.isfile(src):
             src_folder, src_filename = os.path.split(src)