1 #---------------------------------------------------------------------------- 
   2 # Name:         ExtensionService.py 
   3 # Purpose:      Extension Service for IDE 
   9 # Copyright:    (c) 2005-2006 ActiveGrid, Inc. 
  10 # License:      wxWindows License 
  11 #---------------------------------------------------------------------------- 
  14 import wx
.lib
.pydocview
 
  19 import activegrid
.util
.xmlutils 
as xmlutils
 
  23 #---------------------------------------------------------------------------- 
  25 #---------------------------------------------------------------------------- 
  30 #---------------------------------------------------------------------------- 
  32 #---------------------------------------------------------------------------- 
  37     def __init__(self
, menuItemName
=None): 
  38         self
.menuItemName 
= menuItemName
 
  40         self
.menuItemDesc 
= '' 
  42         self
.commandPreArgs 
= '' 
  43         self
.commandPostArgs 
= '' 
  45         self
.opOnSelectedFile 
= True 
  48 class ExtensionService(wx
.lib
.pydocview
.DocService
): 
  50     EXTENSIONS_KEY 
= "/AG_Extensions" 
  56     def __getExtensionKeyName(extensionName
): 
  57         return "%s/%s" % (ExtensionService
.EXTENSIONS_KEY
, extensionName
) 
  60     __getExtensionKeyName 
= staticmethod(__getExtensionKeyName
) 
  63     def LoadExtensions(self
): 
  67         config 
= wx
.ConfigBase_Get() 
  68         path 
= config
.GetPath() 
  70             config
.SetPath(ExtensionService
.EXTENSIONS_KEY
) 
  71             cont
, value
, index 
= config
.GetFirstEntry() 
  73                 extensionNames
.append(value
) 
  74                 cont
, value
, index 
= config
.GetNextEntry(index
) 
  78         for extensionName 
in extensionNames
: 
  79             extensionData 
= config
.Read(self
.__getExtensionKeyName
(extensionName
)) 
  81                 extension 
= xmlutils
.unmarshal(extensionData
.encode('utf-8')) 
  82                 self
._extensions
.append(extension
) 
  85     def SaveExtensions(self
): 
  86         config 
= wx
.ConfigBase_Get() 
  87         config
.DeleteGroup(ExtensionService
.EXTENSIONS_KEY
) 
  88         for extension 
in self
._extensions
: 
  89             config
.Write(self
.__getExtensionKeyName
(extension
.menuItemName
), xmlutils
.marshal(extension
)) 
  92     def GetExtensions(self
): 
  93         return self
._extensions
 
  96     def SetExtensions(self
, extensions
): 
  97         self
._extensions 
= extensions
 
 100     def CheckSumExtensions(self
): 
 101         return xmlutils
.marshal(self
._extensions
) 
 104     def InstallControls(self
, frame
, menuBar 
= None, toolBar 
= None, statusBar 
= None, document 
= None): 
 105         toolsMenuIndex 
= menuBar
.FindMenu(_("&Tools")) 
 106         if toolsMenuIndex 
> -1: 
 107             toolsMenu 
= menuBar
.GetMenu(toolsMenuIndex
) 
 109             toolsMenu 
= wx
.Menu() 
 112             if toolsMenu
.GetMenuItems(): 
 113                 toolsMenu
.AppendSeparator() 
 114             for ext 
in self
._extensions
: 
 115                 # Append a tool menu item for each extension 
 117                 toolsMenu
.Append(ext
.id, ext
.menuItemName
) 
 118                 wx
.EVT_MENU(frame
, ext
.id, frame
.ProcessEvent
) 
 119                 wx
.EVT_UPDATE_UI(frame
, ext
.id, frame
.ProcessUpdateUIEvent
) 
 121         if toolsMenuIndex 
== -1: 
 122             index 
= menuBar
.FindMenu(_("&Run")) 
 124                 index 
= menuBar
.FindMenu(_("&Project")) 
 126                 index 
= menuBar
.FindMenu(_("&Format")) 
 128                 index 
= menuBar
.FindMenu(_("&View")) 
 129             menuBar
.Insert(index 
+ 1, toolsMenu
, _("&Tools")) 
 132     def ProcessEvent(self
, event
): 
 134         for extension 
in self
._extensions
: 
 135             if id == extension
.id: 
 136                 self
.OnExecuteExtension(extension
) 
 141     def ProcessUpdateUIEvent(self
, event
): 
 143         for extension 
in self
._extensions
: 
 144             if id == extension
.id: 
 145                 if extension
.fileExt
: 
 146                     doc 
= wx
.GetApp().GetDocumentManager().GetCurrentDocument() 
 147                     if doc 
and '*' in extension
.fileExt
: 
 151                         for fileExt 
in extension
.fileExt
: 
 152                             if fileExt 
in doc
.GetDocumentTemplate().GetFileFilter(): 
 155                         if extension
.opOnSelectedFile 
and isinstance(doc
, ProjectEditor
.ProjectDocument
): 
 156                             filename 
= doc
.GetFirstView().GetSelectedFile() 
 158                                 template 
= wx
.GetApp().GetDocumentManager().FindTemplateForPath(filename
) 
 159                                 for fileExt 
in extension
.fileExt
: 
 160                                     if fileExt 
in template
.GetFileFilter(): 
 168     def OnExecuteExtension(self
, extension
): 
 169         if extension
.fileExt
: 
 170             doc 
= wx
.GetApp().GetDocumentManager().GetCurrentDocument() 
 173             if extension
.opOnSelectedFile 
and isinstance(doc
, ProjectEditor
.ProjectDocument
): 
 174                 filename 
= doc
.GetFirstView().GetSelectedFile() 
 176                     filename 
= doc
.GetFilename() 
 178                 filename 
= doc
.GetFilename() 
 179             ext 
= os
.path
.splitext(filename
)[1] 
 180             if not '*' in extension
.fileExt
: 
 181                 if not ext 
or ext
[1:] not in extension
.fileExt
: 
 183             cmds 
= [extension
.command
] 
 184             if extension
.commandPreArgs
: 
 185                 cmds
.append(extension
.commandPreArgs
) 
 186             cmds
.append(filename
) 
 187             if extension
.commandPostArgs
: 
 188                 cmds
.append(extension
.commandPostArgs
) 
 189             os
.spawnv(os
.P_NOWAIT
, extension
.command
, cmds
) 
 192             cmd 
= extension
.command
 
 193             if extension
.commandPreArgs
: 
 194                 cmd 
= cmd 
+ ' ' + extension
.commandPreArgs
 
 195             if extension
.commandPostArgs
: 
 196                 cmd 
= cmd 
+ ' ' + extension
.commandPostArgs
 
 198             messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 199             messageService
.ShowWindow() 
 200             view 
= messageService
.GetView() 
 201             for line 
in f
.readlines(): 
 203             view
.GetControl().EnsureCaretVisible() 
 207 class ExtensionOptionsPanel(wx
.Panel
): 
 210     def __init__(self
, parent
, id): 
 211         wx
.Panel
.__init
__(self
, parent
, id) 
 213         extOptionsPanelBorderSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 215         extOptionsPanelSizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 217         extCtrlSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 218         extCtrlSizer
.Add(wx
.StaticText(self
, -1, _("External Tools:")), 0, wx
.BOTTOM
, HALF_SPACE
) 
 219         self
._extListBox 
= wx
.ListBox(self
, -1, style
=wx
.LB_SINGLE
) 
 220         self
.Bind(wx
.EVT_LISTBOX
, self
.OnListBoxSelect
, self
._extListBox
) 
 221         extCtrlSizer
.Add(self
._extListBox
, 1, wx
.BOTTOM | wx
.EXPAND
, SPACE
) 
 222         buttonSizer 
= wx
.GridSizer(cols
=2, vgap
=HALF_SPACE
, hgap
=HALF_SPACE
) 
 223         self
._moveUpButton 
= wx
.Button(self
, -1, _("Move Up")) 
 224         self
.Bind(wx
.EVT_BUTTON
, self
.OnMoveUp
, self
._moveUpButton
) 
 225         buttonSizer
.Add(self
._moveUpButton
, 1, wx
.EXPAND
) 
 226         self
._moveDownButton 
= wx
.Button(self
, -1, _("Move Down")) 
 227         self
.Bind(wx
.EVT_BUTTON
, self
.OnMoveDown
, self
._moveDownButton
) 
 228         buttonSizer
.Add(self
._moveDownButton
, 1, wx
.EXPAND
) 
 229         self
._addButton 
= wx
.Button(self
, wx
.ID_ADD
) 
 230         self
.Bind(wx
.EVT_BUTTON
, self
.OnAdd
, self
._addButton
) 
 231         buttonSizer
.Add(self
._addButton
, 1, wx
.EXPAND
) 
 232         self
._deleteButton 
= wx
.Button(self
, wx
.ID_DELETE
, label
=_("Delete")) # get rid of accelerator for letter d in "&Delete" 
 233         self
.Bind(wx
.EVT_BUTTON
, self
.OnDelete
, self
._deleteButton
) 
 234         buttonSizer
.Add(self
._deleteButton
, 1, wx
.EXPAND
) 
 235         extCtrlSizer
.Add(buttonSizer
, 0, wx
.ALIGN_CENTER
) 
 236         extOptionsPanelSizer
.Add(extCtrlSizer
, 0, wx
.EXPAND
) 
 238         self
._extDetailPanel 
= wx
.Panel(self
) 
 239         staticBox 
= wx
.StaticBox(self
, label
=_("Selected External Tool")) 
 240         staticBoxSizer 
= wx
.StaticBoxSizer(staticBox
, wx
.VERTICAL
) 
 242         extDetailSizer 
= wx
.FlexGridSizer(cols
=2, vgap
=5, hgap
=5) 
 243         extDetailSizer
.AddGrowableCol(1,1) 
 245         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Menu Item Name:")), flag
=wx
.ALIGN_CENTER_VERTICAL
) 
 246         self
._menuItemNameTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 247         extDetailSizer
.Add(self
._menuItemNameTextCtrl
, 0, wx
.EXPAND
) 
 248         self
.Bind(wx
.EVT_TEXT
, self
.SaveCurrentItem
, self
._menuItemNameTextCtrl
) 
 250         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Menu Item Description:")), flag
=wx
.ALIGN_CENTER_VERTICAL
) 
 251         self
._menuItemDescTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 252         extDetailSizer
.Add(self
._menuItemDescTextCtrl
, 0, wx
.EXPAND
) 
 254         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Command Path:")), flag
=wx
.ALIGN_CENTER_VERTICAL
) 
 255         self
._commandTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 256         findFileButton 
= wx
.Button(self
._extDetailPanel
, -1, _("Browse...")) 
 257         def OnBrowseButton(event
): 
 258             fileDlg 
= wx
.FileDialog(self
, _("Choose an Executable:"), style
=wx
.OPEN|wx
.FILE_MUST_EXIST|wx
.HIDE_READONLY|wx
.CHANGE_DIR
) 
 259             path 
= self
._commandTextCtrl
.GetValue() 
 261                 fileDlg
.SetPath(path
) 
 262             # fileDlg.CenterOnParent()  # wxBug: caused crash with wx.FileDialog 
 263             if fileDlg
.ShowModal() == wx
.ID_OK
: 
 264                 self
._commandTextCtrl
.SetValue(fileDlg
.GetPath()) 
 265                 self
._commandTextCtrl
.SetInsertionPointEnd() 
 266                 self
._commandTextCtrl
.SetToolTipString(fileDlg
.GetPath()) 
 268         wx
.EVT_BUTTON(findFileButton
, -1, OnBrowseButton
) 
 269         hsizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 270         hsizer
.Add(self
._commandTextCtrl
, 1, wx
.EXPAND
) 
 271         hsizer
.Add(findFileButton
, 0, wx
.LEFT
, HALF_SPACE
) 
 272         extDetailSizer
.Add(hsizer
, 0, wx
.EXPAND
) 
 274         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Command Pre Args:")), flag
=wx
.ALIGN_CENTER_VERTICAL
) 
 275         self
._commandPreArgsTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 276         extDetailSizer
.Add(self
._commandPreArgsTextCtrl
, 0, wx
.EXPAND
) 
 278         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Command Post Args:")), flag
=wx
.ALIGN_CENTER_VERTICAL
) 
 279         self
._commandPostArgsTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 280         extDetailSizer
.Add(self
._commandPostArgsTextCtrl
, 0, wx
.EXPAND
) 
 282         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("File Extensions:")), flag
=wx
.ALIGN_CENTER_VERTICAL
) 
 283         self
._fileExtTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 284         self
._fileExtTextCtrl
.SetToolTipString(_("""For example: "txt, text" (comma separated) or "*" for all files""")) 
 285         extDetailSizer
.Add(self
._fileExtTextCtrl
, 0, wx
.EXPAND
) 
 287         self
._selFileCtrl 
= wx
.CheckBox(self
._extDetailPanel
, -1, _("Operate on Selected File")) 
 288         extDetailSizer
.Add(self
._selFileCtrl
, 0, wx
.ALIGN_CENTER_VERTICAL|wx
.TOP
, SPACE
) 
 289         self
._selFileCtrl
.SetToolTipString(_("If focus is in the project, instead of operating on the project file, operate on the selected file.")) 
 291         self
._extDetailPanel
.SetSizer(extDetailSizer
) 
 292         staticBoxSizer
.Add(self
._extDetailPanel
, 1, wx
.ALL|wx
.EXPAND
, SPACE
) 
 294         extOptionsPanelSizer
.Add(staticBoxSizer
, 1, wx
.LEFT|wx
.EXPAND
, SPACE
) 
 296         extOptionsPanelBorderSizer
.Add(extOptionsPanelSizer
, 1, wx
.ALL|wx
.EXPAND
, SPACE
) 
 297         self
.SetSizer(extOptionsPanelBorderSizer
) 
 299         if self
.PopulateItems(): 
 300             self
._extListBox
.SetSelection(0) 
 301         self
.OnListBoxSelect() 
 305         parent
.AddPage(self
, _("External Tools")) 
 308     def OnOK(self
, optionsDialog
): 
 309         self
.SaveCurrentItem() 
 310         extensionsService 
= wx
.GetApp().GetService(ExtensionService
) 
 311         extensionsService
.SetExtensions(self
._extensions
) 
 312         extensionsService
.SaveExtensions() 
 313         if extensionsService
.CheckSumExtensions() != self
._oldExtensions
:  # see PopulateItems() note about self._oldExtensions 
 314             msgTitle 
= wx
.GetApp().GetAppName() 
 316                 msgTitle 
= _("Document Options") 
 317             wx
.MessageBox(_("Extension changes will not appear until the application is restarted."), 
 319                           wx
.OK | wx
.ICON_INFORMATION
, 
 323     def PopulateItems(self
): 
 324         extensionsService 
= wx
.GetApp().GetService(ExtensionService
) 
 326         self
._extensions 
= copy
.deepcopy(extensionsService
.GetExtensions()) 
 327         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 
 328         for extension 
in self
._extensions
: 
 329             self
._extListBox
.Append(extension
.menuItemName
, extension
) 
 330         self
._currentItem 
= None 
 331         self
._currentItemIndex 
= -1 
 332         return len(self
._extensions
) 
 335     def OnListBoxSelect(self
, event
=None): 
 336         self
.SaveCurrentItem() 
 337         if self
._extListBox
.GetSelection() == wx
.NOT_FOUND
: 
 338             self
._currentItemIndex 
= -1 
 339             self
._currentItem 
= None 
 340             self
._deleteButton
.Enable(False) 
 341             self
._moveUpButton
.Enable(False) 
 342             self
._moveDownButton
.Enable(False) 
 344             self
._currentItemIndex 
= self
._extListBox
.GetSelection() 
 345             self
._currentItem 
= self
._extListBox
.GetClientData(self
._currentItemIndex
) 
 346             self
._deleteButton
.Enable() 
 347             self
._moveUpButton
.Enable(self
._extListBox
.GetCount() > 1 and self
._currentItemIndex 
> 0) 
 348             self
._moveDownButton
.Enable(self
._extListBox
.GetCount() > 1 and self
._currentItemIndex 
< self
._extListBox
.GetCount() - 1) 
 349         self
.LoadItem(self
._currentItem
) 
 352     def SaveCurrentItem(self
, event
=None): 
 353         extension 
= self
._currentItem
 
 355             if extension
.menuItemName 
!= self
._menuItemNameTextCtrl
.GetValue(): 
 356                 extension
.menuItemName 
= self
._menuItemNameTextCtrl
.GetValue() 
 357                 self
._extListBox
.SetString(self
._currentItemIndex
, extension
.menuItemName
) 
 358             extension
.menuItemDesc 
= self
._menuItemDescTextCtrl
.GetValue() 
 359             extension
.command 
= self
._commandTextCtrl
.GetValue() 
 360             extension
.commandPreArgs 
= self
._commandPreArgsTextCtrl
.GetValue() 
 361             extension
.commandPostArgs 
= self
._commandPostArgsTextCtrl
.GetValue() 
 362             fileExt 
= self
._fileExtTextCtrl
.GetValue().replace(' ','') 
 364                 extension
.fileExt 
= None 
 366                 extension
.fileExt 
= fileExt
.split(',') 
 367             extension
.opOnSelectedFile 
= self
._selFileCtrl
.GetValue() 
 370     def LoadItem(self
, extension
): 
 372             self
._menuItemDescTextCtrl
.SetValue(extension
.menuItemDesc 
or '') 
 373             self
._commandTextCtrl
.SetValue(extension
.command 
or '') 
 374             self
._commandTextCtrl
.SetToolTipString(extension
.command 
or '') 
 375             self
._commandPreArgsTextCtrl
.SetValue(extension
.commandPreArgs 
or '') 
 376             self
._commandPostArgsTextCtrl
.SetValue(extension
.commandPostArgs 
or '') 
 377             if extension
.fileExt
: 
 379                 for ext 
in extension
.fileExt
: 
 383                 self
._fileExtTextCtrl
.SetValue(list) 
 385                 self
._fileExtTextCtrl
.SetValue('') 
 386             self
._selFileCtrl
.SetValue(extension
.opOnSelectedFile
) 
 387             self
._menuItemNameTextCtrl
.SetValue(extension
.menuItemName 
or '')  # Do the name last since it triggers the write event that updates the entire item 
 388             self
._extDetailPanel
.Enable() 
 390             self
._menuItemNameTextCtrl
.SetValue('') 
 391             self
._menuItemDescTextCtrl
.SetValue('') 
 392             self
._commandTextCtrl
.SetValue('') 
 393             self
._commandTextCtrl
.SetToolTipString(_("Path to executable")) 
 394             self
._commandPreArgsTextCtrl
.SetValue('') 
 395             self
._commandPostArgsTextCtrl
.SetValue('') 
 396             self
._fileExtTextCtrl
.SetValue('') 
 397             self
._selFileCtrl
.SetValue(True) 
 398             self
._extDetailPanel
.Enable(False) 
 401     def OnAdd(self
, event
): 
 402         self
.SaveCurrentItem() 
 405         while self
._extListBox
.FindString(name
) != wx
.NOT_FOUND
: 
 407             name 
= _("Untitled%s") % count
 
 408         extension 
= Extension(name
) 
 409         self
._extensions
.append(extension
) 
 410         self
._extListBox
.Append(extension
.menuItemName
, extension
) 
 411         self
._extListBox
.SetStringSelection(extension
.menuItemName
) 
 412         self
.OnListBoxSelect() 
 413         self
._menuItemNameTextCtrl
.SetFocus() 
 414         self
._menuItemNameTextCtrl
.SetSelection(-1, -1) 
 417     def OnDelete(self
, event
): 
 418         self
._extListBox
.Delete(self
._currentItemIndex
) 
 419         self
._extensions
.remove(self
._currentItem
) 
 420         self
._currentItemIndex 
= min(self
._currentItemIndex
, self
._extListBox
.GetCount() - 1) 
 421         if self
._currentItemIndex 
> -1: 
 422             self
._extListBox
.SetSelection(self
._currentItemIndex
) 
 423         self
._currentItem 
= None  # Don't update it since it no longer exists 
 424         self
.OnListBoxSelect() 
 427     def OnMoveUp(self
, event
): 
 428         itemAboveString 
= self
._extListBox
.GetString(self
._currentItemIndex 
- 1) 
 429         itemAboveData 
= self
._extListBox
.GetClientData(self
._currentItemIndex 
- 1) 
 430         self
._extListBox
.Delete(self
._currentItemIndex 
- 1) 
 431         self
._extListBox
.Insert(itemAboveString
, self
._currentItemIndex
) 
 432         self
._extListBox
.SetClientData(self
._currentItemIndex
, itemAboveData
) 
 433         self
._currentItemIndex 
= self
._currentItemIndex 
- 1 
 434         self
.OnListBoxSelect() # Reset buttons 
 437     def OnMoveDown(self
, event
): 
 438         itemBelowString 
= self
._extListBox
.GetString(self
._currentItemIndex 
+ 1) 
 439         itemBelowData 
= self
._extListBox
.GetClientData(self
._currentItemIndex 
+ 1) 
 440         self
._extListBox
.Delete(self
._currentItemIndex 
+ 1) 
 441         self
._extListBox
.Insert(itemBelowString
, self
._currentItemIndex
) 
 442         self
._extListBox
.SetClientData(self
._currentItemIndex
, itemBelowData
) 
 443         self
._currentItemIndex 
= self
._currentItemIndex 
+ 1 
 444         self
.OnListBoxSelect() # Reset buttons