install pywin32-220
This commit is contained in:
parent
15d9012f5a
commit
93f7d415a0
581 changed files with 100203 additions and 0 deletions
0
Lib/site-packages/pythonwin/pywin/framework/__init__.py
Normal file
0
Lib/site-packages/pythonwin/pywin/framework/__init__.py
Normal file
408
Lib/site-packages/pythonwin/pywin/framework/app.py
Normal file
408
Lib/site-packages/pythonwin/pywin/framework/app.py
Normal 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", "http://starship.python.net/crew/mhammond/win32", 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
|
||||
143
Lib/site-packages/pythonwin/pywin/framework/bitmap.py
Normal file
143
Lib/site-packages/pythonwin/pywin/framework/bitmap.py
Normal 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))
|
||||
49
Lib/site-packages/pythonwin/pywin/framework/cmdline.py
Normal file
49
Lib/site-packages/pythonwin/pywin/framework/cmdline.py
Normal 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]
|
||||
170
Lib/site-packages/pythonwin/pywin/framework/dbgcommands.py
Normal file
170
Lib/site-packages/pythonwin/pywin/framework/dbgcommands.py
Normal 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)
|
||||
69
Lib/site-packages/pythonwin/pywin/framework/dlgappcore.py
Normal file
69
Lib/site-packages/pythonwin/pywin/framework/dlgappcore.py
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
257
Lib/site-packages/pythonwin/pywin/framework/editor/configui.py
Normal file
257
Lib/site-packages/pythonwin/pywin/framework/editor/configui.py
Normal 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()
|
||||
332
Lib/site-packages/pythonwin/pywin/framework/editor/document.py
Normal file
332
Lib/site-packages/pythonwin/pywin/framework/editor/document.py
Normal 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)
|
||||
465
Lib/site-packages/pythonwin/pywin/framework/editor/editor.py
Normal file
465
Lib/site-packages/pythonwin/pywin/framework/editor/editor.py
Normal 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)
|
||||
76
Lib/site-packages/pythonwin/pywin/framework/editor/frame.py
Normal file
76
Lib/site-packages/pythonwin/pywin/framework/editor/frame.py
Normal 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()
|
||||
|
||||
|
|
@ -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)
|
||||
93
Lib/site-packages/pythonwin/pywin/framework/editor/vss.py
Normal file
93
Lib/site-packages/pythonwin/pywin/framework/editor/vss.py
Normal 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
|
||||
|
||||
|
||||
151
Lib/site-packages/pythonwin/pywin/framework/help.py
Normal file
151
Lib/site-packages/pythonwin/pywin/framework/help.py
Normal 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])
|
||||
870
Lib/site-packages/pythonwin/pywin/framework/interact.py
Normal file
870
Lib/site-packages/pythonwin/pywin/framework/interact.py
Normal 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()
|
||||
447
Lib/site-packages/pythonwin/pywin/framework/intpyapp.py
Normal file
447
Lib/site-packages/pythonwin/pywin/framework/intpyapp.py
Normal 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()
|
||||
56
Lib/site-packages/pythonwin/pywin/framework/intpydde.py
Normal file
56
Lib/site-packages/pythonwin/pywin/framework/intpydde.py
Normal 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
|
||||
|
||||
629
Lib/site-packages/pythonwin/pywin/framework/mdi_pychecker.py
Normal file
629
Lib/site-packages/pythonwin/pywin/framework/mdi_pychecker.py
Normal 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()
|
||||
623
Lib/site-packages/pythonwin/pywin/framework/scriptutils.py
Normal file
623
Lib/site-packages/pythonwin/pywin/framework/scriptutils.py
Normal 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)
|
||||
530
Lib/site-packages/pythonwin/pywin/framework/sgrepmdi.py
Normal file
530
Lib/site-packages/pythonwin/pywin/framework/sgrepmdi.py
Normal 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()
|
||||
62
Lib/site-packages/pythonwin/pywin/framework/startup.py
Normal file
62
Lib/site-packages/pythonwin/pywin/framework/startup.py
Normal 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()
|
||||
173
Lib/site-packages/pythonwin/pywin/framework/stdin.py
Normal file
173
Lib/site-packages/pythonwin/pywin/framework/stdin.py
Normal 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()
|
||||
|
||||
256
Lib/site-packages/pythonwin/pywin/framework/toolmenu.py
Normal file
256
Lib/site-packages/pythonwin/pywin/framework/toolmenu.py
Normal 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)
|
||||
13
Lib/site-packages/pythonwin/pywin/framework/window.py
Normal file
13
Lib/site-packages/pythonwin/pywin/framework/window.py
Normal 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])
|
||||
518
Lib/site-packages/pythonwin/pywin/framework/winout.py
Normal file
518
Lib/site-packages/pythonwin/pywin/framework/winout.py
Normal 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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue