1 #---------------------------------------------------------------------------- 
   2 # Name:         ExtensionService.py 
   3 # Purpose:      Extension Service for IDE 
   9 # Copyright:    (c) 2005 ActiveGrid, Inc. 
  10 # License:      wxWindows License 
  11 #---------------------------------------------------------------------------- 
  14 import wx
.lib
.pydocview
 
  19 import activegrid
.util
.xmlutils 
as xmlutils
 
  28 EXTENSIONS_CONFIG_STRING 
= "Extensions" 
  32 # TODO: Redo extensions menu on OK, or provide alert that it won't happen until restart 
  35 #---------------------------------------------------------------------------- 
  37 #---------------------------------------------------------------------------- 
  42     def __init__(self
, menuItemName
=None): 
  43         self
.menuItemName 
= menuItemName
 
  45         self
.menuItemDesc 
= '' 
  47         self
.commandPreArgs 
= '' 
  48         self
.commandPostArgs 
= '' 
  50         self
.opOnSelectedFile 
= True 
  53 class ExtensionService(wx
.lib
.pydocview
.DocService
): 
  55     EXTENSIONS_KEY 
= "/AG_Extensions" 
  61     def __getExtensionKeyName(extensionName
): 
  62         return "%s/%s" % (ExtensionService
.EXTENSIONS_KEY
, extensionName
) 
  65     __getExtensionKeyName 
= staticmethod(__getExtensionKeyName
) 
  68     def LoadExtensions(self
): 
  72         config 
= wx
.ConfigBase_Get() 
  73         path 
= config
.GetPath() 
  75             config
.SetPath(ExtensionService
.EXTENSIONS_KEY
) 
  76             cont
, value
, index 
= config
.GetFirstEntry() 
  78                 extensionNames
.append(value
) 
  79                 cont
, value
, index 
= config
.GetNextEntry(index
) 
  83         for extensionName 
in extensionNames
: 
  84             extensionData 
= config
.Read(self
.__getExtensionKeyName
(extensionName
)) 
  86                 extension 
= xmlutils
.unmarshal(extensionData
.encode('utf-8')) 
  87                 self
._extensions
.append(extension
) 
  90     def SaveExtensions(self
): 
  91         config 
= wx
.ConfigBase_Get() 
  92         config
.DeleteGroup(ExtensionService
.EXTENSIONS_KEY
) 
  93         for extension 
in self
._extensions
: 
  94             config
.Write(self
.__getExtensionKeyName
(extension
.menuItemName
), xmlutils
.marshal(extension
)) 
  97     def GetExtensions(self
): 
  98         return self
._extensions
 
 101     def SetExtensions(self
, extensions
): 
 102         self
._extensions 
= extensions
 
 105     def CheckSumExtensions(self
): 
 106         return xmlutils
.marshal(self
._extensions
) 
 109     def InstallControls(self
, frame
, menuBar 
= None, toolBar 
= None, statusBar 
= None, document 
= None): 
 110         toolsMenuIndex 
= menuBar
.FindMenu(_("&Tools")) 
 111         if toolsMenuIndex 
> -1: 
 112             toolsMenu 
= menuBar
.GetMenu(toolsMenuIndex
) 
 114             toolsMenu 
= wx
.Menu() 
 117             if toolsMenu
.GetMenuItems(): 
 118                 toolsMenu
.AppendSeparator()             
 119             for ext 
in self
._extensions
: 
 120                 # Append a tool menu item for each extension 
 122                 toolsMenu
.Append(ext
.id, ext
.menuItemName
) 
 123                 wx
.EVT_MENU(frame
, ext
.id, frame
.ProcessEvent
) 
 124                 wx
.EVT_UPDATE_UI(frame
, ext
.id, frame
.ProcessUpdateUIEvent
) 
 126         if toolsMenuIndex 
== -1: 
 127             index 
= menuBar
.FindMenu(_("&Run")) 
 129                 index 
= menuBar
.FindMenu(_("&Project")) 
 131                 index 
= menuBar
.FindMenu(_("&Format")) 
 133                 index 
= menuBar
.FindMenu(_("&View")) 
 134             menuBar
.Insert(index 
+ 1, toolsMenu
, _("&Tools")) 
 137     def ProcessEvent(self
, event
): 
 139         for extension 
in self
._extensions
: 
 140             if id == extension
.id: 
 141                 self
.OnExecuteExtension(extension
) 
 146     def ProcessUpdateUIEvent(self
, event
): 
 148         for extension 
in self
._extensions
: 
 149             if id == extension
.id: 
 150                 if extension
.fileExt
: 
 151                     doc 
= wx
.GetApp().GetDocumentManager().GetCurrentDocument() 
 152                     if doc 
and '*' in extension
.fileExt
: 
 156                         for fileExt 
in extension
.fileExt
: 
 157                             if fileExt 
in doc
.GetDocumentTemplate().GetFileFilter(): 
 160                         if extension
.opOnSelectedFile 
and isinstance(doc
, ProjectEditor
.ProjectDocument
): 
 161                             filename 
= doc
.GetFirstView().GetSelectedFile() 
 163                                 template 
= wx
.GetApp().GetDocumentManager().FindTemplateForPath(filename
) 
 164                                 for fileExt 
in extension
.fileExt
: 
 165                                     if fileExt 
in template
.GetFileFilter(): 
 173     def OnExecuteExtension(self
, extension
): 
 174         if extension
.fileExt
: 
 175             doc 
= wx
.GetApp().GetDocumentManager().GetCurrentDocument() 
 178             if extension
.opOnSelectedFile 
and isinstance(doc
, ProjectEditor
.ProjectDocument
): 
 179                 filename 
= doc
.GetFirstView().GetSelectedFile() 
 181                     filename 
= doc
.GetFilename() 
 183                 filename 
= doc
.GetFilename() 
 184             ext 
= os
.path
.splitext(filename
)[1] 
 185             if not '*' in extension
.fileExt
: 
 186                 if not ext 
or ext
[1:] not in extension
.fileExt
: 
 188             cmds 
= [extension
.command
] 
 189             if extension
.commandPreArgs
: 
 190                 cmds
.append(extension
.commandPreArgs
) 
 191             cmds
.append(filename
) 
 192             if extension
.commandPostArgs
: 
 193                 cmds
.append(extension
.commandPostArgs
) 
 194             os
.spawnv(os
.P_NOWAIT
, extension
.command
, cmds
) 
 197             cmd 
= extension
.command
 
 198             if extension
.commandPreArgs
: 
 199                 cmd 
= cmd 
+ ' ' + extension
.commandPreArgs
 
 200             if extension
.commandPostArgs
: 
 201                 cmd 
= cmd 
+ ' ' + extension
.commandPostArgs
 
 203             messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 204             messageService
.ShowWindow() 
 205             view 
= messageService
.GetView() 
 206             for line 
in f
.readlines(): 
 208             view
.GetControl().EnsureCaretVisible() 
 212 class ExtensionOptionsPanel(wx
.Panel
): 
 215     def __init__(self
, parent
, id): 
 216         wx
.Panel
.__init
__(self
, parent
, id) 
 218         extOptionsPanelBorderSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 220         extOptionsPanelSizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 222         extCtrlSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 223         extCtrlSizer
.Add(wx
.StaticText(self
, -1, _("External Tools:")), 0, wx
.BOTTOM
, HALF_SPACE
) 
 224         self
._extListBox 
= wx
.ListBox(self
, -1, size
=(-1,160), style
=wx
.LB_SINGLE
) 
 225         self
.Bind(wx
.EVT_LISTBOX
, self
.OnListBoxSelect
, self
._extListBox
) 
 226         extCtrlSizer
.Add(self
._extListBox
, 1, wx
.BOTTOM | wx
.EXPAND
, SPACE
)         
 227         buttonSizer 
= wx
.GridSizer(cols
=2, vgap
=5, hgap
=10) 
 228         self
._moveUpButton 
= wx
.Button(self
, -1, _("Move Up")) 
 229         self
.Bind(wx
.EVT_BUTTON
, self
.OnMoveUp
, self
._moveUpButton
) 
 230         buttonSizer
.Add(self
._moveUpButton
, 1, wx
.EXPAND
) 
 231         self
._moveDownButton 
= wx
.Button(self
, -1, _("Move Down")) 
 232         self
.Bind(wx
.EVT_BUTTON
, self
.OnMoveDown
, self
._moveDownButton
) 
 233         buttonSizer
.Add(self
._moveDownButton
, 1, wx
.EXPAND
) 
 234         self
._addButton 
= wx
.Button(self
, wx
.ID_ADD
) 
 235         self
.Bind(wx
.EVT_BUTTON
, self
.OnAdd
, self
._addButton
) 
 236         buttonSizer
.Add(self
._addButton
, 1, wx
.EXPAND
) 
 237         self
._deleteButton 
= wx
.Button(self
, wx
.ID_DELETE
, label
=_("Delete")) # get rid of accelerator for letter d in "&Delete" 
 238         self
.Bind(wx
.EVT_BUTTON
, self
.OnDelete
, self
._deleteButton
) 
 239         buttonSizer
.Add(self
._deleteButton
, 1, wx
.EXPAND
) 
 240         extCtrlSizer
.Add(buttonSizer
, 0, wx
.ALIGN_CENTER
) 
 241         extOptionsPanelSizer
.Add(extCtrlSizer
, 0, wx
.EXPAND
) 
 243         self
._extDetailPanel 
= wx
.Panel(self
) 
 244         staticBox 
= wx
.StaticBox(self
, label
=_("Selected External Tool")) 
 245         staticBoxSizer 
= wx
.StaticBoxSizer(staticBox
, wx
.VERTICAL
) 
 247         extDetailSizer 
= wx
.FlexGridSizer(cols
=2, hgap
=5, vgap
=3) 
 248         extDetailSizer
.AddGrowableCol(1,1) 
 250         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Menu Item Name:"))) 
 251         self
._menuItemNameTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 252         extDetailSizer
.Add(self
._menuItemNameTextCtrl
, 0, wx
.EXPAND
) 
 253         self
.Bind(wx
.EVT_TEXT
, self
.SaveCurrentItem
, self
._menuItemNameTextCtrl
)         
 255         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Menu Item Description:"))) 
 256         self
._menuItemDescTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 257         extDetailSizer
.Add(self
._menuItemDescTextCtrl
, 0, wx
.EXPAND
) 
 259         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Command Path:"))) 
 260         self
._commandTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1))         
 261         findFileButton 
= wx
.Button(self
._extDetailPanel
, -1, _("Browse...")) 
 262         def OnBrowseButton(event
): 
 263             fileDlg 
= wx
.FileDialog(self
, _("Choose an Executable:"), style
=wx
.OPEN|wx
.FILE_MUST_EXIST|wx
.HIDE_READONLY|wx
.CHANGE_DIR
) 
 264             path 
= self
._commandTextCtrl
.GetValue() 
 266                 fileDlg
.SetPath(path
) 
 267             # fileDlg.CenterOnParent()  # wxBug: caused crash with wx.FileDialog 
 268             if fileDlg
.ShowModal() == wx
.ID_OK
: 
 269                 self
._commandTextCtrl
.SetValue(fileDlg
.GetPath()) 
 270                 self
._commandTextCtrl
.SetInsertionPointEnd() 
 271                 self
._commandTextCtrl
.SetToolTipString(fileDlg
.GetPath()) 
 273         wx
.EVT_BUTTON(findFileButton
, -1, OnBrowseButton
) 
 274         hsizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 275         hsizer
.Add(self
._commandTextCtrl
, 1, wx
.EXPAND
) 
 276         hsizer
.Add(findFileButton
, 0, wx
.LEFT
, HALF_SPACE
) 
 277         extDetailSizer
.Add(hsizer
, 0, wx
.EXPAND
) 
 279         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Command Pre Args:"))) 
 280         self
._commandPreArgsTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 281         extDetailSizer
.Add(self
._commandPreArgsTextCtrl
, 0, wx
.EXPAND
) 
 283         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("Command Post Args:"))) 
 284         self
._commandPostArgsTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 285         extDetailSizer
.Add(self
._commandPostArgsTextCtrl
, 0, wx
.EXPAND
) 
 287         extDetailSizer
.Add(wx
.StaticText(self
._extDetailPanel
, -1, _("File Extensions:"))) 
 288         self
._fileExtTextCtrl 
= wx
.TextCtrl(self
._extDetailPanel
, -1, size 
= (-1, -1)) 
 289         self
._fileExtTextCtrl
.SetToolTipString(_("""For example: "txt, text" (comma separated) or "*" for all files""")) 
 290         extDetailSizer
.Add(self
._fileExtTextCtrl
, 0, wx
.EXPAND
) 
 292         self
._selFileCtrl 
= wx
.CheckBox(self
._extDetailPanel
, -1, _("Operate on Selected File")) 
 293         extDetailSizer
.Add(self
._selFileCtrl
) 
 294         self
._selFileCtrl
.SetToolTipString(_("If focus is in the project, instead of operating on the project file, operate on the selected file.")) 
 296         self
._extDetailPanel
.SetSizer(extDetailSizer
) 
 297         staticBoxSizer
.Add(self
._extDetailPanel
, 1, wx
.ALL|wx
.EXPAND
, SPACE
) 
 299         extOptionsPanelSizer
.Add(staticBoxSizer
, 1, wx
.LEFT|wx
.EXPAND
, SPACE
) 
 301         extOptionsPanelBorderSizer
.Add(extOptionsPanelSizer
, 1, wx
.ALL|wx
.EXPAND
, SPACE
) 
 302         self
.SetSizer(extOptionsPanelBorderSizer
) 
 304         if self
.PopulateItems(): 
 305             self
._extListBox
.SetSelection(0) 
 306         self
.OnListBoxSelect() 
 310         parent
.AddPage(self
, _("External Tools"))         
 313     def OnOK(self
, optionsDialog
): 
 314         self
.SaveCurrentItem() 
 315         extensionsService 
= wx
.GetApp().GetService(ExtensionService
) 
 316         extensionsService
.SetExtensions(self
._extensions
) 
 317         extensionsService
.SaveExtensions() 
 318         if extensionsService
.CheckSumExtensions() != self
._oldExtensions
:  # see PopulateItems() note about self._oldExtensions 
 319             msgTitle 
= wx
.GetApp().GetAppName() 
 321                 msgTitle 
= _("Document Options") 
 322             wx
.MessageBox(_("Extension changes will not appear until the application is restarted."), 
 324                           wx
.OK | wx
.ICON_INFORMATION
, 
 328     def PopulateItems(self
): 
 329         extensionsService 
= wx
.GetApp().GetService(ExtensionService
) 
 331         self
._extensions 
= copy
.deepcopy(extensionsService
.GetExtensions()) 
 332         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 
 333         for extension 
in self
._extensions
: 
 334             self
._extListBox
.Append(extension
.menuItemName
, extension
) 
 335         self
._currentItem 
= None 
 336         self
._currentItemIndex 
= -1 
 337         return len(self
._extensions
) 
 340     def OnListBoxSelect(self
, event
=None): 
 341         self
.SaveCurrentItem() 
 342         if self
._extListBox
.GetSelection() == wx
.NOT_FOUND
: 
 343             self
._currentItemIndex 
= -1 
 344             self
._currentItem 
= None 
 345             self
._deleteButton
.Enable(False) 
 346             self
._moveUpButton
.Enable(False) 
 347             self
._moveDownButton
.Enable(False) 
 349             self
._currentItemIndex 
= self
._extListBox
.GetSelection() 
 350             self
._currentItem 
= self
._extListBox
.GetClientData(self
._currentItemIndex
) 
 351             self
._deleteButton
.Enable() 
 352             self
._moveUpButton
.Enable(self
._extListBox
.GetCount() > 1 and self
._currentItemIndex 
> 0) 
 353             self
._moveDownButton
.Enable(self
._extListBox
.GetCount() > 1 and self
._currentItemIndex 
< self
._extListBox
.GetCount() - 1) 
 354         self
.LoadItem(self
._currentItem
) 
 357     def SaveCurrentItem(self
, event
=None): 
 358         extension 
= self
._currentItem
 
 360             if extension
.menuItemName 
!= self
._menuItemNameTextCtrl
.GetValue(): 
 361                 extension
.menuItemName 
= self
._menuItemNameTextCtrl
.GetValue() 
 362                 self
._extListBox
.SetString(self
._currentItemIndex
, extension
.menuItemName
) 
 363             extension
.menuItemDesc 
= self
._menuItemDescTextCtrl
.GetValue() 
 364             extension
.command 
= self
._commandTextCtrl
.GetValue() 
 365             extension
.commandPreArgs 
= self
._commandPreArgsTextCtrl
.GetValue() 
 366             extension
.commandPostArgs 
= self
._commandPostArgsTextCtrl
.GetValue() 
 367             fileExt 
= self
._fileExtTextCtrl
.GetValue().replace(' ','') 
 369                 extension
.fileExt 
= None 
 371                 extension
.fileExt 
= fileExt
.split(',') 
 372             extension
.opOnSelectedFile 
= self
._selFileCtrl
.GetValue() 
 375     def LoadItem(self
, extension
): 
 377             self
._menuItemDescTextCtrl
.SetValue(extension
.menuItemDesc 
or '') 
 378             self
._commandTextCtrl
.SetValue(extension
.command 
or '') 
 379             self
._commandTextCtrl
.SetToolTipString(extension
.command 
or '') 
 380             self
._commandPreArgsTextCtrl
.SetValue(extension
.commandPreArgs 
or '') 
 381             self
._commandPostArgsTextCtrl
.SetValue(extension
.commandPostArgs 
or '') 
 382             if extension
.fileExt
: 
 384                 for ext 
in extension
.fileExt
: 
 388                 self
._fileExtTextCtrl
.SetValue(list) 
 390                 self
._fileExtTextCtrl
.SetValue('') 
 391             self
._selFileCtrl
.SetValue(extension
.opOnSelectedFile
) 
 392             self
._menuItemNameTextCtrl
.SetValue(extension
.menuItemName 
or '')  # Do the name last since it triggers the write event that updates the entire item 
 393             self
._extDetailPanel
.Enable() 
 395             self
._menuItemNameTextCtrl
.SetValue('') 
 396             self
._menuItemDescTextCtrl
.SetValue('') 
 397             self
._commandTextCtrl
.SetValue('') 
 398             self
._commandTextCtrl
.SetToolTipString(_("Path to executable")) 
 399             self
._commandPreArgsTextCtrl
.SetValue('') 
 400             self
._commandPostArgsTextCtrl
.SetValue('') 
 401             self
._fileExtTextCtrl
.SetValue('') 
 402             self
._selFileCtrl
.SetValue(True) 
 403             self
._extDetailPanel
.Enable(False) 
 406     def OnAdd(self
, event
): 
 407         self
.SaveCurrentItem() 
 410         while self
._extListBox
.FindString(name
) != wx
.NOT_FOUND
: 
 412             name 
= _("Untitled%s") % count
 
 413         extension 
= Extension(name
) 
 414         self
._extensions
.append(extension
) 
 415         self
._extListBox
.Append(extension
.menuItemName
, extension
) 
 416         self
._extListBox
.SetStringSelection(extension
.menuItemName
) 
 417         self
.OnListBoxSelect() 
 418         self
._menuItemNameTextCtrl
.SetFocus() 
 419         self
._menuItemNameTextCtrl
.SetSelection(-1, -1) 
 422     def OnDelete(self
, event
): 
 423         self
._extListBox
.Delete(self
._currentItemIndex
) 
 424         self
._extensions
.remove(self
._currentItem
)     
 425         self
._currentItemIndex 
= min(self
._currentItemIndex
, self
._extListBox
.GetCount() - 1) 
 426         if self
._currentItemIndex 
> -1: 
 427             self
._extListBox
.SetSelection(self
._currentItemIndex
) 
 428         self
._currentItem 
= None  # Don't update it since it no longer exists 
 429         self
.OnListBoxSelect() 
 432     def OnMoveUp(self
, event
): 
 433         itemAboveString 
= self
._extListBox
.GetString(self
._currentItemIndex 
- 1) 
 434         itemAboveData 
= self
._extListBox
.GetClientData(self
._currentItemIndex 
- 1) 
 435         self
._extListBox
.Delete(self
._currentItemIndex 
- 1) 
 436         self
._extListBox
.Insert(itemAboveString
, self
._currentItemIndex
) 
 437         self
._extListBox
.SetClientData(self
._currentItemIndex
, itemAboveData
) 
 438         self
._currentItemIndex 
= self
._currentItemIndex 
- 1 
 439         self
.OnListBoxSelect() # Reset buttons 
 442     def OnMoveDown(self
, event
): 
 443         itemBelowString 
= self
._extListBox
.GetString(self
._currentItemIndex 
+ 1) 
 444         itemBelowData 
= self
._extListBox
.GetClientData(self
._currentItemIndex 
+ 1) 
 445         self
._extListBox
.Delete(self
._currentItemIndex 
+ 1) 
 446         self
._extListBox
.Insert(itemBelowString
, self
._currentItemIndex
) 
 447         self
._extListBox
.SetClientData(self
._currentItemIndex
, itemBelowData
) 
 448         self
._currentItemIndex 
= self
._currentItemIndex 
+ 1 
 449         self
.OnListBoxSelect() # Reset buttons