+#----------------------------------------------------------------------------
+# Name: ExtensionService.py
+# Purpose: Extension Service for IDE
+#
+# Author: Peter Yared
+#
+# Created: 5/23/05
+# CVS-ID: $ID:$
+# Copyright: (c) 2005 ActiveGrid, Inc.
+# License: wxWindows License
+#----------------------------------------------------------------------------
+
+import wx
+import wx.lib.pydocview
+import MessageService
+import os
+import os.path
+import pickle
+
+_ = wx.GetTranslation
+
+
+SPACE = 10
+HALF_SPACE = 5
+
+
+EXTENSIONS_CONFIG_STRING = "Extensions"
+
+
+
+# TODO: Redo extensions menu on OK, or provide alert that it won't happen until restart
+
+
+#----------------------------------------------------------------------------
+# Classes
+#----------------------------------------------------------------------------
+
+class Extension:
+
+
+ def __init__(self, menuItemName):
+ self.menuItemName = menuItemName
+ self.id = 0
+ self.menuItemDesc = ''
+ self.command = ''
+ self.commandPreArgs = ''
+ self.commandPostArgs = ''
+ self.fileExt = None
+
+
+class ExtensionService(wx.lib.pydocview.DocService):
+
+
+ def __init__(self):
+ self.LoadExtensions()
+
+
+ def LoadExtensions(self):
+ config = wx.ConfigBase_Get()
+ pickledExtensions = config.Read(EXTENSIONS_CONFIG_STRING)
+ if pickledExtensions:
+ try:
+ self._extensions = pickle.loads(pickledExtensions.encode('ascii'))
+ except:
+ tp, val, tb = sys.exc_info()
+ traceback.print_exception(tp,val,tb)
+ self._extensions = []
+ else:
+ self._extensions = []
+
+
+ def SaveExtensions(self):
+ config = wx.ConfigBase_Get()
+ config.Write(EXTENSIONS_CONFIG_STRING, pickle.dumps(self._extensions))
+
+
+ def GetExtensions(self):
+ return self._extensions
+
+
+ def SetExtensions(self, extensions):
+ self._extensions = extensions
+
+
+ def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
+ toolsMenuIndex = menuBar.FindMenu(_("&Tools"))
+ if toolsMenuIndex > -1:
+ toolsMenu = menuBar.GetMenu(toolsMenuIndex)
+ else:
+ toolsMenu = wx.Menu()
+
+ if self._extensions:
+ if toolsMenu.GetMenuItems():
+ toolsMenu.AppendSeparator()
+ for ext in self._extensions:
+ # Append a tool menu item for each extension
+ ext.id = wx.NewId()
+ toolsMenu.Append(ext.id, ext.menuItemName)
+ wx.EVT_MENU(frame, ext.id, frame.ProcessEvent)
+ wx.EVT_UPDATE_UI(frame, ext.id, frame.ProcessUpdateUIEvent)
+
+ if toolsMenuIndex == -1:
+ formatMenuIndex = menuBar.FindMenu(_("&Format"))
+ menuBar.Insert(formatMenuIndex + 1, toolsMenu, _("&Tools"))
+
+ def ProcessEvent(self, event):
+ id = event.GetId()
+ for extension in self._extensions:
+ if id == extension.id:
+ self.OnExecuteExtension(extension)
+ return True
+ return False
+
+
+ def ProcessUpdateUIEvent(self, event):
+ id = event.GetId()
+ for extension in self._extensions:
+ if id == extension.id:
+ if extension.fileExt:
+ doc = wx.GetApp().GetDocumentManager().GetCurrentDocument()
+ if doc and '*' in extension.fileExt:
+ event.Enable(True)
+ return True
+ if doc:
+ for fileExt in extension.fileExt:
+ if fileExt in doc.GetDocumentTemplate().GetFileFilter():
+ event.Enable(True)
+ return True
+ event.Enable(False)
+ return False
+ return False
+
+
+ def OnExecuteExtension(self, extension):
+ if extension.fileExt:
+ doc = wx.GetApp().GetDocumentManager().GetCurrentDocument()
+ if not doc:
+ return
+ filename = doc.GetFilename()
+ ext = os.path.splitext(filename)[1]
+ if not '*' in extension.fileExt:
+ if not ext or ext[1:] not in extension.fileExt:
+ return
+ cmds = [extension.command]
+ if extension.commandPreArgs:
+ cmds.append(extension.commandPreArgs)
+ cmds.append(filename)
+ if extension.commandPostArgs:
+ cmds.append(extension.commandPostArgs)
+ os.spawnv(os.P_NOWAIT, extension.command, cmds)
+
+ else:
+ cmd = extension.command
+ if extension.commandPreArgs:
+ cmd = cmd + ' ' + extension.commandPreArgs
+ if extension.commandPostArgs:
+ cmd = cmd + ' ' + extension.commandPostArgs
+ f = os.popen(cmd)
+ messageService = wx.GetApp().GetService(MessageService.MessageService)
+ messageService.ShowWindow()
+ view = messageService.GetView()
+ for line in f.readlines():
+ view.AddLines(line)
+ view.GetControl().EnsureCaretVisible()
+ f.close()
+
+
+class ExtensionOptionsPanel(wx.Panel):
+
+
+ def __init__(self, parent, id):
+ wx.Panel.__init__(self, parent, id)
+
+ extOptionsPanelBorderSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ extOptionsPanelSizer = wx.FlexGridSizer(cols=2, hgap=SPACE, vgap=HALF_SPACE)
+
+ extCtrlSizer = wx.BoxSizer(wx.VERTICAL)
+ extCtrlSizer.Add(wx.StaticText(self, -1, _("Extensions:")), 0)
+ self._extListBox = wx.ListBox(self, -1, size=(-1,185), style=wx.LB_SINGLE)
+ self.Bind(wx.EVT_LISTBOX, self.OnListBoxSelect, self._extListBox)
+ extCtrlSizer.Add(self._extListBox, 1, wx.TOP | wx.BOTTOM | wx.EXPAND, SPACE)
+ buttonSizer = wx.GridSizer(rows=1, hgap=10, vgap=5)
+ self._moveUpButton = wx.Button(self, -1, _("Move Up"))
+ self.Bind(wx.EVT_BUTTON, self.OnMoveUp, self._moveUpButton)
+ buttonSizer.Add(self._moveUpButton, 0)
+ self._moveDownButton = wx.Button(self, -1, _("Move Down"))
+ self.Bind(wx.EVT_BUTTON, self.OnMoveDown, self._moveDownButton)
+ buttonSizer.Add(self._moveDownButton, 0)
+ extCtrlSizer.Add(buttonSizer, 0, wx.ALIGN_CENTER | wx.BOTTOM, HALF_SPACE)
+ buttonSizer = wx.GridSizer(rows=1, hgap=10, vgap=5)
+ self._addButton = wx.Button(self, -1, _("Add"))
+ self.Bind(wx.EVT_BUTTON, self.OnAdd, self._addButton)
+ buttonSizer.Add(self._addButton, 0)
+ self._deleteButton = wx.Button(self, wx.ID_DELETE)
+ self.Bind(wx.EVT_BUTTON, self.OnDelete, self._deleteButton)
+ buttonSizer.Add(self._deleteButton, 0)
+ extCtrlSizer.Add(buttonSizer, 0, wx.ALIGN_CENTER)
+ extOptionsPanelSizer.Add(extCtrlSizer, 0)
+
+ self._extDetailPanel = wx.Panel(self)
+ staticBox = wx.StaticBox(self._extDetailPanel, label=_("Selected Extension"))
+ staticBoxSizer = wx.StaticBoxSizer(staticBox)
+ self._extDetailPanel.SetSizer(staticBoxSizer)
+ extDetailSizer = wx.FlexGridSizer(cols=1, hgap=5, vgap=3)
+ staticBoxSizer.AddSizer(extDetailSizer, 0, wx.ALL, 5)
+
+ extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Name:")))
+ self._menuItemNameTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
+ extDetailSizer.Add(self._menuItemNameTextCtrl, 1, wx.EXPAND)
+ self.Bind(wx.EVT_TEXT, self.SaveCurrentItem, self._menuItemNameTextCtrl)
+
+ extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Description:")))
+ self._menuItemDescTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
+ extDetailSizer.Add(self._menuItemDescTextCtrl, 1, wx.EXPAND)
+
+ extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Path:")))
+ self._commandTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
+ findFileButton = wx.Button(self._extDetailPanel, -1, _("Browse..."))
+ def OnBrowseButton(event):
+ fileDlg = wx.FileDialog(self, _("Choose an Executable:"), style=wx.OPEN | wx.HIDE_READONLY)
+ path = self._commandTextCtrl.GetValue()
+ if path:
+ fileDlg.SetPath(path)
+ if fileDlg.ShowModal() == wx.ID_OK:
+ self._commandTextCtrl.SetValue(fileDlg.GetPath())
+ self._commandTextCtrl.SetInsertionPointEnd()
+ fileDlg.Destroy()
+ wx.EVT_BUTTON(findFileButton, -1, OnBrowseButton)
+ hsizer = wx.BoxSizer(wx.HORIZONTAL)
+ hsizer.Add(self._commandTextCtrl, 1, wx.EXPAND)
+ hsizer.Add(findFileButton, 0, wx.LEFT, HALF_SPACE)
+ extDetailSizer.Add(hsizer, 0)
+
+ extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Pre Arguments:")))
+ self._commandPreArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
+ extDetailSizer.Add(self._commandPreArgsTextCtrl, 1, wx.EXPAND)
+
+ extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Post Arguments:")))
+ self._commandPostArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
+ extDetailSizer.Add(self._commandPostArgsTextCtrl, 1, wx.EXPAND)
+
+ extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("File Extensions (Comma Separated):")))
+ self._fileExtTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
+ self._fileExtTextCtrl.SetToolTipString(_("""For example: "txt, text" or "*" for all files"""))
+ extDetailSizer.Add(self._fileExtTextCtrl, 1, wx.EXPAND)
+
+ extOptionsPanelSizer.Add(self._extDetailPanel, 0)
+
+ extOptionsPanelBorderSizer.Add(extOptionsPanelSizer, 0, wx.ALL | wx.EXPAND, SPACE)
+ self.SetSizer(extOptionsPanelBorderSizer)
+ self.Layout()
+ parent.AddPage(self, _("Extensions"))
+
+ if self.PopulateItems():
+ self._extListBox.SetSelection(0)
+ self.OnListBoxSelect(None)
+
+
+ def OnOK(self, optionsDialog):
+ self.SaveCurrentItem()
+ extensionsService = wx.GetApp().GetService(ExtensionService)
+ oldExtensions = extensionsService.GetExtensions()
+ extensionsService.SetExtensions(self._extensions)
+ extensionsService.SaveExtensions()
+ if oldExtensions.__repr__() != self._extensions.__repr__():
+ msgTitle = wx.GetApp().GetAppName()
+ if not msgTitle:
+ msgTitle = _("Document Options")
+ wx.MessageBox(_("Extension changes will not appear until the application is restarted."),
+ msgTitle,
+ wx.OK | wx.ICON_INFORMATION,
+ self.GetParent())
+
+
+ def PopulateItems(self):
+ extensionsService = wx.GetApp().GetService(ExtensionService)
+ import copy
+ self._extensions = copy.deepcopy(extensionsService.GetExtensions())
+ for extension in self._extensions:
+ self._extListBox.Append(extension.menuItemName, extension)
+ self._currentItem = None
+ self._currentItemIndex = -1
+ return len(self._extensions)
+
+
+ def OnListBoxSelect(self, event):
+ self.SaveCurrentItem()
+ if not self._extListBox.GetSelections():
+ self._currentItemIndex = -1
+ self._currentItem = None
+ self._deleteButton.Enable(False)
+ self._moveUpButton.Enable(False)
+ self._moveDownButton.Enable(False)
+ else:
+ self._currentItemIndex = self._extListBox.GetSelection()
+ self._currentItem = self._extListBox.GetClientData(self._currentItemIndex)
+ self._deleteButton.Enable()
+ self._moveUpButton.Enable(self._extListBox.GetCount() > 1 and self._currentItemIndex > 0)
+ self._moveDownButton.Enable(self._extListBox.GetCount() > 1 and self._currentItemIndex < self._extListBox.GetCount() - 1)
+ self.LoadItem(self._currentItem)
+
+
+ def SaveCurrentItem(self, event=None):
+ extension = self._currentItem
+ if extension:
+ if extension.menuItemName != self._menuItemNameTextCtrl.GetValue():
+ extension.menuItemName = self._menuItemNameTextCtrl.GetValue()
+ self._extListBox.SetString(self._currentItemIndex, extension.menuItemName)
+ extension.menuItemDesc = self._menuItemDescTextCtrl.GetValue()
+ extension.command = self._commandTextCtrl.GetValue()
+ extension.commandPreArgs = self._commandPreArgsTextCtrl.GetValue()
+ extension.commandPostArgs = self._commandPostArgsTextCtrl.GetValue()
+ fileExt = self._fileExtTextCtrl.GetValue().replace(' ','')
+ if not fileExt:
+ extension.fileExt = None
+ else:
+ extension.fileExt = fileExt.split(',')
+
+
+ def LoadItem(self, extension):
+ if extension:
+ self._menuItemDescTextCtrl.SetValue(extension.menuItemDesc or '')
+ self._commandTextCtrl.SetValue(extension.command or '')
+ self._commandPreArgsTextCtrl.SetValue(extension.commandPreArgs or '')
+ self._commandPostArgsTextCtrl.SetValue(extension.commandPostArgs or '')
+ if extension.fileExt:
+ self._fileExtTextCtrl.SetValue(extension.fileExt.__repr__()[1:-1].replace("'","")) # Make the list a string, strip the brakcet on either side
+ else:
+ self._fileExtTextCtrl.SetValue('')
+ self._menuItemNameTextCtrl.SetValue(extension.menuItemName or '') # Do the name last since it triggers the write event that updates the entire item
+ self._extDetailPanel.Enable()
+ else:
+ self._menuItemNameTextCtrl.SetValue('')
+ self._menuItemDescTextCtrl.SetValue('')
+ self._commandTextCtrl.SetValue('')
+ self._commandPreArgsTextCtrl.SetValue('')
+ self._commandPostArgsTextCtrl.SetValue('')
+ self._fileExtTextCtrl.SetValue('')
+ self._extDetailPanel.Enable(False)
+
+
+ def OnAdd(self, event):
+ self.SaveCurrentItem()
+ extensionNames = map(lambda extension: extension.menuItemName, self._extensions)
+ name = _("Untitled")
+ count = 1
+ while name in extensionNames:
+ count = count + 1
+ name = _("Untitled %s") % count
+ extension = Extension(name)
+ self._extensions.append(extension)
+ self._extListBox.Append(extension.menuItemName, extension)
+ self._extListBox.SetSelection(self._extListBox.GetCount() - 1)
+ self.OnListBoxSelect(None)
+ self._menuItemNameTextCtrl.SetFocus()
+ self._menuItemNameTextCtrl.SetSelection(-1, -1)
+
+
+ def OnDelete(self, event):
+ self._extListBox.Delete(self._currentItemIndex)
+ self._extensions.remove(self._currentItem)
+ self._currentItemIndex = min(self._currentItemIndex, self._extListBox.GetCount() - 1)
+ if self._currentItemIndex > -1:
+ self._extListBox.SetSelection(self._currentItemIndex)
+ self._currentItem = None # Don't update it since it no longer exists
+ self.OnListBoxSelect(None)
+
+
+ def OnMoveUp(self, event):
+ itemAboveString = self._extListBox.GetString(self._currentItemIndex - 1)
+ itemAboveData = self._extListBox.GetClientData(self._currentItemIndex - 1)
+ self._extListBox.Delete(self._currentItemIndex - 1)
+ self._extListBox.Insert(itemAboveString, self._currentItemIndex)
+ self._extListBox.SetClientData(self._currentItemIndex, itemAboveData)
+ self._currentItemIndex = self._currentItemIndex - 1
+ self.OnListBoxSelect(None) # Reset buttons
+
+
+ def OnMoveDown(self, event):
+ itemBelowString = self._extListBox.GetString(self._currentItemIndex + 1)
+ itemBelowData = self._extListBox.GetClientData(self._currentItemIndex + 1)
+ self._extListBox.Delete(self._currentItemIndex + 1)
+ self._extListBox.Insert(itemBelowString, self._currentItemIndex)
+ self._extListBox.SetClientData(self._currentItemIndex, itemBelowData)
+ self._currentItemIndex = self._currentItemIndex + 1
+ self.OnListBoxSelect(None) # Reset buttons