mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-03 14:49:07 +00:00
first commit
This commit is contained in:
454
.venv/Lib/site-packages/pythonwin/pywin/framework/app.py
Normal file
454
.venv/Lib/site-packages/pythonwin/pywin/framework/app.py
Normal file
@ -0,0 +1,454 @@
|
||||
# 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 sysconfig
|
||||
|
||||
site_packages = sysconfig.get_paths()["platlib"]
|
||||
try:
|
||||
build_no = (
|
||||
open(os.path.join(site_packages, "pywin32.version.txt")).read().strip()
|
||||
)
|
||||
ver = "pywin32 build %s" % build_no
|
||||
except EnvironmentError:
|
||||
ver = None
|
||||
if ver is None:
|
||||
# See if we are Part of Active Python
|
||||
ver = _GetRegistryValue(
|
||||
"SOFTWARE\\ActiveState\\ActivePython", "CurrentVersion"
|
||||
)
|
||||
if ver is not None:
|
||||
ver = "ActivePython build %s" % (ver,)
|
||||
if ver is None:
|
||||
ver = ""
|
||||
self.SetDlgItemText(win32ui.IDC_ABOUT_VERSION, ver)
|
||||
self.HookCommand(self.OnButHomePage, win32ui.IDC_BUTTON1)
|
||||
|
||||
def OnButHomePage(self, id, code):
|
||||
if code == win32con.BN_CLICKED:
|
||||
win32api.ShellExecute(
|
||||
0, "open", "https://github.com/mhammond/pywin32", None, "", 1
|
||||
)
|
||||
|
||||
|
||||
def Win32RawInput(prompt=None):
|
||||
"Provide raw_input() for gui apps"
|
||||
# flush stderr/out first.
|
||||
try:
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
except:
|
||||
pass
|
||||
if prompt is None:
|
||||
prompt = ""
|
||||
ret = dialog.GetSimpleInput(prompt)
|
||||
if ret == None:
|
||||
raise KeyboardInterrupt("operation cancelled")
|
||||
return ret
|
||||
|
||||
|
||||
def Win32Input(prompt=None):
|
||||
"Provide input() for gui apps"
|
||||
return eval(input(prompt))
|
||||
|
||||
|
||||
def HookInput():
|
||||
try:
|
||||
raw_input
|
||||
# must be py2x...
|
||||
sys.modules["__builtin__"].raw_input = Win32RawInput
|
||||
sys.modules["__builtin__"].input = Win32Input
|
||||
except NameError:
|
||||
# must be py3k
|
||||
import code
|
||||
|
||||
sys.modules["builtins"].input = Win32RawInput
|
||||
|
||||
|
||||
def HaveGoodGUI():
|
||||
"""Returns true if we currently have a good gui available."""
|
||||
return "pywin.framework.startup" in sys.modules
|
||||
|
||||
|
||||
def CreateDefaultGUI(appClass=None):
|
||||
"""Creates a default GUI environment"""
|
||||
if appClass is None:
|
||||
from . import intpyapp # Bring in the default app - could be param'd later.
|
||||
|
||||
appClass = intpyapp.InteractivePythonApp
|
||||
# Create and init the app.
|
||||
appClass().InitInstance()
|
||||
|
||||
|
||||
def CheckCreateDefaultGUI():
|
||||
"""Checks and creates if necessary a default GUI environment."""
|
||||
rc = HaveGoodGUI()
|
||||
if not rc:
|
||||
CreateDefaultGUI()
|
||||
return rc
|
165
.venv/Lib/site-packages/pythonwin/pywin/framework/bitmap.py
Normal file
165
.venv/Lib/site-packages/pythonwin/pywin/framework/bitmap.py
Normal file
@ -0,0 +1,165 @@
|
||||
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))
|
55
.venv/Lib/site-packages/pythonwin/pywin/framework/cmdline.py
Normal file
55
.venv/Lib/site-packages/pythonwin/pywin/framework/cmdline.py
Normal file
@ -0,0 +1,55 @@
|
||||
# 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]
|
187
.venv/Lib/site-packages/pythonwin/pywin/framework/dbgcommands.py
Normal file
187
.venv/Lib/site-packages/pythonwin/pywin/framework/dbgcommands.py
Normal file
@ -0,0 +1,187 @@
|
||||
# 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)
|
@ -0,0 +1,74 @@
|
||||
# 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,235 @@
|
||||
# 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,103 @@
|
||||
# __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,667 @@
|
||||
# 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)
|
@ -0,0 +1,307 @@
|
||||
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()
|
@ -0,0 +1,378 @@
|
||||
# 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)
|
@ -0,0 +1,526 @@
|
||||
#####################################################################
|
||||
#
|
||||
# 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)
|
@ -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,59 @@
|
||||
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)
|
100
.venv/Lib/site-packages/pythonwin/pywin/framework/editor/vss.py
Normal file
100
.venv/Lib/site-packages/pythonwin/pywin/framework/editor/vss.py
Normal file
@ -0,0 +1,100 @@
|
||||
# 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
|
174
.venv/Lib/site-packages/pythonwin/pywin/framework/help.py
Normal file
174
.venv/Lib/site-packages/pythonwin/pywin/framework/help.py
Normal file
@ -0,0 +1,174 @@
|
||||
# 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])
|
985
.venv/Lib/site-packages/pythonwin/pywin/framework/interact.py
Normal file
985
.venv/Lib/site-packages/pythonwin/pywin/framework/interact.py
Normal file
@ -0,0 +1,985 @@
|
||||
##################################################################
|
||||
##
|
||||
## Interactive Shell Window
|
||||
##
|
||||
|
||||
import sys, os
|
||||
import code
|
||||
import string
|
||||
|
||||
import win32ui
|
||||
import win32api
|
||||
import win32clipboard
|
||||
import win32con
|
||||
import traceback
|
||||
import afxres
|
||||
import array
|
||||
import __main__
|
||||
|
||||
import pywin.scintilla.formatter
|
||||
import pywin.scintilla.control
|
||||
import pywin.scintilla.IDLEenvironment
|
||||
import pywin.framework.app
|
||||
|
||||
## sequential after ID_GOTO_LINE defined in editor.py
|
||||
ID_EDIT_COPY_CODE = 0xE2002
|
||||
ID_EDIT_EXEC_CLIPBOARD = 0x2003
|
||||
|
||||
trace = pywin.scintilla.formatter.trace
|
||||
|
||||
from . import winout
|
||||
|
||||
import re
|
||||
|
||||
# from IDLE.
|
||||
_is_block_opener = re.compile(r":\s*(#.*)?$").search
|
||||
_is_block_closer = re.compile(
|
||||
r"""
|
||||
\s*
|
||||
( return
|
||||
| break
|
||||
| continue
|
||||
| raise
|
||||
| pass
|
||||
)
|
||||
\b
|
||||
""",
|
||||
re.VERBOSE,
|
||||
).match
|
||||
|
||||
tracebackHeader = "Traceback (".encode("ascii")
|
||||
|
||||
sectionProfile = "Interactive Window"
|
||||
valueFormatTitle = "FormatTitle"
|
||||
valueFormatInput = "FormatInput"
|
||||
valueFormatOutput = "FormatOutput"
|
||||
valueFormatOutputError = "FormatOutputError"
|
||||
|
||||
# These are defaults only. Values are read from the registry.
|
||||
formatTitle = (-536870897, 0, 220, 0, 16711680, 184, 34, "Arial")
|
||||
formatInput = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New")
|
||||
formatOutput = (-402653169, 0, 200, 0, 8421376, 0, 49, "Courier New")
|
||||
formatOutputError = (-402653169, 0, 200, 0, 255, 0, 49, "Courier New")
|
||||
|
||||
try:
|
||||
sys.ps1
|
||||
except AttributeError:
|
||||
sys.ps1 = ">>> "
|
||||
sys.ps2 = "... "
|
||||
|
||||
|
||||
def LoadPreference(preference, default=""):
|
||||
return win32ui.GetProfileVal(sectionProfile, preference, default)
|
||||
|
||||
|
||||
def SavePreference(prefName, prefValue):
|
||||
win32ui.WriteProfileVal(sectionProfile, prefName, prefValue)
|
||||
|
||||
|
||||
def GetPromptPrefix(line):
|
||||
ps1 = sys.ps1
|
||||
if line[: len(ps1)] == ps1:
|
||||
return ps1
|
||||
ps2 = sys.ps2
|
||||
if line[: len(ps2)] == ps2:
|
||||
return ps2
|
||||
|
||||
|
||||
#############################################################
|
||||
#
|
||||
# Colorizer related code.
|
||||
#
|
||||
#############################################################
|
||||
STYLE_INTERACTIVE_EOL = "Interactive EOL"
|
||||
STYLE_INTERACTIVE_OUTPUT = "Interactive Output"
|
||||
STYLE_INTERACTIVE_PROMPT = "Interactive Prompt"
|
||||
STYLE_INTERACTIVE_BANNER = "Interactive Banner"
|
||||
STYLE_INTERACTIVE_ERROR = "Interactive Error"
|
||||
STYLE_INTERACTIVE_ERROR_FINALLINE = "Interactive Error (final line)"
|
||||
|
||||
INTERACTIVE_STYLES = [
|
||||
STYLE_INTERACTIVE_EOL,
|
||||
STYLE_INTERACTIVE_OUTPUT,
|
||||
STYLE_INTERACTIVE_PROMPT,
|
||||
STYLE_INTERACTIVE_BANNER,
|
||||
STYLE_INTERACTIVE_ERROR,
|
||||
STYLE_INTERACTIVE_ERROR_FINALLINE,
|
||||
]
|
||||
|
||||
FormatterParent = pywin.scintilla.formatter.PythonSourceFormatter
|
||||
|
||||
|
||||
class InteractiveFormatter(FormatterParent):
|
||||
def __init__(self, scintilla):
|
||||
FormatterParent.__init__(self, scintilla)
|
||||
self.bannerDisplayed = False
|
||||
|
||||
def SetStyles(self):
|
||||
FormatterParent.SetStyles(self)
|
||||
Style = pywin.scintilla.formatter.Style
|
||||
self.RegisterStyle(Style(STYLE_INTERACTIVE_EOL, STYLE_INTERACTIVE_PROMPT))
|
||||
self.RegisterStyle(Style(STYLE_INTERACTIVE_PROMPT, formatInput))
|
||||
self.RegisterStyle(Style(STYLE_INTERACTIVE_OUTPUT, formatOutput))
|
||||
self.RegisterStyle(Style(STYLE_INTERACTIVE_BANNER, formatTitle))
|
||||
self.RegisterStyle(Style(STYLE_INTERACTIVE_ERROR, formatOutputError))
|
||||
self.RegisterStyle(
|
||||
Style(STYLE_INTERACTIVE_ERROR_FINALLINE, STYLE_INTERACTIVE_ERROR)
|
||||
)
|
||||
|
||||
def LoadPreference(self, name, default):
|
||||
rc = win32ui.GetProfileVal("Format", name, default)
|
||||
if rc == default:
|
||||
rc = win32ui.GetProfileVal(sectionProfile, name, default)
|
||||
return rc
|
||||
|
||||
def ColorizeInteractiveCode(self, cdoc, styleStart, stylePyStart):
|
||||
lengthDoc = len(cdoc)
|
||||
if lengthDoc == 0:
|
||||
return
|
||||
state = styleStart
|
||||
# As per comments in Colorize(), we work with the raw utf8
|
||||
# bytes. To avoid too muych py3k pain, we treat each utf8 byte
|
||||
# as a latin-1 unicode character - we only use it to compare
|
||||
# against ascii chars anyway...
|
||||
chNext = cdoc[0:1].decode("latin-1")
|
||||
startSeg = 0
|
||||
i = 0
|
||||
lastState = state # debug only
|
||||
while i < lengthDoc:
|
||||
ch = chNext
|
||||
chNext = cdoc[i + 1 : i + 2].decode("latin-1")
|
||||
|
||||
# trace("ch=%r, i=%d, next=%r, state=%s" % (ch, i, chNext, state))
|
||||
if state == STYLE_INTERACTIVE_EOL:
|
||||
if ch not in "\r\n":
|
||||
self.ColorSeg(startSeg, i - 1, state)
|
||||
startSeg = i
|
||||
if ch in [sys.ps1[0], sys.ps2[0]]:
|
||||
state = STYLE_INTERACTIVE_PROMPT
|
||||
elif cdoc[i : i + len(tracebackHeader)] == tracebackHeader:
|
||||
state = STYLE_INTERACTIVE_ERROR
|
||||
else:
|
||||
state = STYLE_INTERACTIVE_OUTPUT
|
||||
elif state == STYLE_INTERACTIVE_PROMPT:
|
||||
if ch not in sys.ps1 + sys.ps2 + " ":
|
||||
self.ColorSeg(startSeg, i - 1, state)
|
||||
startSeg = i
|
||||
if ch in "\r\n":
|
||||
state = STYLE_INTERACTIVE_EOL
|
||||
else:
|
||||
state = stylePyStart # Start coloring Python code.
|
||||
elif state in [STYLE_INTERACTIVE_OUTPUT]:
|
||||
if ch in "\r\n":
|
||||
self.ColorSeg(startSeg, i - 1, state)
|
||||
startSeg = i
|
||||
state = STYLE_INTERACTIVE_EOL
|
||||
elif state == STYLE_INTERACTIVE_ERROR:
|
||||
if ch in "\r\n" and chNext and chNext not in string.whitespace:
|
||||
# Everything including me
|
||||
self.ColorSeg(startSeg, i, state)
|
||||
startSeg = i + 1
|
||||
state = STYLE_INTERACTIVE_ERROR_FINALLINE
|
||||
elif i == 0 and ch not in string.whitespace:
|
||||
# If we are coloring from the start of a line,
|
||||
# we need this better check for the last line
|
||||
# Color up to not including me
|
||||
self.ColorSeg(startSeg, i - 1, state)
|
||||
startSeg = i
|
||||
state = STYLE_INTERACTIVE_ERROR_FINALLINE
|
||||
elif state == STYLE_INTERACTIVE_ERROR_FINALLINE:
|
||||
if ch in "\r\n":
|
||||
self.ColorSeg(startSeg, i - 1, state)
|
||||
startSeg = i
|
||||
state = STYLE_INTERACTIVE_EOL
|
||||
elif state == STYLE_INTERACTIVE_BANNER:
|
||||
if ch in "\r\n" and (chNext == "" or chNext in ">["):
|
||||
# Everything including me
|
||||
self.ColorSeg(startSeg, i - 1, state)
|
||||
startSeg = i
|
||||
state = STYLE_INTERACTIVE_EOL
|
||||
else:
|
||||
# It is a PythonColorizer state - seek past the end of the line
|
||||
# and ask the Python colorizer to color that.
|
||||
end = startSeg
|
||||
while end < lengthDoc and cdoc[end] not in "\r\n".encode("ascii"):
|
||||
end = end + 1
|
||||
self.ColorizePythonCode(cdoc[:end], startSeg, state)
|
||||
stylePyStart = self.GetStringStyle(end - 1)
|
||||
if stylePyStart is None:
|
||||
stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT
|
||||
else:
|
||||
stylePyStart = stylePyStart.name
|
||||
startSeg = end
|
||||
i = end - 1 # ready for increment.
|
||||
chNext = cdoc[end : end + 1].decode("latin-1")
|
||||
state = STYLE_INTERACTIVE_EOL
|
||||
if lastState != state:
|
||||
lastState = state
|
||||
i = i + 1
|
||||
# and the rest
|
||||
if startSeg < i:
|
||||
self.ColorSeg(startSeg, i - 1, state)
|
||||
|
||||
def Colorize(self, start=0, end=-1):
|
||||
# scintilla's formatting is all done in terms of utf, so
|
||||
# we work with utf8 bytes instead of unicode. This magically
|
||||
# works as any extended chars found in the utf8 don't change
|
||||
# the semantics.
|
||||
stringVal = self.scintilla.GetTextRange(start, end, decode=False)
|
||||
styleStart = None
|
||||
stylePyStart = None
|
||||
if start > 1:
|
||||
# Likely we are being asked to color from the start of the line.
|
||||
# We find the last formatted character on the previous line.
|
||||
# If TQString, we continue it. Otherwise, we reset.
|
||||
look = start - 1
|
||||
while look and self.scintilla.SCIGetCharAt(look) in "\n\r":
|
||||
look = look - 1
|
||||
if look and look < start - 1: # Did we find a char before the \n\r sets?
|
||||
strstyle = self.GetStringStyle(look)
|
||||
quote_char = None
|
||||
if strstyle is not None:
|
||||
if strstyle.name == pywin.scintilla.formatter.STYLE_TQSSTRING:
|
||||
quote_char = "'"
|
||||
elif strstyle.name == pywin.scintilla.formatter.STYLE_TQDSTRING:
|
||||
quote_char = '"'
|
||||
if quote_char is not None:
|
||||
# It is a TQS. If the TQS is not terminated, we
|
||||
# carry the style through.
|
||||
if look > 2:
|
||||
look_str = (
|
||||
self.scintilla.SCIGetCharAt(look - 2)
|
||||
+ self.scintilla.SCIGetCharAt(look - 1)
|
||||
+ self.scintilla.SCIGetCharAt(look)
|
||||
)
|
||||
if look_str != quote_char * 3:
|
||||
stylePyStart = strstyle.name
|
||||
if stylePyStart is None:
|
||||
stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT
|
||||
|
||||
if start > 0:
|
||||
stylenum = self.scintilla.SCIGetStyleAt(start - 1)
|
||||
styleStart = self.GetStyleByNum(stylenum).name
|
||||
elif self.bannerDisplayed:
|
||||
styleStart = STYLE_INTERACTIVE_EOL
|
||||
else:
|
||||
styleStart = STYLE_INTERACTIVE_BANNER
|
||||
self.bannerDisplayed = True
|
||||
self.scintilla.SCIStartStyling(start, 31)
|
||||
self.style_buffer = array.array("b", (0,) * len(stringVal))
|
||||
self.ColorizeInteractiveCode(stringVal, styleStart, stylePyStart)
|
||||
self.scintilla.SCISetStylingEx(self.style_buffer)
|
||||
self.style_buffer = None
|
||||
|
||||
|
||||
###############################################################
|
||||
#
|
||||
# This class handles the Python interactive interpreter.
|
||||
#
|
||||
# It uses a basic EditWindow, and does all the magic.
|
||||
# This is triggered by the enter key hander attached by the
|
||||
# start-up code. It determines if a command is to be executed
|
||||
# or continued (ie, emit "... ") by snooping around the current
|
||||
# line, looking for the prompts
|
||||
#
|
||||
class PythonwinInteractiveInterpreter(code.InteractiveInterpreter):
|
||||
def __init__(self, locals=None, globals=None):
|
||||
if locals is None:
|
||||
locals = __main__.__dict__
|
||||
if globals is None:
|
||||
globals = locals
|
||||
self.globals = globals
|
||||
code.InteractiveInterpreter.__init__(self, locals)
|
||||
|
||||
def showsyntaxerror(self, filename=None):
|
||||
sys.stderr.write(
|
||||
tracebackHeader.decode("ascii")
|
||||
) # So the color syntaxer recognises it.
|
||||
code.InteractiveInterpreter.showsyntaxerror(self, filename)
|
||||
|
||||
def runcode(self, code):
|
||||
try:
|
||||
exec(code, self.globals, self.locals)
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
self.showtraceback()
|
||||
|
||||
|
||||
class InteractiveCore:
|
||||
def __init__(self, banner=None):
|
||||
self.banner = banner
|
||||
|
||||
# LoadFontPreferences()
|
||||
def Init(self):
|
||||
self.oldStdOut = self.oldStdErr = None
|
||||
|
||||
# self.SetWordWrap(win32ui.CRichEditView_WrapNone)
|
||||
self.interp = PythonwinInteractiveInterpreter()
|
||||
|
||||
self.OutputGrab() # Release at cleanup.
|
||||
|
||||
if self.GetTextLength() == 0:
|
||||
if self.banner is None:
|
||||
suffix = ""
|
||||
if win32ui.debug:
|
||||
suffix = ", debug build"
|
||||
sys.stderr.write(
|
||||
"PythonWin %s on %s%s.\n" % (sys.version, sys.platform, suffix)
|
||||
)
|
||||
sys.stderr.write(
|
||||
"Portions %s - see 'Help/About PythonWin' for further copyright information.\n"
|
||||
% (win32ui.copyright,)
|
||||
)
|
||||
else:
|
||||
sys.stderr.write(banner)
|
||||
rcfile = os.environ.get("PYTHONSTARTUP")
|
||||
if rcfile:
|
||||
import __main__
|
||||
|
||||
try:
|
||||
exec(
|
||||
compile(
|
||||
open(rcfile, "rb").read(), rcfile, "exec", dont_inherit=True
|
||||
),
|
||||
__main__.__dict__,
|
||||
__main__.__dict__,
|
||||
)
|
||||
except:
|
||||
sys.stderr.write(
|
||||
">>> \nError executing PYTHONSTARTUP script %r\n" % (rcfile)
|
||||
)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
self.AppendToPrompt([])
|
||||
|
||||
def SetContext(self, globals, locals, name="Dbg"):
|
||||
oldPrompt = sys.ps1
|
||||
if globals is None:
|
||||
# Reset
|
||||
sys.ps1 = ">>> "
|
||||
sys.ps2 = "... "
|
||||
locals = globals = __main__.__dict__
|
||||
else:
|
||||
sys.ps1 = "[%s]>>> " % name
|
||||
sys.ps2 = "[%s]... " % name
|
||||
self.interp.locals = locals
|
||||
self.interp.globals = globals
|
||||
self.AppendToPrompt([], oldPrompt)
|
||||
|
||||
def GetContext(self):
|
||||
return self.interp.globals, self.interp.locals
|
||||
|
||||
def DoGetLine(self, line=-1):
|
||||
if line == -1:
|
||||
line = self.LineFromChar()
|
||||
line = self.GetLine(line)
|
||||
while line and line[-1] in ["\r", "\n"]:
|
||||
line = line[:-1]
|
||||
return line
|
||||
|
||||
def AppendToPrompt(self, bufLines, oldPrompt=None):
|
||||
"Take a command and stick it at the end of the buffer (with python prompts inserted if required)."
|
||||
self.flush()
|
||||
lastLineNo = self.GetLineCount() - 1
|
||||
line = self.DoGetLine(lastLineNo)
|
||||
if oldPrompt and line == oldPrompt:
|
||||
self.SetSel(self.GetTextLength() - len(oldPrompt), self.GetTextLength())
|
||||
self.ReplaceSel(sys.ps1)
|
||||
elif line != str(sys.ps1):
|
||||
if len(line) != 0:
|
||||
self.write("\n")
|
||||
self.write(sys.ps1)
|
||||
self.flush()
|
||||
self.idle.text.mark_set("iomark", "end-1c")
|
||||
if not bufLines:
|
||||
return
|
||||
terms = (["\n" + sys.ps2] * (len(bufLines) - 1)) + [""]
|
||||
for bufLine, term in zip(bufLines, terms):
|
||||
if bufLine.strip():
|
||||
self.write(bufLine + term)
|
||||
self.flush()
|
||||
|
||||
def EnsureNoPrompt(self):
|
||||
# Get ready to write some text NOT at a Python prompt.
|
||||
self.flush()
|
||||
lastLineNo = self.GetLineCount() - 1
|
||||
line = self.DoGetLine(lastLineNo)
|
||||
if not line or line in [sys.ps1, sys.ps2]:
|
||||
self.SetSel(self.GetTextLength() - len(line), self.GetTextLength())
|
||||
self.ReplaceSel("")
|
||||
else:
|
||||
# Just add a new line.
|
||||
self.write("\n")
|
||||
|
||||
def _GetSubConfigNames(self):
|
||||
return ["interactive"] # Allow [Keys:Interactive] sections to be specific
|
||||
|
||||
def HookHandlers(self):
|
||||
# Hook menu command (executed when a menu item with that ID is selected from a menu/toolbar
|
||||
self.HookCommand(self.OnSelectBlock, win32ui.ID_EDIT_SELECT_BLOCK)
|
||||
self.HookCommand(self.OnEditCopyCode, ID_EDIT_COPY_CODE)
|
||||
self.HookCommand(self.OnEditExecClipboard, ID_EDIT_EXEC_CLIPBOARD)
|
||||
mod = pywin.scintilla.IDLEenvironment.GetIDLEModule("IdleHistory")
|
||||
if mod is not None:
|
||||
self.history = mod.History(self.idle.text, "\n" + sys.ps2)
|
||||
else:
|
||||
self.history = None
|
||||
# hack for now for event handling.
|
||||
|
||||
# GetBlockBoundary takes a line number, and will return the
|
||||
# start and and line numbers of the block, and a flag indicating if the
|
||||
# block is a Python code block.
|
||||
# If the line specified has a Python prompt, then the lines are parsed
|
||||
# backwards and forwards, and the flag is true.
|
||||
# If the line does not start with a prompt, the block is searched forward
|
||||
# and backward until a prompt _is_ found, and all lines in between without
|
||||
# prompts are returned, and the flag is false.
|
||||
def GetBlockBoundary(self, lineNo):
|
||||
line = self.DoGetLine(lineNo)
|
||||
maxLineNo = self.GetLineCount() - 1
|
||||
prefix = GetPromptPrefix(line)
|
||||
if prefix is None: # Non code block
|
||||
flag = 0
|
||||
startLineNo = lineNo
|
||||
while startLineNo > 0:
|
||||
if GetPromptPrefix(self.DoGetLine(startLineNo - 1)) is not None:
|
||||
break # there _is_ a prompt
|
||||
startLineNo = startLineNo - 1
|
||||
endLineNo = lineNo
|
||||
while endLineNo < maxLineNo:
|
||||
if GetPromptPrefix(self.DoGetLine(endLineNo + 1)) is not None:
|
||||
break # there _is_ a prompt
|
||||
endLineNo = endLineNo + 1
|
||||
else: # Code block
|
||||
flag = 1
|
||||
startLineNo = lineNo
|
||||
while startLineNo > 0 and prefix != str(sys.ps1):
|
||||
prefix = GetPromptPrefix(self.DoGetLine(startLineNo - 1))
|
||||
if prefix is None:
|
||||
break
|
||||
# there is no prompt.
|
||||
startLineNo = startLineNo - 1
|
||||
endLineNo = lineNo
|
||||
while endLineNo < maxLineNo:
|
||||
prefix = GetPromptPrefix(self.DoGetLine(endLineNo + 1))
|
||||
if prefix is None:
|
||||
break # there is no prompt
|
||||
if prefix == str(sys.ps1):
|
||||
break # this is another command
|
||||
endLineNo = endLineNo + 1
|
||||
# continue until end of buffer, or no prompt
|
||||
return (startLineNo, endLineNo, flag)
|
||||
|
||||
def ExtractCommand(self, lines):
|
||||
start, end = lines
|
||||
retList = []
|
||||
while end >= start:
|
||||
thisLine = self.DoGetLine(end)
|
||||
promptLen = len(GetPromptPrefix(thisLine))
|
||||
retList = [thisLine[promptLen:]] + retList
|
||||
end = end - 1
|
||||
return retList
|
||||
|
||||
def OutputGrab(self):
|
||||
# import win32traceutil; return
|
||||
self.oldStdOut = sys.stdout
|
||||
self.oldStdErr = sys.stderr
|
||||
sys.stdout = self
|
||||
sys.stderr = self
|
||||
self.flush()
|
||||
|
||||
def OutputRelease(self):
|
||||
# a command may have overwritten these - only restore if not.
|
||||
if self.oldStdOut is not None:
|
||||
if sys.stdout == self:
|
||||
sys.stdout = self.oldStdOut
|
||||
if self.oldStdErr is not None:
|
||||
if sys.stderr == self:
|
||||
sys.stderr = self.oldStdErr
|
||||
self.oldStdOut = None
|
||||
self.oldStdErr = None
|
||||
self.flush()
|
||||
|
||||
###################################
|
||||
#
|
||||
# Message/Command/Key Hooks.
|
||||
#
|
||||
# Enter key handler
|
||||
#
|
||||
def ProcessEnterEvent(self, event):
|
||||
# If autocompletion has been triggered, complete and do not process event
|
||||
if self.SCIAutoCActive():
|
||||
self.SCIAutoCComplete()
|
||||
self.SCICancel()
|
||||
return
|
||||
|
||||
self.SCICancel()
|
||||
# First, check for an error message
|
||||
haveGrabbedOutput = 0
|
||||
if self.HandleSpecialLine():
|
||||
return 0
|
||||
|
||||
lineNo = self.LineFromChar()
|
||||
start, end, isCode = self.GetBlockBoundary(lineNo)
|
||||
# If we are not in a code block just go to the prompt (or create a new one)
|
||||
if not isCode:
|
||||
self.AppendToPrompt([])
|
||||
win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
|
||||
return
|
||||
|
||||
lines = self.ExtractCommand((start, end))
|
||||
|
||||
# If we are in a code-block, but it isnt at the end of the buffer
|
||||
# then copy it to the end ready for editing and subsequent execution
|
||||
if end != self.GetLineCount() - 1:
|
||||
win32ui.SetStatusText("Press ENTER to execute command")
|
||||
self.AppendToPrompt(lines)
|
||||
self.SetSel(-2)
|
||||
return
|
||||
|
||||
# If SHIFT held down, we want new code here and now!
|
||||
bNeedIndent = (
|
||||
win32api.GetKeyState(win32con.VK_SHIFT) < 0
|
||||
or win32api.GetKeyState(win32con.VK_CONTROL) < 0
|
||||
)
|
||||
if bNeedIndent:
|
||||
self.ReplaceSel("\n")
|
||||
else:
|
||||
self.SetSel(-2)
|
||||
self.ReplaceSel("\n")
|
||||
source = "\n".join(lines)
|
||||
while source and source[-1] in "\t ":
|
||||
source = source[:-1]
|
||||
self.OutputGrab() # grab the output for the command exec.
|
||||
try:
|
||||
if self.interp.runsource(
|
||||
source, "<interactive input>"
|
||||
): # Need more input!
|
||||
bNeedIndent = 1
|
||||
else:
|
||||
# If the last line isnt empty, append a newline
|
||||
if self.history is not None:
|
||||
self.history.history_store(source)
|
||||
self.AppendToPrompt([])
|
||||
win32ui.SetStatusText(
|
||||
win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)
|
||||
)
|
||||
# win32ui.SetStatusText('Successfully executed statement')
|
||||
finally:
|
||||
self.OutputRelease()
|
||||
if bNeedIndent:
|
||||
win32ui.SetStatusText("Ready to continue the command")
|
||||
# Now attempt correct indentation (should use IDLE?)
|
||||
curLine = self.DoGetLine(lineNo)[len(sys.ps2) :]
|
||||
pos = 0
|
||||
indent = ""
|
||||
while len(curLine) > pos and curLine[pos] in string.whitespace:
|
||||
indent = indent + curLine[pos]
|
||||
pos = pos + 1
|
||||
if _is_block_opener(curLine):
|
||||
indent = indent + "\t"
|
||||
elif _is_block_closer(curLine):
|
||||
indent = indent[:-1]
|
||||
# use ReplaceSel to ensure it goes at the cursor rather than end of buffer.
|
||||
self.ReplaceSel(sys.ps2 + indent)
|
||||
return 0
|
||||
|
||||
# ESC key handler
|
||||
def ProcessEscEvent(self, event):
|
||||
# Implement a cancel.
|
||||
if self.SCIAutoCActive() or self.SCICallTipActive():
|
||||
self.SCICancel()
|
||||
else:
|
||||
win32ui.SetStatusText("Cancelled.")
|
||||
self.AppendToPrompt(("",))
|
||||
return 0
|
||||
|
||||
def OnSelectBlock(self, command, code):
|
||||
lineNo = self.LineFromChar()
|
||||
start, end, isCode = self.GetBlockBoundary(lineNo)
|
||||
startIndex = self.LineIndex(start)
|
||||
endIndex = self.LineIndex(end + 1) - 2 # skip \r + \n
|
||||
if endIndex < 0: # must be beyond end of buffer
|
||||
endIndex = -2 # self.Length()
|
||||
self.SetSel(startIndex, endIndex)
|
||||
|
||||
def OnEditCopyCode(self, command, code):
|
||||
"""Sanitizes code from interactive window, removing prompts and output,
|
||||
and inserts it in the clipboard."""
|
||||
code = self.GetSelText()
|
||||
lines = code.splitlines()
|
||||
out_lines = []
|
||||
for line in lines:
|
||||
if line.startswith(sys.ps1):
|
||||
line = line[len(sys.ps1) :]
|
||||
out_lines.append(line)
|
||||
elif line.startswith(sys.ps2):
|
||||
line = line[len(sys.ps2) :]
|
||||
out_lines.append(line)
|
||||
out_code = os.linesep.join(out_lines)
|
||||
win32clipboard.OpenClipboard()
|
||||
try:
|
||||
win32clipboard.SetClipboardData(
|
||||
win32clipboard.CF_UNICODETEXT, str(out_code)
|
||||
)
|
||||
finally:
|
||||
win32clipboard.CloseClipboard()
|
||||
|
||||
def OnEditExecClipboard(self, command, code):
|
||||
"""Executes python code directly from the clipboard."""
|
||||
win32clipboard.OpenClipboard()
|
||||
try:
|
||||
code = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
|
||||
finally:
|
||||
win32clipboard.CloseClipboard()
|
||||
|
||||
code = code.replace("\r\n", "\n") + "\n"
|
||||
try:
|
||||
o = compile(code, "<clipboard>", "exec")
|
||||
exec(o, __main__.__dict__)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
def GetRightMenuItems(self):
|
||||
# Just override parents
|
||||
ret = []
|
||||
flags = 0
|
||||
ret.append((flags, win32ui.ID_EDIT_UNDO, "&Undo"))
|
||||
ret.append(win32con.MF_SEPARATOR)
|
||||
ret.append((flags, win32ui.ID_EDIT_CUT, "Cu&t"))
|
||||
ret.append((flags, win32ui.ID_EDIT_COPY, "&Copy"))
|
||||
|
||||
start, end = self.GetSel()
|
||||
if start != end:
|
||||
ret.append((flags, ID_EDIT_COPY_CODE, "Copy code without prompts"))
|
||||
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT):
|
||||
ret.append(
|
||||
(flags, ID_EDIT_EXEC_CLIPBOARD, "Execute python code from clipboard")
|
||||
)
|
||||
|
||||
ret.append((flags, win32ui.ID_EDIT_PASTE, "&Paste"))
|
||||
ret.append(win32con.MF_SEPARATOR)
|
||||
ret.append((flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all"))
|
||||
ret.append((flags, win32ui.ID_EDIT_SELECT_BLOCK, "Select &block"))
|
||||
ret.append((flags, win32ui.ID_VIEW_WHITESPACE, "View &Whitespace"))
|
||||
return ret
|
||||
|
||||
def MDINextEvent(self, event):
|
||||
win32ui.GetMainFrame().MDINext(0)
|
||||
|
||||
def MDIPrevEvent(self, event):
|
||||
win32ui.GetMainFrame().MDINext(0)
|
||||
|
||||
def WindowBackEvent(self, event):
|
||||
parent = self.GetParentFrame()
|
||||
if parent == win32ui.GetMainFrame():
|
||||
# It is docked.
|
||||
try:
|
||||
wnd, isactive = parent.MDIGetActive()
|
||||
wnd.SetFocus()
|
||||
except win32ui.error:
|
||||
# No MDI window active!
|
||||
pass
|
||||
else:
|
||||
# Normal Window
|
||||
try:
|
||||
lastActive = self.GetParentFrame().lastActive
|
||||
# If the window is invalid, reset it.
|
||||
if lastActive is not None and (
|
||||
lastActive._obj_ is None or lastActive.GetSafeHwnd() == 0
|
||||
):
|
||||
lastActive = self.GetParentFrame().lastActive = None
|
||||
win32ui.SetStatusText("The last active Window has been closed.")
|
||||
except AttributeError:
|
||||
print("Can't find the last active window!")
|
||||
lastActive = None
|
||||
if lastActive is not None:
|
||||
lastActive.MDIActivate()
|
||||
|
||||
|
||||
class InteractiveView(InteractiveCore, winout.WindowOutputView):
|
||||
def __init__(self, doc):
|
||||
InteractiveCore.__init__(self)
|
||||
winout.WindowOutputView.__init__(self, doc)
|
||||
self.encoding = pywin.default_scintilla_encoding
|
||||
|
||||
def _MakeColorizer(self):
|
||||
return InteractiveFormatter(self)
|
||||
|
||||
def OnInitialUpdate(self):
|
||||
winout.WindowOutputView.OnInitialUpdate(self)
|
||||
self.SetWordWrap()
|
||||
self.Init()
|
||||
|
||||
def HookHandlers(self):
|
||||
winout.WindowOutputView.HookHandlers(self)
|
||||
InteractiveCore.HookHandlers(self)
|
||||
|
||||
|
||||
class CInteractivePython(winout.WindowOutput):
|
||||
def __init__(self, makeDoc=None, makeFrame=None):
|
||||
self.IsFinalDestroy = 0
|
||||
winout.WindowOutput.__init__(
|
||||
self,
|
||||
sectionProfile,
|
||||
sectionProfile,
|
||||
winout.flags.WQ_LINE,
|
||||
1,
|
||||
None,
|
||||
makeDoc,
|
||||
makeFrame,
|
||||
InteractiveView,
|
||||
)
|
||||
self.Create()
|
||||
|
||||
def OnViewDestroy(self, view):
|
||||
if self.IsFinalDestroy:
|
||||
view.OutputRelease()
|
||||
winout.WindowOutput.OnViewDestroy(self, view)
|
||||
|
||||
def Close(self):
|
||||
self.IsFinalDestroy = 1
|
||||
winout.WindowOutput.Close(self)
|
||||
|
||||
|
||||
class InteractiveFrame(winout.WindowOutputFrame):
|
||||
def __init__(self):
|
||||
self.lastActive = None
|
||||
winout.WindowOutputFrame.__init__(self)
|
||||
|
||||
def OnMDIActivate(self, bActive, wndActive, wndDeactive):
|
||||
if bActive:
|
||||
self.lastActive = wndDeactive
|
||||
|
||||
|
||||
######################################################################
|
||||
##
|
||||
## Dockable Window Support
|
||||
##
|
||||
######################################################################
|
||||
ID_DOCKED_INTERACTIVE_CONTROLBAR = 0xE802
|
||||
|
||||
DockedInteractiveViewParent = InteractiveView
|
||||
|
||||
|
||||
class DockedInteractiveView(DockedInteractiveViewParent):
|
||||
def HookHandlers(self):
|
||||
DockedInteractiveViewParent.HookHandlers(self)
|
||||
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
|
||||
self.HookMessage(self.OnKillFocus, win32con.WM_KILLFOCUS)
|
||||
|
||||
def OnSetFocus(self, msg):
|
||||
self.GetParentFrame().SetActiveView(self)
|
||||
return 1
|
||||
|
||||
def OnKillFocus(self, msg):
|
||||
# If we are losing focus to another in this app, reset the main frame's active view.
|
||||
hwnd = wparam = msg[2]
|
||||
try:
|
||||
wnd = win32ui.CreateWindowFromHandle(hwnd)
|
||||
reset = wnd.GetTopLevelFrame() == self.GetTopLevelFrame()
|
||||
except win32ui.error:
|
||||
reset = 0 # Not my window
|
||||
if reset:
|
||||
self.GetParentFrame().SetActiveView(None)
|
||||
return 1
|
||||
|
||||
def OnDestroy(self, msg):
|
||||
newSize = self.GetWindowPlacement()[4]
|
||||
pywin.framework.app.SaveWindowSize("Interactive Window", newSize, "docked")
|
||||
try:
|
||||
if self.GetParentFrame().GetActiveView == self:
|
||||
self.GetParentFrame().SetActiveView(None)
|
||||
except win32ui.error:
|
||||
pass
|
||||
try:
|
||||
if win32ui.GetMainFrame().GetActiveView() == self:
|
||||
win32ui.GetMainFrame().SetActiveView(None)
|
||||
except win32ui.error:
|
||||
pass
|
||||
return DockedInteractiveViewParent.OnDestroy(self, msg)
|
||||
|
||||
|
||||
class CDockedInteractivePython(CInteractivePython):
|
||||
def __init__(self, dockbar):
|
||||
self.bFirstCreated = 0
|
||||
self.dockbar = dockbar
|
||||
CInteractivePython.__init__(self)
|
||||
|
||||
def NeedRecreateWindow(self):
|
||||
if self.bCreating:
|
||||
return 0
|
||||
try:
|
||||
frame = win32ui.GetMainFrame()
|
||||
if frame.closing:
|
||||
return 0 # Dieing!
|
||||
except (win32ui.error, AttributeError):
|
||||
return 0 # The app is dieing!
|
||||
try:
|
||||
cb = frame.GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
|
||||
return not cb.IsWindowVisible()
|
||||
except win32ui.error:
|
||||
return 1 # Control bar does not exist!
|
||||
|
||||
def RecreateWindow(self):
|
||||
try:
|
||||
dockbar = win32ui.GetMainFrame().GetControlBar(
|
||||
ID_DOCKED_INTERACTIVE_CONTROLBAR
|
||||
)
|
||||
win32ui.GetMainFrame().ShowControlBar(dockbar, 1, 1)
|
||||
except win32ui.error:
|
||||
CreateDockedInteractiveWindow()
|
||||
|
||||
def Create(self):
|
||||
self.bCreating = 1
|
||||
doc = InteractiveDocument(None, self.DoCreateDoc())
|
||||
view = DockedInteractiveView(doc)
|
||||
defRect = pywin.framework.app.LoadWindowSize("Interactive Window", "docked")
|
||||
if defRect[2] - defRect[0] == 0:
|
||||
defRect = 0, 0, 500, 200
|
||||
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER
|
||||
id = 1050 # win32ui.AFX_IDW_PANE_FIRST
|
||||
view.CreateWindow(self.dockbar, id, style, defRect)
|
||||
view.OnInitialUpdate()
|
||||
self.bFirstCreated = 1
|
||||
|
||||
self.currentView = doc.GetFirstView()
|
||||
self.bCreating = 0
|
||||
if self.title:
|
||||
doc.SetTitle(self.title)
|
||||
|
||||
|
||||
# The factory we pass to the dockable window support.
|
||||
def InteractiveViewCreator(parent):
|
||||
global edit
|
||||
edit = CDockedInteractivePython(parent)
|
||||
return edit.currentView
|
||||
|
||||
|
||||
def CreateDockedInteractiveWindow():
|
||||
# Later, the DockingBar should be capable of hosting multiple
|
||||
# children.
|
||||
from pywin.docking.DockingBar import DockingBar
|
||||
|
||||
bar = DockingBar()
|
||||
creator = InteractiveViewCreator
|
||||
bar.CreateWindow(
|
||||
win32ui.GetMainFrame(),
|
||||
creator,
|
||||
"Interactive Window",
|
||||
ID_DOCKED_INTERACTIVE_CONTROLBAR,
|
||||
)
|
||||
bar.SetBarStyle(
|
||||
bar.GetBarStyle()
|
||||
| afxres.CBRS_TOOLTIPS
|
||||
| afxres.CBRS_FLYBY
|
||||
| afxres.CBRS_SIZE_DYNAMIC
|
||||
)
|
||||
bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
|
||||
win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM)
|
||||
|
||||
|
||||
######################################################################
|
||||
#
|
||||
# The public interface to this module.
|
||||
#
|
||||
######################################################################
|
||||
# No extra functionality now, but maybe later, so
|
||||
# publicize these names.
|
||||
InteractiveDocument = winout.WindowOutputDocument
|
||||
|
||||
# We remember our one and only interactive window in the "edit" variable.
|
||||
edit = None
|
||||
|
||||
|
||||
def CreateInteractiveWindowUserPreference(makeDoc=None, makeFrame=None):
|
||||
"""Create some sort of interactive window if the user's preference say we should."""
|
||||
bCreate = LoadPreference("Show at startup", 1)
|
||||
if bCreate:
|
||||
CreateInteractiveWindow(makeDoc, makeFrame)
|
||||
|
||||
|
||||
def CreateInteractiveWindow(makeDoc=None, makeFrame=None):
|
||||
"""Create a standard or docked interactive window unconditionally"""
|
||||
assert edit is None, "Creating second interactive window!"
|
||||
bDocking = LoadPreference("Docking", 0)
|
||||
if bDocking:
|
||||
CreateDockedInteractiveWindow()
|
||||
else:
|
||||
CreateMDIInteractiveWindow(makeDoc, makeFrame)
|
||||
assert edit is not None, "Created interactive window, but did not set the global!"
|
||||
edit.currentView.SetFocus()
|
||||
|
||||
|
||||
def CreateMDIInteractiveWindow(makeDoc=None, makeFrame=None):
|
||||
"""Create a standard (non-docked) interactive window unconditionally"""
|
||||
global edit
|
||||
if makeDoc is None:
|
||||
makeDoc = InteractiveDocument
|
||||
if makeFrame is None:
|
||||
makeFrame = InteractiveFrame
|
||||
edit = CInteractivePython(makeDoc=makeDoc, makeFrame=makeFrame)
|
||||
|
||||
|
||||
def DestroyInteractiveWindow():
|
||||
"""Destroy the interactive window.
|
||||
This is different to Closing the window,
|
||||
which may automatically re-appear. Once destroyed, it can never be recreated,
|
||||
and a complete new instance must be created (which the various other helper
|
||||
functions will then do after making this call
|
||||
"""
|
||||
global edit
|
||||
if edit is not None and edit.currentView is not None:
|
||||
if edit.currentView.GetParentFrame() == win32ui.GetMainFrame():
|
||||
# It is docked - do nothing now (this is only called at shutdown!)
|
||||
pass
|
||||
else:
|
||||
# It is a standard window - call Close on the container.
|
||||
edit.Close()
|
||||
edit = None
|
||||
|
||||
|
||||
def CloseInteractiveWindow():
|
||||
"""Close the interactive window, allowing it to be re-created on demand."""
|
||||
global edit
|
||||
if edit is not None and edit.currentView is not None:
|
||||
if edit.currentView.GetParentFrame() == win32ui.GetMainFrame():
|
||||
# It is docked, just hide the dock bar.
|
||||
frame = win32ui.GetMainFrame()
|
||||
cb = frame.GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
|
||||
frame.ShowControlBar(cb, 0, 1)
|
||||
else:
|
||||
# It is a standard window - destroy the frame/view, allowing the object itself to remain.
|
||||
edit.currentView.GetParentFrame().DestroyWindow()
|
||||
|
||||
|
||||
def ToggleInteractiveWindow():
|
||||
"""If the interactive window is visible, hide it, otherwise show it."""
|
||||
if edit is None:
|
||||
CreateInteractiveWindow()
|
||||
else:
|
||||
if edit.NeedRecreateWindow():
|
||||
edit.RecreateWindow()
|
||||
else:
|
||||
# Close it, allowing a reopen.
|
||||
CloseInteractiveWindow()
|
||||
|
||||
|
||||
def ShowInteractiveWindow():
|
||||
"""Shows (or creates if necessary) an interactive window"""
|
||||
if edit is None:
|
||||
CreateInteractiveWindow()
|
||||
else:
|
||||
if edit.NeedRecreateWindow():
|
||||
edit.RecreateWindow()
|
||||
else:
|
||||
parent = edit.currentView.GetParentFrame()
|
||||
if parent == win32ui.GetMainFrame(): # It is docked.
|
||||
edit.currentView.SetFocus()
|
||||
else: # It is a "normal" window
|
||||
edit.currentView.GetParentFrame().AutoRestore()
|
||||
win32ui.GetMainFrame().MDIActivate(edit.currentView.GetParentFrame())
|
||||
|
||||
|
||||
def IsInteractiveWindowVisible():
|
||||
return edit is not None and not edit.NeedRecreateWindow()
|
553
.venv/Lib/site-packages/pythonwin/pywin/framework/intpyapp.py
Normal file
553
.venv/Lib/site-packages/pythonwin/pywin/framework/intpyapp.py
Normal file
@ -0,0 +1,553 @@
|
||||
# intpyapp.py - Interactive Python application class
|
||||
#
|
||||
import win32con
|
||||
import win32api
|
||||
import win32ui
|
||||
import __main__
|
||||
import sys
|
||||
import os
|
||||
from . import app
|
||||
import traceback
|
||||
from pywin.mfc import 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:
|
||||
connection.Exec("self.Activate()")
|
||||
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"
|
||||
)
|
||||
return 1
|
||||
|
||||
def InitInstance(self):
|
||||
# Allow "/nodde" and "/new" to optimize this!
|
||||
if (
|
||||
"/nodde" not in sys.argv
|
||||
and "/new" not in sys.argv
|
||||
and "-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.
|
||||
try:
|
||||
self.ProcessArgs(sys.argv)
|
||||
except:
|
||||
# too early for printing anything.
|
||||
win32ui.DisplayTraceback(
|
||||
sys.exc_info(), " - error processing command line args"
|
||||
)
|
||||
|
||||
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 (
|
||||
len(args) < 1 or not args[0]
|
||||
): # argv[0]=='' when started without args, just like Python.exe!
|
||||
return
|
||||
|
||||
i = 0
|
||||
while i < len(args):
|
||||
argType = args[i]
|
||||
i += 1
|
||||
if argType.startswith("-"):
|
||||
# Support dash options. Slash options are misinterpreted by python init
|
||||
# as path and not finding usually 'C:\\' ends up in sys.path[0]
|
||||
argType = "/" + argType[1:]
|
||||
if not argType.startswith("/"):
|
||||
argType = win32ui.GetProfileVal(
|
||||
"Python", "Default Arg Type", "/edit"
|
||||
).lower()
|
||||
i -= 1 # arg is /edit's parameter
|
||||
par = i < len(args) and args[i] or "MISSING"
|
||||
if argType in ["/nodde", "/new", "-nodde", "-new"]:
|
||||
# Already handled
|
||||
pass
|
||||
elif argType.startswith("/goto:"):
|
||||
gotoline = int(argType[len("/goto:") :])
|
||||
if dde:
|
||||
dde.Exec(
|
||||
"from pywin.framework import scriptutils\n"
|
||||
"ed = scriptutils.GetActiveEditControl()\n"
|
||||
"if ed: ed.SetSel(ed.LineIndex(%s - 1))" % gotoline
|
||||
)
|
||||
else:
|
||||
from . import scriptutils
|
||||
|
||||
ed = scriptutils.GetActiveEditControl()
|
||||
if ed:
|
||||
ed.SetSel(ed.LineIndex(gotoline - 1))
|
||||
elif argType == "/edit":
|
||||
# Load up the default application.
|
||||
i += 1
|
||||
fname = win32api.GetFullPathName(par)
|
||||
if not os.path.isfile(fname):
|
||||
# if we don't catch this, OpenDocumentFile() (actually
|
||||
# PyCDocument.SetPathName() in
|
||||
# pywin.scintilla.document.CScintillaDocument.OnOpenDocument)
|
||||
# segfaults Pythonwin on recent PY3 builds (b228)
|
||||
win32ui.MessageBox(
|
||||
"No such file: %s\n\nCommand Line: %s"
|
||||
% (fname, win32api.GetCommandLine()),
|
||||
"Open file for edit",
|
||||
win32con.MB_ICONERROR,
|
||||
)
|
||||
continue
|
||||
if dde:
|
||||
dde.Exec("win32ui.GetApp().OpenDocumentFile(%s)" % (repr(fname)))
|
||||
else:
|
||||
win32ui.GetApp().OpenDocumentFile(par)
|
||||
elif argType == "/rundlg":
|
||||
if dde:
|
||||
dde.Exec(
|
||||
"from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 1)"
|
||||
% (par, " ".join(args[i + 1 :]))
|
||||
)
|
||||
else:
|
||||
from . import scriptutils
|
||||
|
||||
scriptutils.RunScript(par, " ".join(args[i + 1 :]))
|
||||
return
|
||||
elif argType == "/run":
|
||||
if dde:
|
||||
dde.Exec(
|
||||
"from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 0)"
|
||||
% (par, " ".join(args[i + 1 :]))
|
||||
)
|
||||
else:
|
||||
from . import scriptutils
|
||||
|
||||
scriptutils.RunScript(par, " ".join(args[i + 1 :]), 0)
|
||||
return
|
||||
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(par)
|
||||
else:
|
||||
win32ui.MessageBox(
|
||||
"The /dde command can only be used\r\nwhen Pythonwin is already running"
|
||||
)
|
||||
i += 1
|
||||
else:
|
||||
raise ValueError("Command line argument not recognised: %s" % argType)
|
||||
|
||||
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.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 . import scriptutils
|
||||
|
||||
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()
|
@ -0,0 +1,58 @@
|
||||
# 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
|
@ -0,0 +1,843 @@
|
||||
######################################################################
|
||||
##
|
||||
## 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
|
||||
import sysconfig
|
||||
|
||||
pychecker = os.path.join(
|
||||
sysconfig.get_paths()["purelib"], "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()
|
685
.venv/Lib/site-packages/pythonwin/pywin/framework/scriptutils.py
Normal file
685
.venv/Lib/site-packages/pythonwin/pywin/framework/scriptutils.py
Normal file
@ -0,0 +1,685 @@
|
||||
"""
|
||||
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 getattr(mod, "__file__", None):
|
||||
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:
|
||||
from importlib import reload
|
||||
|
||||
mod = 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)
|
747
.venv/Lib/site-packages/pythonwin/pywin/framework/sgrepmdi.py
Normal file
747
.venv/Lib/site-packages/pythonwin/pywin/framework/sgrepmdi.py
Normal file
@ -0,0 +1,747 @@
|
||||
# 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()
|
78
.venv/Lib/site-packages/pythonwin/pywin/framework/startup.py
Normal file
78
.venv/Lib/site-packages/pythonwin/pywin/framework/startup.py
Normal file
@ -0,0 +1,78 @@
|
||||
# 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 os
|
||||
import win32api
|
||||
import win32ui
|
||||
|
||||
if not sys.argv:
|
||||
# Initialize sys.argv from commandline. When sys.argv is empty list (
|
||||
# different from [''] meaning "no cmd line arguments" ), then C
|
||||
# bootstrapping or another method of invocation failed to initialize
|
||||
# sys.argv and it will be done here. ( This was a workaround for a bug in
|
||||
# win32ui but is retained for other situations. )
|
||||
argv = win32api.CommandLineToArgv(win32api.GetCommandLine())
|
||||
sys.argv = argv[1:]
|
||||
if os.getcwd() not in sys.path and "." not in sys.path:
|
||||
sys.path.insert(0, os.getcwd())
|
||||
|
||||
# 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() in ("/app", "-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()
|
176
.venv/Lib/site-packages/pythonwin/pywin/framework/stdin.py
Normal file
176
.venv/Lib/site-packages/pythonwin/pywin/framework/stdin.py
Normal file
@ -0,0 +1,176 @@
|
||||
# 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()
|
280
.venv/Lib/site-packages/pythonwin/pywin/framework/toolmenu.py
Normal file
280
.venv/Lib/site-packages/pythonwin/pywin/framework/toolmenu.py
Normal file
@ -0,0 +1,280 @@
|
||||
# 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)
|
14
.venv/Lib/site-packages/pythonwin/pywin/framework/window.py
Normal file
14
.venv/Lib/site-packages/pythonwin/pywin/framework/window.py
Normal file
@ -0,0 +1,14 @@
|
||||
# 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])
|
588
.venv/Lib/site-packages/pythonwin/pywin/framework/winout.py
Normal file
588
.venv/Lib/site-packages/pythonwin/pywin/framework/winout.py
Normal file
@ -0,0 +1,588 @@
|
||||
# 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()
|
Reference in New Issue
Block a user