3 # Copyright (c) Benjamin Golinvaux
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation; either version 2 of the
8 # License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 # Originally started by
22 # benjamin.golinvaux@euresys.com
23 # messenger: bgolinvaux@hotmail.com
25 # currently maintained by:
26 # Christopher Frauenberger - frauenberger@iem.at
27 # John Glover - glover.john@gmail.com
28 # Martin Victory - martin.victory@gmail.com
32 # - Help file navigation
33 # - Find and Replace dialogs
39 # - Remembers the size and position of the post window
40 # - Allows you to save the contents of the post window (file > save/save as)
41 # - Automatically stops sc server and swing osc when closing the main (post) window
42 # - Allows you to change the default window size
43 # - Double clicking on brackets selects the block of text they surround (update by Christopher)
44 # - Like the GEdit SC plugin (linux), you can execute a block of text surrounded by round brackets
45 # by placing the cursor at the opening bracket and pressing evaluate (ctrl + enter).
46 # This only happens if the open bracket is the first character on the line (not including white space)
47 # - Disabled word wrap in the text editor
48 # - Can toggle displaying of line numbers on/off in code editor
49 # (effects all code windows and is saved to config)
50 # - added ability to clear the recent file list (file history)
51 # - added the option to set the tab size in code windows (saved to config)
53 # ---------------------------------------------------------------------
58 import wx
.html
as html
59 import wx
.richtext
as richtext
60 import os
, string
, keyword
, sys
, time
62 if wx
.Platform
== '__WXMSW__':
63 faces
= { 'times': 'Times New Roman', 'mono' : 'Courier New', 'helv' : 'Arial', 'other': 'Comic Sans MS', 'size' : 10, 'size2': 8, }
64 gAppHelpFolder
= 'help_windows'
66 faces
= { 'times': 'Times', 'mono' : 'Courier', 'helv' : 'Helvetica', 'other': 'new century schoolbook', 'size' : 10, 'size2': 8, }
67 gAppHelpFolder
= 'Help-windows'
70 gUserExtensionFolder
= os
.path
.join(os
.path
.expanduser("~"), "SuperCollider\\Extensions")
78 #----------------------------------------------------------------------
79 # set SC3_KEYWORDS as a global variable.
81 file = open("keywords.list","r")
82 SC3_KEYWORDS
= string
.split( file.read() )
85 SC3_KEYWORDS
= [ "var", "arg", "Server" ]
87 print "SC3-keywords definition file \"keywords.list\" was not found."
88 print "so now, these following words are the KEYWORDS for the meantime."
94 # ---------------------------------------------------------------------
97 # Base class for all windows
98 # - creates the default menus
99 # - asks to save a modified file when closing
100 # - adds file history
101 # - holds an ID number for each window, for PsycolliderDocument to refer to self
102 nextPsycolliderWindowId
= 1
103 class PsycolliderWindow(wx
.Frame
):
104 config
= None # wx.FileConfig object
105 menubar
= None # wx.MenuBar object
106 fileMenu
= None # file menu (wx.Menu object)
107 editMenu
= None # edit menu (wx.Menu)
108 langMenu
= None # lang menu (wx.Menu)
109 optionsMenu
= None # options menu (wx.Menu)
110 helpMenu
= None # help menu (wx.Menu)
111 title
= "" # the window title
112 isModified
= False # whether or not window contents have been modified
113 filePath
= "" # path to file being displayed
116 def __init__(self
, parent
, id, title
="", winStyle
=wx
.DEFAULT_FRAME_STYLE
):
118 self
.config
= wx
.GetApp().config
119 wx
.Frame
.__init
__(self
, parent
, id, title
, style
=winStyle
)
120 global nextPsycolliderWindowId
121 windowId
= nextPsycolliderWindowId
122 nextPsycolliderWindowId
= nextPsycolliderWindowId
+ 1
123 #sys.stdout.write("windowId in pythonland is ")
124 #sys.stdout.write(str(windowId))
125 #sys.stdout.write("\n")
127 self
.config
.SetPath("/WindowSettings")
128 sizeX
= self
.config
.ReadInt('DefaultSizeX', DEFAULT_SIZEX
)
129 sizeY
= self
.config
.ReadInt('DefaultSizeY', DEFAULT_SIZEY
)
130 self
.SetSize(wx
.Size(sizeX
, sizeY
))
133 self
.Bind(wx
.EVT_CLOSE
, self
.OnCloseWindow
)
136 def OnCloseWindow(self
, event
):
137 if not self
.CanCloseWindow():
140 wx
.GetApp().fileHistory
.RemoveMenu(self
.openRecentMenu
)
141 wx
.GetApp().ClosedWindow(self
)
144 def OnNewCodeWin(self
, event
):
145 wx
.GetApp().NewCodeWindow()
147 def OnHtmlToCode(self
, event
):
148 wx
.GetApp().HtmlToCode(self
)
150 def OnOpenFile(self
, event
):
151 wx
.GetApp().OpenFile()
153 def OnSaveFile(self
, event
):
156 def OnSaveFileAs(self
, event
):
159 # edit menu actions - need to be overriden by inheriting class
160 def OnUndo(self
, event
):
163 def OnRedo(self
, event
):
166 def OnCut(self
, event
):
169 def OnCopy(self
, event
):
172 def OnPaste(self
, event
):
175 def OnDelete(self
, event
):
178 def OnSelectAll(self
, event
):
181 def OnShowFind(self
, event
):
182 findData
= wx
.FindReplaceData()
183 findDialog
= wx
.FindReplaceDialog(self
, findData
, "Find")
184 findDialog
.findData
= findData
185 findDialog
.Show(True)
187 def OnShowFindReplace(self
, event
):
188 findReplaceData
= wx
.FindReplaceData()
189 findReplaceDialog
= wx
.FindReplaceDialog(self
, findReplaceData
, "Find & Replace", wx
.FR_REPLACEDIALOG
)
190 findReplaceDialog
.findReplaceData
= findReplaceData
191 findReplaceDialog
.Show(True)
193 def OnFindClose(self
, event
):
194 event
.GetDialog().Destroy()
196 def OnFind(self
, event
):
200 def OnStopServer(self
, event
):
201 wx
.GetApp().StopServer()
203 def OnRun(self
, event
):
206 def OnStop(self
, event
):
209 def OnCompileLibrary(self
, event
):
210 wx
.GetApp().CompileLibrary()
212 def OnOpenClassDef(self
, event
):
215 def OnImpOf(self
, event
):
218 def OnRefsTo(self
, event
):
221 def OnEval(self
, event
):
224 def OnClearPostWindow(self
, event
):
225 wx
.GetApp().ClearPostWindow()
228 def OnScHelp(self
, event
):
229 wx
.GetApp().GoToHelpFile(self
.GetSelectedTextOrLine())
231 def OnBrowseHelp(self
, event
):
232 wx
.GetApp().Eval("Help.gui")
234 def OnBrowseClasses(self
, event
):
235 wx
.GetApp().Eval("Help.browse")
237 def OnAbout(self
, event
):
239 description
= 'a programming language and engine for real time audio synthesis.'
241 info
= wx
.AboutDialogInfo()
243 info
.SetIcon(wx
.Icon('Help/GUI/Cocoa-GUI/SCImage/icon.supercollider.png', wx
.BITMAP_TYPE_PNG
))
244 info
.SetName('SuperCollider')
245 info
.SetVersion('3.3.2')
246 info
.SetDescription(description
)
247 info
.SetWebSite('http://supercollider.sourceforge.net')
251 #options menu actions
252 def OnSetDefaultWindowSize(self
, event
):
253 size
= self
.GetSize()
254 wx
.GetApp().SetDefaultWindowSize(size
.x
, size
.y
)
256 def OnClearRecentFileList(self
, event
):
257 wx
.GetApp().ClearRecentFileList()
259 # should be overwritten by inheriting classes
261 wx
.MessageBox("Error: Saving not implemented for this type of window")
263 # should be overwritten by inheriting classes
264 def SaveFileAs(self
):
265 wx
.MessageBox("Error: Saving not implemented for this type of window")
267 def OpenClassDef(self
):
268 wx
.GetApp().OpenClassDef(self
.GetSelectedTextOrLine())
271 wx
.GetApp().ImpOf(self
.GetSelectedTextOrLine())
274 wx
.GetApp().RefsTo(self
.GetSelectedTextOrLine())
277 wx
.GetApp().Eval(self
.GetSelectedTextOrLine())
280 # needs to be overwritten by inheriting classes
284 # should be overwritten by inheriting classes
285 def GetSelectedTextOrLine(self
):
288 def CanCloseWindow(self
):
290 if self
.filePath
== "":
291 dlg
= wx
.MessageDialog(self
,"Do you want to save %s ? " % self
.title
,"SuperCollider",wx
.CANCEL | wx
.YES_NO
)
292 reply
= dlg
.ShowModal()
293 if reply
== wx
.ID_YES
:
296 elif reply
== wx
.ID_NO
:
298 elif reply
== wx
.ID_CANCEL
:
301 dlg
= wx
.MessageDialog(self
,"Do you want to save %s ?" % self
.filePath
,"SuperCollider",wx
.CANCEL | wx
.YES_NO
)
302 reply
= dlg
.ShowModal()
303 if reply
== wx
.ID_YES
:
306 elif reply
== wx
.ID_NO
:
308 elif reply
== wx
.ID_CANCEL
:
313 def CreateMenuBar(self
):
314 self
.fileMenu
= wx
.Menu()
315 self
.openRecentMenu
= wx
.Menu()
316 self
.editMenu
= wx
.Menu()
317 self
.langMenu
= wx
.Menu()
318 self
.optionsMenu
= wx
.Menu()
319 self
.helpMenu
= wx
.Menu()
320 self
.menubar
= wx
.MenuBar()
321 self
.menubar
.Append(self
.fileMenu
, "&File")
322 self
.menubar
.Append(self
.editMenu
, "&Edit")
323 self
.menubar
.Append(self
.langMenu
, "&Lang")
324 self
.menubar
.Append(self
.optionsMenu
, "&Options")
325 self
.menubar
.Append(self
.helpMenu
, "&Help")
326 self
.SetMenuBar(self
.menubar
)
328 self
.newCodeWin
= wx
.MenuItem(self
.fileMenu
, -1, '&New\tCtrl+N')
329 self
.htmlToCode
= wx
.MenuItem(self
.fileMenu
, -1, 'H&TML to Code\tCtrl+T')
330 self
.openFile
= wx
.MenuItem(self
.fileMenu
, -1, '&Open...\tCtrl+O')
331 self
.saveFile
= wx
.MenuItem(self
.fileMenu
, -1, '&Save\tCtrl+S')
332 self
.saveFileAs
= wx
.MenuItem(self
.fileMenu
, -1, 'Save &As...\tCtrl+Shift+S')
333 self
.closeWindow
= wx
.MenuItem(self
.fileMenu
, -1, 'Close\tCtrl+W')
335 self
.undo
= wx
.MenuItem(self
.editMenu
, -1, '&Undo\tCtrl+Z')
336 self
.redo
= wx
.MenuItem(self
.editMenu
, -1, '&Redo\tCtrl+Y')
337 self
.cut
= wx
.MenuItem(self
.editMenu
, -1, '&Cut\tCtrl+X')
338 self
.copy
= wx
.MenuItem(self
.editMenu
, -1, 'C&opy\tCtrl+C')
339 self
.paste
= wx
.MenuItem(self
.editMenu
, -1, '&Paste\tCtrl+V')
340 self
.delete
= wx
.MenuItem(self
.editMenu
, -1, '&Delete\tDel')
341 self
.selectAll
= wx
.MenuItem(self
.editMenu
, -1, '&Select All\tCtrl+A')
342 self
.find
= wx
.MenuItem(self
.editMenu
, -1, '&Find\tCtrl+F')
343 self
.replace
= wx
.MenuItem(self
.editMenu
, -1, '&Replace\tCtrl+H')
345 self
.stopServer
= wx
.MenuItem(self
.langMenu
, -1, 'Stop Server')
346 self
.run
= wx
.MenuItem(self
.langMenu
, -1, 'Run\tAlt+R')
347 self
.stop
= wx
.MenuItem(self
.langMenu
, -1, '&Stop\tAlt+.')
348 self
.compileLibrary
= wx
.MenuItem(self
.langMenu
, -1, 'Compile Library\tAlt+K')
349 self
.openClassDef
= wx
.MenuItem(self
.langMenu
, -1, 'Open Class Def\tAlt+J')
350 self
.impOf
= wx
.MenuItem(self
.langMenu
, -1, 'Implementations of\tAlt+Y')
351 self
.refsTo
= wx
.MenuItem(self
.langMenu
, -1, 'References to\tShift+Alt+Y')
352 self
.eval = wx
.MenuItem(self
.langMenu
, -1, '&Evaluate Selection\tCtrl+Enter')
353 self
.clearPostWindow
= wx
.MenuItem(self
.langMenu
, -1, '&Clear Post Window\tAlt+P')
355 self
.setDefaultWindowSize
= wx
.MenuItem(self
.optionsMenu
, -1, '&Set This Window Size As Default')
356 self
.clearRecentFileList
= wx
.MenuItem(self
.optionsMenu
, -1, '&Clear Recent File List')
358 self
.scHelp
= wx
.MenuItem(self
.helpMenu
, -1, '&SuperCollider Help\tF1')
359 self
.helpBrowser
= wx
.MenuItem(self
.helpMenu
, -1, '&Browse and Search Documentation\t')
360 self
.classBrowser
= wx
.MenuItem(self
.helpMenu
, -1, '&Class Browser\t')
361 self
.about
= wx
.MenuItem(self
.helpMenu
, -1, 'About\t')
364 self
.fileMenu
.AppendItem(self
.newCodeWin
)
365 self
.fileMenu
.AppendItem(self
.openFile
)
366 self
.fileMenu
.AppendSubMenu(self
.openRecentMenu
, "Open Recent")
367 self
.fileMenu
.AppendSeparator()
368 self
.fileMenu
.AppendItem(self
.saveFile
)
369 self
.fileMenu
.AppendItem(self
.saveFileAs
)
370 self
.fileMenu
.AppendItem(self
.htmlToCode
)
371 self
.fileMenu
.AppendSeparator()
372 self
.fileMenu
.AppendItem(self
.closeWindow
)
374 self
.editMenu
.AppendItem(self
.undo
)
375 self
.editMenu
.AppendItem(self
.redo
)
376 self
.editMenu
.AppendSeparator()
377 self
.editMenu
.AppendItem(self
.cut
)
378 self
.editMenu
.AppendItem(self
.copy
)
379 self
.editMenu
.AppendItem(self
.paste
)
380 self
.editMenu
.AppendItem(self
.delete
)
381 self
.editMenu
.AppendItem(self
.selectAll
)
382 self
.editMenu
.AppendSeparator()
383 self
.editMenu
.AppendItem(self
.find
)
384 self
.editMenu
.AppendItem(self
.replace
)
386 self
.langMenu
.AppendItem(self
.stopServer
)
387 self
.langMenu
.AppendItem(self
.run
)
388 self
.langMenu
.AppendItem(self
.stop
)
389 self
.langMenu
.AppendItem(self
.compileLibrary
)
390 self
.langMenu
.AppendItem(self
.openClassDef
)
391 self
.langMenu
.AppendItem(self
.impOf
)
392 self
.langMenu
.AppendItem(self
.refsTo
)
393 self
.langMenu
.AppendItem(self
.eval)
394 self
.langMenu
.AppendItem(self
.clearPostWindow
)
396 self
.optionsMenu
.AppendItem(self
.setDefaultWindowSize
)
397 self
.optionsMenu
.AppendItem(self
.clearRecentFileList
)
399 self
.helpMenu
.AppendItem(self
.scHelp
)
400 self
.helpMenu
.AppendItem(self
.helpBrowser
)
401 self
.helpMenu
.AppendItem(self
.classBrowser
)
402 self
.helpMenu
.AppendSeparator()
403 self
.helpMenu
.AppendItem(self
.about
)
405 self
.Bind(wx
.EVT_MENU
, self
.OnNewCodeWin
, id=self
.newCodeWin
.GetId())
406 self
.Bind(wx
.EVT_MENU
, self
.OnHtmlToCode
, id=self
.htmlToCode
.GetId())
407 self
.Bind(wx
.EVT_MENU
, self
.OnOpenFile
, id=self
.openFile
.GetId())
408 self
.Bind(wx
.EVT_MENU
, self
.OnSaveFile
, id=self
.saveFile
.GetId())
409 self
.Bind(wx
.EVT_MENU
, self
.OnSaveFileAs
, id=self
.saveFileAs
.GetId())
410 self
.Bind(wx
.EVT_MENU
, self
.OnCloseWindow
, id=self
.closeWindow
.GetId())
412 self
.Bind(wx
.EVT_MENU
, self
.OnUndo
, id=self
.undo
.GetId())
413 self
.Bind(wx
.EVT_MENU
, self
.OnRedo
, id=self
.redo
.GetId())
414 self
.Bind(wx
.EVT_MENU
, self
.OnCut
, id=self
.cut
.GetId())
415 self
.Bind(wx
.EVT_MENU
, self
.OnCopy
, id=self
.copy
.GetId())
416 self
.Bind(wx
.EVT_MENU
, self
.OnPaste
, id=self
.paste
.GetId())
417 self
.Bind(wx
.EVT_MENU
, self
.OnDelete
, id=self
.delete
.GetId())
418 self
.Bind(wx
.EVT_MENU
, self
.OnSelectAll
, id=self
.selectAll
.GetId())
419 self
.Bind(wx
.EVT_MENU
, self
.OnShowFind
, id=self
.find
.GetId())
420 self
.Bind(wx
.EVT_MENU
, self
.OnShowFindReplace
, id=self
.replace
.GetId())
422 self
.Bind(wx
.EVT_FIND
, self
.OnFind
)
423 self
.Bind(wx
.EVT_FIND_NEXT
, self
.OnFind
)
424 self
.Bind(wx
.EVT_FIND_REPLACE
, self
.OnFind
)
425 self
.Bind(wx
.EVT_FIND_REPLACE_ALL
, self
.OnFind
)
426 self
.Bind(wx
.EVT_FIND_CLOSE
, self
.OnFindClose
)
428 self
.Bind(wx
.EVT_MENU
, self
.OnStopServer
, id=self
.stopServer
.GetId())
429 self
.Bind(wx
.EVT_MENU
, self
.OnRun
, id=self
.run
.GetId())
430 self
.Bind(wx
.EVT_MENU
, self
.OnStop
, id=self
.stop
.GetId())
431 self
.Bind(wx
.EVT_MENU
, self
.OnCompileLibrary
, id=self
.compileLibrary
.GetId())
432 self
.Bind(wx
.EVT_MENU
, self
.OnOpenClassDef
, id=self
.openClassDef
.GetId())
433 self
.Bind(wx
.EVT_MENU
, self
.OnImpOf
, id=self
.impOf
.GetId())
434 self
.Bind(wx
.EVT_MENU
, self
.OnRefsTo
, id=self
.refsTo
.GetId())
435 self
.Bind(wx
.EVT_MENU
, self
.OnEval
, id=self
.eval.GetId())
436 self
.Bind(wx
.EVT_MENU
, self
.OnClearPostWindow
, id=self
.clearPostWindow
.GetId())
438 self
.Bind(wx
.EVT_MENU
, self
.OnSetDefaultWindowSize
, id=self
.setDefaultWindowSize
.GetId())
439 self
.Bind(wx
.EVT_MENU
, self
.OnClearRecentFileList
, id=self
.clearRecentFileList
.GetId())
441 self
.Bind(wx
.EVT_MENU
, self
.OnScHelp
, id=self
.scHelp
.GetId())
442 self
.Bind(wx
.EVT_MENU
, self
.OnBrowseHelp
, id=self
.helpBrowser
.GetId())
443 self
.Bind(wx
.EVT_MENU
, self
.OnBrowseClasses
, id=self
.classBrowser
.GetId())
444 self
.Bind(wx
.EVT_MENU
, self
.OnAbout
, id=self
.about
.GetId())
446 wx
.GetApp().fileHistory
.UseMenu(self
.openRecentMenu
)
447 wx
.GetApp().fileHistory
.AddFilesToThisMenu(self
.openRecentMenu
)
448 self
.Bind(wx
.EVT_MENU_RANGE
, wx
.GetApp().doFileHistory
, id=wx
.ID_FILE1
, id2
=wx
.ID_FILE9
)
451 # ---------------------------------------------------------------------
452 # PsycolliderCodeSubWin - plain text window for code
453 class PsycolliderCodeSubWin(wx
.stc
.StyledTextCtrl
):
457 def __init__ (self
,parent
):
458 stc
.StyledTextCtrl
.__init
__(self
,parent
)
459 self
.SetModEventMask(wx
.stc
.STC_MOD_INSERTTEXT | wx
.stc
.STC_MOD_DELETETEXT | wx
.stc
.STC_PERFORMED_USER
)
462 self
.Bind(stc
.EVT_STC_CHANGE
, self
.OnStcChange
)
463 self
.Bind(stc
.EVT_STC_UPDATEUI
, self
.OnUpdateUI
)
464 self
.Bind(stc
.EVT_STC_MARGINCLICK
, self
.OnMarginClick
)
465 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyPressed
)
466 self
.Bind(wx
.EVT_CHAR
, self
.OnChar
) # this hack is to enable the alt+. shortcut
467 # to stop playback, which doesn't seem to work otherwise
469 self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnDoubleClick
)
471 self
.SetLexer(wx
.stc
.STC_LEX_CPP
) # yssr
472 self
.SetKeyWords(0, " ".join(SC3_KEYWORDS
)) # yssr
474 self
.SetProperty("fold", "1")
475 self
.SetProperty("tab.timmy.whinge.level", "1")
476 self
.SetMargins(1,0) # yssr
478 # set end-of-line character to LF
479 self
.SetEOLMode(wx
.stc
.STC_EOL_LF
);
481 # some settings for appearance
482 self
.SetViewWhiteSpace(False)
483 self
.SetViewEOL(False)
484 self
.SetUseAntiAliasing(True)
485 self
.SetWrapMode(False)
486 self
.SetEdgeMode(stc
.STC_EDGE_NONE
)
488 # Setup a margin to hold fold markers
489 self
.SetMarginType(2, stc
.STC_MARGIN_SYMBOL
)
490 self
.SetMarginMask(2, stc
.STC_MASK_FOLDERS
)
491 self
.SetMarginSensitive(2, True)
492 self
.SetMarginWidth(2, 12)
494 # Like a flattened tree control using square headers
495 self
.MarkerDefine(stc
.STC_MARKNUM_FOLDEROPEN
, stc
.STC_MARK_BOXMINUS
, "white", "#808080")
496 self
.MarkerDefine(stc
.STC_MARKNUM_FOLDER
, stc
.STC_MARK_BOXPLUS
, "white", "#808080")
497 self
.MarkerDefine(stc
.STC_MARKNUM_FOLDERSUB
, stc
.STC_MARK_VLINE
, "white", "#808080")
498 self
.MarkerDefine(stc
.STC_MARKNUM_FOLDERTAIL
, stc
.STC_MARK_LCORNER
, "white", "#808080")
499 self
.MarkerDefine(stc
.STC_MARKNUM_FOLDEREND
, stc
.STC_MARK_BOXPLUSCONNECTED
, "white", "#808080")
500 self
.MarkerDefine(stc
.STC_MARKNUM_FOLDEROPENMID
, stc
.STC_MARK_BOXMINUSCONNECTED
, "white", "#808080")
501 self
.MarkerDefine(stc
.STC_MARKNUM_FOLDERMIDTAIL
, stc
.STC_MARK_TCORNER
, "white", "#808080")
504 def OnChar(self
, event
):
505 if event
.GetKeyCode() == 0x2e and event
.AltDown():
506 self
.GetParent().OnStop(None)
510 def OnStcChange(self
, event
):
511 self
.GetParent().OnStcChange(event
)
513 def OnKeyPressed(self
, event
):
514 #if self.CallTipActive():
515 # self.CallTipCancel()
516 key
= event
.GetKeyCode()
518 if key
== 32 and event
.ControlDown():
519 pos
= self
.GetCurrentPos()
522 if event
.ShiftDown():
523 self
.CallTipSetBackground("yellow")
524 self
.CallTipShow(pos
, 'we can display tips here')
528 #for x in range(50000):
529 # lst.append('%05d' % x)
532 #self.AutoCompShow(0, st)
534 kw
= keyword
.kwlist
[:]
535 #kw.append("zzzzzz?2")
537 kw
.sort() # Python sorts are case sensitive
538 self
.AutoCompSetIgnoreCase(False) # so this needs to match
540 # Images are specified with a appended "?type"
541 for i
in range(len(kw
)):
542 if kw
[i
] in keyword
.kwlist
:
544 self
.AutoCompShow(0, " ".join(kw
))
548 def OnDoubleClick(self
, evt
):
549 braceAtCaret
, braceOpposite
= self
.CheckForMatchingBraces()
551 if braceAtCaret
!= -1 and braceOpposite
!= -1:
552 if braceAtCaret
< braceOpposite
:
553 self
.SetSelection(braceAtCaret
+1, braceOpposite
)
555 self
.SetSelection(braceOpposite
+1, braceAtCaret
)
559 def OnUpdateUI(self
, evt
):
560 braceAtCaret
, braceOpposite
= self
.CheckForMatchingBraces()
562 if braceAtCaret
!= -1 and braceOpposite
== -1:
563 self
.BraceBadLight(braceAtCaret
)
565 self
.BraceHighlight(braceAtCaret
, braceOpposite
)
567 def OnMarginClick(self
, evt
):
568 # fold and unfold as needed
569 if evt
.GetMargin() == 2:
570 if evt
.GetShift() and evt
.GetControl():
573 lineClicked
= self
.LineFromPosition(evt
.GetPosition())
575 if self
.GetFoldLevel(lineClicked
) & stc
.STC_FOLDLEVELHEADERFLAG
:
577 self
.SetFoldExpanded(lineClicked
, True)
578 self
.Expand(lineClicked
, True, True, 1)
579 elif evt
.GetControl():
580 if self
.GetFoldExpanded(lineClicked
):
581 self
.SetFoldExpanded(lineClicked
, False)
582 self
.Expand(lineClicked
, False, True, 0)
584 self
.SetFoldExpanded(lineClicked
, True)
585 self
.Expand(lineClicked
, True, True, 100)
587 self
.ToggleFold(lineClicked
)
589 def CheckForMatchingBraces(self
):
594 caretPos
= self
.GetCurrentPos()
597 charBefore
= self
.GetCharAt(caretPos
- 1)
598 styleBefore
= self
.GetStyleAt(caretPos
- 1)
601 if charBefore
and chr(charBefore
) in "[]{}()" and styleBefore
== stc
.STC_C_OPERATOR
:
602 braceAtCaret
= caretPos
- 1
606 charAfter
= self
.GetCharAt(caretPos
)
607 styleAfter
= self
.GetStyleAt(caretPos
)
609 if charAfter
and chr(charAfter
) in "[]{}()" and styleAfter
== stc
.STC_C_OPERATOR
:
610 braceAtCaret
= caretPos
612 if braceAtCaret
>= 0:
613 braceOpposite
= self
.BraceMatch(braceAtCaret
)
615 return braceAtCaret
, braceOpposite
618 lineCount
= self
.GetLineCount()
621 # find out if we are folding or unfolding
622 for lineNum
in range(lineCount
):
623 if self
.GetFoldLevel(lineNum
) & stc
.STC_FOLDLEVELHEADERFLAG
:
624 expanding
= not self
.GetFoldExpanded(lineNum
)
629 while lineNum
< lineCount
:
630 level
= self
.GetFoldLevel(lineNum
)
631 if level
& stc
.STC_FOLDLEVELHEADERFLAG
and \
632 (level
& stc
.STC_FOLDLEVELNUMBERMASK
) == stc
.STC_FOLDLEVELBASE
:
635 self
.SetFoldExpanded(lineNum
, True)
636 lineNum
= self
.Expand(lineNum
, True)
637 lineNum
= lineNum
- 1
639 lastChild
= self
.GetLastChild(lineNum
, -1)
640 self
.SetFoldExpanded(lineNum
, False)
642 if lastChild
> lineNum
:
643 self
.HideLines(lineNum
+1, lastChild
)
645 lineNum
= lineNum
+ 1
648 def Expand(self
, line
, doExpand
, force
=False, visLevels
=0, level
=-1):
649 lastChild
= self
.GetLastChild(line
, level
)
652 while line
<= lastChild
:
655 self
.ShowLines(line
, line
)
657 self
.HideLines(line
, line
)
660 self
.ShowLines(line
, line
)
663 level
= self
.GetFoldLevel(line
)
665 if level
& stc
.STC_FOLDLEVELHEADERFLAG
:
668 self
.SetFoldExpanded(line
, True)
670 self
.SetFoldExpanded(line
, False)
672 line
= self
.Expand(line
, doExpand
, force
, visLevels
-1)
675 if doExpand
and self
.GetFoldExpanded(line
):
676 line
= self
.Expand(line
, True, force
, visLevels
-1)
678 line
= self
.Expand(line
, False, force
, visLevels
-1)
684 def SetShowLineNumbers(self
, value
):
686 self
.SetMarginType(2, stc
.STC_MARGIN_NUMBER
)
688 self
.SetMarginType(2, stc
.STC_MARGIN_SYMBOL
)
690 def GetTabSize(self
):
691 return self
.GetTabWidth()
693 def SetTabSize(self
, tabSize
):
694 self
.SetTabWidth(tabSize
)
697 # append an extra space, as GetTextUTF8() seems to remove the last character, wx bug?
698 self
.AppendTextUTF8(" ")
699 return self
.GetTextUTF8()
702 # ---------------------------------------------------------------------
704 # accomodates the code sub window
705 class PsycolliderCodeWin(PsycolliderWindow
):
707 SHOW_LINE_NUMBERS
= False # default
708 TAB_SIZE
= 8 # default tab size
710 def __init__ (self
, parent
, id, title
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
):
711 PsycolliderWindow
.__init
__(self
, parent
, id, title
)
712 self
.fileMenu
.Remove(self
.htmlToCode
.GetId()) # Remove unnecessary menu item
713 self
.codeSubWin
= PsycolliderCodeSubWin(self
)
715 # this will be our default font
716 font
= wx
.Font(10, wx
.MODERN
, wx
.NORMAL
, wx
.NORMAL
, False, "Courier New")
717 self
.fontSize
= font
.GetPointSize()
718 self
.fontFace
= font
.GetFaceName()
720 self
.ChangeFont(self
.fontFace
, self
.fontSize
)
723 self
.config
.SetPath("/CodeWindowOptions")
724 self
.showLineNumbers
.Check(self
.config
.ReadInt('ShowLineNumbers', self
.SHOW_LINE_NUMBERS
))
725 self
.codeSubWin
.SetShowLineNumbers(self
.showLineNumbers
.IsChecked())
728 self
.codeSubWin
.SetTabSize(self
.config
.ReadInt('TabSize', self
.TAB_SIZE
))
730 def OnStcChange(self
, event
):
731 if not self
.isModified
:
732 self
.SetTitle(self
.GetTitle() + "*")
733 self
.isModified
= True
735 def OnShowLineNumbers(self
, event
):
736 for window
in wx
.GetApp().GetOpenWindows():
737 if type(window
) == PsycolliderCodeWin
:
738 window
.SetShowLineNumbers(self
.showLineNumbers
.IsChecked())
740 self
.config
.SetPath("/CodeWindowOptions")
741 self
.config
.WriteInt('ShowLineNumbers', self
.showLineNumbers
.IsChecked())
743 def OnSetTabSize(self
, event
):
745 getNewTabSize
= wx
.TextEntryDialog(self
, 'Set tab size to:', 'Set Tab Size', str(self
.TAB_SIZE
))
746 getNewTabSize
.SetValue(str(self
.codeSubWin
.GetTabSize()))
748 if getNewTabSize
.ShowModal() == wx
.ID_OK
:
750 newTabSize
= int(getNewTabSize
.GetValue())
754 for window
in wx
.GetApp().GetOpenWindows():
755 if type(window
) == PsycolliderCodeWin
:
756 window
.codeSubWin
.SetTabSize(newTabSize
)
758 self
.config
.SetPath("/CodeWindowOptions")
759 self
.config
.WriteInt('TabSize', newTabSize
)
762 WriteInLogWindow("Invalid tab size, ignoring. Please enter a positive integer\n")
765 getNewTabSize
.Destroy()
767 def GetSelectedText(self
):
768 return self
.codeSubWin
.GetSelectedTextUTF8()
770 def GetCurLineAsString(self
):
771 return self
.codeSubWin
.GetCurLine()
773 def SetSubWinFocus(self
):
774 self
.codeSubWin
.SetFocus()
776 def SelectRange(self
,rangeStart
,rangeSize
):
777 self
.codeSubWin
.SetSelection(rangeStart
,rangeStart
+rangeSize
)
779 def SetShowLineNumbers(self
, value
):
780 self
.showLineNumbers
.Check(value
)
781 self
.codeSubWin
.SetShowLineNumbers(value
)
784 if self
.filePath
== "":
788 file = open(self
.filePath
, "w")
789 content
= self
.codeSubWin
.GetText()
791 self
.SetTitle(self
.filePath
)
792 self
.isModified
= False
795 # Todo: better error handling? Just print error message for now
796 wx
.MessageBox("Error: Could not save file " + self
.filePath
)
798 def SaveFileAs(self
):
799 fileDlg
= wx
.FileDialog(self
,style
=wx
.SAVE
)
800 if fileDlg
.ShowModal() == wx
.ID_OK
:
801 self
.filePath
= fileDlg
.GetPath()
803 file = open(self
.filePath
,"w")
804 content
= self
.codeSubWin
.GetText()
808 # Todo: better error handling? Just print error message for now
809 wx
.MessageBox("Error: Could not save file " + self
.filePath
)
812 self
.SetTitle(self
.filePath
)
813 self
.isModified
= False
814 wx
.GetApp().AddFileToHistory(self
.filePath
)
816 def GetSelectedTextOrLine(self
):
817 """Returns selected text if any. If not, returns the current line"""
818 selection
= str(self
.codeSubWin
.GetSelectedTextUTF8())
821 # get current line, return if not at an open '(' bracket
822 currentLine
= self
.codeSubWin
.GetLineUTF8(self
.codeSubWin
.GetCurrentLine())
823 selection
= str(currentLine
)
825 # see if the cursor is at a matched bracket
826 x
, y
= self
.codeSubWin
.CheckForMatchingBraces()
827 if x
!= -1 and y
!= -1:
828 if chr(self
.codeSubWin
.GetCharAt(x
)) == "(":
829 # make sure the open bracket is the first character in the line
830 if currentLine
.strip().find('(') == 0:
831 # get all text up to and including the closing bracket
832 selection
= str(self
.codeSubWin
.GetTextRangeUTF8(x
, y
+1))
837 self
.codeSubWin
.LineDown()
839 def OnOpenFonts(self
, event
):
841 data
.EnableEffects(False)
843 dlg
= wx
.FontDialog(self
, data
)
845 if dlg
.ShowModal() == wx
.ID_OK
:
846 data
= dlg
.GetFontData()
847 font
= data
.GetChosenFont()
849 self
.fontFace
= font
.GetFaceName()
850 self
.fontSize
= font
.GetPointSize()
851 self
.ChangeFont(self
.fontFace
, self
.fontSize
)
855 def ChangeFont(self
, face
, size
):
856 self
.codeSubWin
.StyleClearAll() # Reset all to be like the default
857 self
.codeSubWin
.StyleSetSpec(stc
.STC_STYLE_DEFAULT
,
858 "face:%s, size:%d" % (face
, size
))
860 self
.codeSubWin
.StyleSetSpec(stc
.STC_STYLE_LINENUMBER
,
861 "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces
)
863 self
.codeSubWin
.StyleSetSpec(
864 stc
.STC_STYLE_CONTROLCHAR
, "face:%s, size:%d" % (face
, size
))
866 self
.codeSubWin
.StyleSetSpec(stc
.STC_STYLE_BRACELIGHT
,
867 "fore:#FFFFFF,back:#00FFFF,bold, face:%s, size:%d" % (face
, size
))
869 self
.codeSubWin
.StyleSetSpec(stc
.STC_STYLE_BRACEBAD
,
870 "fore:#000000,back:#FF3333,bold,face:%s, size:%d" % (face
, size
))
873 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_DEFAULT
,
874 "fore:#000000,face:%s,size:%d" % (face
, size
))
876 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_COMMENTLINE
,
877 "fore:#bf0000,face:%s,size:%d" % (face
, size
))
878 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_COMMENT
,
879 "fore:#bf0000,face:%s,size:%d" % (face
, size
))
881 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_NUMBER
,
882 "fore:#333333,face:%s,size:%d" % (face
, size
))
884 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_STRING
,
885 "italic,fore:#606060,face:%s,size:%d" % (face
, size
))
886 # Single quoted string
887 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_CHARACTER
,
888 "fore:#007300,face:%s,size:%d" % (face
, size
))
890 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_WORD
,
891 "fore:#0000bf,face:%s,size:%d" % (face
, size
))
892 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_WORD2
,
893 "fore:#0000bf,face:%s,size:%d" % (face
, size
))
895 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_OPERATOR
,
896 "face:%s,size:%d" % (face
, size
))
898 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_IDENTIFIER
,
899 "fore:#000000,face:%s,size:%d" % (face
, size
))
900 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_UUID
,
901 "fore:#000000,face:%s,size:%d" % (face
, size
))
902 # End of line where string is not closed
903 self
.codeSubWin
.StyleSetSpec(stc
.STC_C_STRINGEOL
,
904 "fore:#000000,face:%s,back:#ffffff,eol,size:%d" % (face
, size
))
906 self
.codeSubWin
.SetCaretForeground("BLACK")
909 def OnBiggerFont(self
, event
):
911 self
.ChangeFont(self
.fontFace
, self
.fontSize
)
913 def OnSmallerFont(self
, event
):
915 self
.ChangeFont(self
.fontFace
, self
.fontSize
)
917 def CreateMenuBar(self
):
918 PsycolliderWindow
.CreateMenuBar(self
)
920 self
.showLineNumbers
= wx
.MenuItem(self
.optionsMenu
, -1, 'S&how Line Numbers', kind
=wx
.ITEM_CHECK
)
921 self
.setTabSize
= wx
.MenuItem(self
.optionsMenu
, -1, 'S&et Tab Size')
922 self
.optionsMenu
.AppendSeparator()
923 self
.optionsMenu
.AppendItem(self
.showLineNumbers
)
924 self
.optionsMenu
.AppendItem(self
.setTabSize
)
925 self
.Bind(wx
.EVT_MENU
, self
.OnShowLineNumbers
, id=self
.showLineNumbers
.GetId())
926 self
.Bind(wx
.EVT_MENU
, self
.OnSetTabSize
, id=self
.setTabSize
.GetId())
929 formatMenu
= wx
.Menu()
930 self
.menubar
.Insert(4, formatMenu
, "Fo&rmat")
931 self
.openFonts
= wx
.MenuItem(formatMenu
, -1, '&Show Fonts\tAlt+T')
932 self
.biggerFont
= wx
.MenuItem(formatMenu
, -1, '&Bigger Font\tCtrl++')
933 self
.smallerFont
= wx
.MenuItem(formatMenu
, -1, '&Smaller Font\tCtrl+-')
934 formatMenu
.AppendItem(self
.openFonts
)
935 formatMenu
.AppendSeparator()
936 formatMenu
.AppendItem(self
.biggerFont
)
937 formatMenu
.AppendItem(self
.smallerFont
)
938 self
.Bind(wx
.EVT_MENU
, self
.OnOpenFonts
, id=self
.openFonts
.GetId())
939 self
.Bind(wx
.EVT_MENU
, self
.OnBiggerFont
, id=self
.biggerFont
.GetId())
940 self
.Bind(wx
.EVT_MENU
, self
.OnSmallerFont
, id=self
.smallerFont
.GetId())
943 def OnUndo(self
, event
):
944 self
.codeSubWin
.Undo()
946 def OnRedo(self
, event
):
947 self
.codeSubWin
.Redo()
949 def OnCut(self
, event
):
950 self
.codeSubWin
.Cut()
952 def OnCopy(self
, event
):
953 self
.codeSubWin
.Copy()
955 def OnPaste(self
, event
):
956 self
.codeSubWin
.Paste()
958 def OnDelete(self
, event
):
959 self
.codeSubWin
.Clear()
961 def OnSelectAll(self
, event
):
962 self
.codeSubWin
.SelectAll()
964 def OnFind(self
, event
):
966 wx
.wxEVT_COMMAND_FIND
: "FIND",
967 wx
.wxEVT_COMMAND_FIND_NEXT
: "FIND_NEXT",
968 wx
.wxEVT_COMMAND_FIND_REPLACE
: "REPLACE",
969 wx
.wxEVT_COMMAND_FIND_REPLACE_ALL
: "REPLACE_ALL",
972 et
= event
.GetEventType()
974 length
= self
.codeSubWin
.GetTextLength()
975 str = event
.GetFindString()
978 if et
== wx
.wxEVT_COMMAND_FIND
or et
== wx
.wxEVT_COMMAND_FIND_NEXT
:
980 if event
.GetFlags() & wx
.FR_MATCHCASE
:
981 flags
= flags | stc
.STC_FIND_MATCHCASE
982 print "matching case"
984 if event
.GetFlags() & wx
.FR_WHOLEWORD
:
985 flags
= flags | stc
.STC_FIND_WHOLEWORD
986 print "searching wholeword"
988 if event
.GetFlags() & wx
.FR_DOWN
:
989 if et
== wx
.wxEVT_COMMAND_FIND_NEXT
:
990 startPos
= self
.codeSubWin
.GetCurrentPos()
991 endPos
= self
.codeSubWin
.GetTextLength()
994 endPos
= self
.codeSubWin
.GetTextLength()
996 if et
== wx
.wxEVT_COMMAND_FIND_NEXT
:
997 startPos
= self
.codeSubWin
.GetCurrentPos()-1
1000 startPos
= self
.codeSubWin
.GetTextLength()
1003 pos
= self
.codeSubWin
.FindText(startPos
, endPos
, str, flags
)
1006 self
.codeSubWin
.SetSelection(pos
, pos
+len(str))
1007 self
.codeSubWin
.EnsureCaretVisible()
1009 wx
.MessageBox("Reached end of document", "Find", wx
.ICON_EXCLAMATION | wx
.OK
, self
.codeSubWin
)
1012 elif et
== wx
.wxEVT_COMMAND_FIND_REPLACE
:
1014 if event
.GetFlags() & wx
.FR_MATCHCASE
:
1015 flags
= flags | stc
.STC_FIND_MATCHCASE
1017 if event
.GetFlags() & wx
.FR_WHOLEWORD
:
1018 flags
= flags | stc
.STC_FIND_WHOLEWORD
1021 endPos
= self
.codeSubWin
.GetTextLength()
1023 pos
= self
.codeSubWin
.FindText(startPos
, endPos
, str, flags
)
1025 self
.codeSubWin
.SetSelection(pos
, pos
+len(str))
1026 self
.codeSubWin
.ReplaceSelection(event
.GetReplaceString())
1027 self
.codeSubWin
.EnsureCaretVisible()
1029 wx
.MessageBox("Reached end of document", "Replace", wx
.ICON_EXCLAMATION | wx
.OK
, self
.codeSubWin
)
1032 elif et
== wx
.wxEVT_COMMAND_FIND_REPLACE_ALL
:
1034 if event
.GetFlags() & wx
.FR_MATCHCASE
:
1035 flags
= flags | stc
.STC_FIND_MATCHCASE
1037 if event
.GetFlags() & wx
.FR_WHOLEWORD
:
1038 flags
= flags | stc
.STC_FIND_WHOLEWORD
1040 initPos
= self
.codeSubWin
.GetCurrentPos()
1042 endPos
= self
.codeSubWin
.GetTextLength()
1045 pos
= self
.codeSubWin
.FindText(startPos
, endPos
, str, flags
)
1047 numTokens
= numTokens
+1
1048 self
.codeSubWin
.SetSelection(pos
, pos
+len(str))
1049 self
.codeSubWin
.ReplaceSelection(event
.GetReplaceString())
1050 self
.codeSubWin
.EnsureCaretVisible()
1051 pos
= self
.codeSubWin
.FindText(pos
+len(str), endPos
, str, flags
)
1053 self
.codeSubWin
.GotoPos(initPos
)
1055 wx
.MessageBox("%d instance(s) replaced" % (numTokens
), "Replace All", wx
.ICON_EXCLAMATION | wx
.OK
, self
.codeSubWin
)
1057 # ---------------------------------------------------------------------
1060 class PsycolliderHTMLSubWin(wx
.html
.HtmlWindow
):
1062 def __init__ (self
,parent
):
1063 wx
.html
.HtmlWindow
.__init
__(self
,parent
)
1064 self
.parent
= parent
1065 self
.Bind(wx
.EVT_CHAR
, self
.OnChar
) # this hack is to enable the alt+. shortcut
1066 self
.Bind(html
.EVT_HTML_LINK_CLICKED
, self
.OnClicked
)
1067 self
.titles
= [parent
.GetTitle()]
1070 def OnChar(self
, event
):
1071 if event
.GetKeyCode() == 0x2e and event
.AltDown():
1072 self
.GetParent().OnStop(None)
1073 elif event
.GetKeyCode() == wx
.WXK_LEFT
and event
.AltDown():
1075 elif event
.GetKeyCode() == wx
.WXK_RIGHT
and event
.AltDown():
1076 self
.HistoryForward()
1080 # this allows us to correctly set the title of the parent window
1081 def OnClicked(self
, event
):
1083 # clicking on a link effectively removes forward history
1084 self
.titles
= self
.titles
[:self
.titlePos
+1]
1086 info
= event
.GetLinkInfo()
1087 href
= info
.GetHref()
1090 pageTitle
= os
.path
.splitext(os
.path
.basename(href
))[0]
1091 self
.parent
.SetTitle(pageTitle
)
1092 self
.titles
.append(pageTitle
)
1095 def GoForward(self
, event
):
1096 if self
.HistoryCanForward():
1097 self
.HistoryForward()
1099 self
.parent
.SetTitle(self
.titles
[self
.titlePos
])
1101 def GoBack(self
, event
):
1102 if self
.HistoryCanBack():
1105 self
.parent
.SetTitle(self
.titles
[self
.titlePos
])
1107 def GoHome(self
, event
):
1108 filePath
= os
.path
.join(gHelpFolder
,"Help.html")
1109 self
.parent
.SetTitle("Help")
1110 self
.LoadPage(filePath
)
1111 self
.titles
.append("Help")
1114 # ---------------------------------------------------------------------
1116 # accomodates HTML sub window
1117 class PsycolliderHTMLWin(PsycolliderWindow
):
1119 def __init__ (self
,parent
,id,title
,pos
=wx
.DefaultPosition
,size
=wx
.DefaultSize
):
1120 PsycolliderWindow
.__init
__(self
, parent
, id, title
)
1121 self
.fileMenu
.Remove(self
.saveFile
.GetId()) # Remove unnecessary menu items
1122 self
.fileMenu
.Remove(self
.saveFileAs
.GetId())
1123 self
.htmlSubWin
= PsycolliderHTMLSubWin(self
)
1126 self
.menubar
.Remove(1)
1128 # Add navigation menu to HTML windows
1129 self
.navMenu
= wx
.Menu()
1130 self
.menubar
.Insert(3, self
.navMenu
, "&Navigation")
1131 self
.home
= wx
.MenuItem(self
.navMenu
, -1, '&Home')
1132 self
.forward
= wx
.MenuItem(self
.navMenu
, -1, '&Forward\tAlt+Right')
1133 self
.back
= wx
.MenuItem(self
.navMenu
, -1, '&Back\tAlt+Left')
1135 self
.navMenu
.AppendItem(self
.home
)
1136 self
.navMenu
.AppendItem(self
.forward
)
1137 self
.navMenu
.AppendItem(self
.back
)
1138 self
.SetMenuBar(self
.menubar
)
1140 self
.Bind(wx
.EVT_MENU
, self
.htmlSubWin
.GoHome
, id=self
.home
.GetId())
1141 self
.Bind(wx
.EVT_MENU
, self
.htmlSubWin
.GoForward
, id=self
.forward
.GetId())
1142 self
.Bind(wx
.EVT_MENU
, self
.htmlSubWin
.GoBack
, id=self
.back
.GetId())
1144 def GetSelectedText(self
):
1145 return self
.htmlSubWin
.SelectionToText()
1147 def GetCurLineAsString(self
):
1148 posInText
= self
.htmlSubWin
.GetInsertionPoint()
1149 (x
,y
) = self
.htmlSubWin
.PositionToXY(posInText
)
1150 return self
.htmlSubWin
.SelectLine(y
)
1152 def GetSelectedTextOrLine(self
):
1153 """Returns selected text"""
1154 return str(self
.GetSelectedText())
1156 def SetSubWinFocus(self
):
1157 self
.htmlSubWin
.SetFocus()
1160 # ---------------------------------------------------------------------
1161 # Psycollider Post Window
1162 class PsycolliderPostWindow(PsycolliderWindow
):
1163 log
= None # The wx.TextCtrl object that displays post info
1165 def __init__(self
, parent
, id, title
):
1166 # init (no maximise button)
1167 PsycolliderWindow
.__init
__(self
, parent
, id, title
, wx
.MINIMIZE_BOX | wx
.CLOSE_BOX |wx
.RESIZE_BORDER | wx
.SYSTEM_MENU | wx
.CAPTION
)
1169 self
.config
.SetPath("/WindowSettings")
1170 sizeX
= self
.config
.ReadInt('PostWindow-sizeX', DEFAULT_SIZEX
)
1171 sizeY
= self
.config
.ReadInt('PostWindow-sizeY', DEFAULT_SIZEY
)
1172 posX
= self
.config
.ReadInt('PostWindow-posX', DEFAULT_POSX
)
1173 posY
= self
.config
.ReadInt('PostWindow-posY', DEFAULT_POSY
)
1175 # check that position is > 0
1181 self
.SetSize(wx
.Size(sizeX
, sizeY
))
1182 self
.SetPosition(wx
.Point(posX
, posY
))
1184 self
.fileMenu
.Remove(self
.htmlToCode
.GetId()) # Remove unnecessary menu items
1185 self
.langMenu
.Remove(self
.run
.GetId())
1186 self
.langMenu
.Remove(self
.stop
.GetId())
1187 self
.langMenu
.Remove(self
.openClassDef
.GetId())
1188 self
.langMenu
.Remove(self
.impOf
.GetId())
1189 self
.langMenu
.Remove(self
.refsTo
.GetId())
1190 self
.langMenu
.Remove(self
.eval.GetId())
1191 self
.menubar
.Remove(1)
1193 mainPanel
= wx
.Panel(self
, -1)
1194 mainSizer
= wx
.BoxSizer(wx
.VERTICAL
)
1195 mainPanel
.SetSizer(mainSizer
)
1197 self
.log
= wx
.TextCtrl(mainPanel
, -1, style
= wx
.TE_MULTILINE | wx
.TE_READONLY
)
1198 mainSizer
.Add(self
.log
, proportion
= 1, flag
= wx
.EXPAND
)
1202 def OnCloseWindow(self
, event
):
1203 dlg
= wx
.MessageDialog(self
, "This will shut down SuperCollider, stop all servers and close all code windows.\n Do you want to quit?")
1204 reply
= dlg
.ShowModal()
1206 if reply
== wx
.ID_OK
:
1207 PsycolliderWindow
.OnCloseWindow(self
, event
)
1208 size
= self
.GetSize()
1209 pos
= self
.GetPosition()
1210 self
.config
.SetPath("/WindowSettings")
1211 self
.config
.WriteInt('PostWindow-sizeX', size
.x
)
1212 self
.config
.WriteInt('PostWindow-sizeY', size
.y
)
1213 self
.config
.WriteInt('PostWindow-posX', pos
.x
)
1214 self
.config
.WriteInt('PostWindow-posY', pos
.y
)
1216 wx
.GetApp().Shutdown()
1218 # No need? wx.MessageBox("Canceled");
1222 if self
.filePath
== "":
1226 file = open(self
.filePath
, "w")
1227 content
= self
.log
.GetRange(0, self
.log
.GetLastPosition())
1231 # Todo: better error handling? Just print error message for now
1232 wx
.MessageBox("Error: Could not save file " + filePath
)
1234 def SaveFileAs(self
):
1235 fileDlg
= wx
.FileDialog(self
,style
=wx
.SAVE
)
1236 if fileDlg
.ShowModal() == wx
.ID_OK
:
1237 self
.filePath
= fileDlg
.GetPath()
1239 file = open(self
.filePath
,"w")
1240 content
= self
.log
.GetRange(0, self
.log
.GetLastPosition())
1244 # Todo: better error handling? Just print error message for now
1245 wx
.MessageBox("Error: Could not save file " + self
.filePath
)
1248 # ---------------------------------------------------------------------
1250 class Psycollider(wx
.App
):
1251 theMainFrame
= None # Points to the post window object
1252 openWindows
= [] # List of all windows currently open
1253 config
= None # Main wx.Config object, used by all windows
1254 fileHistory
= None # wx.FileHistory object
1257 self
.config
= wx
.Config()
1260 self
.fileHistory
= wx
.FileHistory()
1261 self
.config
.SetPath("/RecentFiles")
1262 self
.fileHistory
.Load(self
.config
)
1264 # Create post window
1265 self
.theMainFrame
= PsycolliderPostWindow(None, -1, "SuperCollider (Post Window)")
1266 self
.theMainFrame
.Show(True)
1267 self
.SetTopWindow(self
.theMainFrame
)
1269 # enable images for html
1270 wx
.InitAllImageHandlers()
1272 # Set the log sink function (writes to post window)
1273 # On windows, we can write directly to it, and using the PostText function
1274 # causes the post window to be updated slightly later which doesn't look too nice.
1276 # Can't do this on Linux, as gtk is not thread safe, so must use PostText.
1277 if(os
.name
== 'nt'):
1278 PySCLang
.setSCLogSink(WriteInLogWindow
)
1280 PySCLang
.setSCLogSink(PostText
)
1282 PySCLang
.setPyPrOpenWinTextFile(OpenTextFile
)
1284 if not self
.ChangeDirToSCClassLibraryFolder():
1289 (addr
, port
) = self
.GetServerAddressAndPort()
1293 def doFileHistory(self
, event
):
1294 """Open a file from file history"""
1295 fileNumber
= event
.GetId() - wx
.ID_FILE1
1296 filename
= self
.fileHistory
.GetHistoryFile(fileNumber
)
1297 newWindow
= self
.OpenFile(filename
)
1298 if newWindow
!= None:
1299 self
.openWindows
.append(newWindow
)
1300 self
.AddFileToHistory(filename
) # add it back to the history so it will be moved up the list
1302 def GetServerAddressAndPort(self
):
1303 return ("127.1.0.1", "57110")
1305 def ChangeDirToSCClassLibraryFolder(self
):
1306 # first, we check if the current working folder
1307 # contains an item called 'SCClassLibrary'
1308 curPath
= os
.getcwd()
1309 listOfFolders
= os
.listdir(curPath
)
1311 # if the cwd contains 'SCClassLibrary', we're done
1312 if 'SCClassLibrary' in listOfFolders
:
1315 # uniqueName is what gets returned from config.Read(...)
1316 # if nothing was stored in the config yet
1317 uniqueName
= "{1FB0EC09-A883-4684-AD73-1D49A98A89DE}"
1318 self
.config
.SetPath("/GeneralSettings")
1319 classLibPath
= self
.config
.Read("SCClassLibraryPath", uniqueName
)
1320 leafName
= (os
.path
.split(classLibPath
))[1]
1322 # if the folder stored in the config is actually an existing
1323 # folder called 'SCClassLibrary', we change cwd to that
1324 # folder and we're done
1325 if os
.path
.isdir(classLibPath
) and leafName
== 'SCClassLibrary':
1326 classLibPath_split
= os
.path
.split(classLibPath
)
1327 classLibParentFolder
= classLibPath_split
[0]
1328 os
.chdir(classLibParentFolder
)
1331 # if something was stored in the config, but does not exist
1332 # anymore or is not correct, let's warn the user
1333 if classLibPath
!= uniqueName
:
1334 wx
.MessageBox("The path stored in the application preferences is not a valid SCClassLibrary folder. You will now be requested to select an existing SCClassLibrary folder","Error", wx
.OK | wx
.ICON_ERROR
)
1336 # ask the user to locate the folder
1337 continueLookingForFolder
= True
1338 classLibFolderFound
= False
1339 while continueLookingForFolder
:
1340 dlg
= wx
.DirDialog(None, "Please locate the SCClassLibrary")
1341 if dlg
.ShowModal() == wx
.ID_CANCEL
:
1342 wx
.MessageBox("Sorry. No class library available. SuperCollider will not work correctly","Error", wx
.OK | wx
.ICON_ERROR
)
1343 continueLookingForFolder
= False
1345 classLibPath
= dlg
.GetPath()
1346 leafName
= (os
.path
.split(classLibPath
))[1]
1347 if leafName
!= 'SCClassLibrary':
1348 wx
.MessageBox("The folder needs to be called SCClassLibrary for SuperCollider to work correctly", "Error", wx
.OK | wx
.ICON_ERROR
)
1350 continueLookingForFolder
= False
1351 classLibFolderFound
= True
1353 # only if a valid SCClassLibrary folder was found, then
1354 # set the current folder as its parent
1355 if classLibFolderFound
:
1356 self
.config
.Write("SCClassLibraryPath",classLibPath
)
1357 classLibPath_split
= os
.path
.split(classLibPath
)
1358 classLibParentFolder
= classLibPath_split
[0]
1359 os
.chdir(classLibParentFolder
)
1364 def NewCodeWindow(self
):
1365 window
= PsycolliderCodeWin(self
.theMainFrame
, -1, "Untitled %d" % (len(self
.openWindows
)+1))
1366 self
.openWindows
.append(window
)
1368 window
.SetSubWinFocus()
1371 def NewHTMLWindow(self
, filepath
):
1372 window
= PsycolliderHTMLWin(self
.theMainFrame
, -1,
1373 os
.path
.splitext(os
.path
.basename(filepath
))[0])
1374 self
.openWindows
.append(window
)
1376 window
.SetSubWinFocus()
1379 def ClosedWindow(self
, window
):
1381 self
.openWindows
.remove(window
)
1385 def HtmlToCode(self
, window
):
1386 if type(window
) == PsycolliderHTMLWin
:
1387 text
= window
.htmlSubWin
.ToText()
1389 newWindow
= PsycolliderCodeWin(self
.theMainFrame
, -1, "Untitled %d" % (len(self
.openWindows
)+1))
1390 self
.openWindows
.append(newWindow
)
1391 newWindow
.codeSubWin
.AddText(text
)
1392 newWindow
.Show(True)
1393 newWindow
.SetSubWinFocus()
1396 def OpenFile(self
, path
=''):
1398 fileDlg
= wx
.FileDialog(self
.theMainFrame
, style
=wx
.OPEN
)
1399 if not fileDlg
.ShowModal() == wx
.ID_OK
:
1402 path
= fileDlg
.GetPath()
1403 self
.AddFileToHistory(path
)
1406 file = open(path
,"r")
1407 textContent
= file.read()
1410 # Todo: better error handling? Just print error message for now
1411 wx
.MessageBox("Psycollider Error: Could not open file " + path
)
1414 if textContent
[0:5] == '{\\rtf':
1415 win
= self
.NewCodeWindow()
1416 win
.codeSubWin
.AddTextUTF8('Sorry, still no RTF support, wxRichTextControl does not yet support reading RTF files...')
1417 win
.isModified
= False
1420 elif (textContent
.find('<html') >= 0 or textContent
.find('<HTML') >= 0):
1421 win
= self
.NewHTMLWindow(path
)
1422 win
.htmlSubWin
.LoadPage(path
)
1426 # everything else is plain text code window
1427 win
= self
.NewCodeWindow()
1428 win
.codeSubWin
.AddTextUTF8(textContent
)
1434 def StopServer(self
):
1435 if PySCLang
.compiledOK():
1436 PySCLang
.setCmdLine('s.sendMsg("/quit");')
1437 PySCLang
.sendMain("interpretPrintCmdLine")
1439 def StopSwingOSC(self
):
1440 if PySCLang
.compiledOK():
1441 PySCLang
.setCmdLine('SwingOSC.default.sendMsg("/quit");')
1442 PySCLang
.sendMain("interpretPrintCmdLine")
1445 PySCLang
.sendMain("run");
1448 PySCLang
.sendMain("stop");
1450 def CompileLibrary(self
):
1454 PySCLang
.compileLibrary()
1456 def OpenClassDef(self
, text
):
1457 PySCLang
.setCmdLine(text
)
1458 PySCLang
.sendMain("openWinCodeFile")
1460 def ImpOf(self
, text
):
1461 PySCLang
.setCmdLine(text
)
1462 PySCLang
.sendMain("methodTemplates")
1464 def RefsTo(self
, text
):
1465 PySCLang
.setCmdLine(text
)
1466 PySCLang
.sendMain("methodReferences")
1468 def Eval(self
, text
):
1469 PySCLang
.setCmdLine(text
)
1470 PySCLang
.sendMain("interpretPrintCmdLine")
1472 def GoToHelpFile(self
, sel
=""):
1473 # TODO : test this : most help files don't open. remove trailing and leading spaces from sel, too, since wxTextCtrl is behaving strangely
1476 if sel
== "-" : sel
= "subtraction" # "subtraction.rtf"
1477 elif sel
== "/" : sel
= "division" # "division.rtf"
1478 elif sel
== "*" : sel
= "multiplication" # from "*.rtf"
1479 elif sel
== "**": sel
= "exponentiation" # from "**.rtf"
1480 elif sel
== "<" : sel
= "lessthan" # from "<.rtf"
1481 elif sel
== "<=": sel
= "lessorequalthan" # from "<=.rtf"
1482 elif sel
== ">" : sel
= "greaterthan" # from ">.rtf"
1483 elif sel
== ">=": sel
= "greaterorequalthan" # from ">=.rtf"
1484 elif sel
== "%" : sel
= "modulo" # from "%.rtf"
1487 for helpFolder
in [gHelpFolder
, gUserExtensionFolder
]:
1488 for folderPath
, foldersInPath
, fileNamesInFolder
in os
.walk(helpFolder
):
1489 # don't visit CVS directories
1490 if 'CVS' in foldersInPath
:
1491 foldersInPath
.remove('CVS')
1492 # don't visit .svn directories
1493 if '.svn' in foldersInPath
:
1494 foldersInPath
.remove('.svn')
1495 for fileName
in fileNamesInFolder
:
1496 filePath
= os
.path
.join(folderPath
, fileName
)
1497 if fileName
== sel
+ ".help.html":
1498 foundFilePath
= filePath
1500 if fileName
== sel
+ ".html":
1501 foundFilePath
= filePath
1504 # if file is found, let's break
1505 if foundFilePath
!= "":
1507 # for folderPath, ....
1508 # if file is found, let's break
1509 if foundFilePath
!= "":
1512 if foundFilePath
== "":
1513 foundFilePath
= os
.path
.join(gHelpFolder
,"Help.html")
1514 self
.OpenFile(foundFilePath
)
1516 def ClearPostWindow(self
):
1517 self
.theMainFrame
.log
.Clear()
1519 def SetDefaultWindowSize(self
, sizeX
, sizeY
):
1520 self
.config
.SetPath("/WindowSettings")
1521 self
.config
.WriteInt('DefaultSizeX', sizeX
)
1522 self
.config
.WriteInt('DefaultSizeY', sizeY
)
1523 WriteInLogWindow("Set default window size to " + str(sizeX
) + " x " + str(sizeY
) + "\n")
1525 def ClearRecentFileList(self
):
1526 numFiles
= self
.fileHistory
.GetCount()
1527 for i
in range(numFiles
):
1528 self
.fileHistory
.RemoveFileFromHistory(0) # remove the first file every time
1530 def AddFileToHistory(self
, path
):
1531 self
.fileHistory
.AddFileToHistory(path
)
1533 def GetOpenWindows(self
):
1534 return self
.openWindows
1538 self
.config
.SetPath("/RecentFiles")
1539 self
.fileHistory
.Save(self
.config
)
1540 del self
.fileHistory
1542 # stop scsynth and swingosc
1547 # ---------------------------------------------------------------------
1549 def WriteInLogWindow(text
):
1550 if wx
.GetApp().theMainFrame
== None:
1551 sys
.stdout
.write(text
)
1553 wx
.GetApp().theMainFrame
.log
.AppendText(text
)
1556 wx
.CallAfter(WriteInLogWindow
, text
)
1558 # ---------------------------------------------------------------------
1559 def OpenTextFile(path
, rangeStart
, rangeSize
):
1560 if wx
.GetApp().theMainFrame
== None:
1561 wx
.MessageBox("Cannot open %s since the main window is not created yet","Error",wx
.OK | wx
.ICON_ERROR
)
1563 codeWin
= wx
.GetApp().OpenFile(path
)
1564 #codeWin.SelectRange(rangeStart,rangeSize)
1567 # ---------------------------------------------------------------------
1569 app
= Psycollider(0)