Uploaded Test files

This commit is contained in:
Batuhan Berk Başoğlu 2020-11-12 11:05:57 -05:00
parent f584ad9d97
commit 2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions

View file

@ -0,0 +1,408 @@
# App.py
# Application stuff.
# The application is responsible for managing the main frame window.
#
# We also grab the FileOpen command, to invoke our Python editor
" The PythonWin application code. Manages most aspects of MDI, etc "
import win32con
import win32api
import win32ui
import sys
import string
import os
from pywin.mfc import window, dialog, afxres
from pywin.mfc.thread import WinApp
import traceback
import regutil
from . import scriptutils
## NOTE: App and AppBuild should NOT be used - instead, you should contruct your
## APP class manually whenever you like (just ensure you leave these 2 params None!)
## Whoever wants the generic "Application" should get it via win32iu.GetApp()
# These are "legacy"
AppBuilder = None
App = None # default - if used, must end up a CApp derived class.
# Helpers that should one day be removed!
def AddIdleHandler(handler):
print("app.AddIdleHandler is deprecated - please use win32ui.GetApp().AddIdleHandler() instead.")
return win32ui.GetApp().AddIdleHandler(handler)
def DeleteIdleHandler(handler):
print("app.DeleteIdleHandler is deprecated - please use win32ui.GetApp().DeleteIdleHandler() instead.")
return win32ui.GetApp().DeleteIdleHandler(handler)
# Helper for writing a Window position by name, and later loading it.
def SaveWindowSize(section,rect,state=""):
""" Writes a rectangle to an INI file
Args: section = section name in the applications INI file
rect = a rectangle in a (cy, cx, y, x) tuple
(same format as CREATESTRUCT position tuples)."""
left, top, right, bottom = rect
if state: state = state + " "
win32ui.WriteProfileVal(section,state+"left",left)
win32ui.WriteProfileVal(section,state+"top",top)
win32ui.WriteProfileVal(section,state+"right",right)
win32ui.WriteProfileVal(section,state+"bottom",bottom)
def LoadWindowSize(section, state=""):
""" Loads a section from an INI file, and returns a rect in a tuple (see SaveWindowSize)"""
if state: state = state + " "
left = win32ui.GetProfileVal(section,state+"left",0)
top = win32ui.GetProfileVal(section,state+"top",0)
right = win32ui.GetProfileVal(section,state+"right",0)
bottom = win32ui.GetProfileVal(section,state+"bottom",0)
return (left, top, right, bottom)
def RectToCreateStructRect(rect):
return (rect[3]-rect[1], rect[2]-rect[0], rect[1], rect[0] )
# Define FrameWindow and Application objects
#
# The Main Frame of the application.
class MainFrame(window.MDIFrameWnd):
sectionPos = "Main Window"
statusBarIndicators = ( afxres.ID_SEPARATOR, #// status line indicator
afxres.ID_INDICATOR_CAPS,
afxres.ID_INDICATOR_NUM,
afxres.ID_INDICATOR_SCRL,
win32ui.ID_INDICATOR_LINENUM,
win32ui.ID_INDICATOR_COLNUM )
def OnCreate(self, cs):
self._CreateStatusBar()
return 0
def _CreateStatusBar(self):
self.statusBar = win32ui.CreateStatusBar(self)
self.statusBar.SetIndicators(self.statusBarIndicators)
self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_LINENUM)
self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_COLNUM)
def OnUpdatePosIndicator(self, cmdui):
editControl = scriptutils.GetActiveEditControl()
value = " " * 5
if editControl is not None:
try:
startChar, endChar = editControl.GetSel()
lineNo = editControl.LineFromChar(startChar)
colNo = endChar - editControl.LineIndex(lineNo)
if cmdui.m_nID==win32ui.ID_INDICATOR_LINENUM:
value = "%0*d" % (5, lineNo + 1)
else:
value = "%0*d" % (3, colNo + 1)
except win32ui.error:
pass
cmdui.SetText(value)
cmdui.Enable()
def PreCreateWindow(self, cc):
cc = self._obj_.PreCreateWindow(cc)
pos = LoadWindowSize(self.sectionPos)
self.startRect = pos
if pos[2] - pos[0]:
rect = RectToCreateStructRect(pos)
cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8]
return cc
def OnDestroy(self, msg):
# use GetWindowPlacement(), as it works even when min'd or max'd
rectNow = self.GetWindowPlacement()[4]
if rectNow != self.startRect:
SaveWindowSize(self.sectionPos, rectNow)
return 0
class CApp(WinApp):
" A class for the application "
def __init__(self):
self.oldCallbackCaller = None
WinApp.__init__(self, win32ui.GetApp() )
self.idleHandlers = []
def InitInstance(self):
" Called to crank up the app "
HookInput()
numMRU = win32ui.GetProfileVal("Settings","Recent File List Size", 10)
win32ui.LoadStdProfileSettings(numMRU)
# self._obj_.InitMDIInstance()
if win32api.GetVersionEx()[0]<4:
win32ui.SetDialogBkColor()
win32ui.Enable3dControls()
# install a "callback caller" - a manager for the callbacks
# self.oldCallbackCaller = win32ui.InstallCallbackCaller(self.CallbackManager)
self.LoadMainFrame()
self.SetApplicationPaths()
def ExitInstance(self):
" Called as the app dies - too late to prevent it here! "
win32ui.OutputDebug("Application shutdown\n")
# Restore the callback manager, if any.
try:
win32ui.InstallCallbackCaller(self.oldCallbackCaller)
except AttributeError:
pass
if self.oldCallbackCaller:
del self.oldCallbackCaller
self.frame=None # clean Python references to the now destroyed window object.
self.idleHandlers = []
# Attempt cleanup if not already done!
if self._obj_: self._obj_.AttachObject(None)
self._obj_ = None
global App
global AppBuilder
App = None
AppBuilder = None
return 0
def HaveIdleHandler(self, handler):
return handler in self.idleHandlers
def AddIdleHandler(self, handler):
self.idleHandlers.append(handler)
def DeleteIdleHandler(self, handler):
self.idleHandlers.remove(handler)
def OnIdle(self, count):
try:
ret = 0
handlers = self.idleHandlers[:] # copy list, as may be modified during loop
for handler in handlers:
try:
thisRet = handler(handler, count)
except:
print("Idle handler %s failed" % (repr(handler)))
traceback.print_exc()
print("Idle handler removed from list")
try:
self.DeleteIdleHandler(handler)
except ValueError: # Item not in list.
pass
thisRet = 0
ret = ret or thisRet
return ret
except KeyboardInterrupt:
pass
def CreateMainFrame(self):
return MainFrame()
def LoadMainFrame(self):
" Create the main applications frame "
self.frame = self.CreateMainFrame()
self.SetMainFrame(self.frame)
self.frame.LoadFrame(win32ui.IDR_MAINFRAME, win32con.WS_OVERLAPPEDWINDOW)
self.frame.DragAcceptFiles() # we can accept these.
self.frame.ShowWindow(win32ui.GetInitialStateRequest())
self.frame.UpdateWindow()
self.HookCommands()
def OnHelp(self,id, code):
try:
if id==win32ui.ID_HELP_GUI_REF:
helpFile = regutil.GetRegisteredHelpFile("Pythonwin Reference")
helpCmd = win32con.HELP_CONTENTS
else:
helpFile = regutil.GetRegisteredHelpFile("Main Python Documentation")
helpCmd = win32con.HELP_FINDER
if helpFile is None:
win32ui.MessageBox("The help file is not registered!")
else:
from . import help
help.OpenHelpFile(helpFile, helpCmd)
except:
t, v, tb = sys.exc_info()
win32ui.MessageBox("Internal error in help file processing\r\n%s: %s" % (t,v))
tb = None # Prevent a cycle
def DoLoadModules(self, modules):
# XXX - this should go, but the debugger uses it :-(
# dont do much checking!
for module in modules:
__import__(module)
def HookCommands(self):
self.frame.HookMessage(self.OnDropFiles,win32con.WM_DROPFILES)
self.HookCommand(self.HandleOnFileOpen,win32ui.ID_FILE_OPEN)
self.HookCommand(self.HandleOnFileNew,win32ui.ID_FILE_NEW)
self.HookCommand(self.OnFileMRU,win32ui.ID_FILE_MRU_FILE1)
self.HookCommand(self.OnHelpAbout,win32ui.ID_APP_ABOUT)
self.HookCommand(self.OnHelp, win32ui.ID_HELP_PYTHON)
self.HookCommand(self.OnHelp, win32ui.ID_HELP_GUI_REF)
# Hook for the right-click menu.
self.frame.GetWindow(win32con.GW_CHILD).HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
def SetApplicationPaths(self):
# Load the users/application paths
new_path = []
apppath=win32ui.GetProfileVal('Python','Application Path','').split(';')
for path in apppath:
if len(path)>0:
new_path.append(win32ui.FullPath(path))
for extra_num in range(1,11):
apppath=win32ui.GetProfileVal('Python','Application Path %d'%extra_num,'').split(';')
if len(apppath) == 0:
break
for path in apppath:
if len(path)>0:
new_path.append(win32ui.FullPath(path))
sys.path = new_path + sys.path
def OnRClick(self,params):
" Handle right click message "
# put up the entire FILE menu!
menu = win32ui.LoadMenu(win32ui.IDR_TEXTTYPE).GetSubMenu(0)
menu.TrackPopupMenu(params[5]) # track at mouse position.
return 0
def OnDropFiles(self,msg):
" Handle a file being dropped from file manager "
hDropInfo = msg[2]
self.frame.SetActiveWindow() # active us
nFiles = win32api.DragQueryFile(hDropInfo)
try:
for iFile in range(0,nFiles):
fileName = win32api.DragQueryFile(hDropInfo, iFile)
win32ui.GetApp().OpenDocumentFile( fileName )
finally:
win32api.DragFinish(hDropInfo);
return 0
# No longer used by Pythonwin, as the C++ code has this same basic functionality
# but handles errors slightly better.
# It all still works, tho, so if you need similar functionality, you can use it.
# Therefore I havent deleted this code completely!
# def CallbackManager( self, ob, args = () ):
# """Manage win32 callbacks. Trap exceptions, report on them, then return 'All OK'
# to the frame-work. """
# import traceback
# try:
# ret = apply(ob, args)
# return ret
# except:
# # take copies of the exception values, else other (handled) exceptions may get
# # copied over by the other fns called.
# win32ui.SetStatusText('An exception occured in a windows command handler.')
# t, v, tb = sys.exc_info()
# traceback.print_exception(t, v, tb.tb_next)
# try:
# sys.stdout.flush()
# except (NameError, AttributeError):
# pass
# Command handlers.
def OnFileMRU( self, id, code ):
" Called when a File 1-n message is recieved "
fileName = win32ui.GetRecentFileList()[id - win32ui.ID_FILE_MRU_FILE1]
win32ui.GetApp().OpenDocumentFile(fileName)
def HandleOnFileOpen( self, id, code ):
" Called when FileOpen message is received "
win32ui.GetApp().OnFileOpen()
def HandleOnFileNew( self, id, code ):
" Called when FileNew message is received "
win32ui.GetApp().OnFileNew()
def OnHelpAbout( self, id, code ):
" Called when HelpAbout message is received. Displays the About dialog. "
win32ui.InitRichEdit()
dlg=AboutBox()
dlg.DoModal()
def _GetRegistryValue(key, val, default = None):
# val is registry value - None for default val.
try:
hkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, key)
return win32api.RegQueryValueEx(hkey, val)[0]
except win32api.error:
try:
hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, key)
return win32api.RegQueryValueEx(hkey, val)[0]
except win32api.error:
return default
scintilla = "Scintilla is Copyright 1998-2008 Neil Hodgson (http://www.scintilla.org)"
idle = "This program uses IDLE extensions by Guido van Rossum, Tim Peters and others."
contributors = "Thanks to the following people for making significant contributions: Roger Upole, Sidnei da Silva, Sam Rushing, Curt Hagenlocher, Dave Brennan, Roger Burnham, Gordon McMillan, Neil Hodgson, Laramie Leavitt. (let me know if I have forgotten you!)"
# The About Box
class AboutBox(dialog.Dialog):
def __init__(self, idd=win32ui.IDD_ABOUTBOX):
dialog.Dialog.__init__(self, idd)
def OnInitDialog(self):
text = "Pythonwin - Python IDE and GUI Framework for Windows.\n\n%s\n\nPython is %s\n\n%s\n\n%s\n\n%s" % (win32ui.copyright, sys.copyright, scintilla, idle, contributors)
self.SetDlgItemText(win32ui.IDC_EDIT1, text)
# Get the build number - written by installers.
# For distutils build, read pywin32.version.txt
import distutils.sysconfig
site_packages = distutils.sysconfig.get_python_lib(plat_specific=1)
try:
build_no = open(os.path.join(site_packages, "pywin32.version.txt")).read().strip()
ver = "pywin32 build %s" % build_no
except EnvironmentError:
ver = None
if ver is None:
# See if we are Part of Active Python
ver = _GetRegistryValue("SOFTWARE\\ActiveState\\ActivePython", "CurrentVersion")
if ver is not None:
ver = "ActivePython build %s" % (ver,)
if ver is None:
ver = ""
self.SetDlgItemText(win32ui.IDC_ABOUT_VERSION, ver)
self.HookCommand(self.OnButHomePage, win32ui.IDC_BUTTON1)
def OnButHomePage(self, id, code):
if code == win32con.BN_CLICKED:
win32api.ShellExecute(0, "open", "https://github.com/mhammond/pywin32", None, "", 1)
def Win32RawInput(prompt=None):
"Provide raw_input() for gui apps"
# flush stderr/out first.
try:
sys.stdout.flush()
sys.stderr.flush()
except:
pass
if prompt is None: prompt = ""
ret=dialog.GetSimpleInput(prompt)
if ret==None:
raise KeyboardInterrupt("operation cancelled")
return ret
def Win32Input(prompt=None):
"Provide input() for gui apps"
return eval(input(prompt))
def HookInput():
try:
raw_input
# must be py2x...
sys.modules['__builtin__'].raw_input=Win32RawInput
sys.modules['__builtin__'].input=Win32Input
except NameError:
# must be py3k
import code
sys.modules['builtins'].input=Win32RawInput
def HaveGoodGUI():
"""Returns true if we currently have a good gui available.
"""
return "pywin.framework.startup" in sys.modules
def CreateDefaultGUI( appClass = None):
"""Creates a default GUI environment
"""
if appClass is None:
from . import intpyapp # Bring in the default app - could be param'd later.
appClass = intpyapp.InteractivePythonApp
# Create and init the app.
appClass().InitInstance()
def CheckCreateDefaultGUI():
"""Checks and creates if necessary a default GUI environment.
"""
rc = HaveGoodGUI()
if not rc:
CreateDefaultGUI()
return rc

View file

@ -0,0 +1,143 @@
import win32ui
import win32con
import win32api
import string
import os
from . import app
import sys
from pywin.mfc import docview, window
bStretch = 1
class BitmapDocument(docview.Document):
"A bitmap document. Holds the bitmap data itself."
def __init__(self, template):
docview.Document.__init__(self, template)
self.bitmap=None
def OnNewDocument(self):
# I can not create new bitmaps.
win32ui.MessageBox("Bitmaps can not be created.")
def OnOpenDocument(self, filename):
self.bitmap=win32ui.CreateBitmap()
# init data members
f = open(filename, 'rb')
try:
try:
self.bitmap.LoadBitmapFile(f)
except IOError:
win32ui.MessageBox("Could not load the bitmap from %s" % filename)
return 0
finally:
f.close()
self.size = self.bitmap.GetSize()
return 1
def DeleteContents(self):
self.bitmap=None
class BitmapView(docview.ScrollView):
"A view of a bitmap. Obtains data from document."
def __init__(self, doc):
docview.ScrollView.__init__(self, doc)
self.width = self.height = 0
# set up message handlers
self.HookMessage (self.OnSize, win32con.WM_SIZE)
def OnInitialUpdate(self):
doc = self.GetDocument()
if doc.bitmap:
bitmapSize = doc.bitmap.GetSize()
self.SetScrollSizes(win32con.MM_TEXT, bitmapSize)
def OnSize (self, params):
lParam = params[3]
self.width = win32api.LOWORD(lParam)
self.height = win32api.HIWORD(lParam)
def OnDraw (self, dc):
# set sizes used for "non stretch" mode.
doc = self.GetDocument()
if doc.bitmap is None: return
bitmapSize = doc.bitmap.GetSize()
if bStretch:
# stretch BMP.
viewRect = (0,0,self.width, self.height)
bitmapRect = (0,0,bitmapSize[0], bitmapSize[1])
doc.bitmap.Paint(dc, viewRect, bitmapRect)
else:
# non stretch.
doc.bitmap.Paint(dc)
class BitmapFrame(window.MDIChildWnd):
def OnCreateClient( self, createparams, context ):
borderX = win32api.GetSystemMetrics(win32con.SM_CXFRAME)
borderY = win32api.GetSystemMetrics(win32con.SM_CYFRAME)
titleY = win32api.GetSystemMetrics(win32con.SM_CYCAPTION) # includes border
# try and maintain default window pos, else adjust if cant fit
# get the main client window dimensions.
mdiClient = win32ui.GetMainFrame().GetWindow(win32con.GW_CHILD)
clientWindowRect=mdiClient.ScreenToClient(mdiClient.GetWindowRect())
clientWindowSize=(clientWindowRect[2]-clientWindowRect[0],clientWindowRect[3]-clientWindowRect[1])
left, top, right, bottom=mdiClient.ScreenToClient(self.GetWindowRect())
# width, height=context.doc.size[0], context.doc.size[1]
# width = width+borderX*2
# height= height+titleY+borderY*2-1
# if (left+width)>clientWindowSize[0]:
# left = clientWindowSize[0] - width
# if left<0:
# left = 0
# width = clientWindowSize[0]
# if (top+height)>clientWindowSize[1]:
# top = clientWindowSize[1] - height
# if top<0:
# top = 0
# height = clientWindowSize[1]
# self.frame.MoveWindow((left, top, left+width, top+height),0)
window.MDIChildWnd.OnCreateClient(self, createparams, context)
return 1
class BitmapTemplate(docview.DocTemplate):
def __init__(self):
docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, BitmapDocument, BitmapFrame, BitmapView)
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc: return doc
ext = os.path.splitext(fileName)[1].lower()
if ext =='.bmp': # removed due to PIL! or ext=='.ppm':
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_maybeAttemptForeign
# return win32ui.CDocTemplate_Confidence_noAttempt
# For debugging purposes, when this module may be reloaded many times.
try:
win32ui.GetApp().RemoveDocTemplate(bitmapTemplate)
except NameError:
pass
bitmapTemplate = BitmapTemplate()
bitmapTemplate.SetDocStrings('\nBitmap\nBitmap\nBitmap (*.bmp)\n.bmp\nPythonBitmapFileType\nPython Bitmap File')
win32ui.GetApp().AddDocTemplate(bitmapTemplate)
# This works, but just didnt make it through the code reorg.
#class PPMBitmap(Bitmap):
# def LoadBitmapFile(self, file ):
# magic=file.readline()
# if magic <> "P6\n":
# raise TypeError, "The file is not a PPM format file"
# rowcollist=string.split(file.readline())
# cols=string.atoi(rowcollist[0])
# rows=string.atoi(rowcollist[1])
# file.readline() # whats this one?
# self.bitmap.LoadPPMFile(file,(cols,rows))
def t():
bitmapTemplate.OpenDocumentFile('d:\\winnt\\arcade.bmp')
#OpenBMPFile( 'd:\\winnt\\arcade.bmp')
def demo():
import glob
winDir=win32api.GetWindowsDirectory()
for fileName in glob.glob1(winDir, '*.bmp')[:2]:
bitmapTemplate.OpenDocumentFile(os.path.join(winDir, fileName))

View file

@ -0,0 +1,49 @@
# cmdline - command line utilities.
import sys
import win32ui
import string
def ParseArgs( str ):
import string
ret=[]
pos = 0
length=len(str)
while pos<length:
try:
while str[pos] in string.whitespace: pos = pos+1
except IndexError:
break
if pos>=length:
break
if str[pos]=='"':
pos=pos+1
try:
endPos = str.index('"', pos)-1
nextPos = endPos+2
except ValueError:
endPos=length
nextPos=endPos+1
else:
endPos = pos
while endPos<length and not str[endPos] in string.whitespace: endPos = endPos+1
nextPos=endPos+1
ret.append(str[pos:endPos+1].strip())
pos = nextPos
return ret
def FixArgFileName(fileName):
"""Convert a filename on the commandline to something useful.
Given an automatic filename on the commandline, turn it a python module name,
with the path added to sys.path. """
import os
path, fname = os.path.split(fileName)
if len(path)==0:
path = os.curdir
path=os.path.abspath(path)
# must check that the command line arg's path is in sys.path
for syspath in sys.path:
if os.path.abspath(syspath)==path:
break
else:
sys.path.append(path)
return os.path.splitext(fname)[0]

View file

@ -0,0 +1,170 @@
# Command Handlers for the debugger.
# Not in the debugger package, as I always want these interfaces to be
# available, even if the debugger has not yet been (or can not be)
# imported
import win32ui, win32con
from . import scriptutils
import warnings
from pywin.scintilla.control import CScintillaEditInterface
IdToBarNames = {
win32ui.IDC_DBG_STACK : ("Stack",0),
win32ui.IDC_DBG_BREAKPOINTS : ("Breakpoints",0),
win32ui.IDC_DBG_WATCH : ("Watch",1),
}
class DebuggerCommandHandler:
def HookCommands(self):
commands = ( (self.OnStep, None, win32ui.IDC_DBG_STEP),
(self.OnStepOut, self.OnUpdateOnlyBreak, win32ui.IDC_DBG_STEPOUT),
(self.OnStepOver, None, win32ui.IDC_DBG_STEPOVER),
(self.OnGo, None, win32ui.IDC_DBG_GO),
(self.OnClose, self.OnUpdateClose, win32ui.IDC_DBG_CLOSE),
(self.OnAdd, self.OnUpdateAddBreakpoints, win32ui.IDC_DBG_ADD),
(self.OnClearAll, self.OnUpdateClearAllBreakpoints, win32ui.IDC_DBG_CLEAR),
# (self.OnDebuggerToolbar, self.OnUpdateDebuggerToolbar, win32ui.ID_DEBUGGER_TOOLBAR),
)
frame = win32ui.GetMainFrame()
for methHandler, methUpdate, id in commands:
frame.HookCommand(methHandler, id);
if not methUpdate is None:
frame.HookCommandUpdate(methUpdate, id)
for id in list(IdToBarNames.keys()):
frame.HookCommand( self.OnDebuggerBar, id)
frame.HookCommandUpdate(self.OnUpdateDebuggerBar, id)
def OnDebuggerToolbar(self, id, code):
if code==0:
return not win32ui.GetMainFrame().OnBarCheck(id)
def OnUpdateDebuggerToolbar(self, cmdui):
win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)
cmdui.Enable(1)
def _GetDebugger(self):
try:
import pywin.debugger
return pywin.debugger.currentDebugger
except ImportError:
return None
def _DoOrStart(self, doMethod, startFlag):
d=self._GetDebugger()
if d is not None and d.IsDebugging():
method = getattr(d, doMethod)
method()
else:
scriptutils.RunScript(defName=None, defArgs=None, bShowDialog = 0, debuggingType=startFlag)
def OnStep(self, msg, code):
self._DoOrStart("do_set_step", scriptutils.RS_DEBUGGER_STEP)
def OnStepOver(self, msg, code):
self._DoOrStart("do_set_next", scriptutils.RS_DEBUGGER_STEP)
def OnStepOut(self, msg, code):
d=self._GetDebugger()
if d is not None and d.IsDebugging():
d.do_set_return()
def OnGo(self, msg, code):
self._DoOrStart("do_set_continue", scriptutils.RS_DEBUGGER_GO)
def OnClose(self, msg, code):
d=self._GetDebugger()
if d is not None:
if d.IsDebugging():
d.set_quit()
else:
d.close()
def OnUpdateClose(self, cmdui):
d=self._GetDebugger()
if d is not None and d.inited:
cmdui.Enable(1)
else:
cmdui.Enable(0)
def OnAdd(self, msg, code):
doc, view = scriptutils.GetActiveEditorDocument()
if doc is None:
## Don't do a messagebox, as this could be triggered from the app's
## idle loop whenever the debug toolbar is visible, giving a never-ending
## series of dialogs. This can happen when the OnUpdate handler
## for the toolbar button IDC_DBG_ADD fails, since MFC falls back to
## sending a normal command if the UI update command fails.
## win32ui.MessageBox('There is no active window - no breakpoint can be added')
warnings.warn('There is no active window - no breakpoint can be added')
return None
pathName = doc.GetPathName()
lineNo = view.LineFromChar(view.GetSel()[0])+1
# If I have a debugger, then tell it, otherwise just add a marker
d=self._GetDebugger()
if d is None:
import pywin.framework.editor.color.coloreditor
doc.MarkerToggle(lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT)
else:
if d.get_break(pathName, lineNo):
win32ui.SetStatusText('Clearing breakpoint',1)
rc = d.clear_break(pathName, lineNo)
else:
win32ui.SetStatusText('Setting breakpoint',1)
rc = d.set_break(pathName, lineNo)
if rc:
win32ui.MessageBox(rc)
d.GUIRespondDebuggerData()
def OnClearAll(self, msg, code):
win32ui.SetStatusText('Clearing all breakpoints')
d=self._GetDebugger()
if d is None:
import pywin.framework.editor
import pywin.framework.editor.color.coloreditor
for doc in pywin.framework.editor.editorTemplate.GetDocumentList():
doc.MarkerDeleteAll(pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT)
else:
d.clear_all_breaks()
d.UpdateAllLineStates()
d.GUIRespondDebuggerData()
def OnUpdateOnlyBreak(self, cmdui):
d=self._GetDebugger()
ok = d is not None and d.IsBreak()
cmdui.Enable(ok)
def OnUpdateAddBreakpoints(self, cmdui):
doc, view = scriptutils.GetActiveEditorDocument()
if doc is None or not isinstance(view, CScintillaEditInterface):
enabled = 0
else:
enabled = 1
lineNo = view.LineFromChar(view.GetSel()[0])+1
import pywin.framework.editor.color.coloreditor
cmdui.SetCheck(doc.MarkerAtLine(lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT) != 0)
cmdui.Enable(enabled)
def OnUpdateClearAllBreakpoints(self, cmdui):
d=self._GetDebugger()
cmdui.Enable(d is None or len(d.breaks)!=0)
def OnUpdateDebuggerBar(self, cmdui):
name, always = IdToBarNames.get(cmdui.m_nID)
enabled = always
d=self._GetDebugger()
if d is not None and d.IsDebugging() and name is not None:
enabled = 1
bar = d.GetDebuggerBar(name)
cmdui.SetCheck(bar.IsWindowVisible())
cmdui.Enable(enabled)
def OnDebuggerBar(self, id, code):
name = IdToBarNames.get(id)[0]
d=self._GetDebugger()
if d is not None and name is not None:
bar = d.GetDebuggerBar(name)
newState = not bar.IsWindowVisible()
win32ui.GetMainFrame().ShowControlBar(bar, newState, 1)

View file

@ -0,0 +1,69 @@
# dlgappcore.
#
# base classes for dialog based apps.
from . import app
import win32ui
import win32con
import win32api
import sys
from pywin.mfc import dialog
error = "Dialog Application Error"
class AppDialog(dialog.Dialog):
"The dialog box for the application"
def __init__(self, id, dll=None):
self.iconId = win32ui.IDR_MAINFRAME
dialog.Dialog.__init__(self, id, dll)
def OnInitDialog(self):
return dialog.Dialog.OnInitDialog(self)
# Provide support for a dlg app using an icon
def OnPaint(self):
if not self.IsIconic(): return self._obj_.OnPaint()
self.DefWindowProc(win32con.WM_ICONERASEBKGND, dc.GetHandleOutput(), 0)
left, top, right, bottom = self.GetClientRect()
left = (right - win32api.GetSystemMetrics(win32con.SM_CXICON)) >> 1
top = (bottom - win32api.GetSystemMetrics(win32con.SM_CYICON)) >> 1
hIcon = win32ui.GetApp().LoadIcon(self.iconId)
self.GetDC().DrawIcon((left, top), hIcon)
# Only needed to provide a minimized icon (and this seems
# less important under win95/NT4
def OnEraseBkgnd(self, dc):
if self.IsIconic():
return 1
else:
return self._obj_.OnEraseBkgnd(dc)
def OnQueryDragIcon(self):
return win32ui.GetApp().LoadIcon(self.iconId)
def PreDoModal(self):
pass
class DialogApp(app.CApp):
"An application class, for an app with main dialog box"
def InitInstance(self):
# win32ui.SetProfileFileName('dlgapp.ini')
win32ui.LoadStdProfileSettings()
win32ui.EnableControlContainer()
win32ui.Enable3dControls()
self.dlg = self.frame = self.CreateDialog()
if self.frame is None:
raise error("No dialog was created by CreateDialog()")
return
self._obj_.InitDlgInstance(self.dlg)
self.PreDoModal()
self.dlg.PreDoModal()
self.dlg.DoModal()
def CreateDialog(self):
pass
def PreDoModal(self):
pass

View file

@ -0,0 +1,200 @@
# ModuleBrowser.py - A view that provides a module browser for an editor document.
import pywin.mfc.docview
import win32ui
import win32con
import commctrl
import win32api
from pywin.tools import hierlist, browser
import pywin.framework.scriptutils
import afxres
import pyclbr
class HierListCLBRModule(hierlist.HierListItem):
def __init__(self, modName, clbrdata):
self.modName = modName
self.clbrdata = clbrdata
def GetText(self):
return self.modName
def GetSubList(self):
ret = []
for item in self.clbrdata.values():
if item.__class__ != pyclbr.Class: # ie, it is a pyclbr Function instance (only introduced post 1.5.2)
ret.append(HierListCLBRFunction( item ) )
else:
ret.append(HierListCLBRClass( item) )
ret.sort()
return ret
def IsExpandable(self):
return 1
class HierListCLBRItem(hierlist.HierListItem):
def __init__(self, name, file, lineno, suffix = ""):
self.name = str(name)
self.file = file
self.lineno = lineno
self.suffix = suffix
def __lt__(self, other):
return self.name < other.name
def __eq__(self, other):
return self.name == other.name
def GetText(self):
return self.name + self.suffix
def TakeDefaultAction(self):
if self.file:
pywin.framework.scriptutils.JumpToDocument(self.file, self.lineno, bScrollToTop = 1)
else:
win32ui.SetStatusText("Can not locate the source code for this object.")
def PerformItemSelected(self):
if self.file is None:
msg = "%s - source can not be located." % (self.name, )
else:
msg = "%s defined at line %d of %s" % (self.name, self.lineno, self.file)
win32ui.SetStatusText(msg)
class HierListCLBRClass(HierListCLBRItem):
def __init__(self, clbrclass, suffix = ""):
try:
name = clbrclass.name
file = clbrclass.file
lineno = clbrclass.lineno
self.super = clbrclass.super
self.methods = clbrclass.methods
except AttributeError:
name = clbrclass
file = lineno = None
self.super = []; self.methods = {}
HierListCLBRItem.__init__(self, name, file, lineno, suffix)
def GetSubList(self):
r1 = []
for c in self.super:
r1.append(HierListCLBRClass(c, " (Parent class)"))
r1.sort()
r2=[]
for meth, lineno in self.methods.items():
r2.append(HierListCLBRMethod(meth, self.file, lineno))
r2.sort()
return r1+r2
def IsExpandable(self):
return len(self.methods) + len(self.super)
def GetBitmapColumn(self):
return 21
class HierListCLBRFunction(HierListCLBRItem):
def __init__(self, clbrfunc, suffix = ""):
name = clbrfunc.name
file = clbrfunc.file
lineno = clbrfunc.lineno
HierListCLBRItem.__init__(self, name, file, lineno, suffix)
def GetBitmapColumn(self):
return 22
class HierListCLBRMethod(HierListCLBRItem):
def GetBitmapColumn(self):
return 22
class HierListCLBRErrorItem(hierlist.HierListItem):
def __init__(self, text):
self.text = text
def GetText(self):
return self.text
def GetSubList(self):
return [HierListCLBRErrorItem(self.text)]
def IsExpandable(self):
return 0
class HierListCLBRErrorRoot(HierListCLBRErrorItem):
def IsExpandable(self):
return 1
class BrowserView(pywin.mfc.docview.TreeView):
def OnInitialUpdate(self):
self.list = None
rc = self._obj_.OnInitialUpdate()
self.HookMessage(self.OnSize, win32con.WM_SIZE)
self.bDirty = 0
self.destroying = 0
return rc
def DestroyBrowser(self):
self.DestroyList()
def OnActivateView(self, activate, av, dv):
# print "AV", self.bDirty, activate
if activate:
self.CheckRefreshList()
return self._obj_.OnActivateView(activate, av, dv)
def _MakeRoot(self):
path = self.GetDocument().GetPathName()
if not path:
return HierListCLBRErrorRoot("Error: Can not browse a file until it is saved")
else:
mod, path = pywin.framework.scriptutils.GetPackageModuleName(path)
if self.bDirty:
what = "Refreshing"
# Hack for pyclbr being too smart
try:
del pyclbr._modules[mod]
except (KeyError, AttributeError):
pass
else:
what = "Building"
win32ui.SetStatusText("%s class list - please wait..." % (what,), 1)
win32ui.DoWaitCursor(1)
try:
reader = pyclbr.readmodule_ex # new version post 1.5.2
except AttributeError:
reader = pyclbr.readmodule
try:
data = reader(mod, [path])
if data:
return HierListCLBRModule(mod, data)
else:
return HierListCLBRErrorRoot("No Python classes in module.")
finally:
win32ui.DoWaitCursor(0)
win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
def DestroyList(self):
self.destroying = 1
list = getattr(self, "list", None) # If the document was not successfully opened, we may not have a list.
self.list = None
if list is not None:
list.HierTerm()
self.destroying = 0
def CheckMadeList(self):
if self.list is not None or self.destroying: return
self.rootitem = root = self._MakeRoot()
self.list = list = hierlist.HierListWithItems( root, win32ui.IDB_BROWSER_HIER)
list.HierInit(self.GetParentFrame(), self)
list.SetStyle(commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS)
def CheckRefreshList(self):
if self.bDirty:
if self.list is None:
self.CheckMadeList()
else:
new_root = self._MakeRoot()
if self.rootitem.__class__==new_root.__class__==HierListCLBRModule:
self.rootitem.modName = new_root.modName
self.rootitem.clbrdata = new_root.clbrdata
self.list.Refresh()
else:
self.list.AcceptRoot(self._MakeRoot())
self.bDirty = 0
def OnSize(self, params):
lparam = params[3]
w = win32api.LOWORD(lparam)
h = win32api.HIWORD(lparam)
if w != 0:
self.CheckMadeList()
elif w == 0:
self.DestroyList()
return 1
def _UpdateUIForState(self):
self.bDirty = 1

View file

@ -0,0 +1,90 @@
# __init__ for the Pythonwin editor package.
#
# We used to support optional editors - eg, color or non-color.
#
# This really isnt necessary with Scintilla, and scintilla
# is getting so deeply embedded that it was too much work.
import win32ui, sys, win32con
defaultCharacterFormat = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
##def GetDefaultEditorModuleName():
## import pywin
## # If someone has set pywin.editormodulename, then this is what we use
## try:
## prefModule = pywin.editormodulename
## except AttributeError:
## prefModule = win32ui.GetProfileVal("Editor","Module", "")
## return prefModule
##
##def WriteDefaultEditorModule(module):
## try:
## module = module.__name__
## except:
## pass
## win32ui.WriteProfileVal("Editor", "Module", module)
def LoadDefaultEditor():
pass
## prefModule = GetDefaultEditorModuleName()
## restorePrefModule = None
## mod = None
## if prefModule:
## try:
## mod = __import__(prefModule)
## except 'xx':
## msg = "Importing your preferred editor ('%s') failed.\n\nError %s: %s\n\nAn attempt will be made to load the default editor.\n\nWould you like this editor disabled in the future?" % (prefModule, sys.exc_info()[0], sys.exc_info()[1])
## rc = win32ui.MessageBox(msg, "Error importing editor", win32con.MB_YESNO)
## if rc == win32con.IDNO:
## restorePrefModule = prefModule
## WriteDefaultEditorModule("")
## del rc
##
## try:
## # Try and load the default one - dont catch errors here.
## if mod is None:
## prefModule = "pywin.framework.editor.color.coloreditor"
## mod = __import__(prefModule)
##
## # Get at the real module.
## mod = sys.modules[prefModule]
##
## # Do a "from mod import *"
## globals().update(mod.__dict__)
##
## finally:
## # Restore the users default editor if it failed and they requested not to disable it.
## if restorePrefModule:
## WriteDefaultEditorModule(restorePrefModule)
def GetEditorOption(option, defaultValue, min=None, max = None):
rc = win32ui.GetProfileVal("Editor", option, defaultValue)
if min is not None and rc < min: rc = defaultValue
if max is not None and rc > max: rc = defaultValue
return rc
def SetEditorOption(option, newValue):
win32ui.WriteProfileVal("Editor", option, newValue)
def DeleteEditorOption(option):
try:
win32ui.WriteProfileVal("Editor", option, None)
except win32ui.error:
pass
# Load and save font tuples
def GetEditorFontOption(option, default = None):
if default is None: default = defaultCharacterFormat
fmt = GetEditorOption( option, "" )
if fmt == "": return default
try:
return eval(fmt)
except:
print("WARNING: Invalid font setting in registry - setting ignored")
return default
def SetEditorFontOption(option, newValue):
SetEditorOption(option, str(newValue))
from pywin.framework.editor.color.coloreditor import editorTemplate

View file

@ -0,0 +1,525 @@
# Color Editor originally by Neil Hodgson, but restructured by mh to integrate
# even tighter into Pythonwin.
import win32ui
import win32con
import win32api
import sys
import pywin.scintilla.keycodes
from pywin.scintilla import bindings
from pywin.framework.editor import GetEditorOption, SetEditorOption, GetEditorFontOption, SetEditorFontOption, defaultCharacterFormat
#from pywin.framework.editor import EditorPropertyPage
MSG_CHECK_EXTERNAL_FILE = win32con.WM_USER+1999 ## WARNING: Duplicated in document.py and editor.py
# Define a few common markers
MARKER_BOOKMARK = 0
MARKER_BREAKPOINT = 1
MARKER_CURRENT = 2
from pywin.debugger import dbgcon
from pywin.scintilla.document import CScintillaDocument
from pywin.framework.editor.document import EditorDocumentBase
from pywin.scintilla import scintillacon # For the marker definitions
import pywin.scintilla.view
class SyntEditDocument(EditorDocumentBase):
"A SyntEdit document. "
def OnDebuggerStateChange(self, state):
self._ApplyOptionalToViews("OnDebuggerStateChange", state)
def HookViewNotifications(self, view):
EditorDocumentBase.HookViewNotifications(self, view)
view.SCISetUndoCollection(1)
def FinalizeViewCreation(self, view):
EditorDocumentBase.FinalizeViewCreation(self, view)
if view==self.GetFirstView():
self.GetDocTemplate().CheckIDLEMenus(view.idle)
SyntEditViewParent=pywin.scintilla.view.CScintillaView
class SyntEditView(SyntEditViewParent):
"A view of a SyntEdit. Obtains data from document."
def __init__(self, doc):
SyntEditViewParent.__init__(self, doc)
self.bCheckingFile = 0
def OnInitialUpdate(self):
SyntEditViewParent.OnInitialUpdate(self)
self.HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
for id in [win32ui.ID_VIEW_FOLD_COLLAPSE, win32ui.ID_VIEW_FOLD_COLLAPSE_ALL,
win32ui.ID_VIEW_FOLD_EXPAND, win32ui.ID_VIEW_FOLD_EXPAND_ALL]:
self.HookCommand(self.OnCmdViewFold, id)
self.HookCommandUpdate(self.OnUpdateViewFold, id)
self.HookCommand(self.OnCmdViewFoldTopLevel, win32ui.ID_VIEW_FOLD_TOPLEVEL)
# Define the markers
# self.SCIMarkerDeleteAll()
self.SCIMarkerDefineAll(MARKER_BOOKMARK, scintillacon.SC_MARK_ROUNDRECT, win32api.RGB(0x0, 0x0, 0x0), win32api.RGB(0, 0xff, 0xff))
self.SCIMarkerDefine(MARKER_CURRENT, scintillacon.SC_MARK_ARROW)
self.SCIMarkerSetBack(MARKER_CURRENT, win32api.RGB(0xff, 0xff, 0x00))
# Define the folding markers
if 1: #traditional markers
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEROPEN, scintillacon.SC_MARK_MINUS, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDER, scintillacon.SC_MARK_PLUS, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERSUB, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERTAIL, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEREND, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEROPENMID, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERMIDTAIL, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
else: # curved markers
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEROPEN, scintillacon.SC_MARK_CIRCLEMINUS, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDER, scintillacon.SC_MARK_CIRCLEPLUS, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERSUB, scintillacon.SC_MARK_VLINE, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERTAIL, scintillacon.SC_MARK_LCORNERCURVE, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEREND, scintillacon.SC_MARK_CIRCLEPLUSCONNECTED, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEROPENMID, scintillacon.SC_MARK_CIRCLEMINUSCONNECTED, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERMIDTAIL, scintillacon.SC_MARK_TCORNERCURVE, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefine(MARKER_BREAKPOINT, scintillacon.SC_MARK_CIRCLE)
# Marker background depends on debugger state
self.SCIMarkerSetFore(MARKER_BREAKPOINT, win32api.RGB(0x0, 0, 0))
# Get the current debugger state.
try:
import pywin.debugger
if pywin.debugger.currentDebugger is None:
state = dbgcon.DBGSTATE_NOT_DEBUGGING
else:
state = pywin.debugger.currentDebugger.debuggerState
except ImportError:
state = dbgcon.DBGSTATE_NOT_DEBUGGING
self.OnDebuggerStateChange(state)
def _GetSubConfigNames(self):
return ["editor"] # Allow [Keys:Editor] sections to be specific to us
def DoConfigChange(self):
SyntEditViewParent.DoConfigChange(self)
tabSize = GetEditorOption("Tab Size", 4, 2)
indentSize = GetEditorOption("Indent Size", 4, 2)
bUseTabs = GetEditorOption("Use Tabs", 0)
bSmartTabs = GetEditorOption("Smart Tabs", 1)
ext = self.idle.IDLEExtension("AutoIndent") # Required extension.
self.SCISetViewWS( GetEditorOption("View Whitespace", 0) )
self.SCISetViewEOL( GetEditorOption("View EOL", 0) )
self.SCISetIndentationGuides( GetEditorOption("View Indentation Guides", 0) )
if GetEditorOption("Right Edge Enabled", 0):
mode = scintillacon.EDGE_BACKGROUND
else:
mode = scintillacon.EDGE_NONE
self.SCISetEdgeMode(mode)
self.SCISetEdgeColumn( GetEditorOption("Right Edge Column", 75) )
self.SCISetEdgeColor( GetEditorOption("Right Edge Color", win32api.RGB(0xef, 0xef, 0xef)))
width = GetEditorOption("Marker Margin Width", 16)
self.SCISetMarginWidthN(1, width)
width = GetEditorOption("Fold Margin Width", 12)
self.SCISetMarginWidthN(2, width)
width = GetEditorOption("Line Number Margin Width", 0)
self.SCISetMarginWidthN(0, width)
self.bFolding = GetEditorOption("Enable Folding", 1)
fold_flags = 0
self.SendScintilla(scintillacon.SCI_SETMODEVENTMASK, scintillacon.SC_MOD_CHANGEFOLD);
if self.bFolding:
if GetEditorOption("Fold Lines", 1):
fold_flags = 16
self.SCISetProperty("fold", self.bFolding)
self.SCISetFoldFlags(fold_flags)
tt_color = GetEditorOption("Tab Timmy Color", win32api.RGB(0xff, 0, 0))
self.SendScintilla(scintillacon.SCI_INDICSETFORE, 1, tt_color)
tt_use = GetEditorOption("Use Tab Timmy", 1)
if tt_use:
self.SCISetProperty("tab.timmy.whinge.level", "1")
# Auto-indent has very complicated behaviour. In a nutshell, the only
# way to get sensible behaviour from it is to ensure tabwidth != indentsize.
# Further, usetabs will only ever go from 1->0, never 0->1.
# This is _not_ the behaviour Pythonwin wants:
# * Tab width is arbitary, so should have no impact on smarts.
# * bUseTabs setting should reflect how new files are created, and
# if Smart Tabs disabled, existing files are edited
# * If "Smart Tabs" is enabled, bUseTabs should have no bearing
# for existing files (unless of course no context can be determined)
#
# So for smart tabs we configure the widget with completely dummy
# values (ensuring tabwidth != indentwidth), ask it to guess, then
# look at the values it has guessed, and re-configure
if bSmartTabs:
ext.config(usetabs=1, tabwidth=5, indentwidth=4)
ext.set_indentation_params(1)
if ext.indentwidth==5:
# Either 5 literal spaces, or a single tab character. Assume a tab
usetabs = 1
indentwidth = tabSize
else:
# Either Indented with spaces, and indent size has been guessed or
# an empty file (or no context found - tough!)
if self.GetTextLength()==0: # emtpy
usetabs = bUseTabs
indentwidth = indentSize
else: # guessed.
indentwidth = ext.indentwidth
usetabs = 0
# Tab size can never be guessed - set at user preference.
ext.config(usetabs=usetabs, indentwidth=indentwidth, tabwidth=tabSize)
else:
# Dont want smart-tabs - just set the options!
ext.config(usetabs=bUseTabs, tabwidth=tabSize, indentwidth=indentSize)
self.SCISetIndent(indentSize)
self.SCISetTabWidth(tabSize)
def OnDebuggerStateChange(self, state):
if state == dbgcon.DBGSTATE_NOT_DEBUGGING:
# Indicate breakpoints arent really usable.
# Not quite white - useful when no marker margin, so set as background color.
self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0xef, 0xef, 0xef))
else:
# A light-red, so still readable when no marker margin.
self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0xff, 0x80, 0x80))
def HookDocumentHandlers(self):
SyntEditViewParent.HookDocumentHandlers(self)
self.HookMessage(self.OnCheckExternalDocumentUpdated,MSG_CHECK_EXTERNAL_FILE)
def HookHandlers(self):
SyntEditViewParent.HookHandlers(self)
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
def _PrepareUserStateChange(self):
return self.GetSel(), self.GetFirstVisibleLine()
def _EndUserStateChange(self, info):
scrollOff = info[1] - self.GetFirstVisibleLine()
if scrollOff:
self.LineScroll(scrollOff)
# Make sure we dont reset the cursor beyond the buffer.
max = self.GetTextLength()
newPos = min(info[0][0], max), min(info[0][1], max)
self.SetSel(newPos)
#######################################
# The Windows Message or Notify handlers.
#######################################
def OnMarginClick(self, std, extra):
notify = self.SCIUnpackNotifyMessage(extra)
if notify.margin==2: # Our fold margin
line_click = self.LineFromChar(notify.position)
# max_line = self.GetLineCount()
if self.SCIGetFoldLevel(line_click) & scintillacon.SC_FOLDLEVELHEADERFLAG:
# If a fold point.
self.SCIToggleFold(line_click)
return 1
def OnSetFocus(self,msg):
# Even though we use file change notifications, we should be very sure about it here.
self.OnCheckExternalDocumentUpdated(msg)
return 1
def OnCheckExternalDocumentUpdated(self, msg):
if self.bCheckingFile: return
self.bCheckingFile = 1
self.GetDocument().CheckExternalDocumentUpdated()
self.bCheckingFile = 0
def OnRClick(self,params):
menu = win32ui.CreatePopupMenu()
self.AppendMenu(menu, "&Locate module", "LocateModule")
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, "&Undo", "EditUndo")
self.AppendMenu(menu, '&Redo', 'EditRedo')
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, 'Cu&t', 'EditCut')
self.AppendMenu(menu, '&Copy', 'EditCopy')
self.AppendMenu(menu, '&Paste', 'EditPaste')
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, '&Select all', 'EditSelectAll')
self.AppendMenu(menu, 'View &Whitespace', 'ViewWhitespace', checked=self.SCIGetViewWS())
self.AppendMenu(menu, "&Fixed Font", "ViewFixedFont", checked = self._GetColorizer().bUseFixed)
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, "&Goto line...", "GotoLine")
submenu = win32ui.CreatePopupMenu()
newitems = self.idle.GetMenuItems("edit")
for text, event in newitems:
self.AppendMenu(submenu, text, event)
flags=win32con.MF_STRING|win32con.MF_ENABLED|win32con.MF_POPUP
menu.AppendMenu(flags, submenu.GetHandle(), "&Source code")
flags = win32con.TPM_LEFTALIGN|win32con.TPM_LEFTBUTTON|win32con.TPM_RIGHTBUTTON
menu.TrackPopupMenu(params[5], flags, self)
return 0
def OnCmdViewFold(self, cid, code): # Handle the menu command
if cid == win32ui.ID_VIEW_FOLD_EXPAND_ALL:
self.FoldExpandAllEvent(None)
elif cid == win32ui.ID_VIEW_FOLD_EXPAND:
self.FoldExpandEvent(None)
elif cid == win32ui.ID_VIEW_FOLD_COLLAPSE_ALL:
self.FoldCollapseAllEvent(None)
elif cid == win32ui.ID_VIEW_FOLD_COLLAPSE:
self.FoldCollapseEvent(None)
else:
print("Unknown collapse/expand ID")
def OnUpdateViewFold(self, cmdui): # Update the tick on the UI.
if not self.bFolding:
cmdui.Enable(0)
return
id = cmdui.m_nID
if id in [win32ui.ID_VIEW_FOLD_EXPAND_ALL, win32ui.ID_VIEW_FOLD_COLLAPSE_ALL]:
cmdui.Enable()
else:
enable = 0
lineno = self.LineFromChar(self.GetSel()[0])
foldable = self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG
is_expanded = self.SCIGetFoldExpanded(lineno)
if id == win32ui.ID_VIEW_FOLD_EXPAND:
if foldable and not is_expanded:
enable = 1
elif id == win32ui.ID_VIEW_FOLD_COLLAPSE:
if foldable and is_expanded:
enable = 1
cmdui.Enable(enable)
def OnCmdViewFoldTopLevel(self, cid, code): # Handle the menu command
self.FoldTopLevelEvent(None)
#######################################
# The Events
#######################################
def ToggleBookmarkEvent(self, event, pos = -1):
"""Toggle a bookmark at the specified or current position
"""
if pos==-1:
pos, end = self.GetSel()
startLine = self.LineFromChar(pos)
self.GetDocument().MarkerToggle(startLine+1, MARKER_BOOKMARK)
return 0
def GotoNextBookmarkEvent(self, event, fromPos=-1):
""" Move to the next bookmark
"""
if fromPos==-1:
fromPos, end = self.GetSel()
startLine = self.LineFromChar(fromPos)+1 # Zero based line to start
nextLine = self.GetDocument().MarkerGetNext(startLine+1, MARKER_BOOKMARK)-1
if nextLine<0:
nextLine = self.GetDocument().MarkerGetNext(0, MARKER_BOOKMARK)-1
if nextLine <0 or nextLine == startLine-1:
win32api.MessageBeep()
else:
self.SCIEnsureVisible(nextLine)
self.SCIGotoLine(nextLine)
return 0
def TabKeyEvent(self, event):
"""Insert an indent. If no selection, a single indent, otherwise a block indent
"""
# Handle auto-complete first.
if self.SCIAutoCActive():
self.SCIAutoCComplete()
return 0
# Call the IDLE event.
return self.bindings.fire("<<smart-indent>>", event)
def EnterKeyEvent(self, event):
"""Handle the enter key with special handling for auto-complete
"""
# Handle auto-complete first.
if self.SCIAutoCActive():
self.SCIAutoCComplete()
self.SCIAutoCCancel()
# Call the IDLE event.
return self.bindings.fire("<<newline-and-indent>>", event)
def ShowInteractiveWindowEvent(self, event):
import pywin.framework.interact
pywin.framework.interact.ShowInteractiveWindow()
def FoldTopLevelEvent(self, event = None):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
try:
self.Colorize()
maxLine = self.GetLineCount()
# Find the first line, and check out its state.
for lineSeek in range(maxLine):
if self.SCIGetFoldLevel(lineSeek) & scintillacon.SC_FOLDLEVELHEADERFLAG:
expanding = not self.SCIGetFoldExpanded(lineSeek)
break
else:
# no folds here!
return
for lineSeek in range(lineSeek, maxLine):
level = self.SCIGetFoldLevel(lineSeek)
level_no = level & scintillacon.SC_FOLDLEVELNUMBERMASK - scintillacon.SC_FOLDLEVELBASE
is_header = level & scintillacon.SC_FOLDLEVELHEADERFLAG
# print lineSeek, level_no, is_header
if level_no == 0 and is_header:
if (expanding and not self.SCIGetFoldExpanded(lineSeek)) or \
(not expanding and self.SCIGetFoldExpanded(lineSeek)):
self.SCIToggleFold(lineSeek)
finally:
win32ui.DoWaitCursor(-1)
def FoldExpandSecondLevelEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
## I think this is needed since Scintilla may not have
## already formatted parts of file outside visible window.
self.Colorize()
levels=[scintillacon.SC_FOLDLEVELBASE]
## Scintilla's level number is based on amount of whitespace indentation
for lineno in range(self.GetLineCount()):
level = self.SCIGetFoldLevel(lineno)
if not level & scintillacon.SC_FOLDLEVELHEADERFLAG:
continue
curr_level = level & scintillacon.SC_FOLDLEVELNUMBERMASK
if curr_level > levels[-1]:
levels.append(curr_level)
try:
level_ind=levels.index(curr_level)
except ValueError:
## probably syntax error in source file, bail
break
levels=levels[:level_ind+1]
if level_ind == 1 and not self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldCollapseSecondLevelEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
## I think this is needed since Scintilla may not have
## already formatted parts of file outside visible window.
self.Colorize()
levels=[scintillacon.SC_FOLDLEVELBASE]
## Scintilla's level number is based on amount of whitespace indentation
for lineno in range(self.GetLineCount()):
level = self.SCIGetFoldLevel(lineno)
if not level & scintillacon.SC_FOLDLEVELHEADERFLAG:
continue
curr_level = level & scintillacon.SC_FOLDLEVELNUMBERMASK
if curr_level > levels[-1]:
levels.append(curr_level)
try:
level_ind=levels.index(curr_level)
except ValueError:
## probably syntax error in source file, bail
break
levels=levels[:level_ind+1]
if level_ind == 1 and self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldExpandEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
lineno = self.LineFromChar(self.GetSel()[0])
if self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG and \
not self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldExpandAllEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
for lineno in range(0, self.GetLineCount()):
if self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG and \
not self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldCollapseEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
lineno = self.LineFromChar(self.GetSel()[0])
if self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG and \
self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldCollapseAllEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
self.Colorize()
for lineno in range(0, self.GetLineCount()):
if self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG and \
self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
from pywin.framework.editor.frame import EditorFrame
class SplitterFrame(EditorFrame):
def OnCreate(self, cs):
self.HookCommand(self.OnWindowSplit, win32ui.ID_WINDOW_SPLIT)
return 1
def OnWindowSplit(self, id, code):
self.GetDlgItem(win32ui.AFX_IDW_PANE_FIRST).DoKeyboardSplit()
return 1
from pywin.framework.editor.template import EditorTemplateBase
class SyntEditTemplate(EditorTemplateBase):
def __init__(self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None):
if makeDoc is None: makeDoc = SyntEditDocument
if makeView is None: makeView = SyntEditView
if makeFrame is None: makeFrame = SplitterFrame
self.bSetMenus = 0
EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView)
def CheckIDLEMenus(self, idle):
if self.bSetMenus: return
self.bSetMenus = 1
submenu = win32ui.CreatePopupMenu()
newitems = idle.GetMenuItems("edit")
flags=win32con.MF_STRING|win32con.MF_ENABLED
for text, event in newitems:
id = bindings.event_to_commands.get(event)
if id is not None:
keyname = pywin.scintilla.view.configManager.get_key_binding( event, ["editor"] )
if keyname is not None:
text = text + "\t" + keyname
submenu.AppendMenu(flags, id, text)
mainMenu = self.GetSharedMenu()
editMenu = mainMenu.GetSubMenu(1)
editMenu.AppendMenu(win32con.MF_SEPARATOR, 0, "")
editMenu.AppendMenu(win32con.MF_STRING | win32con.MF_POPUP | win32con.MF_ENABLED, submenu.GetHandle(), "&Source Code")
def _CreateDocTemplate(self, resourceId):
return win32ui.CreateDocTemplate(resourceId)
def CreateWin32uiDocument(self):
return self.DoCreateDoc()
def GetPythonPropertyPages(self):
"""Returns a list of property pages
"""
from pywin.scintilla import configui
return EditorTemplateBase.GetPythonPropertyPages(self) + [configui.ScintillaFormatPropertyPage()]
# For debugging purposes, when this module may be reloaded many times.
try:
win32ui.GetApp().RemoveDocTemplate(editorTemplate)
except NameError:
pass
editorTemplate = SyntEditTemplate()
win32ui.GetApp().AddDocTemplate(editorTemplate)

View file

@ -0,0 +1,257 @@
from pywin.mfc import dialog
from . import document
import win32ui
import win32con
import win32api
from pywin.framework.editor import GetEditorOption, SetEditorOption, DeleteEditorOption, GetEditorFontOption, SetEditorFontOption, defaultCharacterFormat, editorTemplate
import pywin.scintilla.config
# The standard 16 color VGA palette should always be possible
paletteVGA = ( ("Black",0,0,0), ("Navy",0,0,128), ("Green",0,128,0), ("Cyan",0,128,128),
("Maroon",128,0,0), ("Purple",128,0,128), ("Olive",128,128,0), ("Gray",128,128,128),
("Silver",192,192,192), ("Blue",0,0,255), ("Lime",0,255,0), ("Aqua",0,255,255),
("Red",255,0,0), ("Fuchsia",255,0,255), ("Yellow",255,255,0), ("White",255,255,255) )
######################################################
#
# Property Page for editor options
#
class EditorPropertyPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_EDITOR)
self.autooptions = []
self._AddEditorOption(win32ui.IDC_AUTO_RELOAD, "i", "Auto Reload", 1)
self._AddEditorOption(win32ui.IDC_COMBO1, "i", "Backup Type", document.BAK_DOT_BAK_BAK_DIR)
self._AddEditorOption(win32ui.IDC_AUTOCOMPLETE, "i", "Autocomplete Attributes", 1)
self._AddEditorOption(win32ui.IDC_CALLTIPS, "i", "Show Call Tips", 1)
self._AddEditorOption(win32ui.IDC_MARGIN_LINENUMBER, "i", "Line Number Margin Width", 0)
self._AddEditorOption(win32ui.IDC_RADIO1, "i", "MarkersInMargin", None)
self._AddEditorOption(win32ui.IDC_MARGIN_MARKER, "i", "Marker Margin Width", None)
self["Marker Margin Width"] = GetEditorOption("Marker Margin Width", 16)
# Folding
self._AddEditorOption(win32ui.IDC_MARGIN_FOLD, "i", "Fold Margin Width", 12)
self._AddEditorOption(win32ui.IDC_FOLD_ENABLE, "i", "Enable Folding", 1)
self._AddEditorOption(win32ui.IDC_FOLD_ON_OPEN, "i", "Fold On Open", 0)
self._AddEditorOption(win32ui.IDC_FOLD_SHOW_LINES, "i", "Fold Lines", 1)
# Right edge.
self._AddEditorOption(win32ui.IDC_RIGHTEDGE_ENABLE, "i", "Right Edge Enabled", 0)
self._AddEditorOption(win32ui.IDC_RIGHTEDGE_COLUMN, "i", "Right Edge Column", 75)
# Source control, etc
self.AddDDX(win32ui.IDC_VSS_INTEGRATE, "bVSS")
self.AddDDX(win32ui.IDC_KEYBOARD_CONFIG, "Configs", "l")
self["Configs"] = pywin.scintilla.config.find_config_files()
def _AddEditorOption(self, idd, typ, optionName, defaultVal):
self.AddDDX(idd, optionName, typ)
# some options are "derived" - ie, can be implied from others
# (eg, "view markers in background" is implied from "markerMarginWidth==0"
# So we don't actually store these values, but they do still get DDX support.
if defaultVal is not None:
self[optionName] = GetEditorOption(optionName, defaultVal)
self.autooptions.append((optionName, defaultVal))
def OnInitDialog(self):
for name, val in self.autooptions:
self[name] = GetEditorOption(name, val)
# Note that these MUST be in the same order as the BAK constants.
cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
cbo.AddString("None")
cbo.AddString(".BAK File")
cbo.AddString("TEMP dir")
cbo.AddString("Own dir")
# Source Safe
bVSS = GetEditorOption("Source Control Module", "") == "pywin.framework.editor.vss"
self['bVSS'] = bVSS
edit = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE)
edit.SetWindowText("Sample Color")
rc = dialog.PropertyPage.OnInitDialog(self)
try:
self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).SelectString(-1, GetEditorOption("Keyboard Config", "default"))
except win32ui.error:
import traceback
traceback.print_exc()
pass
self.HookCommand(self.OnButSimple, win32ui.IDC_FOLD_ENABLE)
self.HookCommand(self.OnButSimple, win32ui.IDC_RADIO1)
self.HookCommand(self.OnButSimple, win32ui.IDC_RADIO2)
self.HookCommand(self.OnButSimple, win32ui.IDC_RIGHTEDGE_ENABLE)
self.HookCommand(self.OnButEdgeColor, win32ui.IDC_RIGHTEDGE_DEFINE)
butMarginEnabled = self['Marker Margin Width'] > 0
self.GetDlgItem(win32ui.IDC_RADIO1).SetCheck(butMarginEnabled)
self.GetDlgItem(win32ui.IDC_RADIO2).SetCheck(not butMarginEnabled)
self.edgeColor = self.initialEdgeColor = GetEditorOption("Right Edge Color", win32api.RGB(0xef, 0xef, 0xef))
for spinner_id in (win32ui.IDC_SPIN1, win32ui.IDC_SPIN2, win32ui.IDC_SPIN3):
spinner=self.GetDlgItem(spinner_id)
spinner.SetRange(0,100)
self.UpdateUIForState()
return rc
def OnButSimple(self, id, code):
if code == win32con.BN_CLICKED:
self.UpdateUIForState()
def OnButEdgeColor(self, id, code):
if code == win32con.BN_CLICKED:
d = win32ui.CreateColorDialog(self.edgeColor, 0, self)
# Ensure the current color is a custom color (as it may not be in the swatch)
# plus some other nice gray scales.
ccs = [self.edgeColor]
for c in range(0xef, 0x4f, -0x10):
ccs.append(win32api.RGB(c,c,c))
d.SetCustomColors( ccs )
if d.DoModal() == win32con.IDOK:
self.edgeColor = d.GetColor()
self.UpdateUIForState()
def UpdateUIForState(self):
folding = self.GetDlgItem(win32ui.IDC_FOLD_ENABLE).GetCheck()
self.GetDlgItem(win32ui.IDC_FOLD_ON_OPEN).EnableWindow(folding)
self.GetDlgItem(win32ui.IDC_FOLD_SHOW_LINES).EnableWindow(folding)
widthEnabled = self.GetDlgItem(win32ui.IDC_RADIO1).GetCheck()
self.GetDlgItem(win32ui.IDC_MARGIN_MARKER).EnableWindow(widthEnabled)
self.UpdateData() # Ensure self[] is up to date with the control data.
if widthEnabled and self["Marker Margin Width"] == 0:
self["Marker Margin Width"] = 16
self.UpdateData(0) # Ensure control up to date with self[]
# Right edge
edgeEnabled = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_ENABLE).GetCheck()
self.GetDlgItem(win32ui.IDC_RIGHTEDGE_COLUMN).EnableWindow(edgeEnabled)
self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE).EnableWindow(edgeEnabled)
self.GetDlgItem(win32ui.IDC_RIGHTEDGE_DEFINE).EnableWindow(edgeEnabled)
edit = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE)
edit.SetBackgroundColor(0, self.edgeColor)
def OnOK(self):
for name, defVal in self.autooptions:
SetEditorOption(name, self[name])
# Margin width gets handled differently.
if self['MarkersInMargin'] == 0:
SetEditorOption("Marker Margin Width", self["Marker Margin Width"])
else:
SetEditorOption("Marker Margin Width", 0)
if self.edgeColor != self.initialEdgeColor:
SetEditorOption("Right Edge Color", self.edgeColor)
if self['bVSS']:
SetEditorOption("Source Control Module", "pywin.framework.editor.vss")
else:
if GetEditorOption("Source Control Module", "")=='pywin.framework.editor.vss':
SetEditorOption("Source Control Module", "")
# Keyboard config
configname = self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).GetWindowText()
if configname:
if configname == "default":
DeleteEditorOption("Keyboard Config")
else:
SetEditorOption("Keyboard Config", configname)
import pywin.scintilla.view
pywin.scintilla.view.LoadConfiguration()
# Now tell all views we have changed.
## for doc in editorTemplate.GetDocumentList():
## for view in doc.GetAllViews():
## try:
## fn = view.OnConfigChange
## except AttributeError:
## continue
## fn()
return 1
class EditorWhitespacePropertyPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_TABS)
self.autooptions = []
self._AddEditorOption(win32ui.IDC_TAB_SIZE, "i", "Tab Size", 4)
self._AddEditorOption(win32ui.IDC_INDENT_SIZE, "i", "Indent Size", 4)
self._AddEditorOption(win32ui.IDC_USE_SMART_TABS, "i", "Smart Tabs", 1)
self._AddEditorOption(win32ui.IDC_VIEW_WHITESPACE, "i", "View Whitespace", 0)
self._AddEditorOption(win32ui.IDC_VIEW_EOL, "i", "View EOL", 0)
self._AddEditorOption(win32ui.IDC_VIEW_INDENTATIONGUIDES, "i", "View Indentation Guides", 0)
def _AddEditorOption(self, idd, typ, optionName, defaultVal):
self.AddDDX(idd, optionName, typ)
self[optionName] = GetEditorOption(optionName, defaultVal)
self.autooptions.append((optionName, defaultVal))
def OnInitDialog(self):
for name, val in self.autooptions:
self[name] = GetEditorOption(name, val)
rc = dialog.PropertyPage.OnInitDialog(self)
idc = win32ui.IDC_TABTIMMY_NONE
if GetEditorOption("Use Tab Timmy", 1):
idc = win32ui.IDC_TABTIMMY_IND
self.GetDlgItem(idc).SetCheck(1)
idc = win32ui.IDC_RADIO1
if GetEditorOption("Use Tabs", 0):
idc = win32ui.IDC_USE_TABS
self.GetDlgItem(idc).SetCheck(1)
tt_color = GetEditorOption("Tab Timmy Color", win32api.RGB(0xff, 0, 0))
self.cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
for c in paletteVGA:
self.cbo.AddString(c[0])
sel = 0
for c in paletteVGA:
if tt_color == win32api.RGB(c[1], c[2], c[3]):
break
sel = sel + 1
else:
sel = -1
self.cbo.SetCurSel(sel)
self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_NONE)
self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_IND)
self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_BG)
# Set ranges for the spinners.
for spinner_id in [win32ui.IDC_SPIN1, win32ui.IDC_SPIN2]:
spinner = self.GetDlgItem(spinner_id)
spinner.SetRange(1, 16)
return rc
def OnButSimple(self, id, code):
if code == win32con.BN_CLICKED:
self.UpdateUIForState()
def UpdateUIForState(self):
timmy = self.GetDlgItem(win32ui.IDC_TABTIMMY_NONE).GetCheck()
self.GetDlgItem(win32ui.IDC_COMBO1).EnableWindow(not timmy)
def OnOK(self):
for name, defVal in self.autooptions:
SetEditorOption(name, self[name])
SetEditorOption("Use Tabs", self.GetDlgItem(win32ui.IDC_USE_TABS).GetCheck())
SetEditorOption("Use Tab Timmy", self.GetDlgItem(win32ui.IDC_TABTIMMY_IND).GetCheck())
c = paletteVGA[self.cbo.GetCurSel()]
SetEditorOption("Tab Timmy Color", win32api.RGB(c[1], c[2], c[3]))
return 1
def testpp():
ps = dialog.PropertySheet("Editor Options")
ps.AddPage(EditorWhitespacePropertyPage())
ps.DoModal()
if __name__=='__main__':
testpp()

View file

@ -0,0 +1,332 @@
# We no longer support the old, non-colour editor!
from pywin.mfc import docview, object
from pywin.framework.editor import GetEditorOption
import win32ui
import os
import win32con
import string
import traceback
import win32api
import shutil
BAK_NONE=0
BAK_DOT_BAK=1
BAK_DOT_BAK_TEMP_DIR=2
BAK_DOT_BAK_BAK_DIR=3
MSG_CHECK_EXTERNAL_FILE = win32con.WM_USER+1999 ## WARNING: Duplicated in editor.py and coloreditor.py
import pywin.scintilla.document
ParentEditorDocument=pywin.scintilla.document.CScintillaDocument
class EditorDocumentBase(ParentEditorDocument):
def __init__(self, template):
self.bAutoReload = GetEditorOption("Auto Reload", 1)
self.bDeclinedReload = 0 # Has the user declined to reload.
self.fileStat = None
self.bReportedFileNotFound = 0
# what sort of bak file should I create.
# default to write to %temp%/bak/filename.ext
self.bakFileType=GetEditorOption("Backup Type", BAK_DOT_BAK_BAK_DIR)
self.watcherThread = FileWatchingThread(self)
self.watcherThread.CreateThread()
# Should I try and use VSS integration?
self.scModuleName=GetEditorOption("Source Control Module", "")
self.scModule = None # Loaded when first used.
ParentEditorDocument.__init__(self, template, template.CreateWin32uiDocument())
def OnCloseDocument(self ):
self.watcherThread.SignalStop()
return self._obj_.OnCloseDocument()
# def OnOpenDocument(self, name):
# rc = ParentEditorDocument.OnOpenDocument(self, name)
# self.GetFirstView()._SetLoadedText(self.text)
# self._DocumentStateChanged()
# return rc
def OnSaveDocument( self, fileName ):
win32ui.SetStatusText("Saving file...",1)
# rename to bak if required.
dir, basename = os.path.split(fileName)
if self.bakFileType==BAK_DOT_BAK:
bakFileName=dir+'\\'+os.path.splitext(basename)[0]+'.bak'
elif self.bakFileType==BAK_DOT_BAK_TEMP_DIR:
bakFileName=win32api.GetTempPath()+'\\'+os.path.splitext(basename)[0]+'.bak'
elif self.bakFileType==BAK_DOT_BAK_BAK_DIR:
tempPath=os.path.join(win32api.GetTempPath(),'bak')
try:
os.mkdir(tempPath,0)
except os.error:
pass
bakFileName=os.path.join(tempPath,basename)
try:
os.unlink(bakFileName) # raise NameError if no bakups wanted.
except (os.error, NameError):
pass
try:
# Do a copy as it might be on different volumes,
# and the file may be a hard-link, causing the link
# to follow the backup.
shutil.copy2(fileName, bakFileName)
except (os.error, NameError, IOError):
pass
try:
self.SaveFile(fileName)
except IOError as details:
win32ui.MessageBox("Error - could not save file\r\n\r\n%s"%details)
return 0
except (UnicodeEncodeError, LookupError) as details:
rc = win32ui.MessageBox("Encoding failed: \r\n%s"%details +
'\r\nPlease add desired source encoding as first line of file, eg \r\n' +
'# -*- coding: mbcs -*-\r\n\r\n' +
'If you continue, the file will be saved as binary and will\r\n' +
'not be valid in the declared encoding.\r\n\r\n' +
'Save the file as binary with an invalid encoding?',
"File save failed",
win32con.MB_YESNO | win32con.MB_DEFBUTTON2)
if rc==win32con.IDYES:
try:
self.SaveFile(fileName, encoding="latin-1")
except IOError as details:
win32ui.MessageBox("Error - could not save file\r\n\r\n%s"%details)
return 0
else:
return 0
self.SetModifiedFlag(0) # No longer dirty
self.bDeclinedReload = 0 # They probably want to know if it changes again!
win32ui.AddToRecentFileList(fileName)
self.SetPathName(fileName)
win32ui.SetStatusText("Ready")
self._DocumentStateChanged()
return 1
def FinalizeViewCreation(self, view):
ParentEditorDocument.FinalizeViewCreation(self, view)
if view == self.GetFirstView():
self._DocumentStateChanged()
if view.bFolding and GetEditorOption("Fold On Open", 0):
view.FoldTopLevelEvent()
def HookViewNotifications(self, view):
ParentEditorDocument.HookViewNotifications(self, view)
# Support for reloading the document from disk - presumably after some
# external application has modified it (or possibly source control has
# checked it out.
def ReloadDocument(self):
"""Reloads the document from disk. Assumes the file has
been saved and user has been asked if necessary - it just does it!
"""
win32ui.SetStatusText("Reloading document. Please wait...", 1)
self.SetModifiedFlag(0)
# Loop over all views, saving their state, then reload the document
views = self.GetAllViews()
states = []
for view in views:
try:
info = view._PrepareUserStateChange()
except AttributeError: # Not our editor view?
info = None
states.append(info)
self.OnOpenDocument(self.GetPathName())
for view, info in zip(views, states):
if info is not None:
view._EndUserStateChange(info)
self._DocumentStateChanged()
win32ui.SetStatusText("Document reloaded.")
# Reloading the file
def CheckExternalDocumentUpdated(self):
if self.bDeclinedReload or not self.GetPathName():
return
try:
newstat = os.stat(self.GetPathName())
except os.error as exc:
if not self.bReportedFileNotFound:
print("The file '%s' is open for editing, but\nchecking it for changes caused the error: %s" % (self.GetPathName(), exc.strerror))
self.bReportedFileNotFound = 1
return
if self.bReportedFileNotFound:
print("The file '%s' has re-appeared - continuing to watch for changes..." % (self.GetPathName(),))
self.bReportedFileNotFound = 0 # Once found again we want to start complaining.
changed = (self.fileStat is None) or \
self.fileStat[0] != newstat[0] or \
self.fileStat[6] != newstat[6] or \
self.fileStat[8] != newstat[8] or \
self.fileStat[9] != newstat[9]
if changed:
question = None
if self.IsModified():
question = "%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it and LOSE THE CHANGES in the source editor?" % self.GetPathName()
mbStyle = win32con.MB_YESNO | win32con.MB_DEFBUTTON2 # Default to "No"
else:
if not self.bAutoReload:
question = "%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it?" % self.GetPathName()
mbStyle = win32con.MB_YESNO # Default to "Yes"
if question:
rc = win32ui.MessageBox(question, None, mbStyle)
if rc!=win32con.IDYES:
self.bDeclinedReload = 1
return
self.ReloadDocument()
def _DocumentStateChanged(self):
"""Called whenever the documents state (on disk etc) has been changed
by the editor (eg, as the result of a save operation)
"""
if self.GetPathName():
try:
self.fileStat = os.stat(self.GetPathName())
except os.error:
self.fileStat = None
else:
self.fileStat = None
self.watcherThread._DocumentStateChanged()
self._UpdateUIForState()
self._ApplyOptionalToViews("_UpdateUIForState")
self._ApplyOptionalToViews("SetReadOnly", self._IsReadOnly())
self._ApplyOptionalToViews("SCISetSavePoint")
# Allow the debugger to reset us too.
import pywin.debugger
if pywin.debugger.currentDebugger is not None:
pywin.debugger.currentDebugger.UpdateDocumentLineStates(self)
# Read-only document support - make it obvious to the user
# that the file is read-only.
def _IsReadOnly(self):
return self.fileStat is not None and (self.fileStat[0] & 128)==0
def _UpdateUIForState(self):
"""Change the title to reflect the state of the document -
eg ReadOnly, Dirty, etc
"""
filename = self.GetPathName()
if not filename: return # New file - nothing to do
try:
# This seems necessary so the internal state of the window becomes
# "visible". without it, it is still shown, but certain functions
# (such as updating the title) dont immediately work?
self.GetFirstView().ShowWindow(win32con.SW_SHOW)
title = win32ui.GetFileTitle(filename)
except win32ui.error:
title = filename
if self._IsReadOnly():
title = title + " (read-only)"
self.SetTitle(title)
def MakeDocumentWritable(self):
pretend_ss = 0 # Set to 1 to test this without source safe :-)
if not self.scModuleName and not pretend_ss: # No Source Control support.
win32ui.SetStatusText("Document is read-only, and no source-control system is configured")
win32api.MessageBeep()
return 0
# We have source control support - check if the user wants to use it.
msg = "Would you like to check this file out?"
defButton = win32con.MB_YESNO
if self.IsModified():
msg = msg + "\r\n\r\nALL CHANGES IN THE EDITOR WILL BE LOST"
defButton = win32con.MB_YESNO
if win32ui.MessageBox(msg, None, defButton)!=win32con.IDYES:
return 0
if pretend_ss:
print("We are only pretending to check it out!")
win32api.SetFileAttributes(self.GetPathName(), win32con.FILE_ATTRIBUTE_NORMAL)
self.ReloadDocument()
return 1
# Now call on the module to do it.
if self.scModule is None:
try:
self.scModule = __import__(self.scModuleName)
for part in self.scModuleName.split('.')[1:]:
self.scModule = getattr(self.scModule, part)
except:
traceback.print_exc()
print("Error loading source control module.")
return 0
if self.scModule.CheckoutFile(self.GetPathName()):
self.ReloadDocument()
return 1
return 0
def CheckMakeDocumentWritable(self):
if self._IsReadOnly():
return self.MakeDocumentWritable()
return 1
def SaveModified(self):
# Called as the document is closed. If we are about
# to prompt for a save, bring the document to the foreground.
if self.IsModified():
frame = self.GetFirstView().GetParentFrame()
try:
frame.MDIActivate()
frame.AutoRestore()
except:
print("Could not bring document to foreground")
return self._obj_.SaveModified()
# NOTE - I DONT use the standard threading module,
# as this waits for all threads to terminate at shutdown.
# When using the debugger, it is possible shutdown will
# occur without Pythonwin getting a complete shutdown,
# so we deadlock at the end - threading is waiting for
import pywin.mfc.thread
import win32event
class FileWatchingThread(pywin.mfc.thread.WinThread):
def __init__(self, doc):
self.doc = doc
self.adminEvent = win32event.CreateEvent(None, 0, 0, None)
self.stopEvent = win32event.CreateEvent(None, 0, 0, None)
self.watchEvent = None
pywin.mfc.thread.WinThread.__init__(self)
def _DocumentStateChanged(self):
win32event.SetEvent(self.adminEvent)
def RefreshEvent(self):
self.hwnd = self.doc.GetFirstView().GetSafeHwnd()
if self.watchEvent is not None:
win32api.FindCloseChangeNotification(self.watchEvent)
self.watchEvent = None
path = self.doc.GetPathName()
if path: path = os.path.dirname(path)
if path:
filter = win32con.FILE_NOTIFY_CHANGE_FILE_NAME | \
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES | \
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE
try:
self.watchEvent = win32api.FindFirstChangeNotification(path, 0, filter)
except win32api.error as exc:
print("Can not watch file", path, "for changes -", exc.strerror)
def SignalStop(self):
win32event.SetEvent(self.stopEvent)
def Run(self):
while 1:
handles = [self.stopEvent, self.adminEvent]
if self.watchEvent is not None:
handles.append(self.watchEvent)
rc = win32event.WaitForMultipleObjects(handles, 0, win32event.INFINITE)
if rc == win32event.WAIT_OBJECT_0:
break
elif rc == win32event.WAIT_OBJECT_0+1:
self.RefreshEvent()
else:
win32api.PostMessage(self.hwnd, MSG_CHECK_EXTERNAL_FILE, 0, 0)
try:
# If the directory has been removed underneath us, we get this error.
win32api.FindNextChangeNotification(self.watchEvent)
except win32api.error as exc:
print("Can not watch file", self.doc.GetPathName(), "for changes -", exc.strerror)
break
# close a circular reference
self.doc = None
if self.watchEvent:
win32api.FindCloseChangeNotification(self.watchEvent)

View file

@ -0,0 +1,465 @@
#####################################################################
#
# editor.py
#
# A general purpose text editor, built on top of the win32ui edit
# type, which is built on an MFC CEditView
#
#
# We now support reloading of externally modified documented
# (eg, presumably by some other process, such as source control or
# another editor.
# We also suport auto-loading of externally modified files.
# - if the current document has not been modified in this
# editor, but has been modified on disk, then the file
# can be automatically reloaded.
#
# Note that it will _always_ prompt you if the file in the editor has been modified.
import win32ui
import win32api
import win32con
import regex
import re
import string
import sys, os
import traceback
from pywin.mfc import docview, dialog, afxres
from pywin.framework.editor import GetEditorOption, SetEditorOption, GetEditorFontOption, SetEditorFontOption, defaultCharacterFormat
patImport=regex.symcomp('import \(<name>.*\)')
patIndent=regex.compile('^\\([ \t]*[~ \t]\\)')
ID_LOCATE_FILE = 0xe200
ID_GOTO_LINE = 0xe2001
MSG_CHECK_EXTERNAL_FILE = win32con.WM_USER+1999 ## WARNING: Duplicated in document.py and coloreditor.py
# Key Codes that modify the bufffer when Ctrl or Alt are NOT pressed.
MODIFYING_VK_KEYS = [win32con.VK_BACK, win32con.VK_TAB, win32con.VK_RETURN, win32con.VK_SPACE, win32con.VK_DELETE]
for k in range(48, 91):
MODIFYING_VK_KEYS.append(k)
# Key Codes that modify the bufffer when Ctrl is pressed.
MODIFYING_VK_KEYS_CTRL = [win32con.VK_BACK, win32con.VK_RETURN, win32con.VK_SPACE, win32con.VK_DELETE]
# Key Codes that modify the bufffer when Alt is pressed.
MODIFYING_VK_KEYS_ALT = [win32con.VK_BACK, win32con.VK_RETURN, win32con.VK_SPACE, win32con.VK_DELETE]
# The editor itself starts here.
# Using the MFC Document/View model, we have an EditorDocument, which is responsible for
# managing the contents of the file, and a view which is responsible for rendering it.
#
# Due to a limitation in the Windows edit controls, we are limited to one view
# per document, although nothing in this code assumes this (I hope!)
isRichText=1 # We are using the Rich Text control. This has not been tested with value "0" for quite some time!
#ParentEditorDocument=docview.Document
from .document import EditorDocumentBase
ParentEditorDocument=EditorDocumentBase
class EditorDocument(ParentEditorDocument):
#
# File loading and saving operations
#
def OnOpenDocument(self, filename):
#
# handle Unix and PC text file format.
#
# Get the "long name" of the file name, as it may have been translated
# to short names by the shell.
self.SetPathName(filename) # Must set this early!
# Now do the work!
self.BeginWaitCursor()
win32ui.SetStatusText("Loading file...",1)
try:
f = open(filename,"rb")
except IOError:
win32ui.MessageBox(filename + '\nCan not find this file\nPlease verify that the correct path and file name are given')
self.EndWaitCursor()
return 0
raw=f.read()
f.close()
contents = self.TranslateLoadedData(raw)
rc = 0
if win32ui.IsWin32s() and len(contents)>62000: # give or take a few bytes
win32ui.MessageBox("This file is too big for Python on Windows 3.1\r\nPlease use another editor to view this file.")
else:
try:
self.GetFirstView().SetWindowText(contents)
rc = 1
except TypeError: # Null byte in file.
win32ui.MessageBox("This file contains NULL bytes, and can not be edited")
rc = 0
self.EndWaitCursor()
self.SetModifiedFlag(0) # No longer dirty
self._DocumentStateChanged()
return rc
def TranslateLoadedData(self, data):
"""Given raw data read from a file, massage it suitable for the edit window"""
# if a CR in the first 250 chars, then perform the expensive translate
if data[:250].find('\r')==-1:
win32ui.SetStatusText("Translating from Unix file format - please wait...",1)
return re.sub('\r*\n','\r\n',data)
else:
return data
def SaveFile(self, fileName, encoding=None):
if isRichText:
view = self.GetFirstView()
view.SaveTextFile(fileName, encoding=encoding)
else: # Old style edit view window.
self.GetFirstView().SaveFile(fileName)
try:
# Make sure line cache has updated info about me!
import linecache
linecache.checkcache()
except:
pass
#
# Color state stuff
#
def SetAllLineColors(self, color = None):
for view in self.GetAllViews():
view.SetAllLineColors(color)
def SetLineColor(self, lineNo, color):
"Color a line of all views"
for view in self.GetAllViews():
view.SetLineColor(lineNo, color)
# def StreamTextOut(self, data): ### This seems unreliable???
# self.saveFileHandle.write(data)
# return 1 # keep em coming!
#ParentEditorView=docview.EditView
ParentEditorView=docview.RichEditView
class EditorView(ParentEditorView):
def __init__(self, doc):
ParentEditorView.__init__(self, doc)
if isRichText:
self.SetWordWrap(win32ui.CRichEditView_WrapNone)
self.addToMRU = 1
self.HookHandlers()
self.bCheckingFile = 0
self.defCharFormat = GetEditorFontOption("Default Font", defaultCharacterFormat)
# Smart tabs override everything else if context can be worked out.
self.bSmartTabs = GetEditorOption("Smart Tabs", 1)
self.tabSize = GetEditorOption("Tab Size", 8)
self.indentSize = GetEditorOption("Indent Size", 8)
# If next indent is at a tab position, and useTabs is set, a tab will be inserted.
self.bUseTabs = GetEditorOption("Use Tabs", 1)
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
self.SetDefaultCharFormat(self.defCharFormat)
return rc
def CutCurLine(self):
curLine = self._obj_.LineFromChar()
nextLine = curLine+1
start = self._obj_.LineIndex(curLine)
end = self._obj_.LineIndex(nextLine)
if end==0: # must be last line.
end = start + self.end.GetLineLength(curLine)
self._obj_.SetSel(start,end)
self._obj_.Cut()
def _PrepareUserStateChange(self):
"Return selection, lineindex, etc info, so it can be restored"
self.SetRedraw(0)
return self.GetModify(), self.GetSel(), self.GetFirstVisibleLine()
def _EndUserStateChange(self, info):
scrollOff = info[2] - self.GetFirstVisibleLine()
if scrollOff:
self.LineScroll(scrollOff)
self.SetSel(info[1])
self.SetModify(info[0])
self.SetRedraw(1)
self.InvalidateRect()
self.UpdateWindow()
def _UpdateUIForState(self):
self.SetReadOnly(self.GetDocument()._IsReadOnly())
def SetAllLineColors(self, color = None):
if isRichText:
info = self._PrepareUserStateChange()
try:
if color is None: color = self.defCharFormat[4]
self.SetSel(0,-1)
self.SetSelectionCharFormat((win32con.CFM_COLOR, 0,0,0,color))
finally:
self._EndUserStateChange(info)
def SetLineColor(self, lineNo, color):
"lineNo is the 1 based line number to set. If color is None, default color is used."
if isRichText:
info = self._PrepareUserStateChange()
try:
if color is None: color = self.defCharFormat[4]
lineNo = lineNo-1
startIndex = self.LineIndex(lineNo)
if startIndex!=-1:
self.SetSel(startIndex, self.LineIndex(lineNo+1))
self.SetSelectionCharFormat((win32con.CFM_COLOR, 0,0,0,color))
finally:
self._EndUserStateChange(info)
def Indent(self):
"""Insert an indent to move the cursor to the next tab position.
Honors the tab size and 'use tabs' settings. Assumes the cursor is already at the
position to be indented, and the selection is a single character (ie, not a block)
"""
start, end = self._obj_.GetSel()
startLine = self._obj_.LineFromChar(start)
line = self._obj_.GetLine(startLine)
realCol = start - self._obj_.LineIndex(startLine)
# Calulate the next tab stop.
# Expand existing tabs.
curCol = 0
for ch in line[:realCol]:
if ch=='\t':
curCol = ((curCol / self.tabSize) + 1) * self.tabSize
else:
curCol = curCol + 1
nextColumn = ((curCol / self.indentSize) + 1) * self.indentSize
# print "curCol is", curCol, "nextColumn is", nextColumn
ins = None
if self.bSmartTabs:
# Look for some context.
if realCol==0: # Start of the line - see if the line above can tell us
lookLine = startLine-1
while lookLine >= 0:
check = self._obj_.GetLine(lookLine)[0:1]
if check in ['\t', ' ']:
ins = check
break
lookLine = lookLine - 1
else: # See if the previous char can tell us
check = line[realCol-1]
if check in ['\t', ' ']:
ins = check
# Either smart tabs off, or not smart enough!
# Use the "old style" settings.
if ins is None:
if self.bUseTabs and nextColumn % self.tabSize==0:
ins = '\t'
else:
ins = ' '
if ins == ' ':
# Calc the number of spaces to take us to the next stop
ins = ins * (nextColumn - curCol)
self._obj_.ReplaceSel(ins)
def BlockDent(self, isIndent, startLine, endLine):
" Indent/Undent all lines specified "
if not self.GetDocument().CheckMakeDocumentWritable(): return 0
tabSize=self.tabSize # hard-code for now!
info = self._PrepareUserStateChange()
try:
for lineNo in range(startLine, endLine):
pos=self._obj_.LineIndex(lineNo)
self._obj_.SetSel(pos, pos)
if isIndent:
self.Indent()
else:
line = self._obj_.GetLine(lineNo)
try:
noToDel = 0
if line[0]=='\t':
noToDel = 1
elif line[0]==' ':
for noToDel in range(0,tabSize):
if line[noToDel]!=' ':
break
else:
noToDel=tabSize
if noToDel:
self._obj_.SetSel(pos, pos+noToDel)
self._obj_.Clear()
except IndexError:
pass
finally:
self._EndUserStateChange(info)
self.GetDocument().SetModifiedFlag(1) # Now dirty
self._obj_.SetSel(self.LineIndex(startLine), self.LineIndex(endLine))
def GotoLine(self, lineNo = None):
try:
if lineNo is None:
lineNo = int(input("Enter Line Number"))
except (ValueError, KeyboardInterrupt):
return 0
self.GetLineCount() # Seems to be needed when file first opened???
charNo = self.LineIndex(lineNo-1)
self.SetSel(charNo)
def HookHandlers(self): # children can override, but should still call me!
# self.HookAllKeyStrokes(self.OnKey)
self.HookMessage(self.OnCheckExternalDocumentUpdated,MSG_CHECK_EXTERNAL_FILE)
self.HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
self.HookKeyStroke(self.OnKeyCtrlY, 25) # ^Y
self.HookKeyStroke(self.OnKeyCtrlG, 7) # ^G
self.HookKeyStroke(self.OnKeyTab, 9) # TAB
self.HookKeyStroke(self.OnKeyEnter, 13) # Enter
self.HookCommand(self.OnCmdLocateFile, ID_LOCATE_FILE)
self.HookCommand(self.OnCmdGotoLine, ID_GOTO_LINE)
self.HookCommand(self.OnEditPaste, afxres.ID_EDIT_PASTE)
self.HookCommand(self.OnEditCut, afxres.ID_EDIT_CUT)
# Hook Handlers
def OnSetFocus(self,msg):
# Even though we use file change notifications, we should be very sure about it here.
self.OnCheckExternalDocumentUpdated(msg)
def OnRClick(self,params):
menu = win32ui.CreatePopupMenu()
# look for a module name
line=self._obj_.GetLine().strip()
flags=win32con.MF_STRING|win32con.MF_ENABLED
if patImport.match(line)==len(line):
menu.AppendMenu(flags, ID_LOCATE_FILE, "&Locate %s.py"%patImport.group('name'))
menu.AppendMenu(win32con.MF_SEPARATOR);
menu.AppendMenu(flags, win32ui.ID_EDIT_UNDO, '&Undo')
menu.AppendMenu(win32con.MF_SEPARATOR);
menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, 'Cu&t')
menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, '&Copy')
menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, '&Paste')
menu.AppendMenu(flags, win32con.MF_SEPARATOR);
menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all')
menu.AppendMenu(flags, win32con.MF_SEPARATOR);
menu.AppendMenu(flags, ID_GOTO_LINE, '&Goto line...')
menu.TrackPopupMenu(params[5])
return 0
def OnCmdGotoLine(self, cmd, code):
self.GotoLine()
return 0
def OnCmdLocateFile(self, cmd, code):
modName = patImport.group('name')
if not modName:
return 0
import pywin.framework.scriptutils
fileName = pywin.framework.scriptutils.LocatePythonFile(modName)
if fileName is None:
win32ui.SetStatusText("Can't locate module %s" % modName)
else:
win32ui.GetApp().OpenDocumentFile(fileName)
return 0
# Key handlers
def OnKeyEnter(self, key):
if not self.GetDocument().CheckMakeDocumentWritable(): return 0
curLine = self._obj_.GetLine()
self._obj_.ReplaceSel('\r\n') # insert the newline
# If the current line indicates the next should be indented,
# then copy the current indentation to this line.
res = patIndent.match(curLine,0)
if res>0 and curLine.strip():
curIndent = patIndent.group(1)
self._obj_.ReplaceSel(curIndent)
return 0 # dont pass on
def OnKeyCtrlY(self, key):
if not self.GetDocument().CheckMakeDocumentWritable(): return 0
self.CutCurLine()
return 0 # dont let him have it!
def OnKeyCtrlG(self, key):
self.GotoLine()
return 0 # dont let him have it!
def OnKeyTab(self, key):
if not self.GetDocument().CheckMakeDocumentWritable(): return 0
start, end = self._obj_.GetSel()
if start==end: # normal TAB key
self.Indent()
return 0 # we handled this.
# Otherwise it is a block indent/dedent.
if start>end:
start, end = end, start # swap them.
startLine = self._obj_.LineFromChar(start)
endLine = self._obj_.LineFromChar(end)
self.BlockDent(win32api.GetKeyState(win32con.VK_SHIFT)>=0, startLine, endLine)
return 0
def OnEditPaste(self, id, code):
# Return 1 if we can make the file editable.(or it already is!)
return self.GetDocument().CheckMakeDocumentWritable()
def OnEditCut(self, id, code):
# Return 1 if we can make the file editable.(or it already is!)
return self.GetDocument().CheckMakeDocumentWritable()
def OnKeyDown(self, msg):
key = msg[2]
if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000:
modList = MODIFYING_VK_KEYS_CTRL
elif win32api.GetKeyState(win32con.VK_MENU) & 0x8000:
modList = MODIFYING_VK_KEYS_ALT
else:
modList = MODIFYING_VK_KEYS
if key in modList:
# Return 1 if we can make the file editable.(or it already is!)
return self.GetDocument().CheckMakeDocumentWritable()
return 1 # Pass it on OK
# def OnKey(self, key):
# return self.GetDocument().CheckMakeDocumentWritable()
def OnCheckExternalDocumentUpdated(self, msg):
if self._obj_ is None or self.bCheckingFile: return
self.bCheckingFile = 1
self.GetDocument().CheckExternalDocumentUpdated()
self.bCheckingFile = 0
from .template import EditorTemplateBase
class EditorTemplate(EditorTemplateBase):
def __init__(self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None):
if makeDoc is None: makeDoc = EditorDocument
if makeView is None: makeView = EditorView
EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView)
def _CreateDocTemplate(self, resourceId):
return win32ui.CreateRichEditDocTemplate(resourceId)
def CreateWin32uiDocument(self):
return self.DoCreateRichEditDoc()
def Create(fileName = None, title=None, template = None):
return editorTemplate.OpenDocumentFile(fileName)
from pywin.framework.editor import GetDefaultEditorModuleName
prefModule = GetDefaultEditorModuleName()
# Initialize only if this is the "default" editor.
if __name__==prefModule:
# For debugging purposes, when this module may be reloaded many times.
try:
win32ui.GetApp().RemoveDocTemplate(editorTemplate)
except (NameError, win32ui.error):
pass
editorTemplate = EditorTemplate()
win32ui.GetApp().AddDocTemplate(editorTemplate)

View file

@ -0,0 +1,76 @@
# frame.py - The MDI frame window for an editor.
import pywin.framework.window
import win32ui
import win32con
import afxres
from . import ModuleBrowser
class EditorFrame(pywin.framework.window.MDIChildWnd):
def OnCreateClient(self, cp, context):
# Create the default view as specified by the template (ie, the editor view)
view = context.template.MakeView(context.doc)
# Create the browser view.
browserView = ModuleBrowser.BrowserView(context.doc)
view2 = context.template.MakeView(context.doc)
splitter = win32ui.CreateSplitter()
style = win32con.WS_CHILD | win32con.WS_VISIBLE
splitter.CreateStatic (self, 1, 2, style, win32ui.AFX_IDW_PANE_FIRST)
sub_splitter = self.sub_splitter = win32ui.CreateSplitter()
sub_splitter.CreateStatic (splitter, 2, 1, style, win32ui.AFX_IDW_PANE_FIRST+1)
# Note we must add the default view first, so that doc.GetFirstView() returns the editor view.
sub_splitter.CreateView(view, 1, 0, (0,0))
splitter.CreateView (browserView, 0, 0, (0,0))
sub_splitter.CreateView(view2,0, 0, (0,0))
## print "First view is", context.doc.GetFirstView()
## print "Views are", view, view2, browserView
## print "Parents are", view.GetParent(), view2.GetParent(), browserView.GetParent()
## print "Splitter is", splitter
## print "sub splitter is", sub_splitter
## Old
## splitter.CreateStatic (self, 1, 2)
## splitter.CreateView(view, 0, 1, (0,0)) # size ignored.
## splitter.CreateView (browserView, 0, 0, (0, 0))
# Restrict the size of the browser splitter (and we can avoid filling
# it until it is shown)
splitter.SetColumnInfo(0, 10, 20)
# And the active view is our default view (so it gets initial focus)
self.SetActiveView(view)
def GetEditorView(self):
# In a multi-view (eg, splitter) environment, get
# an editor (ie, scintilla) view
# Look for the splitter opened the most!
if self.sub_splitter is None:
return self.GetDlgItem(win32ui.AFX_IDW_PANE_FIRST)
v1 = self.sub_splitter.GetPane(0,0)
v2 = self.sub_splitter.GetPane(1,0)
r1 = v1.GetWindowRect()
r2 = v2.GetWindowRect()
if r1[3]-r1[1] > r2[3]-r2[1]:
return v1
return v2
def GetBrowserView(self):
# XXX - should fix this :-)
return self.GetActiveDocument().GetAllViews()[1]
def OnClose(self):
doc=self.GetActiveDocument()
if not doc.SaveModified():
## Cancel button selected from Save dialog, do not actually close
## print 'close cancelled'
return 0
## So the 'Save' dialog doesn't come up twice
doc._obj_.SetModifiedFlag(False)
# Must force the module browser to close itself here (OnDestroy for the view itself is too late!)
self.sub_splitter = None # ensure no circles!
self.GetBrowserView().DestroyBrowser()
return self._obj_.OnClose()

View file

@ -0,0 +1,50 @@
import string
import win32ui
import win32api
from pywin.mfc import docview
import pywin.framework.window
import os
from . import frame
ParentEditorTemplate=docview.DocTemplate
class EditorTemplateBase(ParentEditorTemplate):
def __init__(self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None):
if makeFrame is None: makeFrame = frame.EditorFrame
ParentEditorTemplate.__init__(self, res, makeDoc, makeFrame, makeView)
def _CreateDocTemplate(self, resourceId):
assert 0, "You must override this"
def CreateWin32uiDocument(self):
assert 0, "You must override this"
def GetFileExtensions(self):
return ".txt", ".py"
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc: return doc
ext = os.path.splitext(fileName)[1].lower()
if ext in self.GetFileExtensions():
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_maybeAttemptForeign
def InitialUpdateFrame(self, frame, doc, makeVisible=1):
self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler.
doc._UpdateUIForState()
def GetPythonPropertyPages(self):
"""Returns a list of property pages
"""
from . import configui
return [configui.EditorPropertyPage(), configui.EditorWhitespacePropertyPage()]
def OpenDocumentFile(self, filename, bMakeVisible = 1):
if filename is not None:
try:
path = os.path.split(filename)[0]
# print "The editor is translating", `filename`,"to",
filename = win32api.FindFiles(filename)[0][8]
filename = os.path.join(path, filename)
# print `filename`
except (win32api.error, IndexError) as details:
pass
# print "Couldnt get the full filename!", details
return self._obj_.OpenDocumentFile(filename, bMakeVisible)

View file

@ -0,0 +1,93 @@
# vss.py -- Source Control using Microsoft VSS.
# Provides routines for checking files out of VSS.
#
# Uses an INI file very similar to how VB integrates with VSS - even
# as far as using the same name.
# The file must be named "Mssccprj.scc", and be in the format of
# an INI file. This file may be in a parent directory, in which
# case the project name will be built from what is specified in the
# ini file, plus the path from the INI file to the file itself.
#
# The INI file should have a [Python] section, and a
# Project=Project Name
# and optionally
# Database=??
import win32ui, win32api, win32con, os, string, sys
import traceback
g_iniName = "Mssccprj.scc" # Use the same INI name as VB!
g_sourceSafe = None
def FindVssProjectInfo(fullfname):
"""Looks up the file system for an INI file describing the project.
Looking up the tree is for ni style packages.
Returns (projectName, pathToFileName) where pathToFileName contains
the path from the ini file to the actual file.
"""
path, fnameonly = os.path.split(fullfname)
origPath = path
project = ""
retPaths = [fnameonly]
while not project:
iniName = os.path.join(path, g_iniName)
database = win32api.GetProfileVal("Python","Database", "", iniName)
project = win32api.GetProfileVal("Python","Project", "", iniName)
if project:
break;
# No valid INI file in this directory - look up a level.
path, addpath = os.path.split(path)
if not addpath: # Root?
break
retPaths.insert(0, addpath)
if not project:
win32ui.MessageBox("%s\r\n\r\nThis directory is not configured for Python/VSS" % origPath)
return
return project, "/".join(retPaths), database
def CheckoutFile(fileName):
global g_sourceSafe
import pythoncom
ok = 0
# Assumes the fileName has a complete path,
# and that the INI file can be found in that path
# (or a parent path if a ni style package)
try:
import win32com.client, win32com.client.gencache
mod = win32com.client.gencache.EnsureModule('{783CD4E0-9D54-11CF-B8EE-00608CC9A71F}', 0, 5, 0)
if mod is None:
win32ui.MessageBox("VSS does not appear to be installed. The TypeInfo can not be created")
return ok
rc = FindVssProjectInfo(fileName)
if rc is None:
return
project, vssFname, database = rc
if g_sourceSafe is None:
g_sourceSafe=win32com.client.Dispatch("SourceSafe")
# SS seems a bit wierd. It defaults the arguments as empty strings, but
# then complains when they are used - so we pass "Missing"
if not database:
database = pythoncom.Missing
g_sourceSafe.Open(database, pythoncom.Missing, pythoncom.Missing)
item = g_sourceSafe.VSSItem("$/%s/%s" % (project, vssFname))
item.Checkout(None, fileName)
ok = 1
except pythoncom.com_error as exc:
win32ui.MessageBox(exc.strerror, "Error checking out file")
except:
typ, val, tb = sys.exc_info()
traceback.print_exc()
win32ui.MessageBox("%s - %s" % (str(typ), str(val)),"Error checking out file")
tb = None # Cleanup a cycle
return ok

View file

@ -0,0 +1,151 @@
# help.py - help utilities for PythonWin.
import win32api
import win32con
import win32ui
import string
import sys
import regutil
import string, os
htmlhelp_handle = None
html_help_command_translators = {
win32con.HELP_CONTENTS : 1, # HH_DISPLAY_TOC
win32con.HELP_CONTEXT : 15, # HH_HELP_CONTEXT
win32con.HELP_FINDER : 1, # HH_DISPLAY_TOC
}
def FinalizeHelp():
global htmlhelp_handle
if htmlhelp_handle is not None:
import win32help
try:
#frame = win32ui.GetMainFrame().GetSafeHwnd()
frame = 0
win32help.HtmlHelp(frame, None, win32help.HH_UNINITIALIZE, htmlhelp_handle)
except win32help.error:
print("Failed to finalize htmlhelp!")
htmlhelp_handle = None
def OpenHelpFile(fileName, helpCmd = None, helpArg = None):
"Open a help file, given a full path"
# default help arg.
win32ui.DoWaitCursor(1)
try:
if helpCmd is None: helpCmd = win32con.HELP_CONTENTS
ext = os.path.splitext(fileName)[1].lower()
if ext == ".hlp":
win32api.WinHelp( win32ui.GetMainFrame().GetSafeHwnd(), fileName, helpCmd, helpArg)
# XXX - using the htmlhelp API wreaks havoc with keyboard shortcuts
# so we disable it, forcing ShellExecute, which works fine (but
# doesn't close the help file when Pythonwin is closed.
# Tom Heller also points out http://www.microsoft.com/mind/0499/faq/faq0499.asp,
# which may or may not be related.
elif 0 and ext == ".chm":
import win32help
global htmlhelp_handle
helpCmd = html_help_command_translators.get(helpCmd, helpCmd)
#frame = win32ui.GetMainFrame().GetSafeHwnd()
frame = 0 # Dont want it overlapping ours!
if htmlhelp_handle is None:
htmlhelp_hwnd, htmlhelp_handle = win32help.HtmlHelp(frame, None, win32help.HH_INITIALIZE)
win32help.HtmlHelp(frame, fileName, helpCmd, helpArg)
else:
# Hope that the extension is registered, and we know what to do!
win32api.ShellExecute(0, "open", fileName, None, "", win32con.SW_SHOW)
return fileName
finally:
win32ui.DoWaitCursor(-1)
def ListAllHelpFiles():
ret = []
ret = _ListAllHelpFilesInRoot(win32con.HKEY_LOCAL_MACHINE)
# Ensure we don't get dups.
for item in _ListAllHelpFilesInRoot(win32con.HKEY_CURRENT_USER):
if item not in ret:
ret.append(item)
return ret
def _ListAllHelpFilesInRoot(root):
"""Returns a list of (helpDesc, helpFname) for all registered help files
"""
import regutil
retList = []
try:
key = win32api.RegOpenKey(root, regutil.BuildDefaultPythonKey() + "\\Help", 0, win32con.KEY_READ)
except win32api.error as exc:
import winerror
if exc.winerror!=winerror.ERROR_FILE_NOT_FOUND:
raise
return retList
try:
keyNo = 0
while 1:
try:
helpDesc = win32api.RegEnumKey(key, keyNo)
helpFile = win32api.RegQueryValue(key, helpDesc)
retList.append((helpDesc, helpFile))
keyNo = keyNo + 1
except win32api.error as exc:
import winerror
if exc.winerror!=winerror.ERROR_NO_MORE_ITEMS:
raise
break
finally:
win32api.RegCloseKey(key)
return retList
def SelectAndRunHelpFile():
from pywin.dialogs import list
helpFiles = ListAllHelpFiles()
if len(helpFiles)==1:
# only 1 help file registered - probably ours - no point asking
index = 0
else:
index = list.SelectFromLists("Select Help file", helpFiles, ["Title"])
if index is not None:
OpenHelpFile(helpFiles[index][1])
helpIDMap = None
def SetHelpMenuOtherHelp(mainMenu):
"""Modifies the main Help Menu to handle all registered help files.
mainMenu -- The main menu to modify - usually from docTemplate.GetSharedMenu()
"""
# Load all help files from the registry.
global helpIDMap
if helpIDMap is None:
helpIDMap = {}
cmdID = win32ui.ID_HELP_OTHER
excludeList = ['Main Python Documentation', 'Pythonwin Reference']
firstList = ListAllHelpFiles()
# We actually want to not only exclude these entries, but
# their help file names (as many entries may share the same name)
excludeFnames = []
for desc, fname in firstList:
if desc in excludeList:
excludeFnames.append(fname)
helpDescs = []
for desc, fname in firstList:
if fname not in excludeFnames:
helpIDMap[cmdID] = (desc, fname)
win32ui.GetMainFrame().HookCommand(HandleHelpOtherCommand, cmdID)
cmdID = cmdID + 1
helpMenu = mainMenu.GetSubMenu(mainMenu.GetMenuItemCount()-1) # Help menu always last.
otherHelpMenuPos = 2 # cant search for ID, as sub-menu has no ID.
otherMenu = helpMenu.GetSubMenu(otherHelpMenuPos)
while otherMenu.GetMenuItemCount():
otherMenu.DeleteMenu(0, win32con.MF_BYPOSITION)
if helpIDMap:
for id, (desc, fname) in helpIDMap.items():
otherMenu.AppendMenu(win32con.MF_ENABLED|win32con.MF_STRING,id, desc)
else:
helpMenu.EnableMenuItem(otherHelpMenuPos, win32con.MF_BYPOSITION | win32con.MF_GRAYED)
def HandleHelpOtherCommand(cmd, code):
OpenHelpFile(helpIDMap[cmd][1])

View file

@ -0,0 +1,870 @@
##################################################################
##
## 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:
execfile(rcfile, __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()

View file

@ -0,0 +1,447 @@
# intpyapp.py - Interactive Python application class
#
import win32con
import win32api
import win32ui
import __main__
import sys
import string
from . import app
import traceback
from pywin.mfc import window, afxres, dialog
import commctrl
from . import dbgcommands
lastLocateFileName = ".py" # used in the "File/Locate" dialog...
# todo - _SetupSharedMenu should be moved to a framework class.
def _SetupSharedMenu_(self):
sharedMenu = self.GetSharedMenu()
from pywin.framework import toolmenu
toolmenu.SetToolsMenu(sharedMenu)
from pywin.framework import help
help.SetHelpMenuOtherHelp(sharedMenu)
from pywin.mfc import docview
docview.DocTemplate._SetupSharedMenu_=_SetupSharedMenu_
class MainFrame(app.MainFrame):
def OnCreate(self, createStruct):
self.closing = 0
if app.MainFrame.OnCreate(self, createStruct)==-1:
return -1
style = win32con.WS_CHILD | afxres.CBRS_SIZE_DYNAMIC | afxres.CBRS_TOP | afxres.CBRS_TOOLTIPS | afxres.CBRS_FLYBY
self.EnableDocking(afxres.CBRS_ALIGN_ANY)
tb = win32ui.CreateToolBar (self, style | win32con.WS_VISIBLE)
tb.ModifyStyle(0, commctrl.TBSTYLE_FLAT)
tb.LoadToolBar(win32ui.IDR_MAINFRAME)
tb.EnableDocking(afxres.CBRS_ALIGN_ANY)
tb.SetWindowText("Standard")
self.DockControlBar(tb)
# Any other packages which use toolbars
from pywin.debugger.debugger import PrepareControlBars
PrepareControlBars(self)
# Note "interact" also uses dockable windows, but they already happen
# And a "Tools" menu on the main frame.
menu = self.GetMenu()
from . import toolmenu
toolmenu.SetToolsMenu(menu, 2)
# And fix the "Help" menu on the main frame
from pywin.framework import help
help.SetHelpMenuOtherHelp(menu)
def OnClose(self):
try:
import pywin.debugger
if pywin.debugger.currentDebugger is not None and pywin.debugger.currentDebugger.pumping:
try:
pywin.debugger.currentDebugger.close(1)
except:
traceback.print_exc()
return
except win32ui.error:
pass
self.closing = 1
self.SaveBarState("ToolbarDefault")
self.SetActiveView(None) # Otherwise MFC's OnClose may _not_ prompt for save.
from pywin.framework import help
help.FinalizeHelp()
self.DestroyControlBar(afxres.AFX_IDW_TOOLBAR)
self.DestroyControlBar(win32ui.ID_VIEW_TOOLBAR_DBG)
return self._obj_.OnClose()
def DestroyControlBar(self, id):
try:
bar = self.GetControlBar(id)
except win32ui.error:
return
bar.DestroyWindow()
def OnCommand(self, wparam, lparam):
# By default, the current MDI child frame will process WM_COMMAND
# messages before any docked control bars - even if the control bar
# has focus. This is a problem for the interactive window when docked.
# Therefore, we detect the situation of a view having the main frame
# as its parent, and assume it must be a docked view (which it will in an MDI app)
try:
v = self.GetActiveView() # Raise an exception if none - good - then we want default handling
# Main frame _does_ have a current view (ie, a docking view) - see if it wants it.
if v.OnCommand(wparam, lparam):
return 1
except (win32ui.error, AttributeError):
pass
return self._obj_.OnCommand(wparam, lparam)
class InteractivePythonApp(app.CApp):
# This works if necessary - just we dont need to override the Run method.
# def Run(self):
# return self._obj_.Run()
def HookCommands(self):
app.CApp.HookCommands(self)
dbgcommands.DebuggerCommandHandler().HookCommands()
self.HookCommand(self.OnViewBrowse,win32ui.ID_VIEW_BROWSE)
self.HookCommand(self.OnFileImport,win32ui.ID_FILE_IMPORT)
self.HookCommand(self.OnFileCheck,win32ui.ID_FILE_CHECK)
self.HookCommandUpdate(self.OnUpdateFileCheck, win32ui.ID_FILE_CHECK)
self.HookCommand(self.OnFileRun,win32ui.ID_FILE_RUN)
self.HookCommand(self.OnFileLocate,win32ui.ID_FILE_LOCATE)
self.HookCommand(self.OnInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE)
self.HookCommandUpdate(self.OnUpdateInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE)
self.HookCommand(self.OnViewOptions, win32ui.ID_VIEW_OPTIONS)
self.HookCommand(self.OnHelpIndex, afxres.ID_HELP_INDEX)
self.HookCommand(self.OnFileSaveAll, win32ui.ID_FILE_SAVE_ALL)
self.HookCommand(self.OnViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)
self.HookCommandUpdate(self.OnUpdateViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)
def CreateMainFrame(self):
return MainFrame()
def MakeExistingDDEConnection(self):
# Use DDE to connect to an existing instance
# Return None if no existing instance
try:
from . import intpydde
except ImportError:
# No dde support!
return None
conv = intpydde.CreateConversation(self.ddeServer)
try:
conv.ConnectTo("Pythonwin", "System")
return conv
except intpydde.error:
return None
def InitDDE(self):
# Do all the magic DDE handling.
# Returns TRUE if we have pumped the arguments to our
# remote DDE app, and we should terminate.
try:
from . import intpydde
except ImportError:
self.ddeServer = None
intpydde = None
if intpydde is not None:
self.ddeServer = intpydde.DDEServer(self)
self.ddeServer.Create("Pythonwin", intpydde.CBF_FAIL_SELFCONNECTIONS )
try:
# If there is an existing instance, pump the arguments to it.
connection = self.MakeExistingDDEConnection()
if connection is not None:
if self.ProcessArgs(sys.argv, connection) is None:
return 1
except:
# It is too early to 'print' an exception - we
# don't have stdout setup yet!
win32ui.DisplayTraceback(sys.exc_info(), " - error in DDE conversation with Pythonwin")
def InitInstance(self):
# Allow "/nodde" and "/new" to optimize this!
if "/nodde" not in sys.argv and "/new" not in sys.argv:
if self.InitDDE():
return 1 # A remote DDE client is doing it for us!
else:
self.ddeServer = None
win32ui.SetRegistryKey("Python %s" % (sys.winver,)) # MFC automatically puts the main frame caption on!
app.CApp.InitInstance(self)
# Create the taskbar icon
win32ui.CreateDebuggerThread()
# Allow Pythonwin to host OCX controls.
win32ui.EnableControlContainer()
# Display the interactive window if the user wants it.
from . import interact
interact.CreateInteractiveWindowUserPreference()
# Load the modules we use internally.
self.LoadSystemModules()
# Load additional module the user may want.
self.LoadUserModules()
# Load the ToolBar state near the end of the init process, as
# there may be Toolbar IDs created by the user or other modules.
# By now all these modules should be loaded, so all the toolbar IDs loaded.
try:
self.frame.LoadBarState("ToolbarDefault")
except win32ui.error:
# MFC sucks. It does essentially "GetDlgItem(x)->Something", so if the
# toolbar with ID x does not exist, MFC crashes! Pythonwin has a trap for this
# but I need to investigate more how to prevent it (AFAIK, ensuring all the
# toolbars are created by now _should_ stop it!)
pass
# Finally process the command line arguments.
self.ProcessArgs(sys.argv)
def ExitInstance(self):
win32ui.DestroyDebuggerThread()
try:
from . import interact
interact.DestroyInteractiveWindow()
except:
pass
if self.ddeServer is not None:
self.ddeServer.Shutdown()
self.ddeServer = None
return app.CApp.ExitInstance(self)
def Activate(self):
# Bring to the foreground. Mainly used when another app starts up, it asks
# this one to activate itself, then it terminates.
frame = win32ui.GetMainFrame()
frame.SetForegroundWindow()
if frame.GetWindowPlacement()[1]==win32con.SW_SHOWMINIMIZED:
frame.ShowWindow(win32con.SW_RESTORE)
def ProcessArgs(self, args, dde = None):
# If we are going to talk to a remote app via DDE, then
# activate it!
if dde is not None: dde.Exec("self.Activate()")
if len(args) and args[0] in ['/nodde','/new']: del args[0] # already handled.
if len(args)<1 or not args[0]: # argv[0]=='' when started without args, just like Python.exe!
return
try:
if args[0] and args[0][0]!='/':
argStart = 0
argType = win32ui.GetProfileVal("Python","Default Arg Type","/edit").lower()
else:
argStart = 1
argType = args[0]
if argStart >= len(args):
raise TypeError("The command line requires an additional arg.")
if argType=="/edit":
# Load up the default application.
if dde:
fname = win32api.GetFullPathName(args[argStart])
dde.Exec("win32ui.GetApp().OpenDocumentFile(%s)" % (repr(fname)))
else:
win32ui.GetApp().OpenDocumentFile(args[argStart])
elif argType=="/rundlg":
if dde:
dde.Exec("from pywin.framework import scriptutils;scriptutils.RunScript('%s', '%s', 1)" % (args[argStart], ' '.join(args[argStart+1:])))
else:
from . import scriptutils
scriptutils.RunScript(args[argStart], ' '.join(args[argStart+1:]))
elif argType=="/run":
if dde:
dde.Exec("from pywin.framework import scriptutils;scriptutils.RunScript('%s', '%s', 0)" % (args[argStart], ' '.join(args[argStart+1:])))
else:
from . import scriptutils
scriptutils.RunScript(args[argStart], ' '.join(args[argStart+1:]), 0)
elif argType=="/app":
raise RuntimeError("/app only supported for new instances of Pythonwin.exe")
elif argType=='/dde': # Send arbitary command
if dde is not None:
dde.Exec(args[argStart])
else:
win32ui.MessageBox("The /dde command can only be used\r\nwhen Pythonwin is already running")
else:
raise TypeError("Command line arguments not recognised")
except:
# too early for print anything.
win32ui.DisplayTraceback(sys.exc_info(), " - error processing command line args")
def LoadSystemModules(self):
self.DoLoadModules("pywin.framework.editor,pywin.framework.stdin")
def LoadUserModules(self, moduleNames = None):
# Load the users modules.
if moduleNames is None:
default = "pywin.framework.sgrepmdi,pywin.framework.mdi_pychecker"
moduleNames=win32ui.GetProfileVal('Python','Startup Modules',default)
self.DoLoadModules(moduleNames)
def DoLoadModules(self, moduleNames): # ", sep string of module names.
if not moduleNames: return
modules = moduleNames.split(",")
for module in modules:
try:
__import__(module)
except: # Catch em all, else the app itself dies! 'ImportError:
traceback.print_exc()
msg = 'Startup import of user module "%s" failed' % module
print(msg)
win32ui.MessageBox(msg)
#
# DDE Callback
#
def OnDDECommand(self, command):
try:
exec(command + "\n")
except:
print("ERROR executing DDE command: ", command)
traceback.print_exc()
raise
#
# General handlers
#
def OnViewBrowse( self, id, code ):
" Called when ViewBrowse message is received "
from pywin.mfc import dialog
from pywin.tools import browser
obName = dialog.GetSimpleInput('Object', '__builtins__', 'Browse Python Object')
if obName is None:
return
try:
browser.Browse(eval(obName, __main__.__dict__, __main__.__dict__))
except NameError:
win32ui.MessageBox('This is no object with this name')
except AttributeError:
win32ui.MessageBox('The object has no attribute of that name')
except:
traceback.print_exc()
win32ui.MessageBox('This object can not be browsed')
def OnFileImport( self, id, code ):
" Called when a FileImport message is received. Import the current or specified file"
from . import scriptutils
scriptutils.ImportFile()
def OnFileCheck( self, id, code ):
" Called when a FileCheck message is received. Check the current file."
from . import scriptutils
scriptutils.CheckFile()
def OnUpdateFileCheck(self, cmdui):
from . import scriptutils
cmdui.Enable( scriptutils.GetActiveFileName(0) is not None )
def OnFileRun( self, id, code ):
" Called when a FileRun message is received. "
from . import scriptutils
showDlg = win32api.GetKeyState(win32con.VK_SHIFT) >= 0
scriptutils.RunScript(None, None, showDlg)
def OnFileLocate( self, id, code ):
from pywin.mfc import dialog
from . import scriptutils
import os
global lastLocateFileName # save the new version away for next time...
name = dialog.GetSimpleInput('File name', lastLocateFileName, 'Locate Python File')
if name is None: # Cancelled.
return
lastLocateFileName = name
# if ".py" supplied, rip it off!
# should also check for .pys and .pyw
if lastLocateFileName[-3:].lower()=='.py':
lastLocateFileName = lastLocateFileName[:-3]
lastLocateFileName = lastLocateFileName.replace(".","\\")
newName = scriptutils.LocatePythonFile(lastLocateFileName)
if newName is None:
win32ui.MessageBox("The file '%s' can not be located" % lastLocateFileName)
else:
win32ui.GetApp().OpenDocumentFile(newName)
# Display all the "options" proprety pages we can find
def OnViewOptions(self, id, code):
win32ui.InitRichEdit()
sheet = dialog.PropertySheet("Pythonwin Options")
# Add property pages we know about that need manual work.
from pywin.dialogs import ideoptions
sheet.AddPage( ideoptions.OptionsPropPage() )
from . import toolmenu
sheet.AddPage( toolmenu.ToolMenuPropPage() )
# Get other dynamic pages from templates.
pages = []
for template in self.GetDocTemplateList():
try:
# Dont actually call the function with the exception handler.
getter = template.GetPythonPropertyPages
except AttributeError:
# Template does not provide property pages!
continue
pages = pages + getter()
# Debugger template goes at the end
try:
from pywin.debugger import configui
except ImportError:
configui = None
if configui is not None: pages.append(configui.DebuggerOptionsPropPage())
# Now simply add the pages, and display the dialog.
for page in pages:
sheet.AddPage(page)
if sheet.DoModal()==win32con.IDOK:
win32ui.SetStatusText("Applying configuration changes...", 1)
win32ui.DoWaitCursor(1)
# Tell every Window in our app that win.ini has changed!
win32ui.GetMainFrame().SendMessageToDescendants(win32con.WM_WININICHANGE, 0, 0)
win32ui.DoWaitCursor(0)
def OnInteractiveWindow(self, id, code):
# toggle the existing state.
from . import interact
interact.ToggleInteractiveWindow()
def OnUpdateInteractiveWindow(self, cmdui):
try:
interact=sys.modules['pywin.framework.interact']
state = interact.IsInteractiveWindowVisible()
except KeyError: # Interactive module hasnt ever been imported.
state = 0
cmdui.Enable()
cmdui.SetCheck(state)
def OnFileSaveAll(self, id, code):
# Only attempt to save editor documents.
from pywin.framework.editor import editorTemplate
num = 0
for doc in editorTemplate.GetDocumentList():
if doc.IsModified() and doc.GetPathName():
num = num = 1
doc.OnSaveDocument(doc.GetPathName())
win32ui.SetStatusText("%d documents saved" % num, 1)
def OnViewToolbarDbg(self, id, code):
if code==0:
return not win32ui.GetMainFrame().OnBarCheck(id)
def OnUpdateViewToolbarDbg(self, cmdui):
win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)
cmdui.Enable(1)
def OnHelpIndex( self, id, code ):
from . import help
help.SelectAndRunHelpFile()
# As per the comments in app.py, this use is depreciated.
# app.AppBuilder = InteractivePythonApp
# Now all we do is create the application
thisApp = InteractivePythonApp()

View file

@ -0,0 +1,56 @@
# DDE support for Pythonwin
#
# Seems to work fine (in the context that IE4 seems to have broken
# DDE on _all_ NT4 machines I have tried, but only when a "Command Prompt" window
# is open. Strange, but true. If you have problems with this, close all Command Prompts!
import win32ui
import win32api, win32con
from pywin.mfc import object
from dde import *
import sys, traceback
class DDESystemTopic(object.Object):
def __init__(self, app):
self.app = app
object.Object.__init__(self, CreateServerSystemTopic())
def Exec(self, data):
try:
# print "Executing", cmd
self.app.OnDDECommand(data)
except:
t,v,tb = sys.exc_info()
# The DDE Execution failed.
print("Error executing DDE command.")
traceback.print_exception(t,v,tb)
return 0
class DDEServer(object.Object):
def __init__(self, app):
self.app = app
object.Object.__init__(self, CreateServer())
self.topic = self.item = None
def CreateSystemTopic(self):
return DDESystemTopic(self.app)
def Shutdown(self):
self._obj_.Shutdown()
self._obj_.Destroy()
if self.topic is not None:
self.topic.Destroy()
self.topic = None
if self.item is not None:
self.item.Destroy()
self.item = None
def OnCreate(self):
return 1
def Status(self, msg):
try:
win32ui.SetStatusText(msg)
except win32ui.error:
pass

View file

@ -0,0 +1,629 @@
######################################################################
##
## The Pychecker MDI Plug-In UserModule for Pythonwin
##
## contributed by Robert Kiendl
##
## Style is similar to (and inherited) from the SGrepMDI UserModule
##
## Usage:
##
## Start Pychecker on current file: Menu/File/New../Pychecker.
## Use it: Jump to Pychecker warning source lines by double-click.
## Auto-add "#$pycheck_no" / "#$pycheck_no=specific-re-pattern" tags
## to source lines by context/right-mouse-click on warning lines.
##
## It requires pychecker installed and the pychecker.bat to be on
## the PATH. Example pychecker.bat:
##
## REM pychecker.bat
## C:\bin\python.exe C:\PYTHON23\Lib\site-packages\pychecker\checker.py %1 %2 %3 %4 %5 %6 %7 %8 %9
##
## Adding it as default module in PythonWin:
##
## +++ ./intpyapp.py 2006-10-02 17:59:32.974161600 +0200
## @@ -272,7 +282,7 @@
## def LoadUserModules(self, moduleNames = None):
## # Load the users modules.
## if moduleNames is None:
## - default = "sgrepmdi"
## + default = "sgrepmdi,mdi_pychecker"
## moduleNames=win32ui.GetProfileVal('Python','Startup Modules',default)
## self.DoLoadModules(moduleNames)
##
######################################################################
import win32ui
import win32api
from pywin.mfc import docview, dialog, window
import win32con
import sys, string, re, glob, os, stat, time
from . import scriptutils
def getsubdirs(d):
dlist = []
flist = glob.glob(d+'\\*')
for f in flist:
if os.path.isdir(f):
dlist.append(f)
dlist = dlist + getsubdirs(f)
return dlist
class dirpath:
def __init__(self, str, recurse=0):
dp = str.split(';')
dirs = {}
for d in dp:
if os.path.isdir(d):
d = d.lower()
if d not in dirs:
dirs[d] = None
if recurse:
subdirs = getsubdirs(d)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
elif os.path.isfile(d):
pass
else:
x = None
if d in os.environ:
x = dirpath(os.environ[d])
elif d[:5] == 'HKEY_':
keystr = d.split('\\')
try:
root = eval('win32con.'+keystr[0])
except:
win32ui.MessageBox("Can't interpret registry key name '%s'" % keystr[0])
try:
subkey = '\\'.join(keystr[1:])
val = win32api.RegQueryValue(root, subkey)
if val:
x = dirpath(val)
else:
win32ui.MessageBox("Registry path '%s' did not return a path entry" % d)
except:
win32ui.MessageBox("Can't interpret registry key value: %s" % keystr[1:])
else:
win32ui.MessageBox("Directory '%s' not found" % d)
if x:
for xd in x:
if xd not in dirs:
dirs[xd] = None
if recurse:
subdirs = getsubdirs(xd)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
self.dirs = []
for d in dirs.keys():
self.dirs.append(d)
def __getitem__(self, key):
return self.dirs[key]
def __len__(self):
return len(self.dirs)
def __setitem__(self, key, value):
self.dirs[key] = value
def __delitem__(self, key):
del self.dirs[key]
def __getslice__(self, lo, hi):
return self.dirs[lo:hi]
def __setslice__(self, lo, hi, seq):
self.dirs[lo:hi] = seq
def __delslice__(self, lo, hi):
del self.dirs[lo:hi]
def __add__(self, other):
if type(other) == type(self) or type(other) == type([]):
return self.dirs + other.dirs
def __radd__(self, other):
if type(other) == type(self) or type(other) == type([]):
return other.dirs + self.dirs
# Group(1) is the filename, group(2) is the lineno.
#regexGrepResult=regex.compile("^\\([a-zA-Z]:.*\\)(\\([0-9]+\\))")
#regexGrep=re.compile(r"^([a-zA-Z]:[^(]*)\((\d+)\)")
regexGrep=re.compile(r"^(..[^\(:]+)?[\(:](\d+)[\):]:?\s*(.*)")
#these are the atom numbers defined by Windows for basic dialog controls
BUTTON = 0x80
EDIT = 0x81
STATIC = 0x82
LISTBOX = 0x83
SCROLLBAR = 0x84
COMBOBOX = 0x85
class TheTemplate(docview.RichEditDocTemplate):
def __init__(self):
docview.RichEditDocTemplate.__init__(self, win32ui.IDR_TEXTTYPE, TheDocument, TheFrame, TheView)
self.SetDocStrings("\nPychecker\nPychecker\nPychecker params (*.pychecker)\n.pychecker\n\n\n")
win32ui.GetApp().AddDocTemplate(self)
self.docparams = None
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc: return doc
ext = os.path.splitext(fileName)[1].lower()
if ext =='.pychecker':
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_noAttempt
def setParams(self, params):
self.docparams = params
def readParams(self):
tmp = self.docparams
self.docparams = None
return tmp
class TheFrame(window.MDIChildWnd):
# The template and doc params will one day be removed.
def __init__(self, wnd = None):
window.MDIChildWnd.__init__(self, wnd)
class TheDocument(docview.RichEditDoc):
def __init__(self, template):
docview.RichEditDoc.__init__(self, template)
self.dirpattern = ''
self.filpattern = ''
self.greppattern = ''
self.casesensitive = 1
self.recurse = 1
self.verbose = 0
def OnOpenDocument(self, fnm):
#this bizarre stuff with params is so right clicking in a result window
#and starting a new grep can communicate the default parameters to the
#new grep.
try:
params = open(fnm,'r').read()
except:
params = None
self.setInitParams(params)
return self.OnNewDocument()
def OnCloseDocument(self):
try:
win32ui.GetApp().DeleteIdleHandler(self.idleHandler)
except:
pass
return self._obj_.OnCloseDocument()
def saveInitParams(self):
# Only save the flags, not the text boxes.
paramstr = "\t\t\t%d\t%d" % (self.casesensitive, self.recurse)
win32ui.WriteProfileVal("Pychecker", "Params", paramstr)
def setInitParams(self, paramstr):
if paramstr is None:
paramstr = win32ui.GetProfileVal("Pychecker", "Params", '\t\t\t1\t0\t0')
params = paramstr.split('\t')
if len(params) < 3:
params = params + ['']*(3-len(params))
if len(params) < 6:
params = params + [0]*(6-len(params))
self.dirpattern = params[0]
self.filpattern = params[1]
self.greppattern = params[2] or '-#1000 --only'
self.casesensitive = int(params[3])
self.recurse = int(params[4])
self.verbose = int(params[5])
# setup some reasonable defaults.
if not self.dirpattern:
try:
editor=win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView()
self.dirpattern=os.path.abspath(os.path.dirname(editor.GetDocument().GetPathName()))
except (AttributeError,win32ui.error):
self.dirpattern = os.getcwd()
if not self.filpattern:
try:
editor=win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView()
self.filpattern=editor.GetDocument().GetPathName()
except AttributeError:
self.filpattern = "*.py"
def OnNewDocument(self):
if self.dirpattern == '':
self.setInitParams(greptemplate.readParams())
d = TheDialog(self.dirpattern, self.filpattern, self.greppattern, self.casesensitive, self.recurse, self.verbose)
if d.DoModal() == win32con.IDOK:
self.dirpattern = d['dirpattern']
self.filpattern = d['filpattern']
self.greppattern = d['greppattern']
#self.casesensitive = d['casesensitive']
#self.recurse = d['recursive']
#self.verbose = d['verbose']
self.doSearch()
self.saveInitParams()
return 1
return 0 # cancelled - return zero to stop frame creation.
def doSearch(self):
self.dp = dirpath(self.dirpattern, self.recurse)
self.SetTitle("Pychecker Run '%s' (options: %s)" % (self.filpattern, self.greppattern))
#self.text = []
self.GetFirstView().Append('#Pychecker Run in '+self.dirpattern+' %s\n'%time.asctime())
if self.verbose:
self.GetFirstView().Append('# ='+repr(self.dp.dirs)+'\n')
self.GetFirstView().Append('# Files '+self.filpattern+'\n')
self.GetFirstView().Append('# Options '+self.greppattern+'\n')
self.fplist = self.filpattern.split(';')
self.GetFirstView().Append('# Running... ( double click on result lines in order to jump to the source code ) \n')
win32ui.SetStatusText("Pychecker running. Please wait...", 0)
self.dpndx = self.fpndx = 0
self.fndx = -1
if not self.dp:
self.GetFirstView().Append("# ERROR: '%s' does not resolve to any search locations" % self.dirpattern)
self.SetModifiedFlag(0)
else:
##self.flist = glob.glob(self.dp[0]+'\\'+self.fplist[0])
import operator
self.flist = reduce(operator.add, list(map(glob.glob,self.fplist)) )
#import pywin.debugger;pywin.debugger.set_trace()
self.startPycheckerRun()
def idleHandler(self,handler,count):
import time
time.sleep(0.001)
if self.result!=None:
win32ui.GetApp().DeleteIdleHandler(self.idleHandler)
return 0
return 1 #more
def startPycheckerRun(self):
self.result=None
old=win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_APPSTARTING))
win32ui.GetApp().AddIdleHandler(self.idleHandler)
import _thread
_thread.start_new(self.threadPycheckerRun,())
##win32api.SetCursor(old)
def threadPycheckerRun(self):
result=''
rc=-1
try:
options = self.greppattern
files= ' '.join(self.flist)
# Recently MarkH has failed to run pychecker without it having
# been explicitly installed - so we assume it is and locate it
# from its default location.
# Step1 - get python.exe
py = os.path.join(sys.prefix, 'python.exe')
if not os.path.isfile(py):
if "64 bit" in sys.version:
py = os.path.join(sys.prefix, 'PCBuild', 'amd64', 'python.exe')
else:
py = os.path.join(sys.prefix, 'PCBuild', 'python.exe')
try:
py = win32api.GetShortPathName(py)
except win32api.error:
py = ""
# Find checker.py
from distutils.sysconfig import get_python_lib
pychecker = os.path.join(get_python_lib(), 'pychecker', 'checker.py')
if not os.path.isfile(py):
result = "Can't find python.exe!\n"
elif not os.path.isfile(pychecker):
result = "Can't find checker.py - please install pychecker " \
"(or run 'setup.py install' if you have the source version)\n"
else:
cmd='%s "%s" %s %s 2>&1' % (py, pychecker, options,files)
##fin,fout,ferr=os.popen3(cmd)
##result=ferr.read()+fout.read()
result=os.popen(cmd).read()
##rc=f.close()
self.GetFirstView().Append(result)
finally:
self.result=result
print('== Pychecker run finished ==')
self.GetFirstView().Append('\n'+'== Pychecker run finished ==')
self.SetModifiedFlag(0)
def _inactive_idleHandler(self, handler, count):
self.fndx = self.fndx + 1
if self.fndx < len(self.flist):
f = self.flist[self.fndx]
if self.verbose:
self.GetFirstView().Append('# ..'+f+'\n')
win32ui.SetStatusText("Searching "+f, 0)
lines = open(f, 'r').readlines()
for i in range(len(lines)):
line = lines[i]
if self.pat.search(line) != None:
self.GetFirstView().Append(f+'('+repr(i+1) + ') '+line)
else:
self.fndx = -1
self.fpndx = self.fpndx + 1
if self.fpndx < len(self.fplist):
self.flist = glob.glob(self.dp[self.dpndx] + '\\' + self.fplist[self.fpndx])
else:
self.fpndx = 0
self.dpndx = self.dpndx + 1
if self.dpndx < len(self.dp):
self.flist = glob.glob(self.dp[self.dpndx] + '\\' + self.fplist[self.fpndx])
else:
win32ui.SetStatusText("Search complete.", 0)
self.SetModifiedFlag(0) # default to not modified.
try:
win32ui.GetApp().DeleteIdleHandler(self.idleHandler)
except:
pass
return 0
return 1
def GetParams(self):
return self.dirpattern+'\t'+self.filpattern+'\t'+self.greppattern+'\t'+repr(self.casesensitive)+'\t'+repr(self.recurse)+'\t'+repr(self.verbose)
def OnSaveDocument(self, filename):
# print 'OnSaveDocument() filename=',filename
savefile = open(filename,"wb")
txt = self.GetParams()+'\n'
# print 'writing',txt
savefile.write(txt)
savefile.close()
self.SetModifiedFlag(0)
return 1
ID_OPEN_FILE = 0xe500
ID_PYCHECKER = 0xe501
ID_SAVERESULTS = 0x502
ID_TRYAGAIN = 0x503
ID_ADDCOMMENT = 0x504
ID_ADDPYCHECKNO2 = 0x505
class TheView(docview.RichEditView):
def __init__(self, doc):
docview.RichEditView.__init__(self, doc)
self.SetWordWrap(win32ui.CRichEditView_WrapNone)
self.HookHandlers()
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
format = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
self.SetDefaultCharFormat(format)
return rc
def HookHandlers(self):
self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)
self.HookCommand(self.OnCmdOpenFile, ID_OPEN_FILE)
self.HookCommand(self.OnCmdThe, ID_PYCHECKER)
self.HookCommand(self.OnCmdSave, ID_SAVERESULTS)
self.HookCommand(self.OnTryAgain, ID_TRYAGAIN)
self.HookCommand(self.OnAddComment, ID_ADDCOMMENT)
self.HookCommand(self.OnAddComment, ID_ADDPYCHECKNO2)
self.HookMessage(self.OnLDblClick,win32con.WM_LBUTTONDBLCLK)
def OnLDblClick(self,params):
line = self.GetLine()
regexGrepResult = regexGrep.match(line)
if regexGrepResult:
fname = regexGrepResult.group(1)
line = int(regexGrepResult.group(2))
scriptutils.JumpToDocument(fname, line)
return 0 # dont pass on
return 1 # pass it on by default.
def OnRClick(self, params):
menu = win32ui.CreatePopupMenu()
flags=win32con.MF_STRING|win32con.MF_ENABLED
lineno = self._obj_.LineFromChar(-1) #selection or current line
line = self._obj_.GetLine(lineno)
regexGrepResult = regexGrep.match(line)
charstart, charend = self._obj_.GetSel()
if regexGrepResult:
self.fnm = regexGrepResult.group(1)
self.lnnum = int(regexGrepResult.group(2))
menu.AppendMenu(flags, ID_OPEN_FILE, "&Open "+self.fnm)
menu.AppendMenu(flags, ID_ADDCOMMENT, "&Add to source: Comment Tag/#$pycheck_no ..")
menu.AppendMenu(flags, ID_ADDPYCHECKNO2, "&Add to source: Specific #$pycheck_no=%(errtext)s ..")
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, ID_TRYAGAIN, "&Try Again")
menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, 'Cu&t')
menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, '&Copy')
menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, '&Paste')
menu.AppendMenu(flags, win32con.MF_SEPARATOR);
menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all')
menu.AppendMenu(flags, win32con.MF_SEPARATOR);
menu.AppendMenu(flags, ID_SAVERESULTS, 'Sa&ve results')
menu.TrackPopupMenu(params[5])
return 0
def OnAddComment(self, cmd, code):
addspecific= cmd==ID_ADDPYCHECKNO2
_=list(self.GetSel())
_.sort()
start,end=_
line_start, line_end = self.LineFromChar(start), self.LineFromChar(end)
first=1
for i in range(line_start,line_end+1):
line = self.GetLine(i)
m = regexGrep.match(line)
if m:
if first:
first=0
cmnt=dialog.GetSimpleInput( "Add to %s lines" % (line_end-line_start+1),
addspecific and " #$pycheck_no=%(errtext)s" or " #$pycheck_no" )
if not cmnt:
return 0
##import pywin.debugger;pywin.debugger.set_trace()
fname = m.group(1)
line = int(m.group(2))
view = scriptutils.JumpToDocument(fname,line)
pos=view.LineIndex(line)-1
if view.GetTextRange(pos-1,pos) in ('\r','\n'):
pos -= 1
view.SetSel(pos, pos)
errtext=m.group(3)
if start!=end and line_start==line_end:
errtext=self.GetSelText()
errtext=repr(re.escape(errtext).replace('\ ',' '))
view.ReplaceSel( addspecific and cmnt % locals()
or cmnt )
return 0
def OnCmdOpenFile(self, cmd, code):
doc = win32ui.GetApp().OpenDocumentFile(self.fnm)
if doc:
vw = doc.GetFirstView()
#hope you have an editor that implements GotoLine()!
try:
vw.GotoLine(int(self.lnnum))
except:
pass
return 0
def OnCmdThe(self, cmd, code):
curparamsstr = self.GetDocument().GetParams()
params = curparamsstr.split('\t')
params[2] = self.sel
greptemplate.setParams('\t'.join(params))
greptemplate.OpenDocumentFile()
return 0
def OnTryAgain(self, cmd, code):
greptemplate.setParams(self.GetDocument().GetParams())
greptemplate.OpenDocumentFile()
return 0
def OnCmdSave(self, cmd, code):
flags = win32con.OFN_OVERWRITEPROMPT
dlg = win32ui.CreateFileDialog(0, None, None, flags, "Text Files (*.txt)|*.txt||", self)
dlg.SetOFNTitle("Save Results As")
if dlg.DoModal() == win32con.IDOK:
pn = dlg.GetPathName()
self._obj_.SaveFile(pn)
return 0
def Append(self, strng):
numlines = self.GetLineCount()
endpos = self.LineIndex(numlines-1) + len(self.GetLine(numlines-1))
self.SetSel(endpos, endpos)
self.ReplaceSel(strng)
class TheDialog(dialog.Dialog):
def __init__(self, dp, fp, gp, cs, r, v):
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [ ["Pychecker Run", (0, 0, 210, 90), style, None, (8, "MS Sans Serif")], ]
tmp.append([STATIC, "Files:", -1, (7, 7, 50, 9), CS ])
tmp.append([EDIT, gp, 103, (52, 7, 144, 11), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER])
tmp.append([STATIC, "Directories:", -1, (7, 20, 50, 9), CS ])
tmp.append([EDIT, dp, 102, (52, 20, 128, 11), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER])
tmp.append([BUTTON, '...', 110, (182,20, 16, 11), CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP])
tmp.append([STATIC, "Options:", -1, (7, 33, 50, 9), CS ])
tmp.append([EDIT, fp, 101, (52, 33, 128, 11), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER ])
tmp.append([BUTTON, '...', 111, (182,33, 16, 11), CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP])
#tmp.append([BUTTON,'Case sensitive', 104, (7, 45, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
#tmp.append([BUTTON,'Subdirectories', 105, (7, 56, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
#tmp.append([BUTTON,'Verbose', 106, (7, 67, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
tmp.append([BUTTON,'OK', win32con.IDOK, (166,53, 32, 12), CS | win32con.BS_DEFPUSHBUTTON| win32con.WS_TABSTOP])
tmp.append([BUTTON,'Cancel', win32con.IDCANCEL, (166,67, 32, 12), CS | win32con.BS_PUSHBUTTON| win32con.WS_TABSTOP])
dialog.Dialog.__init__(self, tmp)
self.AddDDX(101,'greppattern')
self.AddDDX(102,'dirpattern')
self.AddDDX(103,'filpattern')
#self.AddDDX(104,'casesensitive')
#self.AddDDX(105,'recursive')
#self.AddDDX(106,'verbose')
self._obj_.data['greppattern'] = gp
self._obj_.data['dirpattern'] = dp
self._obj_.data['filpattern'] = fp
#self._obj_.data['casesensitive'] = cs
#self._obj_.data['recursive'] = r
#self._obj_.data['verbose'] = v
self.HookCommand(self.OnMoreDirectories, 110)
self.HookCommand(self.OnMoreFiles, 111)
def OnMoreDirectories(self, cmd, code):
self.getMore('Pychecker\\Directories', 'dirpattern')
def OnMoreFiles(self, cmd, code):
self.getMore('Pychecker\\File Types', 'filpattern')
def getMore(self, section, key):
self.UpdateData(1)
#get the items out of the ini file
ini = win32ui.GetProfileFileName()
secitems = win32api.GetProfileSection(section, ini)
items = []
for secitem in secitems:
items.append(secitem.split('=')[1])
dlg = TheParamsDialog(items)
if dlg.DoModal() == win32con.IDOK:
itemstr = ';'.join(dlg.getItems())
self._obj_.data[key] = itemstr
#update the ini file with dlg.getNew()
i = 0
newitems = dlg.getNew()
if newitems:
items = items + newitems
for item in items:
win32api.WriteProfileVal(section, repr(i), item, ini)
i = i + 1
self.UpdateData(0)
def OnOK(self):
self.UpdateData(1)
for id, name in [(101,'greppattern'), (102,'dirpattern'), (103,'filpattern')]:
if not self[name]:
self.GetDlgItem(id).SetFocus()
win32api.MessageBeep()
win32ui.SetStatusText("Please enter a value")
return
self._obj_.OnOK()
class TheParamsDialog(dialog.Dialog):
def __init__(self, items):
self.items = items
self.newitems = []
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [ ["Pychecker Parameters", (0, 0, 205, 100), style, None, (8, "MS Sans Serif")], ]
tmp.append([LISTBOX, '', 107, (7, 7, 150, 72), CS | win32con.LBS_MULTIPLESEL| win32con.LBS_STANDARD | win32con.LBS_HASSTRINGS | win32con.WS_TABSTOP | win32con.LBS_NOTIFY])
tmp.append([BUTTON,'OK', win32con.IDOK, (167, 7, 32, 12), CS | win32con.BS_DEFPUSHBUTTON| win32con.WS_TABSTOP])
tmp.append([BUTTON,'Cancel', win32con.IDCANCEL, (167,23, 32, 12), CS | win32con.BS_PUSHBUTTON| win32con.WS_TABSTOP])
tmp.append([STATIC,'New:', -1, (2, 83, 15, 12), CS])
tmp.append([EDIT, '', 108, (18, 83, 139, 12), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER])
tmp.append([BUTTON,'Add', 109, (167,83, 32, 12), CS | win32con.BS_PUSHBUTTON| win32con.WS_TABSTOP])
dialog.Dialog.__init__(self, tmp)
self.HookCommand(self.OnAddItem, 109)
self.HookCommand(self.OnListDoubleClick, 107)
def OnInitDialog(self):
lb = self.GetDlgItem(107)
for item in self.items:
lb.AddString(item)
return self._obj_.OnInitDialog()
def OnAddItem(self, cmd, code):
eb = self.GetDlgItem(108)
item = eb.GetLine(0)
self.newitems.append(item)
lb = self.GetDlgItem(107)
i = lb.AddString(item)
lb.SetSel(i, 1)
return 1
def OnListDoubleClick(self, cmd, code):
if code == win32con.LBN_DBLCLK:
self.OnOK()
return 1
def OnOK(self):
lb = self.GetDlgItem(107)
self.selections = lb.GetSelTextItems()
self._obj_.OnOK()
def getItems(self):
return self.selections
def getNew(self):
return self.newitems
try:
win32ui.GetApp().RemoveDocTemplate(greptemplate)
except NameError:
pass
greptemplate = TheTemplate()

View file

@ -0,0 +1,623 @@
"""
Various utilities for running/importing a script
"""
import sys
import win32ui
import win32api
import win32con
import __main__
from pywin.mfc import dialog
from pywin.mfc.docview import TreeView
import os
import string
import traceback
import linecache
import bdb
from .cmdline import ParseArgs
RS_DEBUGGER_NONE=0 # Dont run under the debugger.
RS_DEBUGGER_STEP=1 # Start stepping under the debugger
RS_DEBUGGER_GO=2 # Just run under the debugger, stopping only at break-points.
RS_DEBUGGER_PM=3 # Dont run under debugger, but do post-mortem analysis on exception.
debugging_options = """No debugging
Step-through in the debugger
Run in the debugger
Post-Mortem of unhandled exceptions""".split("\n")
byte_cr = "\r".encode("ascii")
byte_lf = "\n".encode("ascii")
byte_crlf = "\r\n".encode("ascii")
# A dialog box for the "Run Script" command.
class DlgRunScript(dialog.Dialog):
"A class for the 'run script' dialog"
def __init__(self, bHaveDebugger):
dialog.Dialog.__init__(self, win32ui.IDD_RUN_SCRIPT )
self.AddDDX(win32ui.IDC_EDIT1, "script")
self.AddDDX(win32ui.IDC_EDIT2, "args")
self.AddDDX(win32ui.IDC_COMBO1, "debuggingType", "i")
self.HookCommand(self.OnBrowse, win32ui.IDC_BUTTON2)
self.bHaveDebugger = bHaveDebugger
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
for o in debugging_options:
cbo.AddString(o)
cbo.SetCurSel(self['debuggingType'])
if not self.bHaveDebugger:
cbo.EnableWindow(0)
def OnBrowse(self, id, cmd):
openFlags = win32con.OFN_OVERWRITEPROMPT|win32con.OFN_FILEMUSTEXIST
dlg = win32ui.CreateFileDialog(1,None,None,openFlags, "Python Scripts (*.py)|*.py||", self)
dlg.SetOFNTitle("Run Script")
if dlg.DoModal()!=win32con.IDOK:
return 0
self['script'] = dlg.GetPathName()
self.UpdateData(0)
return 0
def GetDebugger():
"""Get the default Python debugger. Returns the debugger, or None.
It is assumed the debugger has a standard "pdb" defined interface.
Currently always returns the 'pywin.debugger' debugger, or None
(pdb is _not_ returned as it is not effective in this GUI environment)
"""
try:
import pywin.debugger
return pywin.debugger
except ImportError:
return None
def IsOnPythonPath(path):
"Given a path only, see if it is on the Pythonpath. Assumes path is a full path spec."
# must check that the command line arg's path is in sys.path
for syspath in sys.path:
try:
# Python 1.5 and later allows an empty sys.path entry.
if syspath and win32ui.FullPath(syspath)==path:
return 1
except win32ui.error as details:
print("Warning: The sys.path entry '%s' is invalid\n%s" % (syspath, details))
return 0
def GetPackageModuleName(fileName):
"""Given a filename, return (module name, new path).
eg - given "c:\a\b\c\my.py", return ("b.c.my",None) if "c:\a" is on sys.path.
If no package found, will return ("my", "c:\a\b\c")
"""
path, fname = os.path.split(fileName)
path=origPath=win32ui.FullPath(path)
fname = os.path.splitext(fname)[0]
modBits = []
newPathReturn = None
if not IsOnPythonPath(path):
# Module not directly on the search path - see if under a package.
while len(path)>3: # ie 'C:\'
path, modBit = os.path.split(path)
modBits.append(modBit)
# If on path, _and_ existing package of that name loaded.
if IsOnPythonPath(path) and modBit in sys.modules and \
(os.path.exists(os.path.join(path, modBit, '__init__.py')) or \
os.path.exists(os.path.join(path, modBit, '__init__.pyc')) or \
os.path.exists(os.path.join(path, modBit, '__init__.pyo')) \
):
modBits.reverse()
return ".".join(modBits) + "." + fname, newPathReturn
# Not found - look a level higher
else:
newPathReturn = origPath
return fname, newPathReturn
def GetActiveView():
"""Gets the edit control (eg, EditView) with the focus, or None
"""
try:
childFrame, bIsMaximised = win32ui.GetMainFrame().MDIGetActive()
return childFrame.GetActiveView()
except win32ui.error:
return None
def GetActiveEditControl():
view = GetActiveView()
if view is None: return None
if hasattr(view, "SCIAddText"): # Is it a scintilla control?
return view
try:
return view.GetRichEditCtrl()
except AttributeError:
pass
try:
return view.GetEditCtrl()
except AttributeError:
pass
def GetActiveEditorDocument():
"""Returns the active editor document and view, or (None,None) if no
active document or its not an editor document.
"""
view = GetActiveView()
if view is None or isinstance(view, TreeView):
return (None, None)
doc = view.GetDocument()
if hasattr(doc, "MarkerAdd"): # Is it an Editor document?
return doc, view
return (None, None)
def GetActiveFileName(bAutoSave = 1):
"""Gets the file name for the active frame, saving it if necessary.
Returns None if it cant be found, or raises KeyboardInterrupt.
"""
pathName = None
active = GetActiveView()
if active is None:
return None
try:
doc = active.GetDocument()
pathName = doc.GetPathName()
if bAutoSave and \
(len(pathName)>0 or \
doc.GetTitle()[:8]=="Untitled" or \
doc.GetTitle()[:6]=="Script"): # if not a special purpose window
if doc.IsModified():
try:
doc.OnSaveDocument(pathName)
pathName = doc.GetPathName()
# clear the linecache buffer
linecache.clearcache()
except win32ui.error:
raise KeyboardInterrupt
except (win32ui.error, AttributeError):
pass
if not pathName:
return None
return pathName
lastScript = ''
lastArgs = ''
lastDebuggingType = RS_DEBUGGER_NONE
def RunScript(defName=None, defArgs=None, bShowDialog = 1, debuggingType=None):
global lastScript, lastArgs, lastDebuggingType
_debugger_stop_frame_ = 1 # Magic variable so the debugger will hide me!
# Get the debugger - may be None!
debugger = GetDebugger()
if defName is None:
try:
pathName = GetActiveFileName()
except KeyboardInterrupt:
return # User cancelled save.
else:
pathName = defName
if not pathName:
pathName = lastScript
if defArgs is None:
args = ''
if pathName==lastScript:
args = lastArgs
else:
args = defArgs
if debuggingType is None: debuggingType = lastDebuggingType
if not pathName or bShowDialog:
dlg = DlgRunScript(debugger is not None)
dlg['script'] = pathName
dlg['args'] = args
dlg['debuggingType'] = debuggingType
if dlg.DoModal() != win32con.IDOK:
return
script=dlg['script']
args=dlg['args']
debuggingType = dlg['debuggingType']
if not script: return
if debuggingType == RS_DEBUGGER_GO and debugger is not None:
# This may surprise users - they select "Run under debugger", but
# it appears not to! Only warn when they pick from the dialog!
# First - ensure the debugger is activated to pickup any break-points
# set in the editor.
try:
# Create the debugger, but _dont_ init the debugger GUI.
rd = debugger._GetCurrentDebugger()
except AttributeError:
rd = None
if rd is not None and len(rd.breaks)==0:
msg = "There are no active break-points.\r\n\r\nSelecting this debug option without any\r\nbreak-points is unlikely to have the desired effect\r\nas the debugger is unlikely to be invoked..\r\n\r\nWould you like to step-through in the debugger instead?"
rc = win32ui.MessageBox(msg, win32ui.LoadString(win32ui.IDR_DEBUGGER), win32con.MB_YESNOCANCEL | win32con.MB_ICONINFORMATION)
if rc == win32con.IDCANCEL:
return
if rc == win32con.IDYES:
debuggingType = RS_DEBUGGER_STEP
lastDebuggingType = debuggingType
lastScript = script
lastArgs = args
else:
script = pathName
# try and open the script.
if len(os.path.splitext(script)[1])==0: # check if no extension supplied, and give one.
script = script + '.py'
# If no path specified, try and locate the file
path, fnameonly = os.path.split(script)
if len(path)==0:
try:
os.stat(fnameonly) # See if it is OK as is...
script = fnameonly
except os.error:
fullScript = LocatePythonFile(script)
if fullScript is None:
win32ui.MessageBox("The file '%s' can not be located" % script )
return
script = fullScript
else:
path = win32ui.FullPath(path)
if not IsOnPythonPath(path): sys.path.append(path)
# py3k fun: If we use text mode to open the file, we get \r\n
# translated so Python allows the syntax (good!), but we get back
# text already decoded from the default encoding (bad!) and Python
# ignores any encoding decls (bad!). If we use binary mode we get
# the raw bytes and Python looks at the encoding (good!) but \r\n
# chars stay in place so Python throws a syntax error (bad!).
# So: so the binary thing and manually normalize \r\n.
try:
f = open(script, 'rb')
except IOError as exc:
win32ui.MessageBox("The file could not be opened - %s (%d)" % (exc.strerror, exc.errno))
return
# Get the source-code - as above, normalize \r\n
code = f.read().replace(byte_crlf, byte_lf).replace(byte_cr, byte_lf) + byte_lf
# Remember and hack sys.argv for the script.
oldArgv = sys.argv
sys.argv = ParseArgs(args)
sys.argv.insert(0, script)
# sys.path[0] is the path of the script
oldPath0 = sys.path[0]
newPath0 = os.path.split(script)[0]
if not oldPath0: # if sys.path[0] is empty
sys.path[0] = newPath0
insertedPath0 = 0
else:
sys.path.insert(0, newPath0)
insertedPath0 = 1
bWorked = 0
win32ui.DoWaitCursor(1)
base = os.path.split(script)[1]
# Allow windows to repaint before starting.
win32ui.PumpWaitingMessages()
win32ui.SetStatusText('Running script %s...' % base,1 )
exitCode = 0
from pywin.framework import interact
# Check the debugger flags
if debugger is None and (debuggingType != RS_DEBUGGER_NONE):
win32ui.MessageBox("No debugger is installed. Debugging options have been ignored!")
debuggingType = RS_DEBUGGER_NONE
# Get a code object - ignore the debugger for this, as it is probably a syntax error
# at this point
try:
codeObject = compile(code, script, "exec")
except:
# Almost certainly a syntax error!
_HandlePythonFailure("run script", script)
# No code object which to run/debug.
return
__main__.__file__=script
try:
if debuggingType == RS_DEBUGGER_STEP:
debugger.run(codeObject, __main__.__dict__, start_stepping=1)
elif debuggingType == RS_DEBUGGER_GO:
debugger.run(codeObject, __main__.__dict__, start_stepping=0)
else:
# Post mortem or no debugging
exec(codeObject, __main__.__dict__)
bWorked = 1
except bdb.BdbQuit:
# Dont print tracebacks when the debugger quit, but do print a message.
print("Debugging session cancelled.")
exitCode = 1
bWorked = 1
except SystemExit as code:
exitCode = code
bWorked = 1
except KeyboardInterrupt:
# Consider this successful, as we dont want the debugger.
# (but we do want a traceback!)
if interact.edit and interact.edit.currentView:
interact.edit.currentView.EnsureNoPrompt()
traceback.print_exc()
if interact.edit and interact.edit.currentView:
interact.edit.currentView.AppendToPrompt([])
bWorked = 1
except:
if interact.edit and interact.edit.currentView:
interact.edit.currentView.EnsureNoPrompt()
traceback.print_exc()
if interact.edit and interact.edit.currentView:
interact.edit.currentView.AppendToPrompt([])
if debuggingType == RS_DEBUGGER_PM:
debugger.pm()
del __main__.__file__
sys.argv = oldArgv
if insertedPath0:
del sys.path[0]
else:
sys.path[0] = oldPath0
f.close()
if bWorked:
win32ui.SetStatusText("Script '%s' returned exit code %s" %(script, exitCode))
else:
win32ui.SetStatusText('Exception raised while running script %s' % base)
try:
sys.stdout.flush()
except AttributeError:
pass
win32ui.DoWaitCursor(0)
def ImportFile():
""" This code looks for the current window, and determines if it can be imported. If not,
it will prompt for a file name, and allow it to be imported. """
try:
pathName = GetActiveFileName()
except KeyboardInterrupt:
pathName = None
if pathName is not None:
if os.path.splitext(pathName)[1].lower() not in ('.py','.pyw','.pyx'):
pathName = None
if pathName is None:
openFlags = win32con.OFN_OVERWRITEPROMPT|win32con.OFN_FILEMUSTEXIST
dlg = win32ui.CreateFileDialog(1,None,None,openFlags, "Python Scripts (*.py;*.pyw)|*.py;*.pyw;*.pyx||")
dlg.SetOFNTitle("Import Script")
if dlg.DoModal()!=win32con.IDOK:
return 0
pathName = dlg.GetPathName()
# If already imported, dont look for package
path, modName = os.path.split(pathName)
modName, modExt = os.path.splitext(modName)
newPath = None
# note that some packages (*cough* email *cough*) use "lazy importers"
# meaning sys.modules can change as a side-effect of looking at
# module.__file__ - so we must take a copy (ie, items() in py2k,
# list(items()) in py3k)
for key, mod in list(sys.modules.items()):
if hasattr(mod, '__file__'):
fname = mod.__file__
base, ext = os.path.splitext(fname)
if ext.lower() in ['.pyo', '.pyc']:
ext = '.py'
fname = base + ext
if win32ui.ComparePath(fname, pathName):
modName = key
break
else: # for not broken
modName, newPath = GetPackageModuleName(pathName)
if newPath: sys.path.append(newPath)
if modName in sys.modules:
bNeedReload = 1
what = "reload"
else:
what = "import"
bNeedReload = 0
win32ui.SetStatusText(what.capitalize()+'ing module...',1)
win32ui.DoWaitCursor(1)
# win32ui.GetMainFrame().BeginWaitCursor()
try:
# always do an import, as it is cheap if it's already loaded. This ensures
# it is in our name space.
codeObj = compile('import '+modName,'<auto import>','exec')
except SyntaxError:
win32ui.SetStatusText('Invalid filename for import: "' +modName+'"')
return
try:
exec(codeObj, __main__.__dict__)
mod = sys.modules.get(modName)
if bNeedReload:
try:
## The interpreter sees this import as a local assignment, so Python 2.x throws
## UnboundLocalError: local variable 'reload' referenced before assignment
## when you try to use reload after this fails
from imp import reload as my_reload # py3k
except ImportError:
my_reload = reload # reload a builtin in py2k
mod = my_reload(sys.modules[modName])
win32ui.SetStatusText('Successfully ' + what + "ed module '"+modName+"': %s" % getattr(mod,'__file__',"<unkown file>"))
except:
_HandlePythonFailure(what)
win32ui.DoWaitCursor(0)
def CheckFile():
""" This code looks for the current window, and gets Python to check it
without actually executing any code (ie, by compiling only)
"""
try:
pathName = GetActiveFileName()
except KeyboardInterrupt:
return
what = "check"
win32ui.SetStatusText(what.capitalize()+'ing module...',1)
win32ui.DoWaitCursor(1)
try:
f = open(pathName)
except IOError as details:
print("Cant open file '%s' - %s" % (pathName, details))
return
try:
code = f.read() + "\n"
finally:
f.close()
try:
codeObj = compile(code, pathName,'exec')
if RunTabNanny(pathName):
win32ui.SetStatusText("Python and the TabNanny successfully checked the file '"+os.path.basename(pathName)+"'")
except SyntaxError:
_HandlePythonFailure(what, pathName)
except:
traceback.print_exc()
_HandlePythonFailure(what)
win32ui.DoWaitCursor(0)
def RunTabNanny(filename):
import io as io
tabnanny = FindTabNanny()
if tabnanny is None:
win32ui.MessageBox("The TabNanny is not around, so the children can run amok!" )
return
# Capture the tab-nanny output
newout = io.StringIO()
old_out = sys.stderr, sys.stdout
sys.stderr = sys.stdout = newout
try:
tabnanny.check(filename)
finally:
# Restore output
sys.stderr, sys.stdout = old_out
data = newout.getvalue()
if data:
try:
lineno = data.split()[1]
lineno = int(lineno)
_JumpToPosition(filename, lineno)
try: # Try and display whitespace
GetActiveEditControl().SCISetViewWS(1)
except:
pass
win32ui.SetStatusText("The TabNanny found trouble at line %d" % lineno)
except (IndexError, TypeError, ValueError):
print("The tab nanny complained, but I cant see where!")
print(data)
return 0
return 1
def _JumpToPosition(fileName, lineno, col = 1):
JumpToDocument(fileName, lineno, col)
def JumpToDocument(fileName, lineno=0, col = 1, nChars = 0, bScrollToTop = 0):
# Jump to the position in a file.
# If lineno is <= 0, dont move the position - just open/restore.
# if nChars > 0, select that many characters.
# if bScrollToTop, the specified line will be moved to the top of the window
# (eg, bScrollToTop should be false when jumping to an error line to retain the
# context, but true when jumping to a method defn, where we want the full body.
# Return the view which is editing the file, or None on error.
doc = win32ui.GetApp().OpenDocumentFile(fileName)
if doc is None: return None
frame = doc.GetFirstView().GetParentFrame()
try:
view = frame.GetEditorView()
if frame.GetActiveView() != view:
frame.SetActiveView(view)
frame.AutoRestore()
except AttributeError: # Not an editor frame??
view = doc.GetFirstView()
if lineno > 0:
charNo = view.LineIndex(lineno-1)
start = charNo + col - 1
size = view.GetTextLength()
try:
view.EnsureCharsVisible(charNo)
except AttributeError:
print("Doesnt appear to be one of our views?")
view.SetSel(min(start, size), min(start + nChars, size))
if bScrollToTop:
curTop = view.GetFirstVisibleLine()
nScroll = (lineno-1) - curTop
view.LineScroll(nScroll, 0)
view.SetFocus()
return view
def _HandlePythonFailure(what, syntaxErrorPathName = None):
typ, details, tb = sys.exc_info()
if isinstance(details, SyntaxError):
try:
msg, (fileName, line, col, text) = details
if (not fileName or fileName =="<string>") and syntaxErrorPathName:
fileName = syntaxErrorPathName
_JumpToPosition(fileName, line, col)
except (TypeError, ValueError):
msg = str(details)
win32ui.SetStatusText('Failed to ' + what + ' - syntax error - %s' % msg)
else:
traceback.print_exc()
win32ui.SetStatusText('Failed to ' + what + ' - ' + str(details) )
tb = None # Clean up a cycle.
# Find the Python TabNanny in either the standard library or the Python Tools/Scripts directory.
def FindTabNanny():
try:
return __import__("tabnanny")
except ImportError:
pass
# OK - not in the standard library - go looking.
filename = "tabnanny.py"
try:
path = win32api.RegQueryValue(win32con.HKEY_LOCAL_MACHINE, "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % (sys.winver))
except win32api.error:
print("WARNING - The Python registry does not have an 'InstallPath' setting")
print(" The file '%s' can not be located" % (filename))
return None
fname = os.path.join(path, "Tools\\Scripts\\%s" % filename)
try:
os.stat(fname)
except os.error:
print("WARNING - The file '%s' can not be located in path '%s'" % (filename, path))
return None
tabnannyhome, tabnannybase = os.path.split(fname)
tabnannybase = os.path.splitext(tabnannybase)[0]
# Put tab nanny at the top of the path.
sys.path.insert(0, tabnannyhome)
try:
return __import__(tabnannybase)
finally:
# remove the tab-nanny from the path
del sys.path[0]
def LocatePythonFile( fileName, bBrowseIfDir = 1 ):
" Given a file name, return a fully qualified file name, or None "
# first look for the exact file as specified
if not os.path.isfile(fileName):
# Go looking!
baseName = fileName
for path in sys.path:
fileName = os.path.abspath(os.path.join(path, baseName))
if os.path.isdir(fileName):
if bBrowseIfDir:
d=win32ui.CreateFileDialog(1, "*.py", None, 0, "Python Files (*.py)|*.py|All files|*.*")
d.SetOFNInitialDir(fileName)
rc=d.DoModal()
if rc==win32con.IDOK:
fileName = d.GetPathName()
break
else:
return None
else:
fileName = fileName + ".py"
if os.path.isfile(fileName):
break # Found it!
else: # for not broken out of
return None
return win32ui.FullPath(fileName)

View file

@ -0,0 +1,530 @@
#SGrepMDI is by Gordon McMillan (gmcm@hypernet.com)
#It does basically what Find In Files does in MSVC with a couple enhancements.
# - It saves any directories in the app's ini file (if you want to get rid
# of them you'll have to edit the file)
# - "Directories" can be directories,
# - semicolon separated lists of "directories",
# - environment variables that evaluate to "directories",
# - registry path names that evaluate to "directories",
# - all of which is recursive, so you can mix them all up.
# - It is MDI, so you can 'nest' greps and return to earlier ones,
# (ie, have multiple results open at the same time)
# - Like FIF, double clicking a line opens an editor and takes you to the line.
# - You can highlight text, right click and start a new grep with the selected
# text as search pattern and same directories etc as before.
# - You can save grep parameters (so you don't lose your hardearned pattern)
# from File|Save
# - You can save grep results by right clicking in the result window.
# Hats off to Mark Hammond for providing an environment where I could cobble
# something like this together in a couple evenings!
import win32ui
import win32api
from pywin.mfc import docview, dialog, window
import win32con
import string
import re
import glob
import os
import stat
import glob
from . import scriptutils
def getsubdirs(d):
dlist = []
flist = glob.glob(d+'\\*')
for f in flist:
if os.path.isdir(f):
dlist.append(f)
dlist = dlist + getsubdirs(f)
return dlist
class dirpath:
def __init__(self, str, recurse=0):
dp = str.split(';')
dirs = {}
for d in dp:
if os.path.isdir(d):
d = d.lower()
if d not in dirs:
dirs[d] = None
if recurse:
subdirs = getsubdirs(d)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
elif os.path.isfile(d):
pass
else:
x = None
if d in os.environ:
x = dirpath(os.environ[d])
elif d[:5] == 'HKEY_':
keystr = d.split('\\')
try:
root = eval('win32con.'+keystr[0])
except:
win32ui.MessageBox("Can't interpret registry key name '%s'" % keystr[0])
try:
subkey = '\\'.join(keystr[1:])
val = win32api.RegQueryValue(root, subkey)
if val:
x = dirpath(val)
else:
win32ui.MessageBox("Registry path '%s' did not return a path entry" % d)
except:
win32ui.MessageBox("Can't interpret registry key value: %s" % keystr[1:])
else:
win32ui.MessageBox("Directory '%s' not found" % d)
if x:
for xd in x:
if xd not in dirs:
dirs[xd] = None
if recurse:
subdirs = getsubdirs(xd)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
self.dirs = []
for d in list(dirs.keys()):
self.dirs.append(d)
def __getitem__(self, key):
return self.dirs[key]
def __len__(self):
return len(self.dirs)
def __setitem__(self, key, value):
self.dirs[key] = value
def __delitem__(self, key):
del self.dirs[key]
def __getslice__(self, lo, hi):
return self.dirs[lo:hi]
def __setslice__(self, lo, hi, seq):
self.dirs[lo:hi] = seq
def __delslice__(self, lo, hi):
del self.dirs[lo:hi]
def __add__(self, other):
if type(other) == type(self) or type(other) == type([]):
return self.dirs + other.dirs
def __radd__(self, other):
if type(other) == type(self) or type(other) == type([]):
return other.dirs + self.dirs
# Group(1) is the filename, group(2) is the lineno.
#regexGrepResult=regex.compile("^\\([a-zA-Z]:.*\\)(\\([0-9]+\\))")
regexGrep=re.compile(r"^([a-zA-Z]:[^(]*)\(([0-9]+)\)")
#these are the atom numbers defined by Windows for basic dialog controls
BUTTON = 0x80
EDIT = 0x81
STATIC = 0x82
LISTBOX = 0x83
SCROLLBAR = 0x84
COMBOBOX = 0x85
class GrepTemplate(docview.RichEditDocTemplate):
def __init__(self):
docview.RichEditDocTemplate.__init__(self, win32ui.IDR_TEXTTYPE, GrepDocument, GrepFrame, GrepView)
self.SetDocStrings("\nGrep\nGrep\nGrep params (*.grep)\n.grep\n\n\n")
win32ui.GetApp().AddDocTemplate(self)
self.docparams = None
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc: return doc
ext = os.path.splitext(fileName)[1].lower()
if ext =='.grep':
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_noAttempt
def setParams(self, params):
self.docparams = params
def readParams(self):
tmp = self.docparams
self.docparams = None
return tmp
class GrepFrame(window.MDIChildWnd):
# The template and doc params will one day be removed.
def __init__(self, wnd = None):
window.MDIChildWnd.__init__(self, wnd)
class GrepDocument(docview.RichEditDoc):
def __init__(self, template):
docview.RichEditDoc.__init__(self, template)
self.dirpattern = ''
self.filpattern = ''
self.greppattern = ''
self.casesensitive = 1
self.recurse = 1
self.verbose = 0
def OnOpenDocument(self, fnm):
#this bizarre stuff with params is so right clicking in a result window
#and starting a new grep can communicate the default parameters to the
#new grep.
try:
params = open(fnm,'r').read()
except:
params = None
self.setInitParams(params)
return self.OnNewDocument()
def OnCloseDocument(self):
try:
win32ui.GetApp().DeleteIdleHandler(self.SearchFile)
except:
pass
return self._obj_.OnCloseDocument()
def saveInitParams(self):
# Only save the flags, not the text boxes.
paramstr = "\t%s\t\t%d\t%d" % (self.filpattern, self.casesensitive, self.recurse)
win32ui.WriteProfileVal("Grep", "Params", paramstr)
def setInitParams(self, paramstr):
if paramstr is None:
paramstr = win32ui.GetProfileVal("Grep", "Params", '\t\t\t1\t0\t0')
params = paramstr.split('\t')
if len(params) < 3:
params = params + ['']*(3-len(params))
if len(params) < 6:
params = params + [0]*(6-len(params))
self.dirpattern = params[0]
self.filpattern = params[1]
self.greppattern = params[2]
self.casesensitive = int(params[3])
self.recurse = int(params[4])
self.verbose = int(params[5])
# setup some reasonable defaults.
if not self.dirpattern:
try:
editor=win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView()
self.dirpattern=os.path.abspath(os.path.dirname(editor.GetDocument().GetPathName()))
except (AttributeError, win32ui.error):
self.dirpattern = os.getcwd()
if not self.filpattern:
self.filpattern = "*.py"
def OnNewDocument(self):
if self.dirpattern == '':
self.setInitParams(greptemplate.readParams())
d = GrepDialog(self.dirpattern, self.filpattern, self.greppattern, self.casesensitive, self.recurse, self.verbose)
if d.DoModal() == win32con.IDOK:
self.dirpattern = d['dirpattern']
self.filpattern = d['filpattern']
self.greppattern = d['greppattern']
self.casesensitive = d['casesensitive']
self.recurse = d['recursive']
self.verbose = d['verbose']
self.doSearch()
self.saveInitParams()
return 1
return 0 # cancelled - return zero to stop frame creation.
def doSearch(self):
self.dp = dirpath(self.dirpattern, self.recurse)
self.SetTitle("Grep for %s in %s" % (self.greppattern, self.filpattern))
#self.text = []
self.GetFirstView().Append('#Search '+self.dirpattern+'\n')
if self.verbose:
self.GetFirstView().Append('# ='+repr(self.dp.dirs)+'\n')
self.GetFirstView().Append('# Files '+self.filpattern+'\n')
self.GetFirstView().Append('# For '+self.greppattern+'\n')
self.fplist = self.filpattern.split(';')
if self.casesensitive:
self.pat = re.compile(self.greppattern)
else:
self.pat = re.compile(self.greppattern, re.IGNORECASE)
win32ui.SetStatusText("Searching. Please wait...", 0)
self.dpndx = self.fpndx = 0
self.fndx = -1
if not self.dp:
self.GetFirstView().Append("# ERROR: '%s' does not resolve to any search locations" % self.dirpattern)
self.SetModifiedFlag(0)
else:
self.flist = glob.glob(self.dp[0]+'\\'+self.fplist[0])
win32ui.GetApp().AddIdleHandler(self.SearchFile)
def SearchFile(self, handler, count):
self.fndx = self.fndx + 1
if self.fndx < len(self.flist):
f = self.flist[self.fndx]
if self.verbose:
self.GetFirstView().Append('# ..'+f+'\n')
# Directories may match the file type pattern, and files may be removed
# while grep is running
if os.path.isfile(f):
win32ui.SetStatusText("Searching "+f, 0)
lines = open(f, 'r').readlines()
for i in range(len(lines)):
line = lines[i]
if self.pat.search(line) != None:
self.GetFirstView().Append(f+'('+repr(i+1) + ') '+line)
else:
self.fndx = -1
self.fpndx = self.fpndx + 1
if self.fpndx < len(self.fplist):
self.flist = glob.glob(self.dp[self.dpndx] + '\\' + self.fplist[self.fpndx])
else:
self.fpndx = 0
self.dpndx = self.dpndx + 1
if self.dpndx < len(self.dp):
self.flist = glob.glob(self.dp[self.dpndx] + '\\' + self.fplist[self.fpndx])
else:
win32ui.SetStatusText("Search complete.", 0)
self.SetModifiedFlag(0) # default to not modified.
try:
win32ui.GetApp().DeleteIdleHandler(self.SearchFile)
except:
pass
return 0
return 1
def GetParams(self):
return self.dirpattern+'\t'+self.filpattern+'\t'+self.greppattern+'\t'+repr(self.casesensitive)+'\t'+repr(self.recurse)+'\t'+repr(self.verbose)
def OnSaveDocument(self, filename):
# print 'OnSaveDocument() filename=',filename
savefile = open(filename,"wb")
txt = self.GetParams()+'\n'
# print 'writing',txt
savefile.write(txt)
savefile.close()
self.SetModifiedFlag(0)
return 1
ID_OPEN_FILE = 0xe400
ID_GREP = 0xe401
ID_SAVERESULTS = 0x402
ID_TRYAGAIN = 0x403
class GrepView(docview.RichEditView):
def __init__(self, doc):
docview.RichEditView.__init__(self, doc)
self.SetWordWrap(win32ui.CRichEditView_WrapNone)
self.HookHandlers()
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
format = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
self.SetDefaultCharFormat(format)
return rc
def HookHandlers(self):
self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)
self.HookCommand(self.OnCmdOpenFile, ID_OPEN_FILE)
self.HookCommand(self.OnCmdGrep, ID_GREP)
self.HookCommand(self.OnCmdSave, ID_SAVERESULTS)
self.HookCommand(self.OnTryAgain, ID_TRYAGAIN)
self.HookMessage(self.OnLDblClick,win32con.WM_LBUTTONDBLCLK)
def OnLDblClick(self,params):
line = self.GetLine()
regexGrepResult = regexGrep.match(line)
if regexGrepResult:
fname = regexGrepResult.group(1)
line = int(regexGrepResult.group(2))
scriptutils.JumpToDocument(fname, line)
return 0 # dont pass on
return 1 # pass it on by default.
def OnRClick(self, params):
menu = win32ui.CreatePopupMenu()
flags=win32con.MF_STRING|win32con.MF_ENABLED
lineno = self._obj_.LineFromChar(-1) #selection or current line
line = self._obj_.GetLine(lineno)
regexGrepResult = regexGrep.match(line)
if regexGrepResult:
self.fnm = regexGrepResult.group(1)
self.lnnum = int(regexGrepResult.group(2))
menu.AppendMenu(flags, ID_OPEN_FILE, "&Open "+self.fnm)
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, ID_TRYAGAIN, "&Try Again")
charstart, charend = self._obj_.GetSel()
if charstart != charend:
linestart = self._obj_.LineIndex(lineno)
self.sel = line[charstart-linestart:charend-linestart]
menu.AppendMenu(flags, ID_GREP, "&Grep for "+self.sel)
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, 'Cu&t')
menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, '&Copy')
menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, '&Paste')
menu.AppendMenu(flags, win32con.MF_SEPARATOR);
menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all')
menu.AppendMenu(flags, win32con.MF_SEPARATOR);
menu.AppendMenu(flags, ID_SAVERESULTS, 'Sa&ve results')
menu.TrackPopupMenu(params[5])
return 0
def OnCmdOpenFile(self, cmd, code):
doc = win32ui.GetApp().OpenDocumentFile(self.fnm)
if doc:
vw = doc.GetFirstView()
#hope you have an editor that implements GotoLine()!
try:
vw.GotoLine(int(self.lnnum))
except:
pass
return 0
def OnCmdGrep(self, cmd, code):
curparamsstr = self.GetDocument().GetParams()
params = curparamsstr.split('\t')
params[2] = self.sel
greptemplate.setParams('\t'.join(params))
greptemplate.OpenDocumentFile()
return 0
def OnTryAgain(self, cmd, code):
greptemplate.setParams(self.GetDocument().GetParams())
greptemplate.OpenDocumentFile()
return 0
def OnCmdSave(self, cmd, code):
flags = win32con.OFN_OVERWRITEPROMPT
dlg = win32ui.CreateFileDialog(0, None, None, flags, "Text Files (*.txt)|*.txt||", self)
dlg.SetOFNTitle("Save Results As")
if dlg.DoModal() == win32con.IDOK:
pn = dlg.GetPathName()
self._obj_.SaveTextFile(pn)
return 0
def Append(self, strng):
numlines = self.GetLineCount()
endpos = self.LineIndex(numlines-1) + len(self.GetLine(numlines-1))
self.SetSel(endpos, endpos)
self.ReplaceSel(strng)
class GrepDialog(dialog.Dialog):
def __init__(self, dp, fp, gp, cs, r, v):
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [ ["Grep", (0, 0, 210, 90), style, None, (8, "MS Sans Serif")], ]
tmp.append([STATIC, "Grep For:", -1, (7, 7, 50, 9), CS ])
tmp.append([EDIT, gp, 101, (52, 7, 144, 11), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER])
tmp.append([STATIC, "Directories:", -1, (7, 20, 50, 9), CS ])
tmp.append([EDIT, dp, 102, (52, 20, 128, 11), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER])
tmp.append([BUTTON, '...', 110, (182,20, 16, 11), CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP])
tmp.append([STATIC, "File types:", -1, (7, 33, 50, 9), CS ])
tmp.append([EDIT, fp, 103, (52, 33, 128, 11), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER ])
tmp.append([BUTTON, '...', 111, (182,33, 16, 11), CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP])
tmp.append([BUTTON,'Case sensitive', 104, (7, 45, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
tmp.append([BUTTON,'Subdirectories', 105, (7, 56, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
tmp.append([BUTTON,'Verbose', 106, (7, 67, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
tmp.append([BUTTON,'OK', win32con.IDOK, (166,53, 32, 12), CS | win32con.BS_DEFPUSHBUTTON| win32con.WS_TABSTOP])
tmp.append([BUTTON,'Cancel', win32con.IDCANCEL, (166,67, 32, 12), CS | win32con.BS_PUSHBUTTON| win32con.WS_TABSTOP])
dialog.Dialog.__init__(self, tmp)
self.AddDDX(101,'greppattern')
self.AddDDX(102,'dirpattern')
self.AddDDX(103,'filpattern')
self.AddDDX(104,'casesensitive')
self.AddDDX(105,'recursive')
self.AddDDX(106,'verbose')
self._obj_.data['greppattern'] = gp
self._obj_.data['dirpattern'] = dp
self._obj_.data['filpattern'] = fp
self._obj_.data['casesensitive'] = cs
self._obj_.data['recursive'] = r
self._obj_.data['verbose'] = v
self.HookCommand(self.OnMoreDirectories, 110)
self.HookCommand(self.OnMoreFiles, 111)
def OnMoreDirectories(self, cmd, code):
self.getMore('Grep\\Directories', 'dirpattern')
def OnMoreFiles(self, cmd, code):
self.getMore('Grep\\File Types', 'filpattern')
def getMore(self, section, key):
self.UpdateData(1)
#get the items out of the ini file
ini = win32ui.GetProfileFileName()
secitems = win32api.GetProfileSection(section, ini)
items = []
for secitem in secitems:
items.append(secitem.split('=')[1])
dlg = GrepParamsDialog(items)
if dlg.DoModal() == win32con.IDOK:
itemstr = ';'.join(dlg.getItems())
self._obj_.data[key] = itemstr
#update the ini file with dlg.getNew()
i = 0
newitems = dlg.getNew()
if newitems:
items = items + newitems
for item in items:
win32api.WriteProfileVal(section, repr(i), item, ini)
i = i + 1
self.UpdateData(0)
def OnOK(self):
self.UpdateData(1)
for id, name in [(101,'greppattern'), (102,'dirpattern'), (103,'filpattern')]:
if not self[name]:
self.GetDlgItem(id).SetFocus()
win32api.MessageBeep()
win32ui.SetStatusText("Please enter a value")
return
self._obj_.OnOK()
class GrepParamsDialog(dialog.Dialog):
def __init__(self, items):
self.items = items
self.newitems = []
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [ ["Grep Parameters", (0, 0, 205, 100), style, None, (8, "MS Sans Serif")], ]
tmp.append([LISTBOX, '', 107, (7, 7, 150, 72), CS | win32con.LBS_MULTIPLESEL| win32con.LBS_STANDARD | win32con.LBS_HASSTRINGS | win32con.WS_TABSTOP | win32con.LBS_NOTIFY])
tmp.append([BUTTON,'OK', win32con.IDOK, (167, 7, 32, 12), CS | win32con.BS_DEFPUSHBUTTON| win32con.WS_TABSTOP])
tmp.append([BUTTON,'Cancel', win32con.IDCANCEL, (167,23, 32, 12), CS | win32con.BS_PUSHBUTTON| win32con.WS_TABSTOP])
tmp.append([STATIC,'New:', -1, (2, 83, 15, 12), CS])
tmp.append([EDIT, '', 108, (18, 83, 139, 12), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER])
tmp.append([BUTTON,'Add', 109, (167,83, 32, 12), CS | win32con.BS_PUSHBUTTON| win32con.WS_TABSTOP])
dialog.Dialog.__init__(self, tmp)
self.HookCommand(self.OnAddItem, 109)
self.HookCommand(self.OnListDoubleClick, 107)
def OnInitDialog(self):
lb = self.GetDlgItem(107)
for item in self.items:
lb.AddString(item)
return self._obj_.OnInitDialog()
def OnAddItem(self, cmd, code):
eb = self.GetDlgItem(108)
item = eb.GetLine(0)
self.newitems.append(item)
lb = self.GetDlgItem(107)
i = lb.AddString(item)
lb.SetSel(i, 1)
return 1
def OnListDoubleClick(self, cmd, code):
if code == win32con.LBN_DBLCLK:
self.OnOK()
return 1
def OnOK(self):
lb = self.GetDlgItem(107)
self.selections = lb.GetSelTextItems()
self._obj_.OnOK()
def getItems(self):
return self.selections
def getNew(self):
return self.newitems
try:
win32ui.GetApp().RemoveDocTemplate(greptemplate)
except NameError:
pass
greptemplate = GrepTemplate()

View file

@ -0,0 +1,62 @@
# startup.py
#
"The main application startup code for PythonWin."
#
# This does the basic command line handling.
# Keep this as short as possible, cos error output is only redirected if
# this runs OK. Errors in imported modules are much better - the messages go somewhere (not any more :-)
import sys
import win32ui
# You may wish to redirect error output somewhere useful if you have startup errors.
# eg, 'import win32traceutil' will do this for you.
# import win32traceutil # Just uncomment this line to see error output!
# An old class I used to use - generally only useful if Pythonwin is running under MSVC
#class DebugOutput:
# softspace=1
# def write(self,message):
# win32ui.OutputDebug(message)
#sys.stderr=sys.stdout=DebugOutput()
# To fix a problem with Pythonwin when started from the Pythonwin directory,
# we update the pywin path to ensure it is absolute.
# If it is indeed relative, it will be relative to our current directory.
# If its already absolute, then this will have no affect.
import pywin, pywin.framework
pywin.__path__[0] = win32ui.FullPath(pywin.__path__[0])
pywin.framework.__path__[0] = win32ui.FullPath(pywin.framework.__path__[0])
# make a few wierd sys values. This is so later we can clobber sys.argv to trick
# scripts when running under a GUI environment.
moduleName = "pywin.framework.intpyapp"
sys.appargvoffset = 0
sys.appargv = sys.argv[:]
# Must check for /app param here.
if len(sys.argv)>=2 and sys.argv[0].lower()=='/app':
from . import cmdline
moduleName = cmdline.FixArgFileName(sys.argv[1])
sys.appargvoffset = 2
newargv=sys.argv[sys.appargvoffset:]
# newargv.insert(0, sys.argv[0])
sys.argv = newargv
# Import the application module.
__import__(moduleName)
try:
win32ui.GetApp()._obj_
# This worked - an app already exists - do nothing more
except (AttributeError, win32ui.error):
# This means either no app object exists at all, or the one
# that does exist does not have a Python class (ie, was created
# by the host .EXE). In this case, we do the "old style" init...
from . import app
if app.AppBuilder is None:
raise TypeError("No application object has been registered")
app.App = app.AppBuilder()

View file

@ -0,0 +1,173 @@
# Copyright (c) 2000 David Abrahams. Permission to copy, use, modify, sell
# and distribute this software is granted provided this copyright
# notice appears in all copies. This software is provided "as is" without
# express or implied warranty, and with no claim as to its suitability for
# any purpose.
"""Provides a class Stdin which can be used to emulate the regular old
sys.stdin for the PythonWin interactive window. Right now it just pops
up a raw_input() dialog. With luck, someone will integrate it into the
actual PythonWin interactive window someday.
WARNING: Importing this file automatically replaces sys.stdin with an
instance of Stdin (below). This is useful because you can just open
Stdin.py in PythonWin and hit the import button to get it set up right
if you don't feel like changing PythonWin's source. To put things back
the way they were, simply use this magic incantation:
import sys
sys.stdin = sys.stdin.real_file
"""
import sys
try:
get_input_line = raw_input # py2x
except NameError:
get_input_line = input # py3k
class Stdin:
def __init__(self):
self.real_file = sys.stdin # NOTE: Likely to be None in py3k
self.buffer = ""
self.closed = False
def __getattr__(self, name):
"""Forward most functions to the real sys.stdin for absolute realism.
"""
if self.real_file is None:
raise AttributeError(name)
return getattr(self.real_file, name)
def isatty(self):
"""Return 1 if the file is connected to a tty(-like) device, else 0.
"""
return 1
def read(self, size = -1):
"""Read at most size bytes from the file (less if the read
hits EOF or no more data is immediately available on a pipe,
tty or similar device). If the size argument is negative or
omitted, read all data until EOF is reached. The bytes are
returned as a string object. An empty string is returned when
EOF is encountered immediately. (For certain files, like ttys,
it makes sense to continue reading after an EOF is hit.)"""
result_size = self.__get_lines(size)
return self.__extract_from_buffer(result_size)
def readline(self, size = -1):
"""Read one entire line from the file. A trailing newline
character is kept in the string2.6 (but may be absent when a file ends
with an incomplete line). If the size argument is present and
non-negative, it is a maximum byte count (including the trailing
newline) and an incomplete line may be returned. An empty string is
returned when EOF is hit immediately. Note: unlike stdio's fgets(),
the returned string contains null characters ('\0') if they occurred
in the input.
"""
maximum_result_size = self.__get_lines(size, lambda buffer: '\n' in buffer)
if '\n' in self.buffer[:maximum_result_size]:
result_size = self.buffer.find('\n', 0, maximum_result_size) + 1
assert(result_size > 0)
else:
result_size = maximum_result_size
return self.__extract_from_buffer(result_size)
def __extract_from_buffer(self, character_count):
"""Remove the first character_count characters from the internal buffer and
return them.
"""
result = self.buffer[:character_count]
self.buffer = self.buffer[character_count:]
return result
def __get_lines(self, desired_size, done_reading = lambda buffer: False):
"""Keep adding lines to our internal buffer until done_reading(self.buffer)
is true or EOF has been reached or we have desired_size bytes in the buffer.
If desired_size < 0, we are never satisfied until we reach EOF. If done_reading
is not supplied, it is not consulted.
If desired_size < 0, returns the length of the internal buffer. Otherwise,
returns desired_size.
"""
while not done_reading(self.buffer) and (desired_size < 0
or len(self.buffer) < desired_size):
try:
self.__get_line()
except (EOFError, KeyboardInterrupt): # deal with cancellation of get_input_line dialog
desired_size = len(self.buffer) # Be satisfied!
if desired_size < 0:
return len(self.buffer)
else:
return desired_size
def __get_line(self):
"""Grab one line from get_input_line() and append it to the buffer.
"""
line = get_input_line()
print('>>>',line) # echo input to console
self.buffer = self.buffer + line + '\n'
def readlines(self, *sizehint):
"""Read until EOF using readline() and return a list containing the lines
thus read. If the optional sizehint argument is present, instead of
reading up to EOF, whole lines totalling approximately sizehint bytes
(possibly after rounding up to an internal buffer size) are read.
"""
result = []
total_read = 0
while sizehint == () or total_read < sizehint[0]:
line = self.readline()
if line == '':
break
total_read = total_read + len(line)
result.append(line)
return result
if __name__ == "__main__":
test_input = r"""this is some test
input that I am hoping
~
will be very instructive
and when I am done
I will have tested everything.
Twelve and twenty blackbirds
baked in a pie. Patty cake
patty cake so am I.
~
Thirty-five niggling idiots!
Sell you soul to the devil, baby
"""
def fake_raw_input(prompt=None):
"""Replacement for raw_input() which pulls lines out of global test_input.
For testing only!
"""
global test_input
if '\n' not in test_input:
end_of_line_pos = len(test_input)
else:
end_of_line_pos = test_input.find('\n')
result = test_input[:end_of_line_pos]
test_input = test_input[end_of_line_pos + 1:]
if len(result) == 0 or result[0] == '~':
raise EOFError()
return result
get_input_line = fake_raw_input
# Some completely inadequate tests, just to make sure the code's not totally broken
try:
x = Stdin()
print(x.read())
print(x.readline())
print(x.read(12))
print(x.readline(47))
print(x.readline(3))
print(x.readlines())
finally:
get_input_line = raw_input
else:
import sys
sys.stdin = Stdin()

View file

@ -0,0 +1,256 @@
# toolmenu.py
import win32ui
import win32con
import win32api
from . import app
import sys
import string
tools = {}
idPos = 100
# The default items should no tools menu exist in the INI file.
defaultToolMenuItems = [
('Browser', 'win32ui.GetApp().OnViewBrowse(0,0)'),
('Browse PythonPath', 'from pywin.tools import browseProjects;browseProjects.Browse()'),
('Edit Python Path', 'from pywin.tools import regedit;regedit.EditRegistry()'),
('COM Makepy utility', 'from win32com.client import makepy;makepy.main()'),
('COM Browser', 'from win32com.client import combrowse;combrowse.main()'),
('Trace Collector Debugging tool', 'from pywin.tools import TraceCollector;TraceCollector.MakeOutputWindow()'),
]
def LoadToolMenuItems():
# Load from the registry.
items = []
lookNo = 1
while 1:
menu = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "", "")
if menu=="":
break
cmd = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "Command", "")
items.append((menu, cmd))
lookNo = lookNo + 1
if len(items)==0:
items = defaultToolMenuItems
return items
def WriteToolMenuItems( items ):
# Items is a list of (menu, command)
# Delete the entire registry tree.
try:
mainKey = win32ui.GetAppRegistryKey()
toolKey = win32api.RegOpenKey(mainKey, "Tools Menu")
except win32ui.error:
toolKey = None
if toolKey is not None:
while 1:
try:
subkey = win32api.RegEnumKey(toolKey, 0)
except win32api.error:
break
win32api.RegDeleteKey(toolKey, subkey)
# Keys are now removed - write the new ones.
# But first check if we have the defaults - and if so, dont write anything!
if items==defaultToolMenuItems:
return
itemNo = 1
for menu, cmd in items:
win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "", menu)
win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "Command", cmd)
itemNo = itemNo + 1
def SetToolsMenu(menu, menuPos = None):
global tools
global idPos
# todo - check the menu does not already exist.
# Create the new menu
toolsMenu = win32ui.CreatePopupMenu()
# Load from the ini file.
items = LoadToolMenuItems()
for menuString, cmd in items:
tools[idPos] = (menuString, cmd, menuString)
toolsMenu.AppendMenu(win32con.MF_ENABLED|win32con.MF_STRING,idPos, menuString)
win32ui.GetMainFrame().HookCommand(HandleToolCommand, idPos)
idPos=idPos+1
# Find the correct spot to insert the new tools menu.
if menuPos is None:
menuPos = menu.GetMenuItemCount()-2
if menuPos<0: menuPos=0
menu.InsertMenu(menuPos, win32con.MF_BYPOSITION|win32con.MF_ENABLED|win32con.MF_STRING|win32con.MF_POPUP, toolsMenu.GetHandle(), '&Tools')
def HandleToolCommand(cmd, code):
import traceback
import re
global tools
(menuString, pyCmd, desc) = tools[cmd]
win32ui.SetStatusText("Executing tool %s" % desc, 1)
pyCmd = re.sub('\\\\n','\n', pyCmd)
win32ui.DoWaitCursor(1)
oldFlag = None
try:
oldFlag = sys.stdout.template.writeQueueing
sys.stdout.template.writeQueueing = 0
except (NameError, AttributeError):
pass
try:
exec("%s\n" % pyCmd)
worked=1
except SystemExit:
# The program raised a SystemExit - ignore it.
worked = 1
except:
print("Failed to execute command:\n%s" % pyCmd)
traceback.print_exc()
worked=0
if oldFlag is not None:
sys.stdout.template.writeQueueing = oldFlag
win32ui.DoWaitCursor(0)
if worked:
text = "Completed successfully."
else:
text = "Error executing %s." % desc
win32ui.SetStatusText(text, 1)
# The property page for maintaing the items on the Tools menu.
import commctrl
from pywin.mfc import dialog
if win32ui.UNICODE:
LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITW
else:
LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITA
class ToolMenuPropPage(dialog.PropertyPage):
def __init__(self):
self.bImChangingEditControls = 0 # Am I programatically changing the controls?
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_TOOLMENU)
def OnInitDialog(self):
self.editMenuCommand = self.GetDlgItem(win32ui.IDC_EDIT2)
self.butNew = self.GetDlgItem(win32ui.IDC_BUTTON3)
# Now hook the change notification messages for the edit controls.
self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT1)
self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT2)
self.HookNotify(self.OnNotifyListControl, commctrl.LVN_ITEMCHANGED)
self.HookNotify(self.OnNotifyListControlEndLabelEdit, commctrl.LVN_ENDLABELEDIT)
# Hook the button clicks.
self.HookCommand(self.OnButtonNew, win32ui.IDC_BUTTON3) # New Item
self.HookCommand(self.OnButtonDelete, win32ui.IDC_BUTTON4) # Delete item
self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON1) # Move up
self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON2) # Move down
# Setup the columns in the list control
lc = self.GetDlgItem(win32ui.IDC_LIST1)
rect = lc.GetWindowRect()
cx = rect[2] - rect[0]
colSize = cx/2 - win32api.GetSystemMetrics(win32con.SM_CXBORDER) - 1
item = commctrl.LVCFMT_LEFT, colSize, "Menu Text"
lc.InsertColumn(0, item)
item = commctrl.LVCFMT_LEFT, colSize, "Python Command"
lc.InsertColumn(1, item)
# Insert the existing tools menu
itemNo = 0
for desc, cmd in LoadToolMenuItems():
lc.InsertItem(itemNo, desc)
lc.SetItemText(itemNo, 1, cmd)
itemNo = itemNo + 1
self.listControl = lc
return dialog.PropertyPage.OnInitDialog(self)
def OnOK(self):
# Write the menu back to the registry.
items = []
itemLook = 0
while 1:
try:
text = self.listControl.GetItemText(itemLook, 0);
if not text:
break
items.append( (text, self.listControl.GetItemText(itemLook, 1)) )
except win32ui.error:
# no more items!
break
itemLook = itemLook + 1
WriteToolMenuItems( items )
return self._obj_.OnOK()
def OnCommandEditControls(self, id, cmd):
# print "OnEditControls", id, cmd
if cmd==win32con.EN_CHANGE and not self.bImChangingEditControls:
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
newText = self.editMenuCommand.GetWindowText()
self.listControl.SetItemText(itemNo, 1, newText)
return 0
def OnNotifyListControlEndLabelEdit(self, id, cmd):
newText = self.listControl.GetEditControl().GetWindowText()
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
self.listControl.SetItemText(itemNo, 0, newText)
def OnNotifyListControl(self, id, cmd):
# print id, cmd
try:
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
except win32ui.error: # No selection!
return
self.bImChangingEditControls = 1
try:
item = self.listControl.GetItem(itemNo, 1)
self.editMenuCommand.SetWindowText(item[4])
finally:
self.bImChangingEditControls = 0
return 0 # we have handled this!
def OnButtonNew(self, id, cmd):
if cmd==win32con.BN_CLICKED:
newIndex = self.listControl.GetItemCount()
self.listControl.InsertItem(newIndex, "Click to edit the text")
self.listControl.EnsureVisible(newIndex, 0)
def OnButtonMove(self, id, cmd):
if cmd==win32con.BN_CLICKED:
try:
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
except win32ui.error:
return
menu = self.listControl.GetItemText(itemNo, 0)
cmd = self.listControl.GetItemText(itemNo, 1)
if id == win32ui.IDC_BUTTON1:
# Move up
if itemNo > 0:
self.listControl.DeleteItem(itemNo)
# reinsert it.
self.listControl.InsertItem(itemNo-1, menu)
self.listControl.SetItemText(itemNo-1, 1, cmd)
else:
# Move down.
if itemNo < self.listControl.GetItemCount()-1:
self.listControl.DeleteItem(itemNo)
# reinsert it.
self.listControl.InsertItem(itemNo+1, menu)
self.listControl.SetItemText(itemNo+1, 1, cmd)
def OnButtonDelete(self, id, cmd):
if cmd==win32con.BN_CLICKED:
try:
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
except win32ui.error: # No selection!
return
self.listControl.DeleteItem(itemNo)

View file

@ -0,0 +1,13 @@
# Framework Window classes.
# Most Pythonwin windows should use these classes rather than
# the raw MFC ones if they want Pythonwin specific functionality.
import pywin.mfc.window
import win32con
class MDIChildWnd(pywin.mfc.window.MDIChildWnd):
def AutoRestore(self):
"If the window is minimised or maximised, restore it."
p = self.GetWindowPlacement()
if p[1]==win32con.SW_MINIMIZE or p[1]==win32con.SW_SHOWMINIMIZED:
self.SetWindowPlacement(p[0], win32con.SW_RESTORE, p[2], p[3], p[4])

View file

@ -0,0 +1,518 @@
# winout.py
#
# generic "output window"
#
# This Window will detect itself closing, and recreate next time output is
# written to it.
# This has the option of writing output at idle time (by hooking the
# idle message, and queueing output) or writing as each
# write is executed.
# Updating the window directly gives a jerky appearance as many writes
# take place between commands, and the windows scrolls, and updates etc
# Updating at idle-time may defer all output of a long process, giving the
# appearence nothing is happening.
# There is a compromise "line" mode, which will output whenever
# a complete line is available.
# behaviour depends on self.writeQueueing
# This module is thread safe - output can originate from any thread. If any thread
# other than the main thread attempts to print, it is always queued until next idle time
import sys, string, re
from pywin.mfc import docview
from pywin.framework import app, window
import win32ui, win32api, win32con
import queue
debug = lambda msg: None
##debug=win32ui.OutputDebugString
##import win32trace;win32trace.InitWrite() # for debugging - delete me!
##debug = win32trace.write
class flags:
# queueing of output.
WQ_NONE = 0
WQ_LINE = 1
WQ_IDLE = 2
#WindowOutputDocumentParent=docview.RichEditDoc
#WindowOutputDocumentParent=docview.Document
import pywin.scintilla.document
from pywin.scintilla import scintillacon
from pywin import default_scintilla_encoding
WindowOutputDocumentParent=pywin.scintilla.document.CScintillaDocument
class WindowOutputDocument(WindowOutputDocumentParent):
def SaveModified(self):
return 1 # say it is OK to destroy my document
def OnSaveDocument( self, fileName ):
win32ui.SetStatusText("Saving file...",1)
try:
self.SaveFile(fileName)
except IOError as details:
win32ui.MessageBox("Error - could not save file\r\n\r\n%s"%details)
return 0
win32ui.SetStatusText("Ready")
return 1
class WindowOutputFrame(window.MDIChildWnd):
def __init__(self, wnd = None):
window.MDIChildWnd.__init__(self, wnd)
self.HookMessage(self.OnSizeMove, win32con.WM_SIZE)
self.HookMessage(self.OnSizeMove, win32con.WM_MOVE)
def LoadFrame( self, idResource, style, wndParent, context ):
self.template = context.template
return self._obj_.LoadFrame(idResource, style, wndParent, context)
def PreCreateWindow(self, cc):
cc = self._obj_.PreCreateWindow(cc)
if self.template.defSize and self.template.defSize[0] != self.template.defSize[1]:
rect = app.RectToCreateStructRect(self.template.defSize)
cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8]
return cc
def OnSizeMove(self, msg):
# so recreate maintains position.
# Need to map coordinates from the
# frame windows first child.
mdiClient = self.GetParent()
self.template.defSize = mdiClient.ScreenToClient(self.GetWindowRect())
def OnDestroy(self, message):
self.template.OnFrameDestroy(self)
return 1
class WindowOutputViewImpl:
def __init__(self):
self.patErrorMessage=re.compile('\W*File "(.*)", line ([0-9]+)')
self.template = self.GetDocument().GetDocTemplate()
def HookHandlers(self):
# Hook for the right-click menu.
self.HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
def OnDestroy(self, msg):
self.template.OnViewDestroy(self)
def OnInitialUpdate(self):
self.RestoreKillBuffer()
self.SetSel(-2) # end of buffer
def GetRightMenuItems(self):
ret = []
flags=win32con.MF_STRING|win32con.MF_ENABLED
ret.append((flags, win32ui.ID_EDIT_COPY, '&Copy'))
ret.append((flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all'))
return ret
#
# Windows command handlers, virtuals, etc.
#
def OnRClick(self,params):
paramsList = self.GetRightMenuItems()
menu = win32ui.CreatePopupMenu()
for appendParams in paramsList:
if type(appendParams)!=type(()):
appendParams = (appendParams,)
menu.AppendMenu(*appendParams)
menu.TrackPopupMenu(params[5]) # track at mouse position.
return 0
# as this is often used as an output window, exeptions will often
# be printed. Therefore, we support this functionality at this level.
# Returns TRUE if the current line is an error message line, and will
# jump to it. FALSE if no error (and no action taken)
def HandleSpecialLine(self):
from . import scriptutils
line = self.GetLine()
if line[:11]=="com_error: ":
# An OLE Exception - pull apart the exception
# and try and locate a help file.
try:
import win32api, win32con
det = eval(line[line.find(":")+1:].strip())
win32ui.SetStatusText("Opening help file on OLE error...");
from . import help
help.OpenHelpFile(det[2][3],win32con.HELP_CONTEXT, det[2][4])
return 1
except win32api.error as details:
win32ui.SetStatusText("The help file could not be opened - %s" % details.strerror)
return 1
except:
win32ui.SetStatusText("Line is a COM error, but no WinHelp details can be parsed");
# Look for a Python traceback.
matchResult = self.patErrorMessage.match(line)
if matchResult is None:
# No match - try the previous line
lineNo = self.LineFromChar()
if lineNo > 0:
line = self.GetLine(lineNo-1)
matchResult = self.patErrorMessage.match(line)
if matchResult is not None:
# we have an error line.
fileName = matchResult.group(1)
if fileName[0]=="<":
win32ui.SetStatusText("Can not load this file")
return 1 # still was an error message.
else:
lineNoString = matchResult.group(2)
# Attempt to locate the file (in case it is a relative spec)
fileNameSpec = fileName
fileName = scriptutils.LocatePythonFile(fileName)
if fileName is None:
# Dont force update, so it replaces the idle prompt.
win32ui.SetStatusText("Cant locate the file '%s'" % (fileNameSpec), 0)
return 1
win32ui.SetStatusText("Jumping to line "+lineNoString+" of file "+fileName,1)
if not scriptutils.JumpToDocument(fileName, int(lineNoString)):
win32ui.SetStatusText("Could not open %s" % fileName)
return 1 # still was an error message.
return 1
return 0 # not an error line
def write(self, msg):
return self.template.write(msg)
def writelines(self, lines):
for line in lines:
self.write(line)
def flush(self):
self.template.flush()
class WindowOutputViewRTF(docview.RichEditView, WindowOutputViewImpl):
def __init__(self, doc):
docview.RichEditView.__init__(self, doc)
WindowOutputViewImpl.__init__(self)
def OnInitialUpdate(self):
WindowOutputViewImpl.OnInitialUpdate(self)
return docview.RichEditView.OnInitialUpdate(self)
def OnDestroy(self, msg):
WindowOutputViewImpl.OnDestroy(self, msg)
docview.RichEditView.OnDestroy(self, msg)
def HookHandlers(self):
WindowOutputViewImpl.HookHandlers(self)
# Hook for finding and locating error messages
self.HookMessage(self.OnLDoubleClick,win32con.WM_LBUTTONDBLCLK)
# docview.RichEditView.HookHandlers(self)
def OnLDoubleClick(self,params):
if self.HandleSpecialLine():
return 0 # dont pass on
return 1 # pass it on by default.
def RestoreKillBuffer(self):
if len(self.template.killBuffer):
self.StreamIn(win32con.SF_RTF, self._StreamRTFIn)
self.template.killBuffer = []
def SaveKillBuffer(self):
self.StreamOut(win32con.SF_RTFNOOBJS, self._StreamRTFOut)
def _StreamRTFOut(self, data):
self.template.killBuffer.append(data)
return 1 # keep em coming!
def _StreamRTFIn(self, bytes):
try:
item = self.template.killBuffer[0]
self.template.killBuffer.remove(item)
if bytes < len(item):
print("Warning - output buffer not big enough!")
return item
except IndexError:
return None
def dowrite(self, str):
self.SetSel(-2)
self.ReplaceSel(str)
import pywin.scintilla.view
class WindowOutputViewScintilla(pywin.scintilla.view.CScintillaView, WindowOutputViewImpl):
def __init__(self, doc):
pywin.scintilla.view.CScintillaView.__init__(self, doc)
WindowOutputViewImpl.__init__(self)
def OnInitialUpdate(self):
pywin.scintilla.view.CScintillaView.OnInitialUpdate(self)
self.SCISetMarginWidth(3)
WindowOutputViewImpl.OnInitialUpdate(self)
def OnDestroy(self, msg):
WindowOutputViewImpl.OnDestroy(self, msg)
pywin.scintilla.view.CScintillaView.OnDestroy(self, msg)
def HookHandlers(self):
WindowOutputViewImpl.HookHandlers(self)
pywin.scintilla.view.CScintillaView.HookHandlers(self)
self.GetParent().HookNotify(self.OnScintillaDoubleClick, scintillacon.SCN_DOUBLECLICK)
## self.HookMessage(self.OnLDoubleClick,win32con.WM_LBUTTONDBLCLK)
def OnScintillaDoubleClick(self, std, extra):
self.HandleSpecialLine()
## def OnLDoubleClick(self,params):
## return 0 # never dont pass on
def RestoreKillBuffer(self):
assert len(self.template.killBuffer) in [0,1], "Unexpected killbuffer contents"
if self.template.killBuffer:
self.SCIAddText(self.template.killBuffer[0])
self.template.killBuffer = []
def SaveKillBuffer(self):
self.template.killBuffer = [self.GetTextRange(0,-1)]
def dowrite(self, str):
end = self.GetTextLength()
atEnd = end==self.GetSel()[0]
self.SCIInsertText(str, end)
if atEnd:
self.SetSel(self.GetTextLength())
def SetWordWrap(self, bWrapOn = 1):
if bWrapOn:
wrap_mode = scintillacon.SC_WRAP_WORD
else:
wrap_mode = scintillacon.SC_WRAP_NONE
self.SCISetWrapMode(wrap_mode)
def _MakeColorizer(self):
return None # No colorizer for me!
WindowOutputView = WindowOutputViewScintilla
# The WindowOutput class is actually an MFC template. This is a conventient way of
# making sure that my state can exist beyond the life of the windows themselves.
# This is primarily to support the functionality of a WindowOutput window automatically
# being recreated if necessary when written to.
class WindowOutput(docview.DocTemplate):
""" Looks like a general Output Window - text can be written by the 'write' method.
Will auto-create itself on first write, and also on next write after being closed """
softspace=1
def __init__(self, title=None, defSize=None, queueing = flags.WQ_LINE, \
bAutoRestore = 1, style=None,
makeDoc = None, makeFrame = None, makeView = None):
""" init the output window -
Params
title=None -- What is the title of the window
defSize=None -- What is the default size for the window - if this
is a string, the size will be loaded from the ini file.
queueing = flags.WQ_LINE -- When should output be written
bAutoRestore=1 -- Should a minimized window be restored.
style -- Style for Window, or None for default.
makeDoc, makeFrame, makeView -- Classes for frame, view and window respectively.
"""
if makeDoc is None: makeDoc = WindowOutputDocument
if makeFrame is None: makeFrame = WindowOutputFrame
if makeView is None: makeView = WindowOutputViewScintilla
docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, \
makeDoc, makeFrame, makeView)
self.SetDocStrings("\nOutput\n\nText Documents (*.txt)\n.txt\n\n\n")
win32ui.GetApp().AddDocTemplate(self)
self.writeQueueing = queueing
self.errorCantRecreate = 0
self.killBuffer=[]
self.style = style
self.bAutoRestore = bAutoRestore
self.title = title
self.bCreating = 0
self.interruptCount = 0
if type(defSize)==type(''): # is a string - maintain size pos from ini file.
self.iniSizeSection = defSize
self.defSize = app.LoadWindowSize(defSize)
self.loadedSize = self.defSize
else:
self.iniSizeSection = None
self.defSize=defSize
self.currentView = None
self.outputQueue = queue.Queue(-1)
self.mainThreadId = win32api.GetCurrentThreadId()
self.idleHandlerSet = 0
self.SetIdleHandler()
def __del__(self):
self.Close()
def Create(self, title=None, style = None):
self.bCreating = 1
if title: self.title = title
if style: self.style = style
doc=self.OpenDocumentFile()
if doc is None: return
self.currentView = doc.GetFirstView()
self.bCreating = 0
if self.title: doc.SetTitle(self.title)
def Close(self):
self.RemoveIdleHandler()
try:
parent = self.currentView.GetParent()
except (AttributeError, win32ui.error): # Already closed
return
parent.DestroyWindow()
def SetTitle(self, title):
self.title = title
if self.currentView: self.currentView.GetDocument().SetTitle(self.title)
def OnViewDestroy(self, view):
self.currentView.SaveKillBuffer()
self.currentView = None
def OnFrameDestroy(self, frame):
if self.iniSizeSection:
# use GetWindowPlacement(), as it works even when min'd or max'd
newSize = frame.GetWindowPlacement()[4]
if self.loadedSize!=newSize:
app.SaveWindowSize(self.iniSizeSection, newSize)
def SetIdleHandler(self):
if not self.idleHandlerSet:
debug("Idle handler set\n")
win32ui.GetApp().AddIdleHandler(self.QueueIdleHandler)
self.idleHandlerSet = 1
def RemoveIdleHandler(self):
if self.idleHandlerSet:
debug("Idle handler reset\n")
if (win32ui.GetApp().DeleteIdleHandler(self.QueueIdleHandler)==0):
debug('Error deleting idle handler\n')
self.idleHandlerSet = 0
def RecreateWindow(self):
if self.errorCantRecreate:
debug("Error = not trying again")
return 0
try:
# This will fail if app shutting down
win32ui.GetMainFrame().GetSafeHwnd()
self.Create()
return 1
except (win32ui.error, AttributeError):
self.errorCantRecreate = 1
debug("Winout can not recreate the Window!\n")
return 0
# this handles the idle message, and does the printing.
def QueueIdleHandler(self,handler,count):
try:
bEmpty = self.QueueFlush(20)
# If the queue is empty, then we are back to idle and restart interrupt logic.
if bEmpty: self.interruptCount = 0
except KeyboardInterrupt:
# First interrupt since idle we just pass on.
# later ones we dump the queue and give up.
self.interruptCount = self.interruptCount + 1
if self.interruptCount > 1:
# Drop the queue quickly as the user is already annoyed :-)
self.outputQueue = queue.Queue(-1)
print("Interrupted.")
bEmpty = 1
else:
raise # re-raise the error so the users exception filters up.
return not bEmpty # More to do if not empty.
# Returns true if the Window needs to be recreated.
def NeedRecreateWindow(self):
try:
if self.currentView is not None and self.currentView.IsWindow():
return 0
except (win32ui.error, AttributeError): # Attribute error if the win32ui object has died.
pass
return 1
# Returns true if the Window is OK (either cos it was, or because it was recreated
def CheckRecreateWindow(self):
if self.bCreating: return 1
if not self.NeedRecreateWindow():
return 1
if self.bAutoRestore:
if self.RecreateWindow():
return 1
return 0
def QueueFlush(self, max = None):
# Returns true if the queue is empty after the flush
# debug("Queueflush - %d, %d\n" % (max, self.outputQueue.qsize()))
if self.bCreating: return 1
items = []
rc = 0
while max is None or max > 0:
try:
item = self.outputQueue.get_nowait()
items.append(item)
except queue.Empty:
rc = 1
break
if max is not None:
max = max - 1
if len(items) != 0:
if not self.CheckRecreateWindow():
debug(":Recreate failed!\n")
return 1 # In trouble - so say we have nothing to do.
win32ui.PumpWaitingMessages() # Pump paint messages
self.currentView.dowrite(''.join(items))
return rc
def HandleOutput(self,message):
# debug("QueueOutput on thread %d, flags %d with '%s'...\n" % (win32api.GetCurrentThreadId(), self.writeQueueing, message ))
self.outputQueue.put(message)
if win32api.GetCurrentThreadId() != self.mainThreadId:
pass
# debug("not my thread - ignoring queue options!\n")
elif self.writeQueueing==flags.WQ_LINE:
pos = message.rfind('\n')
if pos>=0:
# debug("Line queueing - forcing flush\n")
self.QueueFlush()
return
elif self.writeQueueing==flags.WQ_NONE:
# debug("WQ_NONE - flushing!\n")
self.QueueFlush()
return
# Let our idle handler get it - wake it up
try:
win32ui.GetMainFrame().PostMessage(win32con.WM_USER) # Kick main thread off.
except win32ui.error:
# This can happen as the app is shutting down, so we send it to the C++ debugger
win32api.OutputDebugString(message)
# delegate certain fns to my view.
def writelines(self, lines):
for line in lines:
self.write(line)
def write(self,message):
self.HandleOutput(message)
def flush(self):
self.QueueFlush()
def HandleSpecialLine(self):
self.currentView.HandleSpecialLine()
def RTFWindowOutput(*args, **kw):
kw['makeView'] = WindowOutputViewRTF
return WindowOutput(*args, **kw)
def thread_test(o):
for i in range(5):
o.write("Hi from thread %d\n" % (win32api.GetCurrentThreadId()))
win32api.Sleep(100)
def test():
w = WindowOutput(queueing=flags.WQ_IDLE)
w.write("First bit of text\n")
import _thread
for i in range(5):
w.write("Hello from the main thread\n")
_thread.start_new(thread_test, (w,))
for i in range(2):
w.write("Hello from the main thread\n")
win32api.Sleep(50)
return w
if __name__=='__main__':
test()