]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/ExtensionService.py
Tweaks for demos on MacOSX
[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 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
21 _ = wx.GetTranslation
22
23
24 SPACE = 10
25 HALF_SPACE = 5
26
27
28 EXTENSIONS_CONFIG_STRING = "Extensions"
29
30
31
32 # TODO: Redo extensions menu on OK, or provide alert that it won't happen until restart
33
34
35 #----------------------------------------------------------------------------
36 # Classes
37 #----------------------------------------------------------------------------
38
39 class Extension:
40
41
42 def __init__(self, menuItemName=None):
43 self.menuItemName = menuItemName
44 self.id = 0
45 self.menuItemDesc = ''
46 self.command = ''
47 self.commandPreArgs = ''
48 self.commandPostArgs = ''
49 self.fileExt = None
50 self.opOnSelectedFile = True
51
52
53 class ExtensionService(wx.lib.pydocview.DocService):
54
55 EXTENSIONS_KEY = "/AG_Extensions"
56
57 def __init__(self):
58 self.LoadExtensions()
59
60
61 def __getExtensionKeyName(extensionName):
62 return "%s/%s" % (ExtensionService.EXTENSIONS_KEY, extensionName)
63
64
65 __getExtensionKeyName = staticmethod(__getExtensionKeyName)
66
67
68 def LoadExtensions(self):
69 self._extensions = []
70
71 extensionNames = []
72 config = wx.ConfigBase_Get()
73 path = config.GetPath()
74 try:
75 config.SetPath(ExtensionService.EXTENSIONS_KEY)
76 cont, value, index = config.GetFirstEntry()
77 while cont:
78 extensionNames.append(value)
79 cont, value, index = config.GetNextEntry(index)
80 finally:
81 config.SetPath(path)
82
83 for extensionName in extensionNames:
84 extensionData = config.Read(self.__getExtensionKeyName(extensionName))
85 if extensionData:
86 extension = xmlutils.unmarshal(extensionData.encode('utf-8'))
87 self._extensions.append(extension)
88
89
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))
95
96
97 def GetExtensions(self):
98 return self._extensions
99
100
101 def SetExtensions(self, extensions):
102 self._extensions = extensions
103
104
105 def CheckSumExtensions(self):
106 return xmlutils.marshal(self._extensions)
107
108
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)
113 else:
114 toolsMenu = wx.Menu()
115
116 if self._extensions:
117 if toolsMenu.GetMenuItems():
118 toolsMenu.AppendSeparator()
119 for ext in self._extensions:
120 # Append a tool menu item for each extension
121 ext.id = wx.NewId()
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)
125
126 if toolsMenuIndex == -1:
127 index = menuBar.FindMenu(_("&Run"))
128 if index == -1:
129 index = menuBar.FindMenu(_("&Project"))
130 if index == -1:
131 index = menuBar.FindMenu(_("&Format"))
132 if index == -1:
133 index = menuBar.FindMenu(_("&View"))
134 menuBar.Insert(index + 1, toolsMenu, _("&Tools"))
135
136
137 def ProcessEvent(self, event):
138 id = event.GetId()
139 for extension in self._extensions:
140 if id == extension.id:
141 self.OnExecuteExtension(extension)
142 return True
143 return False
144
145
146 def ProcessUpdateUIEvent(self, event):
147 id = event.GetId()
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:
153 event.Enable(True)
154 return True
155 if doc:
156 for fileExt in extension.fileExt:
157 if fileExt in doc.GetDocumentTemplate().GetFileFilter():
158 event.Enable(True)
159 return True
160 if extension.opOnSelectedFile and isinstance(doc, ProjectEditor.ProjectDocument):
161 filename = doc.GetFirstView().GetSelectedFile()
162 if filename:
163 template = wx.GetApp().GetDocumentManager().FindTemplateForPath(filename)
164 for fileExt in extension.fileExt:
165 if fileExt in template.GetFileFilter():
166 event.Enable(True)
167 return True
168 event.Enable(False)
169 return False
170 return False
171
172
173 def OnExecuteExtension(self, extension):
174 if extension.fileExt:
175 doc = wx.GetApp().GetDocumentManager().GetCurrentDocument()
176 if not doc:
177 return
178 if extension.opOnSelectedFile and isinstance(doc, ProjectEditor.ProjectDocument):
179 filename = doc.GetFirstView().GetSelectedFile()
180 if not filename:
181 filename = doc.GetFilename()
182 else:
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:
187 return
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)
195
196 else:
197 cmd = extension.command
198 if extension.commandPreArgs:
199 cmd = cmd + ' ' + extension.commandPreArgs
200 if extension.commandPostArgs:
201 cmd = cmd + ' ' + extension.commandPostArgs
202 f = os.popen(cmd)
203 messageService = wx.GetApp().GetService(MessageService.MessageService)
204 messageService.ShowWindow()
205 view = messageService.GetView()
206 for line in f.readlines():
207 view.AddLines(line)
208 view.GetControl().EnsureCaretVisible()
209 f.close()
210
211
212 class ExtensionOptionsPanel(wx.Panel):
213
214
215 def __init__(self, parent, id):
216 wx.Panel.__init__(self, parent, id)
217
218 extOptionsPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
219
220 extOptionsPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
221
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)
242
243 self._extDetailPanel = wx.Panel(self)
244 staticBox = wx.StaticBox(self, label=_("Selected External Tool"))
245 staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
246
247 extDetailSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=3)
248 extDetailSizer.AddGrowableCol(1,1)
249
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)
254
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)
258
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()
265 if path:
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())
272 fileDlg.Destroy()
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)
278
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)
282
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)
286
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)
291
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."))
295
296 self._extDetailPanel.SetSizer(extDetailSizer)
297 staticBoxSizer.Add(self._extDetailPanel, 1, wx.ALL|wx.EXPAND, SPACE)
298
299 extOptionsPanelSizer.Add(staticBoxSizer, 1, wx.LEFT|wx.EXPAND, SPACE)
300
301 extOptionsPanelBorderSizer.Add(extOptionsPanelSizer, 1, wx.ALL|wx.EXPAND, SPACE)
302 self.SetSizer(extOptionsPanelBorderSizer)
303
304 if self.PopulateItems():
305 self._extListBox.SetSelection(0)
306 self.OnListBoxSelect()
307
308 self.Layout()
309
310 parent.AddPage(self, _("External Tools"))
311
312
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()
320 if not msgTitle:
321 msgTitle = _("Document Options")
322 wx.MessageBox(_("Extension changes will not appear until the application is restarted."),
323 msgTitle,
324 wx.OK | wx.ICON_INFORMATION,
325 self.GetParent())
326
327
328 def PopulateItems(self):
329 extensionsService = wx.GetApp().GetService(ExtensionService)
330 import copy
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)
338
339
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)
348 else:
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)
355
356
357 def SaveCurrentItem(self, event=None):
358 extension = self._currentItem
359 if extension:
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(' ','')
368 if not fileExt:
369 extension.fileExt = None
370 else:
371 extension.fileExt = fileExt.split(',')
372 extension.opOnSelectedFile = self._selFileCtrl.GetValue()
373
374
375 def LoadItem(self, extension):
376 if 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:
383 list = ""
384 for ext in extension.fileExt:
385 if list:
386 list = list + ", "
387 list = list + ext
388 self._fileExtTextCtrl.SetValue(list)
389 else:
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()
394 else:
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)
404
405
406 def OnAdd(self, event):
407 self.SaveCurrentItem()
408 name = _("Untitled")
409 count = 1
410 while self._extListBox.FindString(name) != wx.NOT_FOUND:
411 count = count + 1
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)
420
421
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()
430
431
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
440
441
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