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