123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871 |
- ##################################################################
- ##
- ## Interactive Shell Window
- ##
- import sys, os
- import code
- import string
- import win32ui
- import win32api
- import win32clipboard
- import win32con
- import traceback
- import afxres
- import array
- import __main__
- import pywin.scintilla.formatter
- import pywin.scintilla.control
- import pywin.scintilla.IDLEenvironment
- import pywin.framework.app
- ## sequential after ID_GOTO_LINE defined in editor.py
- ID_EDIT_COPY_CODE = 0xe2002
- ID_EDIT_EXEC_CLIPBOARD = 0x2003
- trace=pywin.scintilla.formatter.trace
- from . import winout
- import re
- # from IDLE.
- _is_block_opener = re.compile(r":\s*(#.*)?$").search
- _is_block_closer = re.compile(r"""
- \s*
- ( return
- | break
- | continue
- | raise
- | pass
- )
- \b
- """, re.VERBOSE).match
- tracebackHeader = "Traceback (".encode("ascii")
- sectionProfile = "Interactive Window"
- valueFormatTitle = "FormatTitle"
- valueFormatInput = "FormatInput"
- valueFormatOutput = "FormatOutput"
- valueFormatOutputError = "FormatOutputError"
- # These are defaults only. Values are read from the registry.
- formatTitle = (-536870897, 0, 220, 0, 16711680, 184, 34, 'Arial')
- formatInput = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
- formatOutput = (-402653169, 0, 200, 0, 8421376, 0, 49, 'Courier New')
- formatOutputError = (-402653169, 0, 200, 0, 255, 0, 49, 'Courier New')
- try:
- sys.ps1
- except AttributeError:
- sys.ps1 = '>>> '
- sys.ps2 = '... '
- def LoadPreference(preference, default = ""):
- return win32ui.GetProfileVal(sectionProfile, preference, default)
- def SavePreference( prefName, prefValue ):
- win32ui.WriteProfileVal( sectionProfile, prefName, prefValue )
- def GetPromptPrefix(line):
- ps1=sys.ps1
- if line[:len(ps1)]==ps1: return ps1
- ps2=sys.ps2
- if line[:len(ps2)]==ps2: return ps2
- #############################################################
- #
- # Colorizer related code.
- #
- #############################################################
- STYLE_INTERACTIVE_EOL = "Interactive EOL"
- STYLE_INTERACTIVE_OUTPUT = "Interactive Output"
- STYLE_INTERACTIVE_PROMPT = "Interactive Prompt"
- STYLE_INTERACTIVE_BANNER = "Interactive Banner"
- STYLE_INTERACTIVE_ERROR = "Interactive Error"
- STYLE_INTERACTIVE_ERROR_FINALLINE = "Interactive Error (final line)"
- INTERACTIVE_STYLES = [STYLE_INTERACTIVE_EOL, STYLE_INTERACTIVE_OUTPUT, STYLE_INTERACTIVE_PROMPT, STYLE_INTERACTIVE_BANNER, STYLE_INTERACTIVE_ERROR, STYLE_INTERACTIVE_ERROR_FINALLINE]
- FormatterParent = pywin.scintilla.formatter.PythonSourceFormatter
- class InteractiveFormatter(FormatterParent):
- def __init__(self, scintilla):
- FormatterParent.__init__(self, scintilla)
- self.bannerDisplayed = False
- def SetStyles(self):
- FormatterParent.SetStyles(self)
- Style = pywin.scintilla.formatter.Style
- self.RegisterStyle( Style(STYLE_INTERACTIVE_EOL, STYLE_INTERACTIVE_PROMPT ) )
- self.RegisterStyle( Style(STYLE_INTERACTIVE_PROMPT, formatInput ) )
- self.RegisterStyle( Style(STYLE_INTERACTIVE_OUTPUT, formatOutput) )
- self.RegisterStyle( Style(STYLE_INTERACTIVE_BANNER, formatTitle ) )
- self.RegisterStyle( Style(STYLE_INTERACTIVE_ERROR, formatOutputError ) )
- self.RegisterStyle( Style(STYLE_INTERACTIVE_ERROR_FINALLINE, STYLE_INTERACTIVE_ERROR ) )
- def LoadPreference(self, name, default):
- rc = win32ui.GetProfileVal("Format", name, default)
- if rc==default:
- rc = win32ui.GetProfileVal(sectionProfile, name, default)
- return rc
- def ColorizeInteractiveCode(self, cdoc, styleStart, stylePyStart):
- lengthDoc = len(cdoc)
- if lengthDoc == 0: return
- state = styleStart
- # As per comments in Colorize(), we work with the raw utf8
- # bytes. To avoid too muych py3k pain, we treat each utf8 byte
- # as a latin-1 unicode character - we only use it to compare
- # against ascii chars anyway...
- chNext = cdoc[0:1].decode('latin-1')
- startSeg = 0
- i = 0
- lastState=state # debug only
- while i < lengthDoc:
- ch = chNext
- chNext = cdoc[i+1:i+2].decode('latin-1')
-
- # trace("ch=%r, i=%d, next=%r, state=%s" % (ch, i, chNext, state))
- if state == STYLE_INTERACTIVE_EOL:
- if ch not in '\r\n':
- self.ColorSeg(startSeg, i-1, state)
- startSeg = i
- if ch in [sys.ps1[0], sys.ps2[0]]:
- state = STYLE_INTERACTIVE_PROMPT
- elif cdoc[i:i+len(tracebackHeader)]==tracebackHeader:
- state = STYLE_INTERACTIVE_ERROR
- else:
- state = STYLE_INTERACTIVE_OUTPUT
- elif state == STYLE_INTERACTIVE_PROMPT:
- if ch not in sys.ps1 + sys.ps2 + " ":
- self.ColorSeg(startSeg, i-1, state)
- startSeg = i
- if ch in '\r\n':
- state = STYLE_INTERACTIVE_EOL
- else:
- state = stylePyStart # Start coloring Python code.
- elif state in [STYLE_INTERACTIVE_OUTPUT]:
- if ch in '\r\n':
- self.ColorSeg(startSeg, i-1, state)
- startSeg = i
- state = STYLE_INTERACTIVE_EOL
- elif state == STYLE_INTERACTIVE_ERROR:
- if ch in '\r\n' and chNext and chNext not in string.whitespace:
- # Everything including me
- self.ColorSeg(startSeg, i, state)
- startSeg = i+1
- state = STYLE_INTERACTIVE_ERROR_FINALLINE
- elif i == 0 and ch not in string.whitespace:
- # If we are coloring from the start of a line,
- # we need this better check for the last line
- # Color up to not including me
- self.ColorSeg(startSeg, i-1, state)
- startSeg = i
- state = STYLE_INTERACTIVE_ERROR_FINALLINE
- elif state == STYLE_INTERACTIVE_ERROR_FINALLINE:
- if ch in '\r\n':
- self.ColorSeg(startSeg, i-1, state)
- startSeg = i
- state = STYLE_INTERACTIVE_EOL
- elif state == STYLE_INTERACTIVE_BANNER:
- if ch in '\r\n' and (chNext=='' or chNext in ">["):
- # Everything including me
- self.ColorSeg(startSeg, i-1, state)
- startSeg = i
- state = STYLE_INTERACTIVE_EOL
- else:
- # It is a PythonColorizer state - seek past the end of the line
- # and ask the Python colorizer to color that.
- end = startSeg
- while end < lengthDoc and cdoc[end] not in '\r\n'.encode('ascii'):
- end = end + 1
- self.ColorizePythonCode( cdoc[:end], startSeg, state)
- stylePyStart = self.GetStringStyle(end-1)
- if stylePyStart is None:
- stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT
- else:
- stylePyStart = stylePyStart.name
- startSeg =end
- i = end - 1 # ready for increment.
- chNext = cdoc[end:end+1].decode('latin-1')
- state = STYLE_INTERACTIVE_EOL
- if lastState != state:
- lastState = state
- i = i + 1
- # and the rest
- if startSeg<i:
- self.ColorSeg(startSeg, i-1, state)
- def Colorize(self, start=0, end=-1):
- # scintilla's formatting is all done in terms of utf, so
- # we work with utf8 bytes instead of unicode. This magically
- # works as any extended chars found in the utf8 don't change
- # the semantics.
- stringVal = self.scintilla.GetTextRange(start, end, decode=False)
- styleStart = None
- stylePyStart = None
- if start > 1:
- # Likely we are being asked to color from the start of the line.
- # We find the last formatted character on the previous line.
- # If TQString, we continue it. Otherwise, we reset.
- look = start -1
- while look and self.scintilla.SCIGetCharAt(look) in '\n\r':
- look = look - 1
- if look and look < start-1: # Did we find a char before the \n\r sets?
- strstyle = self.GetStringStyle(look)
- quote_char = None
- if strstyle is not None:
- if strstyle.name == pywin.scintilla.formatter.STYLE_TQSSTRING:
- quote_char = "'"
- elif strstyle.name == pywin.scintilla.formatter.STYLE_TQDSTRING:
- quote_char = '"'
- if quote_char is not None:
- # It is a TQS. If the TQS is not terminated, we
- # carry the style through.
- if look > 2:
- look_str = self.scintilla.SCIGetCharAt(look-2) + self.scintilla.SCIGetCharAt(look-1) + self.scintilla.SCIGetCharAt(look)
- if look_str != quote_char * 3:
- stylePyStart = strstyle.name
- if stylePyStart is None: stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT
- if start > 0:
- stylenum = self.scintilla.SCIGetStyleAt(start - 1)
- styleStart = self.GetStyleByNum(stylenum).name
- elif self.bannerDisplayed:
- styleStart = STYLE_INTERACTIVE_EOL
- else:
- styleStart = STYLE_INTERACTIVE_BANNER
- self.bannerDisplayed = True
- self.scintilla.SCIStartStyling(start, 31)
- self.style_buffer = array.array("b", (0,)*len(stringVal))
- self.ColorizeInteractiveCode(stringVal, styleStart, stylePyStart)
- self.scintilla.SCISetStylingEx(self.style_buffer)
- self.style_buffer = None
- ###############################################################
- #
- # This class handles the Python interactive interpreter.
- #
- # It uses a basic EditWindow, and does all the magic.
- # This is triggered by the enter key hander attached by the
- # start-up code. It determines if a command is to be executed
- # or continued (ie, emit "... ") by snooping around the current
- # line, looking for the prompts
- #
- class PythonwinInteractiveInterpreter(code.InteractiveInterpreter):
- def __init__(self, locals = None, globals = None):
- if locals is None: locals = __main__.__dict__
- if globals is None: globals = locals
- self.globals = globals
- code.InteractiveInterpreter.__init__(self, locals)
- def showsyntaxerror(self, filename=None):
- sys.stderr.write(tracebackHeader.decode('ascii')) # So the color syntaxer recognises it.
- code.InteractiveInterpreter.showsyntaxerror(self, filename)
- def runcode(self, code):
- try:
- exec(code, self.globals, self.locals)
- except SystemExit:
- raise
- except:
- self.showtraceback()
- class InteractiveCore:
- def __init__(self, banner = None):
- self.banner = banner
- # LoadFontPreferences()
- def Init(self):
- self.oldStdOut = self.oldStdErr = None
- # self.SetWordWrap(win32ui.CRichEditView_WrapNone)
- self.interp = PythonwinInteractiveInterpreter()
- self.OutputGrab() # Release at cleanup.
- if self.GetTextLength()==0:
- if self.banner is None:
- suffix = ""
- if win32ui.debug: suffix = ", debug build"
- sys.stderr.write("PythonWin %s on %s%s.\n" % (sys.version, sys.platform, suffix) )
- sys.stderr.write("Portions %s - see 'Help/About PythonWin' for further copyright information.\n" % (win32ui.copyright,) )
- else:
- sys.stderr.write(banner)
- rcfile = os.environ.get('PYTHONSTARTUP')
- if rcfile:
- import __main__
- try:
- exec(compile(open(rcfile, "rb").read(), rcfile, 'exec', dont_inherit=True),
- __main__.__dict__, __main__.__dict__)
- except:
- sys.stderr.write(">>> \nError executing PYTHONSTARTUP script %r\n" % (rcfile))
- traceback.print_exc(file=sys.stderr)
- self.AppendToPrompt([])
- def SetContext(self, globals, locals, name = "Dbg"):
- oldPrompt = sys.ps1
- if globals is None:
- # Reset
- sys.ps1 = ">>> "
- sys.ps2 = "... "
- locals = globals = __main__.__dict__
- else:
- sys.ps1 = "[%s]>>> " % name
- sys.ps2 = "[%s]... " % name
- self.interp.locals = locals
- self.interp.globals = globals
- self.AppendToPrompt([], oldPrompt)
- def GetContext(self):
- return self.interp.globals, self.interp.locals
- def DoGetLine(self, line=-1):
- if line==-1: line = self.LineFromChar()
- line = self.GetLine(line)
- while line and line[-1] in ['\r', '\n']:
- line = line[:-1]
- return line
- def AppendToPrompt(self,bufLines, oldPrompt = None):
- " Take a command and stick it at the end of the buffer (with python prompts inserted if required)."
- self.flush()
- lastLineNo = self.GetLineCount()-1
- line = self.DoGetLine(lastLineNo)
- if oldPrompt and line==oldPrompt:
- self.SetSel(self.GetTextLength()-len(oldPrompt), self.GetTextLength())
- self.ReplaceSel(sys.ps1)
- elif (line!=str(sys.ps1)):
- if len(line)!=0: self.write('\n')
- self.write(sys.ps1)
- self.flush()
- self.idle.text.mark_set("iomark", "end-1c")
- if not bufLines:
- return
- terms = (["\n" + sys.ps2] * (len(bufLines)-1)) + ['']
- for bufLine, term in zip(bufLines, terms):
- if bufLine.strip():
- self.write( bufLine + term )
- self.flush()
- def EnsureNoPrompt(self):
- # Get ready to write some text NOT at a Python prompt.
- self.flush()
- lastLineNo = self.GetLineCount()-1
- line = self.DoGetLine(lastLineNo)
- if not line or line in [sys.ps1, sys.ps2]:
- self.SetSel(self.GetTextLength()-len(line), self.GetTextLength())
- self.ReplaceSel('')
- else:
- # Just add a new line.
- self.write('\n')
-
- def _GetSubConfigNames(self):
- return ["interactive"] # Allow [Keys:Interactive] sections to be specific
- def HookHandlers(self):
- # Hook menu command (executed when a menu item with that ID is selected from a menu/toolbar
- self.HookCommand(self.OnSelectBlock, win32ui.ID_EDIT_SELECT_BLOCK)
- self.HookCommand(self.OnEditCopyCode, ID_EDIT_COPY_CODE)
- self.HookCommand(self.OnEditExecClipboard, ID_EDIT_EXEC_CLIPBOARD)
- mod = pywin.scintilla.IDLEenvironment.GetIDLEModule("IdleHistory")
- if mod is not None:
- self.history = mod.History(self.idle.text, "\n" + sys.ps2)
- else:
- self.history = None
- # hack for now for event handling.
- # GetBlockBoundary takes a line number, and will return the
- # start and and line numbers of the block, and a flag indicating if the
- # block is a Python code block.
- # If the line specified has a Python prompt, then the lines are parsed
- # backwards and forwards, and the flag is true.
- # If the line does not start with a prompt, the block is searched forward
- # and backward until a prompt _is_ found, and all lines in between without
- # prompts are returned, and the flag is false.
- def GetBlockBoundary( self, lineNo ):
- line = self.DoGetLine(lineNo)
- maxLineNo = self.GetLineCount()-1
- prefix = GetPromptPrefix(line)
- if prefix is None: # Non code block
- flag = 0
- startLineNo = lineNo
- while startLineNo>0:
- if GetPromptPrefix(self.DoGetLine(startLineNo-1)) is not None:
- break # there _is_ a prompt
- startLineNo = startLineNo-1
- endLineNo = lineNo
- while endLineNo<maxLineNo:
- if GetPromptPrefix(self.DoGetLine(endLineNo+1)) is not None:
- break # there _is_ a prompt
- endLineNo = endLineNo+1
- else: # Code block
- flag = 1
- startLineNo = lineNo
- while startLineNo>0 and prefix!=str(sys.ps1):
- prefix = GetPromptPrefix(self.DoGetLine(startLineNo-1))
- if prefix is None:
- break; # there is no prompt.
- startLineNo = startLineNo - 1
- endLineNo = lineNo
- while endLineNo<maxLineNo:
- prefix = GetPromptPrefix(self.DoGetLine(endLineNo+1))
- if prefix is None:
- break # there is no prompt
- if prefix==str(sys.ps1):
- break # this is another command
- endLineNo = endLineNo+1
- # continue until end of buffer, or no prompt
- return (startLineNo, endLineNo, flag)
- def ExtractCommand( self, lines ):
- start, end = lines
- retList = []
- while end >= start:
- thisLine = self.DoGetLine(end)
- promptLen = len(GetPromptPrefix(thisLine))
- retList = [thisLine[promptLen:]] + retList
- end = end-1
- return retList
- def OutputGrab(self):
- # import win32traceutil; return
- self.oldStdOut = sys.stdout
- self.oldStdErr = sys.stderr
- sys.stdout=self
- sys.stderr=self
- self.flush()
- def OutputRelease(self):
- # a command may have overwritten these - only restore if not.
- if self.oldStdOut is not None:
- if sys.stdout == self:
- sys.stdout=self.oldStdOut
- if self.oldStdErr is not None:
- if sys.stderr == self:
- sys.stderr=self.oldStdErr
- self.oldStdOut = None
- self.oldStdErr = None
- self.flush()
- ###################################
- #
- # Message/Command/Key Hooks.
- #
- # Enter key handler
- #
- def ProcessEnterEvent(self, event ):
- #If autocompletion has been triggered, complete and do not process event
- if self.SCIAutoCActive():
- self.SCIAutoCComplete()
- self.SCICancel()
- return
-
- self.SCICancel()
- # First, check for an error message
- haveGrabbedOutput = 0
- if self.HandleSpecialLine(): return 0
- lineNo = self.LineFromChar()
- start, end, isCode = self.GetBlockBoundary(lineNo)
- # If we are not in a code block just go to the prompt (or create a new one)
- if not isCode:
- self.AppendToPrompt([])
- win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
- return
- lines = self.ExtractCommand((start,end))
- # If we are in a code-block, but it isnt at the end of the buffer
- # then copy it to the end ready for editing and subsequent execution
- if end!=self.GetLineCount()-1:
- win32ui.SetStatusText('Press ENTER to execute command')
- self.AppendToPrompt(lines)
- self.SetSel(-2)
- return
- # If SHIFT held down, we want new code here and now!
- bNeedIndent = win32api.GetKeyState(win32con.VK_SHIFT)<0 or win32api.GetKeyState(win32con.VK_CONTROL)<0
- if bNeedIndent:
- self.ReplaceSel("\n")
- else:
- self.SetSel(-2)
- self.ReplaceSel("\n")
- source = '\n'.join(lines)
- while source and source[-1] in '\t ':
- source = source[:-1]
- self.OutputGrab() # grab the output for the command exec.
- try:
- if self.interp.runsource(source, "<interactive input>"): # Need more input!
- bNeedIndent = 1
- else:
- # If the last line isnt empty, append a newline
- if self.history is not None:
- self.history.history_store(source)
- self.AppendToPrompt([])
- win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
- # win32ui.SetStatusText('Successfully executed statement')
- finally:
- self.OutputRelease()
- if bNeedIndent:
- win32ui.SetStatusText('Ready to continue the command')
- # Now attempt correct indentation (should use IDLE?)
- curLine = self.DoGetLine(lineNo)[len(sys.ps2):]
- pos = 0
- indent=''
- while len(curLine)>pos and curLine[pos] in string.whitespace:
- indent = indent + curLine[pos]
- pos = pos + 1
- if _is_block_opener(curLine):
- indent = indent + '\t'
- elif _is_block_closer(curLine):
- indent = indent[:-1]
- # use ReplaceSel to ensure it goes at the cursor rather than end of buffer.
- self.ReplaceSel(sys.ps2+indent)
- return 0
- # ESC key handler
- def ProcessEscEvent(self, event):
- # Implement a cancel.
- if self.SCIAutoCActive() or self.SCICallTipActive():
- self.SCICancel()
- else:
- win32ui.SetStatusText('Cancelled.')
- self.AppendToPrompt(('',))
- return 0
- def OnSelectBlock(self,command, code):
- lineNo = self.LineFromChar()
- start, end, isCode = self.GetBlockBoundary(lineNo)
- startIndex = self.LineIndex(start)
- endIndex = self.LineIndex(end+1)-2 # skip \r + \n
- if endIndex<0: # must be beyond end of buffer
- endIndex = -2 # self.Length()
- self.SetSel(startIndex,endIndex)
- def OnEditCopyCode(self, command, code):
- """ Sanitizes code from interactive window, removing prompts and output,
- and inserts it in the clipboard."""
- code=self.GetSelText()
- lines=code.splitlines()
- out_lines=[]
- for line in lines:
- if line.startswith(sys.ps1):
- line=line[len(sys.ps1):]
- out_lines.append(line)
- elif line.startswith(sys.ps2):
- line=line[len(sys.ps2):]
- out_lines.append(line)
- out_code=os.linesep.join(out_lines)
- win32clipboard.OpenClipboard()
- try:
- win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT, str(out_code))
- finally:
- win32clipboard.CloseClipboard()
- def OnEditExecClipboard(self, command, code):
- """ Executes python code directly from the clipboard."""
- win32clipboard.OpenClipboard()
- try:
- code=win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
- finally:
- win32clipboard.CloseClipboard()
- code=code.replace('\r\n','\n')+'\n'
- try:
- o=compile(code, '<clipboard>', 'exec')
- exec(o, __main__.__dict__)
- except:
- traceback.print_exc()
- def GetRightMenuItems(self):
- # Just override parents
- ret = []
- flags = 0
- ret.append((flags, win32ui.ID_EDIT_UNDO, '&Undo'))
- ret.append(win32con.MF_SEPARATOR)
- ret.append((flags, win32ui.ID_EDIT_CUT, 'Cu&t'))
- ret.append((flags, win32ui.ID_EDIT_COPY, '&Copy'))
- start, end=self.GetSel()
- if start!=end:
- ret.append((flags, ID_EDIT_COPY_CODE, 'Copy code without prompts'))
- if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT):
- ret.append((flags, ID_EDIT_EXEC_CLIPBOARD, 'Execute python code from clipboard'))
- ret.append((flags, win32ui.ID_EDIT_PASTE, '&Paste'))
- ret.append(win32con.MF_SEPARATOR)
- ret.append((flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all'))
- ret.append((flags, win32ui.ID_EDIT_SELECT_BLOCK, 'Select &block'))
- ret.append((flags, win32ui.ID_VIEW_WHITESPACE, "View &Whitespace"))
- return ret
- def MDINextEvent(self, event):
- win32ui.GetMainFrame().MDINext(0)
- def MDIPrevEvent(self, event):
- win32ui.GetMainFrame().MDINext(0)
- def WindowBackEvent(self, event):
- parent = self.GetParentFrame()
- if parent == win32ui.GetMainFrame():
- # It is docked.
- try:
- wnd, isactive = parent.MDIGetActive()
- wnd.SetFocus()
- except win32ui.error:
- # No MDI window active!
- pass
- else:
- # Normal Window
- try:
- lastActive = self.GetParentFrame().lastActive
- # If the window is invalid, reset it.
- if lastActive is not None and (lastActive._obj_ is None or lastActive.GetSafeHwnd()==0):
- lastActive = self.GetParentFrame().lastActive = None
- win32ui.SetStatusText("The last active Window has been closed.")
- except AttributeError:
- print("Can't find the last active window!")
- lastActive = None
- if lastActive is not None:
- lastActive.MDIActivate()
- class InteractiveView(InteractiveCore, winout.WindowOutputView):
- def __init__(self, doc):
- InteractiveCore.__init__(self)
- winout.WindowOutputView.__init__(self, doc)
- self.encoding = pywin.default_scintilla_encoding
-
- def _MakeColorizer(self):
- return InteractiveFormatter(self)
- def OnInitialUpdate(self):
- winout.WindowOutputView.OnInitialUpdate(self)
- self.SetWordWrap()
- self.Init()
- def HookHandlers(self):
- winout.WindowOutputView.HookHandlers(self)
- InteractiveCore.HookHandlers(self)
- class CInteractivePython(winout.WindowOutput):
- def __init__(self, makeDoc = None, makeFrame = None):
- self.IsFinalDestroy = 0
- winout.WindowOutput.__init__(self, sectionProfile, sectionProfile, \
- winout.flags.WQ_LINE, 1, None, makeDoc, makeFrame, InteractiveView )
- self.Create()
- def OnViewDestroy(self, view):
- if self.IsFinalDestroy:
- view.OutputRelease()
- winout.WindowOutput.OnViewDestroy(self, view)
- def Close(self):
- self.IsFinalDestroy = 1
- winout.WindowOutput.Close(self)
- class InteractiveFrame(winout.WindowOutputFrame):
- def __init__(self):
- self.lastActive = None
- winout.WindowOutputFrame.__init__(self)
- def OnMDIActivate(self, bActive, wndActive, wndDeactive):
- if bActive:
- self.lastActive = wndDeactive
- ######################################################################
- ##
- ## Dockable Window Support
- ##
- ######################################################################
- ID_DOCKED_INTERACTIVE_CONTROLBAR = 0xe802
- DockedInteractiveViewParent = InteractiveView
- class DockedInteractiveView(DockedInteractiveViewParent):
- def HookHandlers(self):
- DockedInteractiveViewParent.HookHandlers(self)
- self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
- self.HookMessage(self.OnKillFocus, win32con.WM_KILLFOCUS)
- def OnSetFocus(self, msg):
- self.GetParentFrame().SetActiveView(self)
- return 1
- def OnKillFocus(self, msg):
- # If we are losing focus to another in this app, reset the main frame's active view.
- hwnd = wparam = msg[2]
- try:
- wnd = win32ui.CreateWindowFromHandle(hwnd)
- reset = wnd.GetTopLevelFrame()==self.GetTopLevelFrame()
- except win32ui.error:
- reset = 0 # Not my window
- if reset: self.GetParentFrame().SetActiveView(None)
- return 1
- def OnDestroy(self, msg):
- newSize = self.GetWindowPlacement()[4]
- pywin.framework.app.SaveWindowSize("Interactive Window", newSize, "docked")
- try:
- if self.GetParentFrame().GetActiveView==self:
- self.GetParentFrame().SetActiveView(None)
- except win32ui.error:
- pass
- try:
- if win32ui.GetMainFrame().GetActiveView()==self:
- win32ui.GetMainFrame().SetActiveView(None)
- except win32ui.error:
- pass
- return DockedInteractiveViewParent.OnDestroy(self, msg)
- class CDockedInteractivePython(CInteractivePython):
- def __init__(self, dockbar):
- self.bFirstCreated = 0
- self.dockbar = dockbar
- CInteractivePython.__init__(self)
- def NeedRecreateWindow(self):
- if self.bCreating:
- return 0
- try:
- frame = win32ui.GetMainFrame()
- if frame.closing:
- return 0 # Dieing!
- except (win32ui.error, AttributeError):
- return 0 # The app is dieing!
- try:
- cb = frame.GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
- return not cb.IsWindowVisible()
- except win32ui.error:
- return 1 # Control bar does not exist!
- def RecreateWindow(self):
- try:
- dockbar = win32ui.GetMainFrame().GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
- win32ui.GetMainFrame().ShowControlBar(dockbar, 1, 1)
- except win32ui.error:
- CreateDockedInteractiveWindow()
- def Create(self):
- self.bCreating = 1
- doc = InteractiveDocument(None, self.DoCreateDoc())
- view = DockedInteractiveView(doc)
- defRect = pywin.framework.app.LoadWindowSize("Interactive Window", "docked")
- if defRect[2]-defRect[0]==0:
- defRect = 0, 0, 500, 200
- style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER
- id = 1050 # win32ui.AFX_IDW_PANE_FIRST
- view.CreateWindow(self.dockbar, id, style, defRect)
- view.OnInitialUpdate()
- self.bFirstCreated = 1
- self.currentView = doc.GetFirstView()
- self.bCreating = 0
- if self.title: doc.SetTitle(self.title)
- # The factory we pass to the dockable window support.
- def InteractiveViewCreator(parent):
- global edit
- edit = CDockedInteractivePython(parent)
- return edit.currentView
- def CreateDockedInteractiveWindow():
- # Later, the DockingBar should be capable of hosting multiple
- # children.
- from pywin.docking.DockingBar import DockingBar
- bar = DockingBar()
- creator = InteractiveViewCreator
- bar.CreateWindow(win32ui.GetMainFrame(), creator, "Interactive Window", ID_DOCKED_INTERACTIVE_CONTROLBAR)
- bar.SetBarStyle( bar.GetBarStyle()|afxres.CBRS_TOOLTIPS|afxres.CBRS_FLYBY|afxres.CBRS_SIZE_DYNAMIC)
- bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
- win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM)
- ######################################################################
- #
- # The public interface to this module.
- #
- ######################################################################
- # No extra functionality now, but maybe later, so
- # publicize these names.
- InteractiveDocument = winout.WindowOutputDocument
- # We remember our one and only interactive window in the "edit" variable.
- edit = None
- def CreateInteractiveWindowUserPreference(makeDoc = None, makeFrame = None):
- """Create some sort of interactive window if the user's preference say we should.
- """
- bCreate = LoadPreference("Show at startup", 1)
- if bCreate:
- CreateInteractiveWindow(makeDoc, makeFrame)
- def CreateInteractiveWindow(makeDoc = None, makeFrame = None):
- """Create a standard or docked interactive window unconditionally
- """
- assert edit is None, "Creating second interactive window!"
- bDocking = LoadPreference("Docking", 0)
- if bDocking:
- CreateDockedInteractiveWindow()
- else:
- CreateMDIInteractiveWindow(makeDoc, makeFrame)
- assert edit is not None, "Created interactive window, but did not set the global!"
- edit.currentView.SetFocus()
- def CreateMDIInteractiveWindow(makeDoc = None, makeFrame = None):
- """Create a standard (non-docked) interactive window unconditionally
- """
- global edit
- if makeDoc is None: makeDoc = InteractiveDocument
- if makeFrame is None: makeFrame = InteractiveFrame
- edit = CInteractivePython(makeDoc=makeDoc,makeFrame=makeFrame)
- def DestroyInteractiveWindow():
- """ Destroy the interactive window.
- This is different to Closing the window,
- which may automatically re-appear. Once destroyed, it can never be recreated,
- and a complete new instance must be created (which the various other helper
- functions will then do after making this call
- """
- global edit
- if edit is not None and edit.currentView is not None:
- if edit.currentView.GetParentFrame() == win32ui.GetMainFrame():
- # It is docked - do nothing now (this is only called at shutdown!)
- pass
- else:
- # It is a standard window - call Close on the container.
- edit.Close()
- edit = None
- def CloseInteractiveWindow():
- """Close the interactive window, allowing it to be re-created on demand.
- """
- global edit
- if edit is not None and edit.currentView is not None:
- if edit.currentView.GetParentFrame() == win32ui.GetMainFrame():
- # It is docked, just hide the dock bar.
- frame = win32ui.GetMainFrame()
- cb = frame.GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
- frame.ShowControlBar(cb, 0, 1)
- else:
- # It is a standard window - destroy the frame/view, allowing the object itself to remain.
- edit.currentView.GetParentFrame().DestroyWindow()
- def ToggleInteractiveWindow():
- """If the interactive window is visible, hide it, otherwise show it.
- """
- if edit is None:
- CreateInteractiveWindow()
- else:
- if edit.NeedRecreateWindow():
- edit.RecreateWindow()
- else:
- # Close it, allowing a reopen.
- CloseInteractiveWindow()
- def ShowInteractiveWindow():
- """Shows (or creates if necessary) an interactive window"""
- if edit is None:
- CreateInteractiveWindow()
- else:
- if edit.NeedRecreateWindow():
- edit.RecreateWindow()
- else:
- parent = edit.currentView.GetParentFrame()
- if parent == win32ui.GetMainFrame(): # It is docked.
- edit.currentView.SetFocus()
- else: # It is a "normal" window
- edit.currentView.GetParentFrame().AutoRestore()
- win32ui.GetMainFrame().MDIActivate(edit.currentView.GetParentFrame())
- def IsInteractiveWindowVisible():
- return edit is not None and not edit.NeedRecreateWindow()
|