aca310e5cc
New Features: In Tab-View mode, Ctrl-number will take the user to the numbered tab view. Modified files now show an '*' astrisk in the view title. Debugger framework can now support PHP debugging. Not important for python development, but at least that means the debugger framework is more generalized. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@38852 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
445 lines
19 KiB
Python
445 lines
19 KiB
Python
#----------------------------------------------------------------------------
|
|
# Name: ExtensionService.py
|
|
# Purpose: Extension Service for IDE
|
|
#
|
|
# Author: Peter Yared
|
|
#
|
|
# Created: 5/23/05
|
|
# CVS-ID: $ID:$
|
|
# Copyright: (c) 2005-2006 ActiveGrid, Inc.
|
|
# License: wxWindows License
|
|
#----------------------------------------------------------------------------
|
|
|
|
import wx
|
|
import wx.lib.pydocview
|
|
import MessageService
|
|
import ProjectEditor
|
|
import os
|
|
import os.path
|
|
import activegrid.util.xmlutils as xmlutils
|
|
_ = wx.GetTranslation
|
|
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Constants
|
|
#----------------------------------------------------------------------------
|
|
SPACE = 10
|
|
HALF_SPACE = 5
|
|
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Classes
|
|
#----------------------------------------------------------------------------
|
|
|
|
class Extension:
|
|
|
|
|
|
def __init__(self, menuItemName=None):
|
|
self.menuItemName = menuItemName
|
|
self.id = 0
|
|
self.menuItemDesc = ''
|
|
self.command = ''
|
|
self.commandPreArgs = ''
|
|
self.commandPostArgs = ''
|
|
self.fileExt = None
|
|
self.opOnSelectedFile = True
|
|
|
|
|
|
class ExtensionService(wx.lib.pydocview.DocService):
|
|
|
|
EXTENSIONS_KEY = "/AG_Extensions"
|
|
|
|
def __init__(self):
|
|
self.LoadExtensions()
|
|
|
|
|
|
def __getExtensionKeyName(extensionName):
|
|
return "%s/%s" % (ExtensionService.EXTENSIONS_KEY, extensionName)
|
|
|
|
|
|
__getExtensionKeyName = staticmethod(__getExtensionKeyName)
|
|
|
|
|
|
def LoadExtensions(self):
|
|
self._extensions = []
|
|
|
|
extensionNames = []
|
|
config = wx.ConfigBase_Get()
|
|
path = config.GetPath()
|
|
try:
|
|
config.SetPath(ExtensionService.EXTENSIONS_KEY)
|
|
cont, value, index = config.GetFirstEntry()
|
|
while cont:
|
|
extensionNames.append(value)
|
|
cont, value, index = config.GetNextEntry(index)
|
|
finally:
|
|
config.SetPath(path)
|
|
|
|
for extensionName in extensionNames:
|
|
extensionData = config.Read(self.__getExtensionKeyName(extensionName))
|
|
if extensionData:
|
|
extension = xmlutils.unmarshal(extensionData.encode('utf-8'))
|
|
self._extensions.append(extension)
|
|
|
|
|
|
def SaveExtensions(self):
|
|
config = wx.ConfigBase_Get()
|
|
config.DeleteGroup(ExtensionService.EXTENSIONS_KEY)
|
|
for extension in self._extensions:
|
|
config.Write(self.__getExtensionKeyName(extension.menuItemName), xmlutils.marshal(extension))
|
|
|
|
|
|
def GetExtensions(self):
|
|
return self._extensions
|
|
|
|
|
|
def SetExtensions(self, extensions):
|
|
self._extensions = extensions
|
|
|
|
|
|
def CheckSumExtensions(self):
|
|
return xmlutils.marshal(self._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:
|
|
index = menuBar.FindMenu(_("&Run"))
|
|
if index == -1:
|
|
index = menuBar.FindMenu(_("&Project"))
|
|
if index == -1:
|
|
index = menuBar.FindMenu(_("&Format"))
|
|
if index == -1:
|
|
index = menuBar.FindMenu(_("&View"))
|
|
menuBar.Insert(index + 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
|
|
if extension.opOnSelectedFile and isinstance(doc, ProjectEditor.ProjectDocument):
|
|
filename = doc.GetFirstView().GetSelectedFile()
|
|
if filename:
|
|
template = wx.GetApp().GetDocumentManager().FindTemplateForPath(filename)
|
|
for fileExt in extension.fileExt:
|
|
if fileExt in template.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
|
|
if extension.opOnSelectedFile and isinstance(doc, ProjectEditor.ProjectDocument):
|
|
filename = doc.GetFirstView().GetSelectedFile()
|
|
if not filename:
|
|
filename = doc.GetFilename()
|
|
else:
|
|
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.VERTICAL)
|
|
|
|
extOptionsPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
extCtrlSizer = wx.BoxSizer(wx.VERTICAL)
|
|
extCtrlSizer.Add(wx.StaticText(self, -1, _("External Tools:")), 0, wx.BOTTOM, HALF_SPACE)
|
|
self._extListBox = wx.ListBox(self, -1, style=wx.LB_SINGLE)
|
|
self.Bind(wx.EVT_LISTBOX, self.OnListBoxSelect, self._extListBox)
|
|
extCtrlSizer.Add(self._extListBox, 1, wx.BOTTOM | wx.EXPAND, SPACE)
|
|
buttonSizer = wx.GridSizer(cols=2, vgap=HALF_SPACE, hgap=HALF_SPACE)
|
|
self._moveUpButton = wx.Button(self, -1, _("Move Up"))
|
|
self.Bind(wx.EVT_BUTTON, self.OnMoveUp, self._moveUpButton)
|
|
buttonSizer.Add(self._moveUpButton, 1, wx.EXPAND)
|
|
self._moveDownButton = wx.Button(self, -1, _("Move Down"))
|
|
self.Bind(wx.EVT_BUTTON, self.OnMoveDown, self._moveDownButton)
|
|
buttonSizer.Add(self._moveDownButton, 1, wx.EXPAND)
|
|
self._addButton = wx.Button(self, wx.ID_ADD)
|
|
self.Bind(wx.EVT_BUTTON, self.OnAdd, self._addButton)
|
|
buttonSizer.Add(self._addButton, 1, wx.EXPAND)
|
|
self._deleteButton = wx.Button(self, wx.ID_DELETE, label=_("Delete")) # get rid of accelerator for letter d in "&Delete"
|
|
self.Bind(wx.EVT_BUTTON, self.OnDelete, self._deleteButton)
|
|
buttonSizer.Add(self._deleteButton, 1, wx.EXPAND)
|
|
extCtrlSizer.Add(buttonSizer, 0, wx.ALIGN_CENTER)
|
|
extOptionsPanelSizer.Add(extCtrlSizer, 0, wx.EXPAND)
|
|
|
|
self._extDetailPanel = wx.Panel(self)
|
|
staticBox = wx.StaticBox(self, label=_("Selected External Tool"))
|
|
staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
|
|
|
|
extDetailSizer = wx.FlexGridSizer(cols=2, vgap=5, hgap=5)
|
|
extDetailSizer.AddGrowableCol(1,1)
|
|
|
|
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Name:")), flag=wx.ALIGN_CENTER_VERTICAL)
|
|
self._menuItemNameTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
|
|
extDetailSizer.Add(self._menuItemNameTextCtrl, 0, wx.EXPAND)
|
|
self.Bind(wx.EVT_TEXT, self.SaveCurrentItem, self._menuItemNameTextCtrl)
|
|
|
|
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Description:")), flag=wx.ALIGN_CENTER_VERTICAL)
|
|
self._menuItemDescTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
|
|
extDetailSizer.Add(self._menuItemDescTextCtrl, 0, wx.EXPAND)
|
|
|
|
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Path:")), flag=wx.ALIGN_CENTER_VERTICAL)
|
|
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.FILE_MUST_EXIST|wx.HIDE_READONLY|wx.CHANGE_DIR)
|
|
path = self._commandTextCtrl.GetValue()
|
|
if path:
|
|
fileDlg.SetPath(path)
|
|
# fileDlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
|
|
if fileDlg.ShowModal() == wx.ID_OK:
|
|
self._commandTextCtrl.SetValue(fileDlg.GetPath())
|
|
self._commandTextCtrl.SetInsertionPointEnd()
|
|
self._commandTextCtrl.SetToolTipString(fileDlg.GetPath())
|
|
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, wx.EXPAND)
|
|
|
|
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Pre Args:")), flag=wx.ALIGN_CENTER_VERTICAL)
|
|
self._commandPreArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
|
|
extDetailSizer.Add(self._commandPreArgsTextCtrl, 0, wx.EXPAND)
|
|
|
|
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Post Args:")), flag=wx.ALIGN_CENTER_VERTICAL)
|
|
self._commandPostArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
|
|
extDetailSizer.Add(self._commandPostArgsTextCtrl, 0, wx.EXPAND)
|
|
|
|
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("File Extensions:")), flag=wx.ALIGN_CENTER_VERTICAL)
|
|
self._fileExtTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
|
|
self._fileExtTextCtrl.SetToolTipString(_("""For example: "txt, text" (comma separated) or "*" for all files"""))
|
|
extDetailSizer.Add(self._fileExtTextCtrl, 0, wx.EXPAND)
|
|
|
|
self._selFileCtrl = wx.CheckBox(self._extDetailPanel, -1, _("Operate on Selected File"))
|
|
extDetailSizer.Add(self._selFileCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
|
|
self._selFileCtrl.SetToolTipString(_("If focus is in the project, instead of operating on the project file, operate on the selected file."))
|
|
|
|
self._extDetailPanel.SetSizer(extDetailSizer)
|
|
staticBoxSizer.Add(self._extDetailPanel, 1, wx.ALL|wx.EXPAND, SPACE)
|
|
|
|
extOptionsPanelSizer.Add(staticBoxSizer, 1, wx.LEFT|wx.EXPAND, SPACE)
|
|
|
|
extOptionsPanelBorderSizer.Add(extOptionsPanelSizer, 1, wx.ALL|wx.EXPAND, SPACE)
|
|
self.SetSizer(extOptionsPanelBorderSizer)
|
|
|
|
if self.PopulateItems():
|
|
self._extListBox.SetSelection(0)
|
|
self.OnListBoxSelect()
|
|
|
|
self.Layout()
|
|
|
|
parent.AddPage(self, _("External Tools"))
|
|
|
|
|
|
def OnOK(self, optionsDialog):
|
|
self.SaveCurrentItem()
|
|
extensionsService = wx.GetApp().GetService(ExtensionService)
|
|
extensionsService.SetExtensions(self._extensions)
|
|
extensionsService.SaveExtensions()
|
|
if extensionsService.CheckSumExtensions() != self._oldExtensions: # see PopulateItems() note about self._oldExtensions
|
|
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())
|
|
self._oldExtensions = extensionsService.CheckSumExtensions() # wxBug: need to make a copy now since the deepcopy reorders fields, so we must compare the prestine copy with the modified copy
|
|
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=None):
|
|
self.SaveCurrentItem()
|
|
if self._extListBox.GetSelection() == wx.NOT_FOUND:
|
|
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(',')
|
|
extension.opOnSelectedFile = self._selFileCtrl.GetValue()
|
|
|
|
|
|
def LoadItem(self, extension):
|
|
if extension:
|
|
self._menuItemDescTextCtrl.SetValue(extension.menuItemDesc or '')
|
|
self._commandTextCtrl.SetValue(extension.command or '')
|
|
self._commandTextCtrl.SetToolTipString(extension.command or '')
|
|
self._commandPreArgsTextCtrl.SetValue(extension.commandPreArgs or '')
|
|
self._commandPostArgsTextCtrl.SetValue(extension.commandPostArgs or '')
|
|
if extension.fileExt:
|
|
list = ""
|
|
for ext in extension.fileExt:
|
|
if list:
|
|
list = list + ", "
|
|
list = list + ext
|
|
self._fileExtTextCtrl.SetValue(list)
|
|
else:
|
|
self._fileExtTextCtrl.SetValue('')
|
|
self._selFileCtrl.SetValue(extension.opOnSelectedFile)
|
|
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._commandTextCtrl.SetToolTipString(_("Path to executable"))
|
|
self._commandPreArgsTextCtrl.SetValue('')
|
|
self._commandPostArgsTextCtrl.SetValue('')
|
|
self._fileExtTextCtrl.SetValue('')
|
|
self._selFileCtrl.SetValue(True)
|
|
self._extDetailPanel.Enable(False)
|
|
|
|
|
|
def OnAdd(self, event):
|
|
self.SaveCurrentItem()
|
|
name = _("Untitled")
|
|
count = 1
|
|
while self._extListBox.FindString(name) != wx.NOT_FOUND:
|
|
count = count + 1
|
|
name = _("Untitled%s") % count
|
|
extension = Extension(name)
|
|
self._extensions.append(extension)
|
|
self._extListBox.Append(extension.menuItemName, extension)
|
|
self._extListBox.SetStringSelection(extension.menuItemName)
|
|
self.OnListBoxSelect()
|
|
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()
|
|
|
|
|
|
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() # 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() # Reset buttons
|