]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/ExtensionService.py
DocView and ActiveGrid IDE updates from Morgan Hua:
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / ExtensionService.py
1 #----------------------------------------------------------------------------
2 # Name: ExtensionService.py
3 # Purpose: Extension Service for IDE
4 #
5 # Author: Peter Yared
6 #
7 # Created: 5/23/05
8 # CVS-ID: $ID:$
9 # Copyright: (c) 2005-2006 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12
13 import wx
14 import wx.lib.pydocview
15 import MessageService
16 import ProjectEditor
17 import os
18 import os.path
19 import activegrid.util.xmlutils as xmlutils
20 _ = wx.GetTranslation
21
22
23 #----------------------------------------------------------------------------
24 # Constants
25 #----------------------------------------------------------------------------
26 SPACE = 10
27 HALF_SPACE = 5
28
29
30 #----------------------------------------------------------------------------
31 # Classes
32 #----------------------------------------------------------------------------
33
34 class Extension:
35
36
37 def __init__(self, menuItemName=None):
38 self.menuItemName = menuItemName
39 self.id = 0
40 self.menuItemDesc = ''
41 self.command = ''
42 self.commandPreArgs = ''
43 self.commandPostArgs = ''
44 self.fileExt = None
45 self.opOnSelectedFile = True
46
47
48 class ExtensionService(wx.lib.pydocview.DocService):
49
50 EXTENSIONS_KEY = "/AG_Extensions"
51
52 def __init__(self):
53 self.LoadExtensions()
54
55
56 def __getExtensionKeyName(extensionName):
57 return "%s/%s" % (ExtensionService.EXTENSIONS_KEY, extensionName)
58
59
60 __getExtensionKeyName = staticmethod(__getExtensionKeyName)
61
62
63 def LoadExtensions(self):
64 self._extensions = []
65
66 extensionNames = []
67 config = wx.ConfigBase_Get()
68 path = config.GetPath()
69 try:
70 config.SetPath(ExtensionService.EXTENSIONS_KEY)
71 cont, value, index = config.GetFirstEntry()
72 while cont:
73 extensionNames.append(value)
74 cont, value, index = config.GetNextEntry(index)
75 finally:
76 config.SetPath(path)
77
78 for extensionName in extensionNames:
79 extensionData = config.Read(self.__getExtensionKeyName(extensionName))
80 if extensionData:
81 extension = xmlutils.unmarshal(extensionData.encode('utf-8'))
82 self._extensions.append(extension)
83
84
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))
90
91
92 def GetExtensions(self):
93 return self._extensions
94
95
96 def SetExtensions(self, extensions):
97 self._extensions = extensions
98
99
100 def CheckSumExtensions(self):
101 return xmlutils.marshal(self._extensions)
102
103
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)
108 else:
109 toolsMenu = wx.Menu()
110
111 if self._extensions:
112 if toolsMenu.GetMenuItems():
113 toolsMenu.AppendSeparator()
114 for ext in self._extensions:
115 # Append a tool menu item for each extension
116 ext.id = wx.NewId()
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)
120
121 if toolsMenuIndex == -1:
122 index = menuBar.FindMenu(_("&Run"))
123 if index == -1:
124 index = menuBar.FindMenu(_("&Project"))
125 if index == -1:
126 index = menuBar.FindMenu(_("&Format"))
127 if index == -1:
128 index = menuBar.FindMenu(_("&View"))
129 menuBar.Insert(index + 1, toolsMenu, _("&Tools"))
130
131
132 def ProcessEvent(self, event):
133 id = event.GetId()
134 for extension in self._extensions:
135 if id == extension.id:
136 self.OnExecuteExtension(extension)
137 return True
138 return False
139
140
141 def ProcessUpdateUIEvent(self, event):
142 id = event.GetId()
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:
148 event.Enable(True)
149 return True
150 if doc:
151 for fileExt in extension.fileExt:
152 if fileExt in doc.GetDocumentTemplate().GetFileFilter():
153 event.Enable(True)
154 return True
155 if extension.opOnSelectedFile and isinstance(doc, ProjectEditor.ProjectDocument):
156 filename = doc.GetFirstView().GetSelectedFile()
157 if filename:
158 template = wx.GetApp().GetDocumentManager().FindTemplateForPath(filename)
159 for fileExt in extension.fileExt:
160 if fileExt in template.GetFileFilter():
161 event.Enable(True)
162 return True
163 event.Enable(False)
164 return False
165 return False
166
167
168 def OnExecuteExtension(self, extension):
169 if extension.fileExt:
170 doc = wx.GetApp().GetDocumentManager().GetCurrentDocument()
171 if not doc:
172 return
173 if extension.opOnSelectedFile and isinstance(doc, ProjectEditor.ProjectDocument):
174 filename = doc.GetFirstView().GetSelectedFile()
175 if not filename:
176 filename = doc.GetFilename()
177 else:
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:
182 return
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)
190
191 else:
192 cmd = extension.command
193 if extension.commandPreArgs:
194 cmd = cmd + ' ' + extension.commandPreArgs
195 if extension.commandPostArgs:
196 cmd = cmd + ' ' + extension.commandPostArgs
197 f = os.popen(cmd)
198 messageService = wx.GetApp().GetService(MessageService.MessageService)
199 messageService.ShowWindow()
200 view = messageService.GetView()
201 for line in f.readlines():
202 view.AddLines(line)
203 view.GetControl().EnsureCaretVisible()
204 f.close()
205
206
207 class ExtensionOptionsPanel(wx.Panel):
208
209
210 def __init__(self, parent, id):
211 wx.Panel.__init__(self, parent, id)
212
213 extOptionsPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
214
215 extOptionsPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
216
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)
237
238 self._extDetailPanel = wx.Panel(self)
239 staticBox = wx.StaticBox(self, label=_("Selected External Tool"))
240 staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
241
242 extDetailSizer = wx.FlexGridSizer(cols=2, vgap=5, hgap=5)
243 extDetailSizer.AddGrowableCol(1,1)
244
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)
249
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)
253
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()
260 if path:
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())
267 fileDlg.Destroy()
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)
273
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)
277
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)
281
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)
286
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."))
290
291 self._extDetailPanel.SetSizer(extDetailSizer)
292 staticBoxSizer.Add(self._extDetailPanel, 1, wx.ALL|wx.EXPAND, SPACE)
293
294 extOptionsPanelSizer.Add(staticBoxSizer, 1, wx.LEFT|wx.EXPAND, SPACE)
295
296 extOptionsPanelBorderSizer.Add(extOptionsPanelSizer, 1, wx.ALL|wx.EXPAND, SPACE)
297 self.SetSizer(extOptionsPanelBorderSizer)
298
299 if self.PopulateItems():
300 self._extListBox.SetSelection(0)
301 self.OnListBoxSelect()
302
303 self.Layout()
304
305 parent.AddPage(self, _("External Tools"))
306
307
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()
315 if not msgTitle:
316 msgTitle = _("Document Options")
317 wx.MessageBox(_("Extension changes will not appear until the application is restarted."),
318 msgTitle,
319 wx.OK | wx.ICON_INFORMATION,
320 self.GetParent())
321
322
323 def PopulateItems(self):
324 extensionsService = wx.GetApp().GetService(ExtensionService)
325 import copy
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)
333
334
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)
343 else:
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)
350
351
352 def SaveCurrentItem(self, event=None):
353 extension = self._currentItem
354 if extension:
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(' ','')
363 if not fileExt:
364 extension.fileExt = None
365 else:
366 extension.fileExt = fileExt.split(',')
367 extension.opOnSelectedFile = self._selFileCtrl.GetValue()
368
369
370 def LoadItem(self, extension):
371 if 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:
378 list = ""
379 for ext in extension.fileExt:
380 if list:
381 list = list + ", "
382 list = list + ext
383 self._fileExtTextCtrl.SetValue(list)
384 else:
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()
389 else:
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)
399
400
401 def OnAdd(self, event):
402 self.SaveCurrentItem()
403 name = _("Untitled")
404 count = 1
405 while self._extListBox.FindString(name) != wx.NOT_FOUND:
406 count = count + 1
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)
415
416
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()
425
426
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
435
436
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