Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
|
@ -0,0 +1,528 @@
|
|||
# Code that allows Pythonwin to pretend it is IDLE
|
||||
# (at least as far as most IDLE extensions are concerned)
|
||||
|
||||
import string
|
||||
import win32api
|
||||
import win32ui
|
||||
import win32con
|
||||
import sys
|
||||
|
||||
from pywin.mfc.dialog import GetSimpleInput
|
||||
from pywin import default_scintilla_encoding
|
||||
|
||||
wordchars = string.ascii_uppercase + string.ascii_lowercase + string.digits
|
||||
|
||||
class TextError(Exception): # When a TclError would normally be raised.
|
||||
pass
|
||||
|
||||
|
||||
class EmptyRange(Exception): # Internally raised.
|
||||
pass
|
||||
|
||||
def GetIDLEModule(module):
|
||||
try:
|
||||
# First get it from Pythonwin it is exists.
|
||||
modname = "pywin.idle." + module
|
||||
__import__(modname)
|
||||
except ImportError as details:
|
||||
msg = "The IDLE extension '%s' can not be located.\r\n\r\n" \
|
||||
"Please correct the installation and restart the" \
|
||||
" application.\r\n\r\n%s" % (module, details)
|
||||
win32ui.MessageBox(msg)
|
||||
return None
|
||||
mod=sys.modules[modname]
|
||||
mod.TclError = TextError # A hack that can go soon!
|
||||
return mod
|
||||
|
||||
# A class that is injected into the IDLE auto-indent extension.
|
||||
# It allows for decent performance when opening a new file,
|
||||
# as auto-indent uses the tokenizer module to determine indents.
|
||||
# The default AutoIndent readline method works OK, but it goes through
|
||||
# this layer of Tk index indirection for every single line. For large files
|
||||
# without indents (and even small files with indents :-) it was pretty slow!
|
||||
def fast_readline(self):
|
||||
if self.finished:
|
||||
val = ""
|
||||
else:
|
||||
if "_scint_lines" not in self.__dict__:
|
||||
# XXX - note - assumes this is only called once the file is loaded!
|
||||
self._scint_lines = self.text.edit.GetTextRange().split("\n")
|
||||
sl = self._scint_lines
|
||||
i = self.i = self.i + 1
|
||||
if i >= len(sl):
|
||||
val = ""
|
||||
else:
|
||||
val = sl[i]+"\n"
|
||||
return val.encode(default_scintilla_encoding)
|
||||
|
||||
try:
|
||||
GetIDLEModule("AutoIndent").IndentSearcher.readline = fast_readline
|
||||
except AttributeError: # GetIDLEModule may return None
|
||||
pass
|
||||
|
||||
# A class that attempts to emulate an IDLE editor window.
|
||||
# Construct with a Pythonwin view.
|
||||
class IDLEEditorWindow:
|
||||
def __init__(self, edit):
|
||||
self.edit = edit
|
||||
self.text = TkText(edit)
|
||||
self.extensions = {}
|
||||
self.extension_menus = {}
|
||||
|
||||
def close(self):
|
||||
self.edit = self.text = None
|
||||
self.extension_menus = None
|
||||
try:
|
||||
for ext in self.extensions.values():
|
||||
closer = getattr(ext, "close", None)
|
||||
if closer is not None:
|
||||
closer()
|
||||
finally:
|
||||
self.extensions = {}
|
||||
|
||||
def IDLEExtension(self, extension):
|
||||
ext = self.extensions.get(extension)
|
||||
if ext is not None: return ext
|
||||
mod = GetIDLEModule(extension)
|
||||
if mod is None: return None
|
||||
klass = getattr(mod, extension)
|
||||
ext = self.extensions[extension] = klass(self)
|
||||
# Find and bind all the events defined in the extension.
|
||||
events = [item for item in dir(klass) if item[-6:]=="_event"]
|
||||
for event in events:
|
||||
name = "<<%s>>" % (event[:-6].replace("_", "-"), )
|
||||
self.edit.bindings.bind(name, getattr(ext, event))
|
||||
return ext
|
||||
|
||||
def GetMenuItems(self, menu_name):
|
||||
# Get all menu items for the menu name (eg, "edit")
|
||||
bindings = self.edit.bindings
|
||||
ret = []
|
||||
for ext in self.extensions.values():
|
||||
menudefs = getattr(ext, "menudefs", [])
|
||||
for name, items in menudefs:
|
||||
if name == menu_name:
|
||||
for text, event in [item for item in items if item is not None]:
|
||||
text = text.replace("&", "&&")
|
||||
text = text.replace("_", "&")
|
||||
ret.append((text, event))
|
||||
return ret
|
||||
|
||||
######################################################################
|
||||
# The IDLE "Virtual UI" methods that are exposed to the IDLE extensions.
|
||||
#
|
||||
def askinteger(self, caption, prompt, parent=None, initialvalue=0, minvalue=None, maxvalue=None):
|
||||
while 1:
|
||||
rc = GetSimpleInput(prompt, str(initialvalue), caption)
|
||||
if rc is None: return 0 # Correct "cancel" semantics?
|
||||
err = None
|
||||
try:
|
||||
rc = int(rc)
|
||||
except ValueError:
|
||||
err = "Please enter an integer"
|
||||
if not err and minvalue is not None and rc < minvalue:
|
||||
err = "Please enter an integer greater then or equal to %s" % (minvalue,)
|
||||
if not err and maxvalue is not None and rc > maxvalue:
|
||||
err = "Please enter an integer less then or equal to %s" % (maxvalue,)
|
||||
if err:
|
||||
win32ui.MessageBox(err, caption, win32con.MB_OK)
|
||||
continue
|
||||
return rc
|
||||
def askyesno(self, caption, prompt, parent=None):
|
||||
return win32ui.MessageBox(prompt, caption, win32con.MB_YESNO)==win32con.IDYES
|
||||
|
||||
######################################################################
|
||||
# The IDLE "Virtual Text Widget" methods that are exposed to the IDLE extensions.
|
||||
#
|
||||
|
||||
# Is character at text_index in a Python string? Return 0 for
|
||||
# "guaranteed no", true for anything else.
|
||||
def is_char_in_string(self, text_index):
|
||||
# A helper for the code analyser - we need internal knowledge of
|
||||
# the colorizer to get this information
|
||||
# This assumes the colorizer has got to this point!
|
||||
text_index = self.text._getoffset(text_index)
|
||||
c = self.text.edit._GetColorizer()
|
||||
if c and c.GetStringStyle(text_index) is None:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
# If a selection is defined in the text widget, return
|
||||
# (start, end) as Tkinter text indices, otherwise return
|
||||
# (None, None)
|
||||
def get_selection_indices(self):
|
||||
try:
|
||||
first = self.text.index("sel.first")
|
||||
last = self.text.index("sel.last")
|
||||
return first, last
|
||||
except TextError:
|
||||
return None, None
|
||||
|
||||
def set_tabwidth(self, width ):
|
||||
self.edit.SCISetTabWidth(width)
|
||||
|
||||
def get_tabwidth(self):
|
||||
return self.edit.GetTabWidth()
|
||||
|
||||
# A class providing the generic "Call Tips" interface
|
||||
class CallTips:
|
||||
def __init__(self, edit):
|
||||
self.edit = edit
|
||||
def showtip(self, tip_text):
|
||||
self.edit.SCICallTipShow(tip_text)
|
||||
def hidetip(self):
|
||||
self.edit.SCICallTipCancel()
|
||||
|
||||
########################################
|
||||
#
|
||||
# Helpers for the TkText emulation.
|
||||
def TkOffsetToIndex(offset, edit):
|
||||
lineoff = 0
|
||||
# May be 1 > actual end if we pretended there was a trailing '\n'
|
||||
offset = min(offset, edit.GetTextLength())
|
||||
line = edit.LineFromChar(offset)
|
||||
lineIndex = edit.LineIndex(line)
|
||||
return "%d.%d" % (line+1, offset-lineIndex)
|
||||
|
||||
def _NextTok(str, pos):
|
||||
# Returns (token, endPos)
|
||||
end = len(str)
|
||||
if pos>=end: return None, 0
|
||||
while pos < end and str[pos] in string.whitespace:
|
||||
pos = pos + 1
|
||||
# Special case for +-
|
||||
if str[pos] in '+-':
|
||||
return str[pos],pos+1
|
||||
# Digits also a special case.
|
||||
endPos = pos
|
||||
while endPos < end and str[endPos] in string.digits+".":
|
||||
endPos = endPos + 1
|
||||
if pos!=endPos: return str[pos:endPos], endPos
|
||||
endPos = pos
|
||||
while endPos < end and str[endPos] not in string.whitespace + string.digits + "+-":
|
||||
endPos = endPos + 1
|
||||
if pos!=endPos: return str[pos:endPos], endPos
|
||||
return None, 0
|
||||
|
||||
def TkIndexToOffset(bm, edit, marks):
|
||||
base, nextTokPos = _NextTok(bm, 0)
|
||||
if base is None: raise ValueError("Empty bookmark ID!")
|
||||
if base.find(".")>0:
|
||||
try:
|
||||
line, col = base.split(".", 2)
|
||||
if col=="first" or col=="last":
|
||||
# Tag name
|
||||
if line != "sel": raise ValueError("Tags arent here!")
|
||||
sel = edit.GetSel()
|
||||
if sel[0]==sel[1]:
|
||||
raise EmptyRange
|
||||
if col=="first":
|
||||
pos = sel[0]
|
||||
else:
|
||||
pos = sel[1]
|
||||
else:
|
||||
# Lines are 1 based for tkinter
|
||||
line = int(line)-1
|
||||
if line > edit.GetLineCount():
|
||||
pos = edit.GetTextLength()+1
|
||||
else:
|
||||
pos = edit.LineIndex(line)
|
||||
if pos==-1: pos = edit.GetTextLength()
|
||||
pos = pos + int(col)
|
||||
except (ValueError, IndexError):
|
||||
raise ValueError("Unexpected literal in '%s'" % base)
|
||||
elif base == 'insert':
|
||||
pos = edit.GetSel()[0]
|
||||
elif base=='end':
|
||||
pos = edit.GetTextLength()
|
||||
# Pretend there is a trailing '\n' if necessary
|
||||
if pos and edit.SCIGetCharAt(pos-1) != "\n":
|
||||
pos = pos+1
|
||||
else:
|
||||
try:
|
||||
pos = marks[base]
|
||||
except KeyError:
|
||||
raise ValueError("Unsupported base offset or undefined mark '%s'" % base)
|
||||
|
||||
while 1:
|
||||
word, nextTokPos = _NextTok(bm, nextTokPos)
|
||||
if word is None: break
|
||||
if word in ['+','-']:
|
||||
num, nextTokPos = _NextTok(bm, nextTokPos)
|
||||
if num is None: raise ValueError("+/- operator needs 2 args")
|
||||
what, nextTokPos = _NextTok(bm, nextTokPos)
|
||||
if what is None: raise ValueError("+/- operator needs 2 args")
|
||||
if what[0] != "c": raise ValueError("+/- only supports chars")
|
||||
if word=='+':
|
||||
pos = pos + int(num)
|
||||
else:
|
||||
pos = pos - int(num)
|
||||
elif word=='wordstart':
|
||||
while pos > 0 and edit.SCIGetCharAt(pos-1) in wordchars:
|
||||
pos = pos - 1
|
||||
elif word=='wordend':
|
||||
end = edit.GetTextLength()
|
||||
while pos < end and edit.SCIGetCharAt(pos) in wordchars:
|
||||
pos = pos + 1
|
||||
elif word=='linestart':
|
||||
while pos > 0 and edit.SCIGetCharAt(pos-1) not in '\n\r':
|
||||
pos = pos - 1
|
||||
elif word=='lineend':
|
||||
end = edit.GetTextLength()
|
||||
while pos < end and edit.SCIGetCharAt(pos) not in '\n\r':
|
||||
pos = pos + 1
|
||||
else:
|
||||
raise ValueError("Unsupported relative offset '%s'" % word)
|
||||
return max(pos, 0) # Tkinter is tollerant of -ve indexes - we aren't
|
||||
|
||||
# A class that resembles an IDLE (ie, a Tk) text widget.
|
||||
# Construct with an edit object (eg, an editor view)
|
||||
class TkText:
|
||||
def __init__(self, edit):
|
||||
self.calltips = None
|
||||
self.edit = edit
|
||||
self.marks = {}
|
||||
## def __getattr__(self, attr):
|
||||
## if attr=="tk": return self # So text.tk.call works.
|
||||
## if attr=="master": return None # ditto!
|
||||
## raise AttributeError, attr
|
||||
## def __getitem__(self, item):
|
||||
## if item=="tabs":
|
||||
## size = self.edit.GetTabWidth()
|
||||
## if size==8: return "" # Tk default
|
||||
## return size # correct semantics?
|
||||
## elif item=="font": # Used for measurements we dont need to do!
|
||||
## return "Dont know the font"
|
||||
## raise IndexError, "Invalid index '%s'" % item
|
||||
def make_calltip_window(self):
|
||||
if self.calltips is None:
|
||||
self.calltips = CallTips(self.edit)
|
||||
return self.calltips
|
||||
def _getoffset(self, index):
|
||||
return TkIndexToOffset(index, self.edit, self.marks)
|
||||
def _getindex(self, off):
|
||||
return TkOffsetToIndex(off, self.edit)
|
||||
def _fix_indexes(self, start, end):
|
||||
# first some magic to handle skipping over utf8 extended chars.
|
||||
while start > 0 and ord(self.edit.SCIGetCharAt(start)) & 0xC0 == 0x80:
|
||||
start -= 1
|
||||
while end < self.edit.GetTextLength() and ord(self.edit.SCIGetCharAt(end)) & 0xC0 == 0x80:
|
||||
end += 1
|
||||
# now handling fixing \r\n->\n disparities...
|
||||
if start>0 and self.edit.SCIGetCharAt(start)=='\n' and self.edit.SCIGetCharAt(start-1)=='\r':
|
||||
start = start - 1
|
||||
if end < self.edit.GetTextLength() and self.edit.SCIGetCharAt(end-1)=='\r' and self.edit.SCIGetCharAt(end)=='\n':
|
||||
end = end + 1
|
||||
return start, end
|
||||
## def get_tab_width(self):
|
||||
## return self.edit.GetTabWidth()
|
||||
## def call(self, *rest):
|
||||
## # Crap to support Tk measurement hacks for tab widths
|
||||
## if rest[0] != "font" or rest[1] != "measure":
|
||||
## raise ValueError, "Unsupport call type"
|
||||
## return len(rest[5])
|
||||
## def configure(self, **kw):
|
||||
## for name, val in kw.items():
|
||||
## if name=="tabs":
|
||||
## self.edit.SCISetTabWidth(int(val))
|
||||
## else:
|
||||
## raise ValueError, "Unsupported configuration item %s" % kw
|
||||
def bind(self, binding, handler):
|
||||
self.edit.bindings.bind(binding, handler)
|
||||
def get(self, start, end = None):
|
||||
try:
|
||||
start = self._getoffset(start)
|
||||
if end is None:
|
||||
end = start+1
|
||||
else:
|
||||
end = self._getoffset(end)
|
||||
except EmptyRange:
|
||||
return ""
|
||||
# Simple semantic checks to conform to the Tk text interface
|
||||
if end <= start: return ""
|
||||
max = self.edit.GetTextLength()
|
||||
checkEnd = 0
|
||||
if end > max:
|
||||
end = max
|
||||
checkEnd = 1
|
||||
start, end = self._fix_indexes(start, end)
|
||||
ret = self.edit.GetTextRange(start, end)
|
||||
# pretend a trailing '\n' exists if necessary.
|
||||
if checkEnd and (not ret or ret[-1] != '\n'): ret = ret + '\n'
|
||||
return ret.replace("\r", "")
|
||||
def index(self, spec):
|
||||
try:
|
||||
return self._getindex(self._getoffset(spec))
|
||||
except EmptyRange:
|
||||
return ""
|
||||
def insert(self, pos, text):
|
||||
try:
|
||||
pos = self._getoffset(pos)
|
||||
except EmptyRange:
|
||||
raise TextError("Empty range")
|
||||
self.edit.SetSel((pos, pos))
|
||||
# IDLE only deals with "\n" - we will be nicer
|
||||
|
||||
bits = text.split('\n')
|
||||
self.edit.SCIAddText(bits[0])
|
||||
for bit in bits[1:]:
|
||||
self.edit.SCINewline()
|
||||
self.edit.SCIAddText(bit)
|
||||
|
||||
def delete(self, start, end=None):
|
||||
try:
|
||||
start = self._getoffset(start)
|
||||
if end is not None: end = self._getoffset(end)
|
||||
except EmptyRange:
|
||||
raise TextError("Empty range")
|
||||
# If end is specified and == start, then we must delete nothing.
|
||||
if start==end: return
|
||||
# If end is not specified, delete one char
|
||||
if end is None:
|
||||
end = start+1
|
||||
else:
|
||||
# Tk says not to delete in this case, but our control would.
|
||||
if end<start: return
|
||||
if start==self.edit.GetTextLength(): return # Nothing to delete.
|
||||
old = self.edit.GetSel()[0] # Lose a selection
|
||||
# Hack for partial '\r\n' and UTF-8 char removal
|
||||
start, end = self._fix_indexes(start, end)
|
||||
self.edit.SetSel((start, end))
|
||||
self.edit.Clear()
|
||||
if old>=start and old<end:
|
||||
old=start
|
||||
elif old>=end:
|
||||
old = old - (end-start)
|
||||
self.edit.SetSel(old)
|
||||
|
||||
def bell(self):
|
||||
win32api.MessageBeep()
|
||||
|
||||
def see(self, pos):
|
||||
# Most commands we use in Scintilla actually force the selection
|
||||
# to be seen, making this unnecessary.
|
||||
pass
|
||||
|
||||
def mark_set(self, name, pos):
|
||||
try:
|
||||
pos = self._getoffset(pos)
|
||||
except EmptyRange:
|
||||
raise TextError("Empty range '%s'" % pos)
|
||||
if name == "insert":
|
||||
self.edit.SetSel( pos )
|
||||
else:
|
||||
self.marks[name]=pos
|
||||
|
||||
def tag_add(self, name, start, end):
|
||||
if name != "sel": raise ValueError("Only sel tag is supported")
|
||||
try:
|
||||
start = self._getoffset(start)
|
||||
end = self._getoffset(end)
|
||||
except EmptyRange:
|
||||
raise TextError("Empty range")
|
||||
self.edit.SetSel( start, end )
|
||||
|
||||
def tag_remove(self, name, start, end):
|
||||
if name !="sel" or start != "1.0" or end != "end":
|
||||
raise ValueError("Cant remove this tag")
|
||||
# Turn the sel into a cursor
|
||||
self.edit.SetSel(self.edit.GetSel()[0])
|
||||
|
||||
def compare(self, i1, op, i2):
|
||||
try:
|
||||
i1=self._getoffset(i1)
|
||||
except EmptyRange:
|
||||
i1 = ""
|
||||
try:
|
||||
i2=self._getoffset(i2)
|
||||
except EmptyRange:
|
||||
i2 = ""
|
||||
return eval("%d%s%d" % (i1,op,i2))
|
||||
|
||||
def undo_block_start(self):
|
||||
self.edit.SCIBeginUndoAction()
|
||||
|
||||
def undo_block_stop(self):
|
||||
self.edit.SCIEndUndoAction()
|
||||
|
||||
######################################################################
|
||||
#
|
||||
# Test related code.
|
||||
#
|
||||
######################################################################
|
||||
def TestCheck(index, edit, expected=None):
|
||||
rc = TkIndexToOffset(index, edit, {})
|
||||
if rc != expected:
|
||||
print("ERROR: Index", index,", expected", expected, "but got", rc)
|
||||
|
||||
def TestGet(fr, to, t, expected):
|
||||
got = t.get(fr, to)
|
||||
if got != expected:
|
||||
print("ERROR: get(%s, %s) expected %s, but got %s" % (repr(fr), repr(to), repr(expected), repr(got)))
|
||||
|
||||
def test():
|
||||
import pywin.framework.editor
|
||||
d=pywin.framework.editor.editorTemplate.OpenDocumentFile(None)
|
||||
e=d.GetFirstView()
|
||||
t = TkText(e)
|
||||
e.SCIAddText("hi there how\nare you today\r\nI hope you are well")
|
||||
e.SetSel((4,4))
|
||||
|
||||
skip = """
|
||||
TestCheck("insert", e, 4)
|
||||
TestCheck("insert wordstart", e, 3)
|
||||
TestCheck("insert wordend", e, 8)
|
||||
TestCheck("insert linestart", e, 0)
|
||||
TestCheck("insert lineend", e, 12)
|
||||
TestCheck("insert + 4 chars", e, 8)
|
||||
TestCheck("insert +4c", e, 8)
|
||||
TestCheck("insert - 2 chars", e, 2)
|
||||
TestCheck("insert -2c", e, 2)
|
||||
TestCheck("insert-2c", e, 2)
|
||||
TestCheck("insert-2 c", e, 2)
|
||||
TestCheck("insert- 2c", e, 2)
|
||||
TestCheck("1.1", e, 1)
|
||||
TestCheck("1.0", e, 0)
|
||||
TestCheck("2.0", e, 13)
|
||||
try:
|
||||
TestCheck("sel.first", e, 0)
|
||||
print "*** sel.first worked with an empty selection"
|
||||
except TextError:
|
||||
pass
|
||||
e.SetSel((4,5))
|
||||
TestCheck("sel.first- 2c", e, 2)
|
||||
TestCheck("sel.last- 2c", e, 3)
|
||||
"""
|
||||
# Check EOL semantics
|
||||
e.SetSel((4,4))
|
||||
TestGet("insert lineend", "insert lineend +1c", t, "\n")
|
||||
e.SetSel((20, 20))
|
||||
TestGet("insert lineend", "insert lineend +1c", t, "\n")
|
||||
e.SetSel((35, 35))
|
||||
TestGet("insert lineend", "insert lineend +1c", t, "\n")
|
||||
|
||||
class IDLEWrapper:
|
||||
def __init__(self, control):
|
||||
self.text = control
|
||||
|
||||
def IDLETest(extension):
|
||||
import sys, os
|
||||
modname = "pywin.idle." + extension
|
||||
__import__(modname)
|
||||
mod=sys.modules[modname]
|
||||
mod.TclError = TextError
|
||||
klass = getattr(mod, extension)
|
||||
|
||||
# Create a new Scintilla Window.
|
||||
import pywin.framework.editor
|
||||
d=pywin.framework.editor.editorTemplate.OpenDocumentFile(None)
|
||||
v=d.GetFirstView()
|
||||
fname=os.path.splitext(__file__)[0] + ".py"
|
||||
v.SCIAddText(open(fname).read())
|
||||
d.SetModifiedFlag(0)
|
||||
r=klass( IDLEWrapper( TkText(v) ) )
|
||||
return r
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
||||
|
|
@ -0,0 +1 @@
|
|||
# package init.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
172
venv/Lib/site-packages/pythonwin/pywin/scintilla/bindings.py
Normal file
172
venv/Lib/site-packages/pythonwin/pywin/scintilla/bindings.py
Normal file
|
@ -0,0 +1,172 @@
|
|||
from . import IDLEenvironment
|
||||
import string
|
||||
import win32ui
|
||||
import win32api
|
||||
import win32con
|
||||
from . import keycodes
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
HANDLER_ARGS_GUESS=0
|
||||
HANDLER_ARGS_NATIVE=1
|
||||
HANDLER_ARGS_IDLE=2
|
||||
HANDLER_ARGS_EXTENSION=3
|
||||
|
||||
next_id = 5000
|
||||
|
||||
event_to_commands = {}# dict of integer IDs to event names.
|
||||
command_to_events = {}# dict of event names to int IDs
|
||||
|
||||
def assign_command_id(event, id = 0):
|
||||
global next_id
|
||||
if id == 0:
|
||||
id = event_to_commands.get(event, 0)
|
||||
if id == 0:
|
||||
id = next_id
|
||||
next_id = next_id + 1
|
||||
# Only map the ones we allocated - specified ones are assumed to have a handler
|
||||
command_to_events[id] = event
|
||||
event_to_commands[event] = id
|
||||
return id
|
||||
|
||||
class SendCommandHandler:
|
||||
def __init__(self, cmd):
|
||||
self.cmd = cmd
|
||||
def __call__(self, *args):
|
||||
win32ui.GetMainFrame().SendMessage(win32con.WM_COMMAND, self.cmd)
|
||||
|
||||
class Binding:
|
||||
def __init__(self, handler, handler_args_type):
|
||||
self.handler = handler
|
||||
self.handler_args_type = handler_args_type
|
||||
|
||||
class BindingsManager:
|
||||
def __init__(self, parent_view):
|
||||
self.parent_view = parent_view
|
||||
self.bindings = {} # dict of Binding instances.
|
||||
self.keymap = {}
|
||||
|
||||
def prepare_configure(self):
|
||||
self.keymap = {}
|
||||
|
||||
def complete_configure(self):
|
||||
for id in command_to_events.keys():
|
||||
self.parent_view.HookCommand(self._OnCommand, id)
|
||||
|
||||
def close(self):
|
||||
self.parent_view = self.bindings = self.keymap = None
|
||||
|
||||
def report_error(self, problem):
|
||||
try:
|
||||
win32ui.SetStatusText(problem, 1)
|
||||
except win32ui.error:
|
||||
# No status bar!
|
||||
print(problem)
|
||||
|
||||
def update_keymap(self, keymap):
|
||||
self.keymap.update(keymap)
|
||||
|
||||
def bind(self, event, handler, handler_args_type = HANDLER_ARGS_GUESS, cid = 0):
|
||||
if handler is None:
|
||||
handler = SendCommandHandler(cid)
|
||||
self.bindings[event] = self._new_binding(handler, handler_args_type)
|
||||
self.bind_command(event, cid)
|
||||
|
||||
def bind_command(self, event, id = 0):
|
||||
"Binds an event to a Windows control/command ID"
|
||||
id = assign_command_id(event, id)
|
||||
return id
|
||||
|
||||
def get_command_id(self, event):
|
||||
id = event_to_commands.get(event)
|
||||
if id is None:
|
||||
# See if we even have an event of that name!?
|
||||
if event not in self.bindings:
|
||||
return None
|
||||
id = self.bind_command(event)
|
||||
return id
|
||||
|
||||
def _OnCommand(self, id, code):
|
||||
event = command_to_events.get(id)
|
||||
if event is None:
|
||||
self.report_error("No event associated with event ID %d" % id)
|
||||
return 1
|
||||
return self.fire(event)
|
||||
|
||||
def _new_binding(self, event, handler_args_type):
|
||||
return Binding(event, handler_args_type)
|
||||
|
||||
def _get_IDLE_handler(self, ext, handler):
|
||||
try:
|
||||
instance = self.parent_view.idle.IDLEExtension(ext)
|
||||
name = handler.replace("-", "_") + "_event"
|
||||
return getattr(instance, name)
|
||||
except (ImportError, AttributeError):
|
||||
msg = "Can not find event '%s' in IDLE extension '%s'" % (handler, ext)
|
||||
self.report_error(msg)
|
||||
return None
|
||||
|
||||
def fire(self, event, event_param = None):
|
||||
# Fire the specified event. Result is native Pythonwin result
|
||||
# (ie, 1==pass one, 0 or None==handled)
|
||||
|
||||
# First look up the event directly - if there, we are set.
|
||||
binding = self.bindings.get(event)
|
||||
if binding is None:
|
||||
# If possible, find it!
|
||||
# A native method name
|
||||
handler = getattr(self.parent_view, event + "Event", None)
|
||||
if handler is None:
|
||||
# Can't decide if I should report an error??
|
||||
self.report_error("The event name '%s' can not be found." % event)
|
||||
# Either way, just let the default handlers grab it.
|
||||
return 1
|
||||
binding = self._new_binding(handler, HANDLER_ARGS_NATIVE)
|
||||
# Cache it.
|
||||
self.bindings[event] = binding
|
||||
|
||||
handler_args_type = binding.handler_args_type
|
||||
# Now actually fire it.
|
||||
if handler_args_type==HANDLER_ARGS_GUESS:
|
||||
# Can't be native, as natives are never added with "guess".
|
||||
# Must be extension or IDLE.
|
||||
if event[0]=="<":
|
||||
handler_args_type = HANDLER_ARGS_IDLE
|
||||
else:
|
||||
handler_args_type = HANDLER_ARGS_EXTENSION
|
||||
try:
|
||||
if handler_args_type==HANDLER_ARGS_EXTENSION:
|
||||
args = self.parent_view.idle, event_param
|
||||
else:
|
||||
args = (event_param,)
|
||||
rc = binding.handler(*args)
|
||||
if handler_args_type==HANDLER_ARGS_IDLE:
|
||||
# Convert to our return code.
|
||||
if rc in [None, "break"]:
|
||||
rc = 0
|
||||
else:
|
||||
rc = 1
|
||||
except:
|
||||
message = "Firing event '%s' failed." % event
|
||||
print(message)
|
||||
traceback.print_exc()
|
||||
self.report_error(message)
|
||||
rc = 1 # Let any default handlers have a go!
|
||||
return rc
|
||||
|
||||
def fire_key_event(self, msg):
|
||||
key = msg[2]
|
||||
keyState = 0
|
||||
if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000:
|
||||
keyState = keyState | win32con.RIGHT_CTRL_PRESSED | win32con.LEFT_CTRL_PRESSED
|
||||
if win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000:
|
||||
keyState = keyState | win32con.SHIFT_PRESSED
|
||||
if win32api.GetKeyState(win32con.VK_MENU) & 0x8000:
|
||||
keyState = keyState | win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED
|
||||
keyinfo = key, keyState
|
||||
# Special hacks for the dead-char key on non-US keyboards.
|
||||
# (XXX - which do not work :-(
|
||||
event = self.keymap.get( keyinfo )
|
||||
if event is None:
|
||||
return 1
|
||||
return self.fire(event, None)
|
322
venv/Lib/site-packages/pythonwin/pywin/scintilla/config.py
Normal file
322
venv/Lib/site-packages/pythonwin/pywin/scintilla/config.py
Normal file
|
@ -0,0 +1,322 @@
|
|||
# config.py - deals with loading configuration information.
|
||||
|
||||
# Loads config data from a .cfg file. Also caches the compiled
|
||||
# data back into a .cfc file.
|
||||
|
||||
# If you are wondering how to avoid needing .cfg files (eg,
|
||||
# if you are freezing Pythonwin etc) I suggest you create a
|
||||
# .py file, and put the config info in a docstring. Then
|
||||
# pass a CStringIO file (rather than a filename) to the
|
||||
# config manager.
|
||||
import sys
|
||||
import string
|
||||
from . import keycodes
|
||||
import marshal
|
||||
import stat
|
||||
import os
|
||||
import types
|
||||
import traceback
|
||||
import pywin
|
||||
import glob
|
||||
import imp
|
||||
|
||||
import win32api
|
||||
|
||||
debugging = 0
|
||||
if debugging:
|
||||
import win32traceutil # Some trace statements fire before the interactive window is open.
|
||||
def trace(*args):
|
||||
sys.stderr.write(" ".join(map(str, args)) + "\n")
|
||||
else:
|
||||
trace = lambda *args: None
|
||||
|
||||
compiled_config_version = 3
|
||||
|
||||
def split_line(line, lineno):
|
||||
comment_pos = line.find("#")
|
||||
if comment_pos>=0: line = line[:comment_pos]
|
||||
sep_pos = line.rfind("=")
|
||||
if sep_pos == -1:
|
||||
if line.strip():
|
||||
print("Warning: Line %d: %s is an invalid entry" % (lineno, repr(line)))
|
||||
return None, None
|
||||
return "", ""
|
||||
return line[:sep_pos].strip(), line[sep_pos+1:].strip()
|
||||
|
||||
def get_section_header(line):
|
||||
# Returns the section if the line is a section header, else None
|
||||
if line[0] == "[":
|
||||
end = line.find("]")
|
||||
if end==-1: end=len(line)
|
||||
rc = line[1:end].lower()
|
||||
try:
|
||||
i = rc.index(":")
|
||||
return rc[:i], rc[i+1:]
|
||||
except ValueError:
|
||||
return rc, ""
|
||||
return None, None
|
||||
|
||||
def find_config_file(f):
|
||||
return os.path.join(pywin.__path__[0], f + ".cfg")
|
||||
|
||||
def find_config_files():
|
||||
return [os.path.split(x)[1]
|
||||
for x in [os.path.splitext(x)[0] for x in glob.glob(os.path.join(pywin.__path__[0], "*.cfg"))]
|
||||
]
|
||||
|
||||
class ConfigManager:
|
||||
def __init__(self, f):
|
||||
self.filename = "unknown"
|
||||
self.last_error = None
|
||||
self.key_to_events = {}
|
||||
if hasattr(f, "readline"):
|
||||
fp = f
|
||||
self.filename = "<config string>"
|
||||
compiled_name = None
|
||||
else:
|
||||
try:
|
||||
f = find_config_file(f)
|
||||
src_stat = os.stat(f)
|
||||
except os.error:
|
||||
self.report_error("Config file '%s' not found" % f)
|
||||
return
|
||||
self.filename = f
|
||||
self.basename = os.path.basename(f)
|
||||
trace("Loading configuration", self.basename)
|
||||
compiled_name = os.path.splitext(f)[0] + ".cfc"
|
||||
try:
|
||||
cf = open(compiled_name, "rb")
|
||||
try:
|
||||
ver = marshal.load(cf)
|
||||
ok = compiled_config_version == ver
|
||||
if ok:
|
||||
kblayoutname = marshal.load(cf)
|
||||
magic = marshal.load(cf)
|
||||
size = marshal.load(cf)
|
||||
mtime = marshal.load(cf)
|
||||
if magic == imp.get_magic() and \
|
||||
win32api.GetKeyboardLayoutName() == kblayoutname and \
|
||||
src_stat[stat.ST_MTIME] == mtime and \
|
||||
src_stat[stat.ST_SIZE] == size:
|
||||
self.cache = marshal.load(cf)
|
||||
trace("Configuration loaded cached", compiled_name)
|
||||
return # We are ready to roll!
|
||||
finally:
|
||||
cf.close()
|
||||
except (os.error, IOError, EOFError):
|
||||
pass
|
||||
fp = open(f)
|
||||
self.cache = {}
|
||||
lineno = 1
|
||||
line = fp.readline()
|
||||
while line:
|
||||
# Skip to the next section (maybe already there!)
|
||||
section, subsection = get_section_header(line)
|
||||
while line and section is None:
|
||||
line = fp.readline()
|
||||
if not line: break
|
||||
lineno = lineno + 1
|
||||
section, subsection = get_section_header(line)
|
||||
if not line: break
|
||||
|
||||
if section=="keys":
|
||||
line, lineno = self._load_keys(subsection, fp, lineno)
|
||||
elif section == "extensions":
|
||||
line, lineno = self._load_extensions(subsection, fp, lineno)
|
||||
elif section == "idle extensions":
|
||||
line, lineno = self._load_idle_extensions(subsection, fp, lineno)
|
||||
elif section == "general":
|
||||
line, lineno = self._load_general(subsection, fp, lineno)
|
||||
else:
|
||||
self.report_error("Unrecognised section header '%s:%s'" % (section,subsection))
|
||||
line = fp.readline()
|
||||
lineno = lineno + 1
|
||||
# Check critical data.
|
||||
if not self.cache.get("keys"):
|
||||
self.report_error("No keyboard definitions were loaded")
|
||||
if not self.last_error and compiled_name:
|
||||
try:
|
||||
cf = open(compiled_name, "wb")
|
||||
marshal.dump(compiled_config_version, cf)
|
||||
marshal.dump(win32api.GetKeyboardLayoutName(), cf)
|
||||
marshal.dump(imp.get_magic(), cf)
|
||||
marshal.dump(src_stat[stat.ST_SIZE], cf)
|
||||
marshal.dump(src_stat[stat.ST_MTIME], cf)
|
||||
marshal.dump(self.cache, cf)
|
||||
cf.close()
|
||||
except (IOError, EOFError):
|
||||
pass # Ignore errors - may be read only.
|
||||
|
||||
def configure(self, editor, subsections = None):
|
||||
# Execute the extension code, and find any events.
|
||||
# First, we "recursively" connect any we are based on.
|
||||
if subsections is None: subsections = []
|
||||
subsections = [''] + subsections
|
||||
general = self.get_data("general")
|
||||
if general:
|
||||
parents = general.get("based on", [])
|
||||
for parent in parents:
|
||||
trace("Configuration based on", parent, "- loading.")
|
||||
parent = self.__class__(parent)
|
||||
parent.configure(editor, subsections)
|
||||
if parent.last_error:
|
||||
self.report_error(parent.last_error)
|
||||
|
||||
bindings = editor.bindings
|
||||
codeob = self.get_data("extension code")
|
||||
if codeob is not None:
|
||||
ns = {}
|
||||
try:
|
||||
exec(codeob, ns)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
self.report_error("Executing extension code failed")
|
||||
ns = None
|
||||
if ns:
|
||||
num = 0
|
||||
for name, func in list(ns.items()):
|
||||
if type(func)==types.FunctionType and name[:1] != '_':
|
||||
bindings.bind(name, func)
|
||||
num = num + 1
|
||||
trace("Configuration Extension code loaded", num, "events")
|
||||
# Load the idle extensions
|
||||
for subsection in subsections:
|
||||
for ext in self.get_data("idle extensions", {}).get(subsection, []):
|
||||
try:
|
||||
editor.idle.IDLEExtension(ext)
|
||||
trace("Loaded IDLE extension", ext)
|
||||
except:
|
||||
self.report_error("Can not load the IDLE extension '%s'" % ext)
|
||||
|
||||
# Now bind up the key-map (remembering a reverse map
|
||||
subsection_keymap = self.get_data("keys")
|
||||
num_bound = 0
|
||||
for subsection in subsections:
|
||||
keymap = subsection_keymap.get(subsection, {})
|
||||
bindings.update_keymap(keymap)
|
||||
num_bound = num_bound + len(keymap)
|
||||
trace("Configuration bound", num_bound, "keys")
|
||||
|
||||
def get_key_binding(self, event, subsections = None):
|
||||
if subsections is None: subsections = []
|
||||
subsections = [''] + subsections
|
||||
|
||||
subsection_keymap = self.get_data("keys")
|
||||
for subsection in subsections:
|
||||
map = self.key_to_events.get(subsection)
|
||||
if map is None: # Build it
|
||||
map = {}
|
||||
keymap = subsection_keymap.get(subsection, {})
|
||||
for key_info, map_event in list(keymap.items()):
|
||||
map[map_event] = key_info
|
||||
self.key_to_events[subsection] = map
|
||||
|
||||
info = map.get(event)
|
||||
if info is not None:
|
||||
return keycodes.make_key_name( info[0], info[1] )
|
||||
return None
|
||||
|
||||
def report_error(self, msg):
|
||||
self.last_error = msg
|
||||
print("Error in %s: %s" % (self.filename, msg))
|
||||
def report_warning(self, msg):
|
||||
print("Warning in %s: %s" % (self.filename, msg))
|
||||
|
||||
def _readline(self, fp, lineno, bStripComments = 1):
|
||||
line = fp.readline()
|
||||
lineno = lineno + 1
|
||||
if line:
|
||||
bBreak = get_section_header(line)[0] is not None # A new section is starting
|
||||
if bStripComments and not bBreak:
|
||||
pos = line.find("#")
|
||||
if pos>=0: line=line[:pos]+"\n"
|
||||
else:
|
||||
bBreak=1
|
||||
return line, lineno, bBreak
|
||||
|
||||
def get_data(self, name, default=None):
|
||||
return self.cache.get(name, default)
|
||||
|
||||
def _save_data(self, name, data):
|
||||
self.cache[name] = data
|
||||
return data
|
||||
|
||||
def _load_general(self, sub_section, fp, lineno):
|
||||
map = {}
|
||||
while 1:
|
||||
line, lineno, bBreak = self._readline(fp, lineno)
|
||||
if bBreak: break
|
||||
|
||||
key, val = split_line(line, lineno)
|
||||
if not key: continue
|
||||
key = key.lower()
|
||||
l = map.get(key, [])
|
||||
l.append(val)
|
||||
map[key]=l
|
||||
self._save_data("general", map)
|
||||
return line, lineno
|
||||
|
||||
def _load_keys(self, sub_section, fp, lineno):
|
||||
# Builds a nested dictionary of
|
||||
# (scancode, flags) = event_name
|
||||
main_map = self.get_data("keys", {})
|
||||
map = main_map.get(sub_section, {})
|
||||
while 1:
|
||||
line, lineno, bBreak = self._readline(fp, lineno)
|
||||
if bBreak: break
|
||||
|
||||
key, event = split_line(line, lineno)
|
||||
if not event: continue
|
||||
sc, flag = keycodes.parse_key_name(key)
|
||||
if sc is None:
|
||||
self.report_warning("Line %d: Invalid key name '%s'" % (lineno, key))
|
||||
else:
|
||||
map[sc, flag] = event
|
||||
main_map[sub_section] = map
|
||||
self._save_data("keys", main_map)
|
||||
return line, lineno
|
||||
|
||||
def _load_extensions(self, sub_section, fp, lineno):
|
||||
start_lineno = lineno
|
||||
lines = []
|
||||
while 1:
|
||||
line, lineno, bBreak = self._readline(fp, lineno, 0)
|
||||
if bBreak: break
|
||||
lines.append(line)
|
||||
try:
|
||||
c = compile(
|
||||
"\n" * start_lineno + # produces correct tracebacks
|
||||
"".join(lines), self.filename, "exec")
|
||||
self._save_data("extension code", c)
|
||||
except SyntaxError as details:
|
||||
errlineno = details.lineno + start_lineno
|
||||
# Should handle syntax errors better here, and offset the lineno.
|
||||
self.report_error("Compiling extension code failed:\r\nFile: %s\r\nLine %d\r\n%s" \
|
||||
% (details.filename, errlineno, details.msg))
|
||||
return line, lineno
|
||||
|
||||
def _load_idle_extensions(self, sub_section, fp, lineno):
|
||||
extension_map = self.get_data("idle extensions")
|
||||
if extension_map is None: extension_map = {}
|
||||
extensions = []
|
||||
while 1:
|
||||
line, lineno, bBreak = self._readline(fp, lineno)
|
||||
if bBreak: break
|
||||
line = line.strip()
|
||||
if line:
|
||||
extensions.append(line)
|
||||
extension_map[sub_section] = extensions
|
||||
self._save_data("idle extensions", extension_map)
|
||||
return line, lineno
|
||||
|
||||
def test():
|
||||
import time
|
||||
start = time.clock()
|
||||
f="default"
|
||||
cm = ConfigManager(f)
|
||||
map = cm.get_data("keys")
|
||||
took = time.clock()-start
|
||||
print("Loaded %s items in %.4f secs" % (len(map), took))
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
266
venv/Lib/site-packages/pythonwin/pywin/scintilla/configui.py
Normal file
266
venv/Lib/site-packages/pythonwin/pywin/scintilla/configui.py
Normal file
|
@ -0,0 +1,266 @@
|
|||
from pywin.mfc import dialog
|
||||
import win32api
|
||||
import win32con
|
||||
import win32ui
|
||||
import copy
|
||||
import string
|
||||
from . import scintillacon
|
||||
|
||||
# Used to indicate that style should use default color
|
||||
from win32con import CLR_INVALID
|
||||
|
||||
######################################################
|
||||
# Property Page for syntax formatting options
|
||||
|
||||
# The standard 16 color VGA palette should always be possible
|
||||
paletteVGA = (
|
||||
("Black", win32api.RGB(0,0,0)),
|
||||
("Navy", win32api.RGB(0,0,128)),
|
||||
("Green", win32api.RGB(0,128,0)),
|
||||
("Cyan", win32api.RGB(0,128,128)),
|
||||
("Maroon", win32api.RGB(128,0,0)),
|
||||
("Purple", win32api.RGB(128,0,128)),
|
||||
("Olive", win32api.RGB(128,128,0)),
|
||||
("Gray", win32api.RGB(128,128,128)),
|
||||
("Silver", win32api.RGB(192,192,192)),
|
||||
("Blue", win32api.RGB(0,0,255)),
|
||||
("Lime", win32api.RGB(0,255,0)),
|
||||
("Aqua", win32api.RGB(0,255,255)),
|
||||
("Red", win32api.RGB(255,0,0)),
|
||||
("Fuchsia", win32api.RGB(255,0,255)),
|
||||
("Yellow", win32api.RGB(255,255,0)),
|
||||
("White", win32api.RGB(255,255,255)),
|
||||
# and a few others will generally be possible.
|
||||
("DarkGrey", win32api.RGB(64,64,64)),
|
||||
("PurpleBlue", win32api.RGB(64,64,192)),
|
||||
("DarkGreen", win32api.RGB(0,96,0)),
|
||||
("DarkOlive", win32api.RGB(128,128,64)),
|
||||
("MediumBlue", win32api.RGB(0,0,192)),
|
||||
("DarkNavy", win32api.RGB(0,0,96)),
|
||||
("Magenta", win32api.RGB(96,0,96)),
|
||||
("OffWhite", win32api.RGB(255,255,220)),
|
||||
("LightPurple", win32api.RGB(220,220,255)),
|
||||
("<Default>", win32con.CLR_INVALID)
|
||||
)
|
||||
|
||||
class ScintillaFormatPropertyPage(dialog.PropertyPage):
|
||||
def __init__(self, scintillaClass = None, caption = 0):
|
||||
self.scintillaClass = scintillaClass
|
||||
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_FORMAT, caption=caption)
|
||||
|
||||
def OnInitDialog(self):
|
||||
try:
|
||||
if self.scintillaClass is None:
|
||||
from . import control
|
||||
sc = control.CScintillaEdit
|
||||
else:
|
||||
sc = self.scintillaClass
|
||||
|
||||
self.scintilla = sc()
|
||||
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.ES_MULTILINE
|
||||
# Convert the rect size
|
||||
rect = self.MapDialogRect( (5, 5, 120, 75))
|
||||
self.scintilla.CreateWindow(style, rect, self, 111)
|
||||
self.HookNotify(self.OnBraceMatch, scintillacon.SCN_CHECKBRACE)
|
||||
self.scintilla.HookKeyStroke(self.OnEsc, 27)
|
||||
self.scintilla.SCISetViewWS(1)
|
||||
self.pos_bstart = self.pos_bend = self.pos_bbad = 0
|
||||
|
||||
colorizer = self.scintilla._GetColorizer()
|
||||
text = colorizer.GetSampleText()
|
||||
items = text.split('|', 2)
|
||||
pos = len(items[0])
|
||||
self.scintilla.SCIAddText(''.join(items))
|
||||
self.scintilla.SetSel(pos, pos)
|
||||
self.scintilla.ApplyFormattingStyles()
|
||||
self.styles = self.scintilla._GetColorizer().styles
|
||||
|
||||
self.cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
|
||||
for c in paletteVGA:
|
||||
self.cbo.AddString(c[0])
|
||||
|
||||
self.cboBoldItalic = self.GetDlgItem(win32ui.IDC_COMBO2)
|
||||
for item in ["Bold Italic", "Bold", "Italic", "Regular"]:
|
||||
self.cboBoldItalic.InsertString(0, item)
|
||||
|
||||
self.butIsDefault = self.GetDlgItem(win32ui.IDC_CHECK1)
|
||||
self.butIsDefaultBackground = self.GetDlgItem(win32ui.IDC_CHECK2)
|
||||
self.listbox = self.GetDlgItem(win32ui.IDC_LIST1)
|
||||
self.HookCommand(self.OnListCommand, win32ui.IDC_LIST1)
|
||||
names = list(self.styles.keys())
|
||||
names.sort()
|
||||
for name in names:
|
||||
if self.styles[name].aliased is None:
|
||||
self.listbox.AddString(name)
|
||||
self.listbox.SetCurSel(0)
|
||||
|
||||
idc = win32ui.IDC_RADIO1
|
||||
if not self.scintilla._GetColorizer().bUseFixed: idc = win32ui.IDC_RADIO2
|
||||
self.GetDlgItem(idc).SetCheck(1)
|
||||
self.UpdateUIForStyle(self.styles[names[0]])
|
||||
|
||||
self.scintilla.HookFormatter(self)
|
||||
self.HookCommand(self.OnButDefaultFixedFont, win32ui.IDC_BUTTON1)
|
||||
self.HookCommand(self.OnButDefaultPropFont, win32ui.IDC_BUTTON2)
|
||||
self.HookCommand(self.OnButThisFont, win32ui.IDC_BUTTON3)
|
||||
self.HookCommand(self.OnButUseDefaultFont, win32ui.IDC_CHECK1)
|
||||
self.HookCommand(self.OnButThisBackground, win32ui.IDC_BUTTON4)
|
||||
self.HookCommand(self.OnButUseDefaultBackground, win32ui.IDC_CHECK2)
|
||||
self.HookCommand(self.OnStyleUIChanged, win32ui.IDC_COMBO1)
|
||||
self.HookCommand(self.OnStyleUIChanged, win32ui.IDC_COMBO2)
|
||||
self.HookCommand(self.OnButFixedOrDefault, win32ui.IDC_RADIO1)
|
||||
self.HookCommand(self.OnButFixedOrDefault, win32ui.IDC_RADIO2)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def OnEsc(self, ch):
|
||||
self.GetParent().EndDialog(win32con.IDCANCEL)
|
||||
|
||||
def OnBraceMatch(self, std, extra):
|
||||
import pywin.scintilla.view
|
||||
pywin.scintilla.view.DoBraceMatch(self.scintilla)
|
||||
|
||||
def GetSelectedStyle(self):
|
||||
return self.styles[self.listbox.GetText(self.listbox.GetCurSel())]
|
||||
|
||||
def _DoButDefaultFont(self, extra_flags, attr):
|
||||
baseFormat = getattr(self.scintilla._GetColorizer(), attr)
|
||||
flags = extra_flags | win32con.CF_SCREENFONTS | win32con.CF_EFFECTS | win32con.CF_FORCEFONTEXIST
|
||||
d=win32ui.CreateFontDialog(baseFormat, flags, None, self)
|
||||
if d.DoModal()==win32con.IDOK:
|
||||
setattr(self.scintilla._GetColorizer(), attr, d.GetCharFormat())
|
||||
self.OnStyleUIChanged(0, win32con.BN_CLICKED)
|
||||
|
||||
def OnButDefaultFixedFont(self, id, code):
|
||||
if code==win32con.BN_CLICKED:
|
||||
self._DoButDefaultFont(win32con.CF_FIXEDPITCHONLY, "baseFormatFixed")
|
||||
return 1
|
||||
|
||||
def OnButDefaultPropFont(self, id, code):
|
||||
if code==win32con.BN_CLICKED:
|
||||
self._DoButDefaultFont(win32con.CF_SCALABLEONLY, "baseFormatProp")
|
||||
return 1
|
||||
|
||||
def OnButFixedOrDefault(self, id, code):
|
||||
if code==win32con.BN_CLICKED:
|
||||
bUseFixed = id == win32ui.IDC_RADIO1
|
||||
self.GetDlgItem(win32ui.IDC_RADIO1).GetCheck() != 0
|
||||
self.scintilla._GetColorizer().bUseFixed = bUseFixed
|
||||
self.scintilla.ApplyFormattingStyles(0)
|
||||
return 1
|
||||
|
||||
def OnButThisFont(self, id, code):
|
||||
if code==win32con.BN_CLICKED:
|
||||
flags = win32con.CF_SCREENFONTS | win32con.CF_EFFECTS | win32con.CF_FORCEFONTEXIST
|
||||
style = self.GetSelectedStyle()
|
||||
# If the selected style is based on the default, we need to apply
|
||||
# the default to it.
|
||||
def_format = self.scintilla._GetColorizer().GetDefaultFormat()
|
||||
format = style.GetCompleteFormat(def_format)
|
||||
d=win32ui.CreateFontDialog(format, flags, None, self)
|
||||
if d.DoModal()==win32con.IDOK:
|
||||
style.format = d.GetCharFormat()
|
||||
self.scintilla.ApplyFormattingStyles(0)
|
||||
return 1
|
||||
|
||||
def OnButUseDefaultFont(self, id, code):
|
||||
if code == win32con.BN_CLICKED:
|
||||
isDef = self.butIsDefault.GetCheck()
|
||||
self.GetDlgItem(win32ui.IDC_BUTTON3).EnableWindow(not isDef)
|
||||
if isDef: # Being reset to the default font.
|
||||
style = self.GetSelectedStyle()
|
||||
style.ForceAgainstDefault()
|
||||
self.UpdateUIForStyle(style)
|
||||
self.scintilla.ApplyFormattingStyles(0)
|
||||
else:
|
||||
# User wants to override default -
|
||||
# do nothing!
|
||||
pass
|
||||
|
||||
def OnButThisBackground(self, id, code):
|
||||
if code==win32con.BN_CLICKED:
|
||||
style = self.GetSelectedStyle()
|
||||
bg = win32api.RGB(0xff, 0xff, 0xff)
|
||||
if style.background != CLR_INVALID:
|
||||
bg = style.background
|
||||
d=win32ui.CreateColorDialog(bg, 0, self)
|
||||
if d.DoModal()==win32con.IDOK:
|
||||
style.background = d.GetColor()
|
||||
self.scintilla.ApplyFormattingStyles(0)
|
||||
return 1
|
||||
|
||||
def OnButUseDefaultBackground(self, id, code):
|
||||
if code == win32con.BN_CLICKED:
|
||||
isDef = self.butIsDefaultBackground.GetCheck()
|
||||
self.GetDlgItem(win32ui.IDC_BUTTON4).EnableWindow(not isDef)
|
||||
if isDef: # Being reset to the default color
|
||||
style = self.GetSelectedStyle()
|
||||
style.background = style.default_background
|
||||
self.UpdateUIForStyle(style)
|
||||
self.scintilla.ApplyFormattingStyles(0)
|
||||
else:
|
||||
# User wants to override default -
|
||||
# do nothing!
|
||||
pass
|
||||
|
||||
|
||||
def OnListCommand(self, id, code):
|
||||
if code==win32con.LBN_SELCHANGE:
|
||||
style = self.GetSelectedStyle()
|
||||
self.UpdateUIForStyle(style)
|
||||
return 1
|
||||
|
||||
def UpdateUIForStyle(self, style ):
|
||||
format = style.format
|
||||
sel = 0
|
||||
for c in paletteVGA:
|
||||
if format[4] == c[1]:
|
||||
# print "Style", style.name, "is", c[0]
|
||||
break
|
||||
sel = sel + 1
|
||||
else:
|
||||
sel = -1
|
||||
self.cbo.SetCurSel(sel)
|
||||
self.butIsDefault.SetCheck(style.IsBasedOnDefault())
|
||||
self.GetDlgItem(win32ui.IDC_BUTTON3).EnableWindow(not style.IsBasedOnDefault())
|
||||
|
||||
self.butIsDefaultBackground.SetCheck(style.background == style.default_background)
|
||||
self.GetDlgItem(win32ui.IDC_BUTTON4).EnableWindow(style.background != style.default_background)
|
||||
|
||||
bold = format[1] & win32con.CFE_BOLD != 0; italic = format[1] & win32con.CFE_ITALIC != 0
|
||||
self.cboBoldItalic.SetCurSel( bold*2 + italic )
|
||||
|
||||
def OnStyleUIChanged(self, id, code):
|
||||
if code in [win32con.BN_CLICKED, win32con.CBN_SELCHANGE]:
|
||||
style = self.GetSelectedStyle()
|
||||
self.ApplyUIFormatToStyle(style)
|
||||
self.scintilla.ApplyFormattingStyles(0)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def ApplyUIFormatToStyle(self, style):
|
||||
format = style.format
|
||||
color = paletteVGA[self.cbo.GetCurSel()]
|
||||
effect = 0
|
||||
sel = self.cboBoldItalic.GetCurSel()
|
||||
if sel==0:
|
||||
effect = 0
|
||||
elif sel==1:
|
||||
effect = win32con.CFE_ITALIC
|
||||
elif sel==2:
|
||||
effect = win32con.CFE_BOLD
|
||||
else:
|
||||
effect = win32con.CFE_BOLD | win32con.CFE_ITALIC
|
||||
maskFlags=format[0]|win32con.CFM_COLOR|win32con.CFM_BOLD|win32con.CFM_ITALIC
|
||||
style.format = (maskFlags, effect, style.format[2], style.format[3], color[1]) + style.format[5:]
|
||||
|
||||
def OnOK(self):
|
||||
self.scintilla._GetColorizer().SavePreferences()
|
||||
return 1
|
||||
|
||||
def test():
|
||||
page = ColorEditorPropertyPage()
|
||||
sheet = pywin.mfc.dialog.PropertySheet("Test")
|
||||
sheet.AddPage(page)
|
||||
sheet.CreateWindow()
|
434
venv/Lib/site-packages/pythonwin/pywin/scintilla/control.py
Normal file
434
venv/Lib/site-packages/pythonwin/pywin/scintilla/control.py
Normal file
|
@ -0,0 +1,434 @@
|
|||
# An Python interface to the Scintilla control.
|
||||
#
|
||||
# Exposes Python classes that allow you to use Scintilla as
|
||||
# a "standard" MFC edit control (eg, control.GetTextLength(), control.GetSel()
|
||||
# plus many Scintilla specific features (eg control.SCIAddStyledText())
|
||||
|
||||
from pywin.mfc import window
|
||||
from pywin import default_scintilla_encoding
|
||||
import win32con
|
||||
import win32ui
|
||||
import win32api
|
||||
import array
|
||||
import struct
|
||||
import string
|
||||
import os
|
||||
from . import scintillacon
|
||||
|
||||
# Load Scintilla.dll to get access to the control.
|
||||
# We expect to find this in the same directory as win32ui.pyd
|
||||
dllid = None
|
||||
if win32ui.debug: # If running _d version of Pythonwin...
|
||||
try:
|
||||
dllid = win32api.LoadLibrary(os.path.join(os.path.split(win32ui.__file__)[0], "Scintilla_d.DLL"))
|
||||
except win32api.error: # Not there - we dont _need_ a debug ver, so ignore this error.
|
||||
pass
|
||||
if dllid is None:
|
||||
try:
|
||||
dllid = win32api.LoadLibrary(os.path.join(os.path.split(win32ui.__file__)[0], "Scintilla.DLL"))
|
||||
except win32api.error:
|
||||
pass
|
||||
if dllid is None:
|
||||
# Still not there - lets see if Windows can find it by searching?
|
||||
dllid = win32api.LoadLibrary("Scintilla.DLL")
|
||||
|
||||
# null_byte is str in py2k, bytes on py3k
|
||||
null_byte = "\0".encode('ascii')
|
||||
|
||||
## These are from Richedit.h - need to add to win32con or commctrl
|
||||
EM_GETTEXTRANGE = 1099
|
||||
EM_EXLINEFROMCHAR = 1078
|
||||
EM_FINDTEXTEX = 1103
|
||||
EM_GETSELTEXT = 1086
|
||||
EM_EXSETSEL = win32con.WM_USER + 55
|
||||
|
||||
class ScintillaNotification:
|
||||
def __init__(self, **args):
|
||||
self.__dict__.update(args)
|
||||
|
||||
class ScintillaControlInterface:
|
||||
def SCIUnpackNotifyMessage(self, msg):
|
||||
format = "iiiiPiiiPPiiii"
|
||||
bytes = win32ui.GetBytes( msg, struct.calcsize(format) )
|
||||
position, ch, modifiers, modificationType, text_ptr, \
|
||||
length, linesAdded, msg, wParam, lParam, line, \
|
||||
foldLevelNow, foldLevelPrev, margin \
|
||||
= struct.unpack(format, bytes)
|
||||
return ScintillaNotification(position=position,ch=ch,
|
||||
modifiers=modifiers, modificationType=modificationType,
|
||||
text_ptr = text_ptr, length=length, linesAdded=linesAdded,
|
||||
msg = msg, wParam = wParam, lParam = lParam,
|
||||
line = line, foldLevelNow = foldLevelNow, foldLevelPrev = foldLevelPrev,
|
||||
margin = margin)
|
||||
|
||||
def SCIAddText(self, text):
|
||||
self.SendMessage(scintillacon.SCI_ADDTEXT, text.encode(default_scintilla_encoding))
|
||||
def SCIAddStyledText(self, text, style = None):
|
||||
# If style is None, text is assumed to be a "native" Scintilla buffer.
|
||||
# If style is specified, text is a normal string, and the style is
|
||||
# assumed to apply to the entire string.
|
||||
if style is not None:
|
||||
text = list(map(lambda char, style=style: char+chr(style), text))
|
||||
text = ''.join(text)
|
||||
self.SendMessage(scintillacon.SCI_ADDSTYLEDTEXT, text.encode(default_scintilla_encoding))
|
||||
def SCIInsertText(self, text, pos=-1):
|
||||
# SCIInsertText allows unicode or bytes - but if they are bytes,
|
||||
# the caller must ensure it is encoded correctly.
|
||||
if isinstance(text, str):
|
||||
text = text.encode(default_scintilla_encoding)
|
||||
self.SendScintilla(scintillacon.SCI_INSERTTEXT, pos, text + null_byte)
|
||||
def SCISetSavePoint(self):
|
||||
self.SendScintilla(scintillacon.SCI_SETSAVEPOINT)
|
||||
def SCISetUndoCollection(self, collectFlag):
|
||||
self.SendScintilla(scintillacon.SCI_SETUNDOCOLLECTION, collectFlag)
|
||||
def SCIBeginUndoAction(self):
|
||||
self.SendScintilla(scintillacon.SCI_BEGINUNDOACTION)
|
||||
def SCIEndUndoAction(self):
|
||||
self.SendScintilla(scintillacon.SCI_ENDUNDOACTION)
|
||||
|
||||
def SCIGetCurrentPos(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETCURRENTPOS)
|
||||
def SCIGetCharAt(self, pos):
|
||||
# Must ensure char is unsigned!
|
||||
return chr(self.SendScintilla(scintillacon.SCI_GETCHARAT, pos) & 0xFF)
|
||||
def SCIGotoLine(self, line):
|
||||
self.SendScintilla(scintillacon.SCI_GOTOLINE, line)
|
||||
def SCIBraceMatch(self, pos, maxReStyle):
|
||||
return self.SendScintilla(scintillacon.SCI_BRACEMATCH, pos, maxReStyle)
|
||||
def SCIBraceHighlight(self, pos, posOpposite):
|
||||
return self.SendScintilla(scintillacon.SCI_BRACEHIGHLIGHT, pos, posOpposite)
|
||||
def SCIBraceBadHighlight(self, pos):
|
||||
return self.SendScintilla(scintillacon.SCI_BRACEBADLIGHT, pos)
|
||||
|
||||
####################################
|
||||
# Styling
|
||||
# def SCIColourise(self, start=0, end=-1):
|
||||
# NOTE - dependent on of we use builtin lexer, so handled below.
|
||||
def SCIGetEndStyled(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETENDSTYLED)
|
||||
def SCIStyleSetFore(self, num, v):
|
||||
return self.SendScintilla(scintillacon.SCI_STYLESETFORE, num, v)
|
||||
def SCIStyleSetBack(self, num, v):
|
||||
return self.SendScintilla(scintillacon.SCI_STYLESETBACK, num, v)
|
||||
def SCIStyleSetEOLFilled(self, num, v):
|
||||
return self.SendScintilla(scintillacon.SCI_STYLESETEOLFILLED, num, v)
|
||||
def SCIStyleSetFont(self, num, name, characterset=0):
|
||||
buff = (name + "\0").encode(default_scintilla_encoding)
|
||||
self.SendScintilla(scintillacon.SCI_STYLESETFONT, num, buff)
|
||||
self.SendScintilla(scintillacon.SCI_STYLESETCHARACTERSET, num, characterset)
|
||||
def SCIStyleSetBold(self, num, bBold):
|
||||
self.SendScintilla(scintillacon.SCI_STYLESETBOLD, num, bBold)
|
||||
def SCIStyleSetItalic(self, num, bItalic):
|
||||
self.SendScintilla(scintillacon.SCI_STYLESETITALIC, num, bItalic)
|
||||
def SCIStyleSetSize(self, num, size):
|
||||
self.SendScintilla(scintillacon.SCI_STYLESETSIZE, num, size)
|
||||
def SCIGetViewWS(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETVIEWWS)
|
||||
def SCISetViewWS(self, val):
|
||||
self.SendScintilla(scintillacon.SCI_SETVIEWWS, not (val==0))
|
||||
self.InvalidateRect()
|
||||
def SCISetIndentationGuides(self, val):
|
||||
self.SendScintilla(scintillacon.SCI_SETINDENTATIONGUIDES, val)
|
||||
def SCIGetIndentationGuides(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETINDENTATIONGUIDES)
|
||||
def SCISetIndent(self, val):
|
||||
self.SendScintilla(scintillacon.SCI_SETINDENT, val)
|
||||
def SCIGetIndent(self, val):
|
||||
return self.SendScintilla(scintillacon.SCI_GETINDENT)
|
||||
|
||||
def SCIGetViewEOL(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETVIEWEOL)
|
||||
def SCISetViewEOL(self, val):
|
||||
self.SendScintilla(scintillacon.SCI_SETVIEWEOL, not(val==0))
|
||||
self.InvalidateRect()
|
||||
def SCISetTabWidth(self, width):
|
||||
self.SendScintilla(scintillacon.SCI_SETTABWIDTH, width, 0)
|
||||
def SCIStartStyling(self, pos, mask):
|
||||
self.SendScintilla(scintillacon.SCI_STARTSTYLING, pos, mask)
|
||||
def SCISetStyling(self, pos, attr):
|
||||
self.SendScintilla(scintillacon.SCI_SETSTYLING, pos, attr)
|
||||
def SCISetStylingEx(self, ray): # ray is an array.
|
||||
address, length = ray.buffer_info()
|
||||
self.SendScintilla(scintillacon.SCI_SETSTYLINGEX, length, address)
|
||||
def SCIGetStyleAt(self, pos):
|
||||
return self.SendScintilla(scintillacon.SCI_GETSTYLEAT, pos)
|
||||
def SCISetMarginWidth(self, width):
|
||||
self.SendScintilla(scintillacon.SCI_SETMARGINWIDTHN, 1, width)
|
||||
def SCISetMarginWidthN(self, n, width):
|
||||
self.SendScintilla(scintillacon.SCI_SETMARGINWIDTHN, n, width)
|
||||
def SCISetFoldFlags(self, flags):
|
||||
self.SendScintilla(scintillacon.SCI_SETFOLDFLAGS, flags)
|
||||
# Markers
|
||||
def SCIMarkerDefineAll(self, markerNum, markerType, fore, back):
|
||||
self.SCIMarkerDefine(markerNum, markerType)
|
||||
self.SCIMarkerSetFore(markerNum, fore)
|
||||
self.SCIMarkerSetBack(markerNum, back)
|
||||
def SCIMarkerDefine(self, markerNum, markerType):
|
||||
self.SendScintilla(scintillacon.SCI_MARKERDEFINE, markerNum, markerType)
|
||||
def SCIMarkerSetFore(self, markerNum, fore):
|
||||
self.SendScintilla(scintillacon.SCI_MARKERSETFORE, markerNum, fore)
|
||||
def SCIMarkerSetBack(self, markerNum, back):
|
||||
self.SendScintilla(scintillacon.SCI_MARKERSETBACK, markerNum, back)
|
||||
def SCIMarkerAdd(self, lineNo, markerNum):
|
||||
self.SendScintilla(scintillacon.SCI_MARKERADD, lineNo, markerNum)
|
||||
def SCIMarkerDelete(self, lineNo, markerNum):
|
||||
self.SendScintilla(scintillacon.SCI_MARKERDELETE, lineNo, markerNum)
|
||||
def SCIMarkerDeleteAll(self, markerNum=-1):
|
||||
self.SendScintilla(scintillacon.SCI_MARKERDELETEALL, markerNum)
|
||||
def SCIMarkerGet(self, lineNo):
|
||||
return self.SendScintilla(scintillacon.SCI_MARKERGET, lineNo)
|
||||
def SCIMarkerNext(self, lineNo, markerNum):
|
||||
return self.SendScintilla(scintillacon.SCI_MARKERNEXT, lineNo, markerNum)
|
||||
def SCICancel(self):
|
||||
self.SendScintilla(scintillacon.SCI_CANCEL)
|
||||
# AutoComplete
|
||||
def SCIAutoCShow(self, text):
|
||||
if type(text) in [type([]), type(())]:
|
||||
text = ' '.join(text)
|
||||
buff = (text + "\0").encode(default_scintilla_encoding)
|
||||
return self.SendScintilla(scintillacon.SCI_AUTOCSHOW, 0, buff)
|
||||
def SCIAutoCCancel(self):
|
||||
self.SendScintilla(scintillacon.SCI_AUTOCCANCEL)
|
||||
def SCIAutoCActive(self):
|
||||
return self.SendScintilla(scintillacon.SCI_AUTOCACTIVE)
|
||||
def SCIAutoCComplete(self):
|
||||
return self.SendScintilla(scintillacon.SCI_AUTOCCOMPLETE)
|
||||
def SCIAutoCStops(self, stops):
|
||||
buff = (stops + "\0").encode(default_scintilla_encoding)
|
||||
self.SendScintilla(scintillacon.SCI_AUTOCSTOPS, 0, buff)
|
||||
def SCIAutoCSetAutoHide(self, hide):
|
||||
self.SendScintilla(scintillacon.SCI_AUTOCSETAUTOHIDE, hide)
|
||||
def SCIAutoCSetFillups(self, fillups):
|
||||
self.SendScintilla(scintillacon.SCI_AUTOCSETFILLUPS, fillups)
|
||||
# Call tips
|
||||
def SCICallTipShow(self, text, pos=-1):
|
||||
if pos==-1: pos = self.GetSel()[0]
|
||||
buff = (text + "\0").encode(default_scintilla_encoding)
|
||||
self.SendScintilla(scintillacon.SCI_CALLTIPSHOW, pos, buff)
|
||||
def SCICallTipCancel(self):
|
||||
self.SendScintilla(scintillacon.SCI_CALLTIPCANCEL)
|
||||
def SCICallTipActive(self):
|
||||
return self.SendScintilla(scintillacon.SCI_CALLTIPACTIVE)
|
||||
def SCICallTipPosStart(self):
|
||||
return self.SendScintilla(scintillacon.SCI_CALLTIPPOSSTART)
|
||||
def SCINewline(self):
|
||||
self.SendScintilla(scintillacon.SCI_NEWLINE)
|
||||
# Lexer etc
|
||||
def SCISetKeywords(self, keywords, kw_list_no = 0):
|
||||
buff = (keywords+"\0").encode(default_scintilla_encoding)
|
||||
self.SendScintilla(scintillacon.SCI_SETKEYWORDS, kw_list_no, buff)
|
||||
def SCISetProperty(self, name, value):
|
||||
name_buff = array.array('b', (name + '\0').encode(default_scintilla_encoding))
|
||||
val_buff = array.array("b", (str(value)+'\0').encode(default_scintilla_encoding))
|
||||
address_name_buffer = name_buff.buffer_info()[0]
|
||||
address_val_buffer = val_buff.buffer_info()[0]
|
||||
self.SendScintilla(scintillacon.SCI_SETPROPERTY, address_name_buffer, address_val_buffer)
|
||||
def SCISetStyleBits(self, nbits):
|
||||
self.SendScintilla(scintillacon.SCI_SETSTYLEBITS, nbits)
|
||||
# Folding
|
||||
def SCIGetFoldLevel(self, lineno):
|
||||
return self.SendScintilla(scintillacon.SCI_GETFOLDLEVEL, lineno)
|
||||
def SCIToggleFold(self, lineno):
|
||||
return self.SendScintilla(scintillacon.SCI_TOGGLEFOLD, lineno)
|
||||
def SCIEnsureVisible(self, lineno):
|
||||
self.SendScintilla(scintillacon.SCI_ENSUREVISIBLE, lineno)
|
||||
def SCIGetFoldExpanded(self, lineno):
|
||||
return self.SendScintilla(scintillacon.SCI_GETFOLDEXPANDED, lineno)
|
||||
# right edge
|
||||
def SCISetEdgeColumn(self, edge):
|
||||
self.SendScintilla(scintillacon.SCI_SETEDGECOLUMN, edge)
|
||||
def SCIGetEdgeColumn(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETEDGECOLUMN)
|
||||
def SCISetEdgeMode(self, mode):
|
||||
self.SendScintilla(scintillacon.SCI_SETEDGEMODE, mode)
|
||||
def SCIGetEdgeMode(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETEDGEMODE)
|
||||
def SCISetEdgeColor(self, color):
|
||||
self.SendScintilla(scintillacon.SCI_SETEDGECOLOUR, color)
|
||||
def SCIGetEdgeColor(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETEDGECOLOR)
|
||||
# Multi-doc
|
||||
def SCIGetDocPointer(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETDOCPOINTER)
|
||||
def SCISetDocPointer(self, p):
|
||||
return self.SendScintilla(scintillacon.SCI_SETDOCPOINTER, 0, p)
|
||||
def SCISetWrapMode(self, mode):
|
||||
return self.SendScintilla(scintillacon.SCI_SETWRAPMODE, mode)
|
||||
def SCIGetWrapMode(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETWRAPMODE)
|
||||
|
||||
class CScintillaEditInterface(ScintillaControlInterface):
|
||||
def close(self):
|
||||
self.colorizer = None
|
||||
def Clear(self):
|
||||
self.SendScintilla(win32con.WM_CLEAR)
|
||||
def Clear(self):
|
||||
self.SendScintilla(win32con.WM_CLEAR)
|
||||
def FindText(self, flags, range, findText):
|
||||
""" LPARAM for EM_FINDTEXTEX:
|
||||
typedef struct _findtextex {
|
||||
CHARRANGE chrg;
|
||||
LPCTSTR lpstrText;
|
||||
CHARRANGE chrgText;} FINDTEXTEX;
|
||||
typedef struct _charrange {
|
||||
LONG cpMin;
|
||||
LONG cpMax;} CHARRANGE;
|
||||
"""
|
||||
findtextex_fmt='llPll'
|
||||
## Scintilla does not handle unicode in EM_FINDTEXT msg (FINDTEXTEX struct)
|
||||
txt_buff = (findText+'\0').encode(default_scintilla_encoding)
|
||||
txt_array = array.array('b', txt_buff)
|
||||
ft_buff = struct.pack(findtextex_fmt, range[0], range[1], txt_array.buffer_info()[0], 0, 0)
|
||||
ft_array = array.array('b', ft_buff)
|
||||
rc = self.SendScintilla(EM_FINDTEXTEX, flags, ft_array.buffer_info()[0])
|
||||
ftUnpacked = struct.unpack(findtextex_fmt, ft_array)
|
||||
return rc, (ftUnpacked[3], ftUnpacked[4])
|
||||
|
||||
def GetSel(self):
|
||||
currentPos = self.SendScintilla(scintillacon.SCI_GETCURRENTPOS)
|
||||
anchorPos = self.SendScintilla(scintillacon.SCI_GETANCHOR)
|
||||
if currentPos < anchorPos:
|
||||
return (currentPos, anchorPos)
|
||||
else:
|
||||
return (anchorPos, currentPos)
|
||||
return currentPos;
|
||||
|
||||
def GetSelText(self):
|
||||
start, end = self.GetSel()
|
||||
txtBuf = array.array('b', null_byte * (end-start+1))
|
||||
addressTxtBuf = txtBuf.buffer_info()[0]
|
||||
# EM_GETSELTEXT is documented as returning the number of chars
|
||||
# not including the NULL, but scintilla includes the NULL. A
|
||||
# quick glance at the scintilla impl doesn't make this
|
||||
# obvious - the NULL is included in the 'selection' object
|
||||
# and reflected in the length of that 'selection' object.
|
||||
# I expect that is a bug in scintilla and may be fixed by now,
|
||||
# but we just blindly assume that the last char is \0 and
|
||||
# strip it.
|
||||
self.SendScintilla(EM_GETSELTEXT, 0, addressTxtBuf)
|
||||
return txtBuf.tostring()[:-1].decode(default_scintilla_encoding)
|
||||
|
||||
def SetSel(self, start=0, end=None):
|
||||
if type(start)==type(()):
|
||||
assert end is None, "If you pass a point in the first param, the second must be None"
|
||||
start, end = start
|
||||
elif end is None:
|
||||
end = start
|
||||
if start < 0: start = self.GetTextLength()
|
||||
if end < 0: end = self.GetTextLength()
|
||||
assert start <= self.GetTextLength(), "The start postion is invalid (%d/%d)" % (start, self.GetTextLength())
|
||||
assert end <= self.GetTextLength(), "The end postion is invalid (%d/%d)" % (end, self.GetTextLength())
|
||||
cr = struct.pack('ll', start, end)
|
||||
crBuff = array.array('b', cr)
|
||||
addressCrBuff = crBuff.buffer_info()[0]
|
||||
rc = self.SendScintilla(EM_EXSETSEL, 0, addressCrBuff)
|
||||
|
||||
def GetLineCount(self):
|
||||
return self.SendScintilla(win32con.EM_GETLINECOUNT)
|
||||
|
||||
def LineFromChar(self, charPos=-1):
|
||||
if charPos==-1: charPos = self.GetSel()[0]
|
||||
assert charPos >= 0 and charPos <= self.GetTextLength(), "The charPos postion (%s) is invalid (max=%s)" % (charPos, self.GetTextLength())
|
||||
#return self.SendScintilla(EM_EXLINEFROMCHAR, charPos)
|
||||
# EM_EXLINEFROMCHAR puts charPos in lParam, not wParam
|
||||
return self.SendScintilla(EM_EXLINEFROMCHAR, 0, charPos)
|
||||
|
||||
def LineIndex(self, line):
|
||||
return self.SendScintilla(win32con.EM_LINEINDEX, line)
|
||||
|
||||
def ScrollCaret(self):
|
||||
return self.SendScintilla(win32con.EM_SCROLLCARET)
|
||||
|
||||
def GetCurLineNumber(self):
|
||||
return self.LineFromChar(self.SCIGetCurrentPos())
|
||||
|
||||
def GetTextLength(self):
|
||||
return self.SendScintilla(scintillacon.SCI_GETTEXTLENGTH)
|
||||
|
||||
def GetTextRange(self, start = 0, end = -1, decode = True):
|
||||
if end == -1: end = self.SendScintilla(scintillacon.SCI_GETTEXTLENGTH)
|
||||
assert end>=start, "Negative index requested (%d/%d)" % (start, end)
|
||||
assert start >= 0 and start <= self.GetTextLength(), "The start postion is invalid"
|
||||
assert end >= 0 and end <= self.GetTextLength(), "The end postion is invalid"
|
||||
initer = null_byte * (end - start + 1)
|
||||
buff = array.array('b', initer)
|
||||
addressBuffer = buff.buffer_info()[0]
|
||||
tr = struct.pack('llP', start, end, addressBuffer)
|
||||
trBuff = array.array('b', tr)
|
||||
addressTrBuff = trBuff.buffer_info()[0]
|
||||
num_bytes = self.SendScintilla(EM_GETTEXTRANGE, 0, addressTrBuff)
|
||||
ret = buff.tostring()[:num_bytes]
|
||||
if decode:
|
||||
ret = ret.decode(default_scintilla_encoding)
|
||||
return ret
|
||||
|
||||
def ReplaceSel(self, str):
|
||||
buff = (str + "\0").encode(default_scintilla_encoding)
|
||||
self.SendScintilla(scintillacon.SCI_REPLACESEL, 0, buff)
|
||||
|
||||
def GetLine(self, line=-1):
|
||||
if line == -1: line = self.GetCurLineNumber()
|
||||
start = self.LineIndex(line)
|
||||
end = self.LineIndex(line+1)
|
||||
return self.GetTextRange(start, end)
|
||||
|
||||
def SetReadOnly(self, flag = 1):
|
||||
return self.SendScintilla(win32con.EM_SETREADONLY, flag)
|
||||
|
||||
def LineScroll(self, lines, cols=0):
|
||||
return self.SendScintilla(win32con.EM_LINESCROLL, cols, lines)
|
||||
|
||||
def GetFirstVisibleLine(self):
|
||||
return self.SendScintilla(win32con.EM_GETFIRSTVISIBLELINE)
|
||||
|
||||
def SetWordWrap(self, mode):
|
||||
if mode != win32ui.CRichEditView_WrapNone:
|
||||
raise ValueError("We dont support word-wrap (I dont think :-)")
|
||||
|
||||
class CScintillaColorEditInterface(CScintillaEditInterface):
|
||||
################################
|
||||
# Plug-in colorizer support
|
||||
def _GetColorizer(self):
|
||||
if not hasattr(self, "colorizer"):
|
||||
self.colorizer = self._MakeColorizer()
|
||||
return self.colorizer
|
||||
def _MakeColorizer(self):
|
||||
# Give parent a chance to hook.
|
||||
parent_func = getattr(self.GetParentFrame(), "_MakeColorizer", None)
|
||||
if parent_func is not None:
|
||||
return parent_func()
|
||||
from . import formatter
|
||||
## return formatter.PythonSourceFormatter(self)
|
||||
return formatter.BuiltinPythonSourceFormatter(self)
|
||||
|
||||
def Colorize(self, start=0, end=-1):
|
||||
c = self._GetColorizer()
|
||||
if c is not None: c.Colorize(start, end)
|
||||
|
||||
def ApplyFormattingStyles(self, bReload=1):
|
||||
c = self._GetColorizer()
|
||||
if c is not None: c.ApplyFormattingStyles(bReload)
|
||||
|
||||
# The Parent window will normally hook
|
||||
def HookFormatter(self, parent = None):
|
||||
c = self._GetColorizer()
|
||||
if c is not None: # No need if we have no color!
|
||||
c.HookFormatter(parent)
|
||||
|
||||
class CScintillaEdit(window.Wnd, CScintillaColorEditInterface):
|
||||
def __init__(self, wnd=None):
|
||||
if wnd is None:
|
||||
wnd = win32ui.CreateWnd()
|
||||
window.Wnd.__init__(self, wnd)
|
||||
def SendScintilla(self, msg, w=0, l=0):
|
||||
return self.SendMessage(msg, w, l)
|
||||
def CreateWindow(self, style, rect, parent, id):
|
||||
self._obj_.CreateWindow(
|
||||
"Scintilla",
|
||||
"Scintilla",
|
||||
style,
|
||||
rect,
|
||||
parent,
|
||||
id,
|
||||
None)
|
||||
|
274
venv/Lib/site-packages/pythonwin/pywin/scintilla/document.py
Normal file
274
venv/Lib/site-packages/pythonwin/pywin/scintilla/document.py
Normal file
|
@ -0,0 +1,274 @@
|
|||
import win32ui
|
||||
from pywin.mfc import docview
|
||||
from pywin import default_scintilla_encoding
|
||||
from . import scintillacon
|
||||
import win32con
|
||||
import string
|
||||
import os
|
||||
import codecs
|
||||
import re
|
||||
|
||||
crlf_bytes = "\r\n".encode("ascii")
|
||||
lf_bytes = "\n".encode("ascii")
|
||||
|
||||
# re from pep263 - but we use it both on bytes and strings.
|
||||
re_encoding_bytes = re.compile("coding[:=]\s*([-\w.]+)".encode("ascii"))
|
||||
re_encoding_text = re.compile("coding[:=]\s*([-\w.]+)")
|
||||
|
||||
ParentScintillaDocument=docview.Document
|
||||
class CScintillaDocument(ParentScintillaDocument):
|
||||
"A SyntEdit document. "
|
||||
def __init__(self, *args):
|
||||
self.bom = None # the BOM, if any, read from the file.
|
||||
# the encoding we detected from the source. Might have
|
||||
# detected via the BOM or an encoding decl. Note that in
|
||||
# the latter case (ie, while self.bom is None), it can't be
|
||||
# trusted - the user may have edited the encoding decl between
|
||||
# open and save.
|
||||
self.source_encoding = None
|
||||
ParentScintillaDocument.__init__(self, *args)
|
||||
|
||||
def DeleteContents(self):
|
||||
pass
|
||||
|
||||
def OnOpenDocument(self, filename):
|
||||
# init data members
|
||||
#print "Opening", filename
|
||||
self.SetPathName(filename) # Must set this early!
|
||||
try:
|
||||
# load the text as binary we can get smart
|
||||
# about detecting any existing EOL conventions.
|
||||
f = open(filename, 'rb')
|
||||
try:
|
||||
self._LoadTextFromFile(f)
|
||||
finally:
|
||||
f.close()
|
||||
except IOError:
|
||||
rc = win32ui.MessageBox("Could not load the file from %s\n\nDo you want to create a new file?" % filename,
|
||||
"Pythonwin", win32con.MB_YESNO | win32con.MB_ICONWARNING)
|
||||
if rc == win32con.IDNO:
|
||||
return 0
|
||||
assert rc == win32con.IDYES, rc
|
||||
try:
|
||||
f = open(filename, 'wb+')
|
||||
try:
|
||||
self._LoadTextFromFile(f)
|
||||
finally:
|
||||
f.close()
|
||||
except IOError as e:
|
||||
rc = win32ui.MessageBox("Cannot create the file %s" % filename)
|
||||
return 1
|
||||
|
||||
def SaveFile(self, fileName, encoding=None):
|
||||
view = self.GetFirstView()
|
||||
ok = view.SaveTextFile(fileName, encoding=encoding)
|
||||
if ok:
|
||||
view.SCISetSavePoint()
|
||||
return ok
|
||||
|
||||
def ApplyFormattingStyles(self):
|
||||
self._ApplyOptionalToViews("ApplyFormattingStyles")
|
||||
|
||||
# #####################
|
||||
# File related functions
|
||||
# Helper to transfer text from the MFC document to the control.
|
||||
def _LoadTextFromFile(self, f):
|
||||
# detect EOL mode - we don't support \r only - so find the
|
||||
# first '\n' and guess based on the char before.
|
||||
l = f.readline()
|
||||
l2 = f.readline()
|
||||
# If line ends with \r\n or has no line ending, use CRLF.
|
||||
if l.endswith(crlf_bytes) or not l.endswith(lf_bytes):
|
||||
eol_mode = scintillacon.SC_EOL_CRLF
|
||||
else:
|
||||
eol_mode = scintillacon.SC_EOL_LF
|
||||
|
||||
# Detect the encoding - first look for a BOM, and if not found,
|
||||
# look for a pep263 encoding declaration.
|
||||
for bom, encoding in (
|
||||
(codecs.BOM_UTF8, "utf8"),
|
||||
(codecs.BOM_UTF16_LE, "utf_16_le"),
|
||||
(codecs.BOM_UTF16_BE, "utf_16_be"),
|
||||
):
|
||||
if l.startswith(bom):
|
||||
self.bom = bom
|
||||
self.source_encoding = encoding
|
||||
l = l[len(bom):] # remove it.
|
||||
break
|
||||
else:
|
||||
# no bom detected - look for pep263 encoding decl.
|
||||
for look in (l, l2):
|
||||
# Note we are looking at raw bytes here: so
|
||||
# both the re itself uses bytes and the result
|
||||
# is bytes - but we need the result as a string.
|
||||
match = re_encoding_bytes.search(look)
|
||||
if match is not None:
|
||||
self.source_encoding = match.group(1).decode("ascii")
|
||||
break
|
||||
|
||||
# reading by lines would be too slow? Maybe we can use the
|
||||
# incremental encoders? For now just stick with loading the
|
||||
# entire file in memory.
|
||||
text = l + l2 + f.read()
|
||||
|
||||
# Translate from source encoding to UTF-8 bytes for Scintilla
|
||||
source_encoding = self.source_encoding
|
||||
# If we don't know an encoding, just use latin-1 to treat
|
||||
# it as bytes...
|
||||
if source_encoding is None:
|
||||
source_encoding = 'latin1'
|
||||
# we could optimize this by avoiding utf8 to-ing and from-ing,
|
||||
# but then we would lose the ability to handle invalid utf8
|
||||
# (and even then, the use of encoding aliases makes this tricky)
|
||||
# To create an invalid utf8 file:
|
||||
# >>> open(filename, "wb").write(codecs.BOM_UTF8+"bad \xa9har\r\n")
|
||||
try:
|
||||
dec = text.decode(source_encoding)
|
||||
except UnicodeError:
|
||||
print("WARNING: Failed to decode bytes from '%s' encoding - treating as latin1" % source_encoding)
|
||||
dec = text.decode('latin1')
|
||||
except LookupError:
|
||||
print("WARNING: Invalid encoding '%s' specified - treating as latin1" % source_encoding)
|
||||
dec = text.decode('latin1')
|
||||
# and put it back as utf8 - this shouldn't fail.
|
||||
text = dec.encode(default_scintilla_encoding)
|
||||
|
||||
view = self.GetFirstView()
|
||||
if view.IsWindow():
|
||||
# Turn off undo collection while loading
|
||||
view.SendScintilla(scintillacon.SCI_SETUNDOCOLLECTION, 0, 0)
|
||||
# Make sure the control isnt read-only
|
||||
view.SetReadOnly(0)
|
||||
view.SendScintilla(scintillacon.SCI_CLEARALL)
|
||||
view.SendMessage(scintillacon.SCI_ADDTEXT, text)
|
||||
view.SendScintilla(scintillacon.SCI_SETUNDOCOLLECTION, 1, 0)
|
||||
view.SendScintilla(win32con.EM_EMPTYUNDOBUFFER, 0, 0)
|
||||
# set EOL mode
|
||||
view.SendScintilla(scintillacon.SCI_SETEOLMODE, eol_mode)
|
||||
|
||||
def _SaveTextToFile(self, view, filename, encoding=None):
|
||||
s = view.GetTextRange() # already decoded from scintilla's encoding
|
||||
source_encoding = encoding
|
||||
if source_encoding is None:
|
||||
if self.bom:
|
||||
source_encoding = self.source_encoding
|
||||
else:
|
||||
# no BOM - look for an encoding.
|
||||
bits = re.split("[\r\n]*", s, 3)
|
||||
for look in bits[:-1]:
|
||||
match = re_encoding_text.search(look)
|
||||
if match is not None:
|
||||
source_encoding = match.group(1)
|
||||
self.source_encoding = source_encoding
|
||||
break
|
||||
|
||||
if source_encoding is None:
|
||||
source_encoding = 'latin1'
|
||||
|
||||
## encode data before opening file so script is not lost if encoding fails
|
||||
file_contents = s.encode(source_encoding)
|
||||
# Open in binary mode as scintilla itself ensures the
|
||||
# line endings are already appropriate
|
||||
f = open(filename, 'wb')
|
||||
try:
|
||||
if self.bom:
|
||||
f.write(self.bom)
|
||||
f.write(file_contents)
|
||||
finally:
|
||||
f.close()
|
||||
self.SetModifiedFlag(0)
|
||||
|
||||
def FinalizeViewCreation(self, view):
|
||||
pass
|
||||
|
||||
def HookViewNotifications(self, view):
|
||||
parent = view.GetParentFrame()
|
||||
parent.HookNotify(ViewNotifyDelegate(self, "OnBraceMatch"), scintillacon.SCN_CHECKBRACE)
|
||||
parent.HookNotify(ViewNotifyDelegate(self, "OnMarginClick"), scintillacon.SCN_MARGINCLICK)
|
||||
parent.HookNotify(ViewNotifyDelegate(self, "OnNeedShown"), scintillacon.SCN_NEEDSHOWN)
|
||||
|
||||
parent.HookNotify(DocumentNotifyDelegate(self, "OnSavePointReached"), scintillacon.SCN_SAVEPOINTREACHED)
|
||||
parent.HookNotify(DocumentNotifyDelegate(self, "OnSavePointLeft"), scintillacon.SCN_SAVEPOINTLEFT)
|
||||
parent.HookNotify(DocumentNotifyDelegate(self, "OnModifyAttemptRO"), scintillacon.SCN_MODIFYATTEMPTRO)
|
||||
# Tell scintilla what characters should abort auto-complete.
|
||||
view.SCIAutoCStops(string.whitespace+"()[]:;+-/*=\\?'!#@$%^&,<>\"'|" )
|
||||
|
||||
if view != self.GetFirstView():
|
||||
view.SCISetDocPointer(self.GetFirstView().SCIGetDocPointer())
|
||||
|
||||
|
||||
def OnSavePointReached(self, std, extra):
|
||||
self.SetModifiedFlag(0)
|
||||
|
||||
def OnSavePointLeft(self, std, extra):
|
||||
self.SetModifiedFlag(1)
|
||||
|
||||
def OnModifyAttemptRO(self, std, extra):
|
||||
self.MakeDocumentWritable()
|
||||
|
||||
# All Marker functions are 1 based.
|
||||
def MarkerAdd( self, lineNo, marker ):
|
||||
self.GetEditorView().SCIMarkerAdd(lineNo-1, marker)
|
||||
|
||||
def MarkerCheck(self, lineNo, marker ):
|
||||
v = self.GetEditorView()
|
||||
lineNo = lineNo - 1 # Make 0 based
|
||||
markerState = v.SCIMarkerGet(lineNo)
|
||||
return markerState & (1<<marker) != 0
|
||||
|
||||
def MarkerToggle( self, lineNo, marker ):
|
||||
v = self.GetEditorView()
|
||||
if self.MarkerCheck(lineNo, marker):
|
||||
v.SCIMarkerDelete(lineNo-1, marker)
|
||||
else:
|
||||
v.SCIMarkerAdd(lineNo-1, marker)
|
||||
def MarkerDelete( self, lineNo, marker ):
|
||||
self.GetEditorView().SCIMarkerDelete(lineNo-1, marker)
|
||||
def MarkerDeleteAll( self, marker ):
|
||||
self.GetEditorView().SCIMarkerDeleteAll(marker)
|
||||
def MarkerGetNext(self, lineNo, marker):
|
||||
return self.GetEditorView().SCIMarkerNext( lineNo-1, 1 << marker )+1
|
||||
def MarkerAtLine(self, lineNo, marker):
|
||||
markerState = self.GetEditorView().SCIMarkerGet(lineNo-1)
|
||||
return markerState & (1<<marker)
|
||||
|
||||
# Helper for reflecting functions to views.
|
||||
def _ApplyToViews(self, funcName, *args):
|
||||
for view in self.GetAllViews():
|
||||
func = getattr(view, funcName)
|
||||
func(*args)
|
||||
def _ApplyOptionalToViews(self, funcName, *args):
|
||||
for view in self.GetAllViews():
|
||||
func = getattr(view, funcName, None)
|
||||
if func is not None:
|
||||
func(*args)
|
||||
def GetEditorView(self):
|
||||
# Find the first frame with a view,
|
||||
# then ask it to give the editor view
|
||||
# as it knows which one is "active"
|
||||
try:
|
||||
frame_gev = self.GetFirstView().GetParentFrame().GetEditorView
|
||||
except AttributeError:
|
||||
return self.GetFirstView()
|
||||
return frame_gev()
|
||||
|
||||
# Delegate to the correct view, based on the control that sent it.
|
||||
class ViewNotifyDelegate:
|
||||
def __init__(self, doc, name):
|
||||
self.doc = doc
|
||||
self.name = name
|
||||
def __call__(self, std, extra):
|
||||
(hwndFrom, idFrom, code) = std
|
||||
for v in self.doc.GetAllViews():
|
||||
if v.GetSafeHwnd() == hwndFrom:
|
||||
return getattr(v, self.name)(*(std, extra))
|
||||
|
||||
# Delegate to the document, but only from a single view (as each view sends it seperately)
|
||||
class DocumentNotifyDelegate:
|
||||
def __init__(self, doc, name):
|
||||
self.doc = doc
|
||||
self.delegate = getattr(doc, name)
|
||||
def __call__(self, std, extra):
|
||||
(hwndFrom, idFrom, code) = std
|
||||
if hwndFrom == self.doc.GetEditorView().GetSafeHwnd():
|
||||
self.delegate(*(std, extra))
|
334
venv/Lib/site-packages/pythonwin/pywin/scintilla/find.py
Normal file
334
venv/Lib/site-packages/pythonwin/pywin/scintilla/find.py
Normal file
|
@ -0,0 +1,334 @@
|
|||
# find.py - Find and Replace
|
||||
import win32con, win32api
|
||||
import win32ui
|
||||
from pywin.mfc import dialog
|
||||
import afxres
|
||||
from pywin.framework import scriptutils
|
||||
|
||||
FOUND_NOTHING=0
|
||||
FOUND_NORMAL=1
|
||||
FOUND_LOOPED_BACK=2
|
||||
FOUND_NEXT_FILE=3
|
||||
|
||||
class SearchParams:
|
||||
def __init__(self, other=None):
|
||||
if other is None:
|
||||
self.__dict__['findText'] = ""
|
||||
self.__dict__['replaceText'] = ""
|
||||
self.__dict__['matchCase'] = 0
|
||||
self.__dict__['matchWords'] = 0
|
||||
self.__dict__['acrossFiles'] = 0
|
||||
self.__dict__['remember'] = 1
|
||||
self.__dict__['sel'] = (-1,-1)
|
||||
self.__dict__['keepDialogOpen']=0
|
||||
else:
|
||||
self.__dict__.update(other.__dict__)
|
||||
# Helper so we cant misspell attributes :-)
|
||||
def __setattr__(self, attr, val):
|
||||
if not hasattr(self, attr):
|
||||
raise AttributeError(attr)
|
||||
self.__dict__[attr]=val
|
||||
|
||||
curDialog = None
|
||||
lastSearch = defaultSearch = SearchParams()
|
||||
searchHistory = []
|
||||
|
||||
def ShowFindDialog():
|
||||
_ShowDialog(FindDialog)
|
||||
|
||||
def ShowReplaceDialog():
|
||||
_ShowDialog(ReplaceDialog)
|
||||
|
||||
def _ShowDialog(dlgClass):
|
||||
global curDialog
|
||||
if curDialog is not None:
|
||||
if curDialog.__class__ != dlgClass:
|
||||
curDialog.DestroyWindow()
|
||||
curDialog = None
|
||||
else:
|
||||
curDialog.SetFocus()
|
||||
if curDialog is None:
|
||||
curDialog = dlgClass()
|
||||
curDialog.CreateWindow()
|
||||
|
||||
def FindNext():
|
||||
params = SearchParams(lastSearch)
|
||||
params.sel = (-1,-1)
|
||||
if not params.findText:
|
||||
ShowFindDialog()
|
||||
else:
|
||||
return _FindIt(None, params)
|
||||
|
||||
def _GetControl(control=None):
|
||||
if control is None:
|
||||
control = scriptutils.GetActiveEditControl()
|
||||
return control
|
||||
|
||||
def _FindIt(control, searchParams):
|
||||
global lastSearch, defaultSearch
|
||||
control = _GetControl(control)
|
||||
if control is None: return FOUND_NOTHING
|
||||
|
||||
# Move to the next char, so we find the next one.
|
||||
flags = 0
|
||||
if searchParams.matchWords: flags = flags | win32con.FR_WHOLEWORD
|
||||
if searchParams.matchCase: flags = flags | win32con.FR_MATCHCASE
|
||||
if searchParams.sel == (-1,-1):
|
||||
sel = control.GetSel()
|
||||
# If the position is the same as we found last time,
|
||||
# then we assume it is a "FindNext"
|
||||
if sel==lastSearch.sel:
|
||||
sel = sel[0]+1, sel[0]+1
|
||||
else:
|
||||
sel = searchParams.sel
|
||||
|
||||
if sel[0]==sel[1]: sel=sel[0], control.GetTextLength()
|
||||
|
||||
rc = FOUND_NOTHING
|
||||
# (Old edit control will fail here!)
|
||||
posFind, foundSel = control.FindText(flags, sel, searchParams.findText)
|
||||
lastSearch = SearchParams(searchParams)
|
||||
if posFind >= 0:
|
||||
rc = FOUND_NORMAL
|
||||
lineno = control.LineFromChar(posFind)
|
||||
control.SCIEnsureVisible(lineno)
|
||||
control.SetSel(foundSel)
|
||||
control.SetFocus()
|
||||
win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
|
||||
if rc == FOUND_NOTHING and lastSearch.acrossFiles:
|
||||
# Loop around all documents. First find this document.
|
||||
try:
|
||||
try:
|
||||
doc = control.GetDocument()
|
||||
except AttributeError:
|
||||
try:
|
||||
doc = control.GetParent().GetDocument()
|
||||
except AttributeError:
|
||||
print("Cant find a document for the control!")
|
||||
doc = None
|
||||
if doc is not None:
|
||||
template = doc.GetDocTemplate()
|
||||
alldocs = template.GetDocumentList()
|
||||
mypos = lookpos = alldocs.index(doc)
|
||||
while 1:
|
||||
lookpos = (lookpos+1) % len(alldocs)
|
||||
if lookpos == mypos:
|
||||
break
|
||||
view = alldocs[lookpos].GetFirstView()
|
||||
posFind, foundSel = view.FindText(flags, (0, view.GetTextLength()), searchParams.findText)
|
||||
if posFind >= 0:
|
||||
nChars = foundSel[1]-foundSel[0]
|
||||
lineNo = view.LineFromChar(posFind) # zero based.
|
||||
lineStart = view.LineIndex(lineNo)
|
||||
colNo = posFind - lineStart # zero based.
|
||||
scriptutils.JumpToDocument(alldocs[lookpos].GetPathName(), lineNo+1, colNo+1, nChars)
|
||||
rc = FOUND_NEXT_FILE
|
||||
break
|
||||
except win32ui.error:
|
||||
pass
|
||||
if rc == FOUND_NOTHING:
|
||||
# Loop around this control - attempt to find from the start of the control.
|
||||
posFind, foundSel = control.FindText(flags, (0, sel[0]-1), searchParams.findText)
|
||||
if posFind >= 0:
|
||||
control.SCIEnsureVisible(control.LineFromChar(foundSel[0]))
|
||||
control.SetSel(foundSel)
|
||||
control.SetFocus()
|
||||
win32ui.SetStatusText("Not found! Searching from the top of the file.")
|
||||
rc = FOUND_LOOPED_BACK
|
||||
else:
|
||||
lastSearch.sel=-1,-1
|
||||
win32ui.SetStatusText("Can not find '%s'" % searchParams.findText )
|
||||
|
||||
if rc != FOUND_NOTHING:
|
||||
lastSearch.sel = foundSel
|
||||
|
||||
if lastSearch.remember:
|
||||
defaultSearch = lastSearch
|
||||
|
||||
# track search history
|
||||
try:
|
||||
ix = searchHistory.index(searchParams.findText)
|
||||
except ValueError:
|
||||
if len(searchHistory) > 50:
|
||||
searchHistory[50:] = []
|
||||
else:
|
||||
del searchHistory[ix]
|
||||
searchHistory.insert(0, searchParams.findText)
|
||||
|
||||
return rc
|
||||
|
||||
def _ReplaceIt(control):
|
||||
control = _GetControl(control)
|
||||
statusText = "Can not find '%s'." % lastSearch.findText
|
||||
rc = FOUND_NOTHING
|
||||
if control is not None and lastSearch.sel != (-1,-1):
|
||||
control.ReplaceSel(lastSearch.replaceText)
|
||||
rc = FindNext()
|
||||
if rc !=FOUND_NOTHING:
|
||||
statusText = win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)
|
||||
win32ui.SetStatusText(statusText)
|
||||
return rc
|
||||
|
||||
class FindReplaceDialog(dialog.Dialog):
|
||||
def __init__(self):
|
||||
dialog.Dialog.__init__(self,self._GetDialogTemplate())
|
||||
self.HookCommand(self.OnFindNext, 109)
|
||||
|
||||
def OnInitDialog(self):
|
||||
self.editFindText = self.GetDlgItem(102)
|
||||
self.butMatchWords = self.GetDlgItem(105)
|
||||
self.butMatchCase = self.GetDlgItem(107)
|
||||
self.butKeepDialogOpen = self.GetDlgItem(115)
|
||||
self.butAcrossFiles = self.GetDlgItem(116)
|
||||
self.butRemember = self.GetDlgItem(117)
|
||||
|
||||
self.editFindText.SetWindowText(defaultSearch.findText)
|
||||
control = _GetControl()
|
||||
# _GetControl only gets normal MDI windows; if the interactive
|
||||
# window is docked and no document open, we get None.
|
||||
if control:
|
||||
# If we have a selection, default to that.
|
||||
sel = control.GetSelText()
|
||||
if (len(sel) != 0):
|
||||
self.editFindText.SetWindowText(sel)
|
||||
if (defaultSearch.remember):
|
||||
defaultSearch.findText = sel
|
||||
for hist in searchHistory:
|
||||
self.editFindText.AddString(hist)
|
||||
|
||||
if hasattr(self.editFindText, 'SetEditSel'):
|
||||
self.editFindText.SetEditSel(0, -2)
|
||||
else:
|
||||
self.editFindText.SetSel(0, -2)
|
||||
self.editFindText.SetFocus()
|
||||
self.butMatchWords.SetCheck(defaultSearch.matchWords)
|
||||
self.butMatchCase.SetCheck(defaultSearch.matchCase)
|
||||
self.butKeepDialogOpen.SetCheck(defaultSearch.keepDialogOpen)
|
||||
self.butAcrossFiles.SetCheck(defaultSearch.acrossFiles)
|
||||
self.butRemember.SetCheck(defaultSearch.remember)
|
||||
return dialog.Dialog.OnInitDialog(self)
|
||||
|
||||
def OnDestroy(self, msg):
|
||||
global curDialog
|
||||
curDialog = None
|
||||
return dialog.Dialog.OnDestroy(self, msg)
|
||||
|
||||
def DoFindNext(self):
|
||||
params = SearchParams()
|
||||
params.findText = self.editFindText.GetWindowText()
|
||||
params.matchCase = self.butMatchCase.GetCheck()
|
||||
params.matchWords = self.butMatchWords.GetCheck()
|
||||
params.acrossFiles = self.butAcrossFiles.GetCheck()
|
||||
params.remember = self.butRemember.GetCheck()
|
||||
return _FindIt(None, params)
|
||||
|
||||
def OnFindNext(self, id, code):
|
||||
if not self.editFindText.GetWindowText():
|
||||
win32api.MessageBeep()
|
||||
return
|
||||
if self.DoFindNext() != FOUND_NOTHING:
|
||||
if not self.butKeepDialogOpen.GetCheck():
|
||||
self.DestroyWindow()
|
||||
|
||||
class FindDialog(FindReplaceDialog):
|
||||
def _GetDialogTemplate(self):
|
||||
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
|
||||
visible = win32con.WS_CHILD | win32con.WS_VISIBLE
|
||||
dt = [
|
||||
["Find", (0, 2, 240, 75), style, None, (8, "MS Sans Serif")],
|
||||
["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible],
|
||||
["ComboBox", "", 102, (50, 7, 120, 120), visible | win32con.WS_BORDER | win32con.WS_TABSTOP |
|
||||
win32con.WS_VSCROLL |win32con.CBS_DROPDOWN |win32con.CBS_AUTOHSCROLL],
|
||||
["Button", "Match &whole word only", 105, (5, 23, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "Match &case", 107, (5, 33, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "Keep &dialog open", 115, (5, 43, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "Across &open files", 116, (5, 52, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "&Remember as default search", 117, (5, 61, 150, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "&Find Next", 109, (185, 5, 50, 14), visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP],
|
||||
["Button", "Cancel", win32con.IDCANCEL, (185, 23, 50, 14), visible | win32con.WS_TABSTOP],
|
||||
]
|
||||
return dt
|
||||
|
||||
class ReplaceDialog(FindReplaceDialog):
|
||||
def _GetDialogTemplate(self):
|
||||
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
|
||||
visible = win32con.WS_CHILD | win32con.WS_VISIBLE
|
||||
dt = [
|
||||
["Replace", (0, 2, 240, 95), style, 0, (8, "MS Sans Serif")],
|
||||
["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible],
|
||||
["ComboBox", "", 102, (60, 7, 110, 120), visible | win32con.WS_BORDER | win32con.WS_TABSTOP |
|
||||
win32con.WS_VSCROLL |win32con.CBS_DROPDOWN |win32con.CBS_AUTOHSCROLL],
|
||||
["Static", "Re&place with:", 103, (5, 25, 50, 10), visible],
|
||||
["ComboBox", "", 104, (60, 24, 110, 120), visible | win32con.WS_BORDER | win32con.WS_TABSTOP |
|
||||
win32con.WS_VSCROLL |win32con.CBS_DROPDOWN |win32con.CBS_AUTOHSCROLL],
|
||||
["Button", "Match &whole word only", 105, (5, 42, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "Match &case", 107, (5, 52, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "Keep &dialog open", 115, (5, 62, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "Across &open files", 116, (5, 72, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "&Remember as default search", 117, (5, 81, 150, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
|
||||
["Button", "&Find Next", 109, (185, 5, 50, 14), visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP],
|
||||
["Button", "&Replace", 110, (185, 23, 50, 14), visible | win32con.WS_TABSTOP],
|
||||
["Button", "Replace &All", 111, (185, 41, 50, 14), visible | win32con.WS_TABSTOP],
|
||||
["Button", "Cancel", win32con.IDCANCEL, (185, 59, 50, 14), visible | win32con.WS_TABSTOP],
|
||||
|
||||
|
||||
]
|
||||
return dt
|
||||
|
||||
def OnInitDialog(self):
|
||||
rc = FindReplaceDialog.OnInitDialog(self)
|
||||
self.HookCommand(self.OnReplace, 110)
|
||||
self.HookCommand(self.OnReplaceAll, 111)
|
||||
self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE)
|
||||
self.editReplaceText = self.GetDlgItem(104)
|
||||
self.editReplaceText.SetWindowText(lastSearch.replaceText)
|
||||
if hasattr(self.editReplaceText, 'SetEditSel'):
|
||||
self.editReplaceText.SetEditSel(0, -2)
|
||||
else:
|
||||
self.editReplaceText.SetSel(0, -2)
|
||||
self.butReplace = self.GetDlgItem(110)
|
||||
self.butReplaceAll = self.GetDlgItem(111)
|
||||
self.CheckButtonStates()
|
||||
return rc
|
||||
|
||||
def CheckButtonStates(self):
|
||||
# We can do a "Replace" or "Replace All" if the current selection
|
||||
# is the same as the search text.
|
||||
ft = self.editFindText.GetWindowText()
|
||||
control = _GetControl()
|
||||
# bCanReplace = len(ft)>0 and control.GetSelText() == ft
|
||||
bCanReplace = control is not None and lastSearch.sel == control.GetSel()
|
||||
self.butReplace.EnableWindow(bCanReplace)
|
||||
# self.butReplaceAll.EnableWindow(bCanReplace)
|
||||
|
||||
def OnActivate(self, msg):
|
||||
wparam = msg[2]
|
||||
fActive = win32api.LOWORD(wparam)
|
||||
if fActive != win32con.WA_INACTIVE:
|
||||
self.CheckButtonStates()
|
||||
|
||||
def OnFindNext(self, id, code):
|
||||
self.DoFindNext()
|
||||
self.CheckButtonStates()
|
||||
|
||||
def OnReplace(self, id, code):
|
||||
lastSearch.replaceText = self.editReplaceText.GetWindowText()
|
||||
_ReplaceIt(None)
|
||||
|
||||
def OnReplaceAll(self, id, code):
|
||||
control = _GetControl(None)
|
||||
if control is not None:
|
||||
control.SetSel(0)
|
||||
num = 0
|
||||
if self.DoFindNext() == FOUND_NORMAL:
|
||||
num = 1
|
||||
lastSearch.replaceText = self.editReplaceText.GetWindowText()
|
||||
while _ReplaceIt(control) == FOUND_NORMAL:
|
||||
num = num + 1
|
||||
|
||||
win32ui.SetStatusText("Replaced %d occurrences" % num)
|
||||
if num > 0 and not self.butKeepDialogOpen.GetCheck():
|
||||
self.DestroyWindow()
|
||||
|
||||
if __name__=='__main__':
|
||||
ShowFindDialog()
|
587
venv/Lib/site-packages/pythonwin/pywin/scintilla/formatter.py
Normal file
587
venv/Lib/site-packages/pythonwin/pywin/scintilla/formatter.py
Normal file
|
@ -0,0 +1,587 @@
|
|||
# Does Python source formatting for Scintilla controls.
|
||||
import win32ui
|
||||
import win32api
|
||||
import win32con
|
||||
import winerror
|
||||
import string
|
||||
import array
|
||||
from . import scintillacon
|
||||
|
||||
WM_KICKIDLE = 0x036A
|
||||
|
||||
# Used to indicate that style should use default color
|
||||
from win32con import CLR_INVALID
|
||||
|
||||
debugging = 0
|
||||
if debugging:
|
||||
# Output must go to another process else the result of
|
||||
# the printing itself will trigger again trigger a trace.
|
||||
import sys, win32traceutil, win32trace
|
||||
def trace(*args):
|
||||
win32trace.write(' '.join(map(str, args)) + "\n")
|
||||
else:
|
||||
trace = lambda *args: None
|
||||
|
||||
class Style:
|
||||
"""Represents a single format
|
||||
"""
|
||||
def __init__(self, name, format, background = CLR_INVALID):
|
||||
self.name = name # Name the format representes eg, "String", "Class"
|
||||
# Default background for each style is only used when there are no
|
||||
# saved settings (generally on first startup)
|
||||
self.background = self.default_background = background
|
||||
if type(format)==type(''):
|
||||
self.aliased = format
|
||||
self.format = None
|
||||
else:
|
||||
self.format = format
|
||||
self.aliased = None
|
||||
self.stylenum = None # Not yet registered.
|
||||
def IsBasedOnDefault(self):
|
||||
return len(self.format)==5
|
||||
# If the currently extended font defintion matches the
|
||||
# default format, restore the format to the "simple" format.
|
||||
def NormalizeAgainstDefault(self, defaultFormat):
|
||||
if self.IsBasedOnDefault():
|
||||
return 0 # No more to do, and not changed.
|
||||
bIsDefault = self.format[7] == defaultFormat[7] and \
|
||||
self.format[2] == defaultFormat[2]
|
||||
if bIsDefault:
|
||||
self.ForceAgainstDefault()
|
||||
return bIsDefault
|
||||
def ForceAgainstDefault(self):
|
||||
self.format = self.format[:5]
|
||||
def GetCompleteFormat(self, defaultFormat):
|
||||
# Get the complete style after applying any relevant defaults.
|
||||
if len(self.format)==5: # It is a default one
|
||||
fmt = self.format + defaultFormat[5:]
|
||||
else:
|
||||
fmt = self.format
|
||||
flags = win32con.CFM_BOLD | win32con.CFM_CHARSET | win32con.CFM_COLOR | win32con.CFM_FACE | win32con.CFM_ITALIC | win32con.CFM_SIZE
|
||||
return (flags,) + fmt[1:]
|
||||
|
||||
# The Formatter interface
|
||||
# used primarily when the actual formatting is done by Scintilla!
|
||||
class FormatterBase:
|
||||
def __init__(self, scintilla):
|
||||
self.scintilla = scintilla
|
||||
self.baseFormatFixed = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
|
||||
self.baseFormatProp = (-402653169, 0, 200, 0, 0, 0, 49, 'Arial')
|
||||
self.bUseFixed = 1
|
||||
self.styles = {} # Indexed by name
|
||||
self.styles_by_id = {} # Indexed by allocated ID.
|
||||
self.SetStyles()
|
||||
|
||||
def HookFormatter(self, parent = None):
|
||||
raise NotImplementedError()
|
||||
|
||||
# Used by the IDLE extensions to quickly determine if a character is a string.
|
||||
def GetStringStyle(self, pos):
|
||||
try:
|
||||
style = self.styles_by_id[self.scintilla.SCIGetStyleAt(pos)]
|
||||
except KeyError:
|
||||
# A style we dont know about - probably not even a .py file - can't be a string
|
||||
return None
|
||||
if style.name in self.string_style_names:
|
||||
return style
|
||||
return None
|
||||
|
||||
def RegisterStyle(self, style, stylenum):
|
||||
assert stylenum is not None, "We must have a style number"
|
||||
assert style.stylenum is None, "Style has already been registered"
|
||||
assert stylenum not in self.styles, "We are reusing a style number!"
|
||||
style.stylenum = stylenum
|
||||
self.styles[style.name] = style
|
||||
self.styles_by_id[stylenum] = style
|
||||
|
||||
def SetStyles(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def GetSampleText(self):
|
||||
return "Sample Text for the Format Dialog"
|
||||
|
||||
def GetDefaultFormat(self):
|
||||
if self.bUseFixed:
|
||||
return self.baseFormatFixed
|
||||
return self.baseFormatProp
|
||||
|
||||
# Update the control with the new style format.
|
||||
def _ReformatStyle(self, style):
|
||||
## Selection (background only for now)
|
||||
## Passing False for WPARAM to SCI_SETSELBACK is documented as resetting to scintilla default,
|
||||
## but does not work - selection background is not visible at all.
|
||||
## Default value in SPECIAL_STYLES taken from scintilla source.
|
||||
if style.name == STYLE_SELECTION:
|
||||
clr = style.background
|
||||
self.scintilla.SendScintilla(scintillacon.SCI_SETSELBACK, True, clr)
|
||||
|
||||
## Can't change font for selection, but could set color
|
||||
## However, the font color dropbox has no option for default, and thus would
|
||||
## always override syntax coloring
|
||||
## clr = style.format[4]
|
||||
## self.scintilla.SendScintilla(scintillacon.SCI_SETSELFORE, clr != CLR_INVALID, clr)
|
||||
return
|
||||
|
||||
assert style.stylenum is not None, "Unregistered style."
|
||||
#print "Reformat style", style.name, style.stylenum
|
||||
scintilla=self.scintilla
|
||||
stylenum = style.stylenum
|
||||
# Now we have the style number, indirect for the actual style.
|
||||
if style.aliased is not None:
|
||||
style = self.styles[style.aliased]
|
||||
f=style.format
|
||||
if style.IsBasedOnDefault():
|
||||
baseFormat = self.GetDefaultFormat()
|
||||
else: baseFormat = f
|
||||
scintilla.SCIStyleSetFore(stylenum, f[4])
|
||||
scintilla.SCIStyleSetFont(stylenum, baseFormat[7], baseFormat[5])
|
||||
if f[1] & 1: scintilla.SCIStyleSetBold(stylenum, 1)
|
||||
else: scintilla.SCIStyleSetBold(stylenum, 0)
|
||||
if f[1] & 2: scintilla.SCIStyleSetItalic(stylenum, 1)
|
||||
else: scintilla.SCIStyleSetItalic(stylenum, 0)
|
||||
scintilla.SCIStyleSetSize(stylenum, int(baseFormat[2]/20))
|
||||
scintilla.SCIStyleSetEOLFilled(stylenum, 1) # Only needed for unclosed strings.
|
||||
|
||||
## Default style background to whitespace background if set,
|
||||
## otherwise use system window color
|
||||
bg = style.background
|
||||
if bg == CLR_INVALID:
|
||||
bg = self.styles[STYLE_DEFAULT].background
|
||||
if bg == CLR_INVALID:
|
||||
bg = win32api.GetSysColor(win32con.COLOR_WINDOW)
|
||||
scintilla.SCIStyleSetBack(stylenum, bg)
|
||||
|
||||
|
||||
|
||||
def GetStyleByNum(self, stylenum):
|
||||
return self.styles_by_id[stylenum]
|
||||
|
||||
def ApplyFormattingStyles(self, bReload=1):
|
||||
if bReload:
|
||||
self.LoadPreferences()
|
||||
baseFormat = self.GetDefaultFormat()
|
||||
defaultStyle = Style("default", baseFormat)
|
||||
defaultStyle.stylenum = scintillacon.STYLE_DEFAULT
|
||||
self._ReformatStyle(defaultStyle)
|
||||
for style in list(self.styles.values()):
|
||||
if style.aliased is None:
|
||||
style.NormalizeAgainstDefault(baseFormat)
|
||||
self._ReformatStyle(style)
|
||||
self.scintilla.InvalidateRect()
|
||||
|
||||
# Some functions for loading and saving preferences. By default
|
||||
# an INI file (well, MFC maps this to the registry) is used.
|
||||
def LoadPreferences(self):
|
||||
self.baseFormatFixed = eval(self.LoadPreference("Base Format Fixed", str(self.baseFormatFixed)))
|
||||
self.baseFormatProp = eval(self.LoadPreference("Base Format Proportional", str(self.baseFormatProp)))
|
||||
self.bUseFixed = int(self.LoadPreference("Use Fixed", 1))
|
||||
|
||||
for style in list(self.styles.values()):
|
||||
new = self.LoadPreference(style.name, str(style.format))
|
||||
try:
|
||||
style.format = eval(new)
|
||||
except:
|
||||
print("Error loading style data for", style.name)
|
||||
# Use "vanilla" background hardcoded in PYTHON_STYLES if no settings in registry
|
||||
style.background = int(self.LoadPreference(style.name + " background", style.default_background))
|
||||
|
||||
def LoadPreference(self, name, default):
|
||||
return win32ui.GetProfileVal("Format", name, default)
|
||||
|
||||
def SavePreferences(self):
|
||||
self.SavePreference("Base Format Fixed", str(self.baseFormatFixed))
|
||||
self.SavePreference("Base Format Proportional", str(self.baseFormatProp))
|
||||
self.SavePreference("Use Fixed", self.bUseFixed)
|
||||
for style in list(self.styles.values()):
|
||||
if style.aliased is None:
|
||||
self.SavePreference(style.name, str(style.format))
|
||||
bg_name = style.name + " background"
|
||||
self.SavePreference(bg_name, style.background)
|
||||
|
||||
def SavePreference(self, name, value):
|
||||
win32ui.WriteProfileVal("Format", name, value)
|
||||
|
||||
# An abstract formatter
|
||||
# For all formatters we actually implement here.
|
||||
# (as opposed to those formatters built in to Scintilla)
|
||||
class Formatter(FormatterBase):
|
||||
def __init__(self, scintilla):
|
||||
self.bCompleteWhileIdle = 0
|
||||
self.bHaveIdleHandler = 0 # Dont currently have an idle handle
|
||||
self.nextstylenum = 0
|
||||
FormatterBase.__init__(self, scintilla)
|
||||
|
||||
def HookFormatter(self, parent = None):
|
||||
if parent is None: parent = self.scintilla.GetParent() # was GetParentFrame()!?
|
||||
parent.HookNotify(self.OnStyleNeeded, scintillacon.SCN_STYLENEEDED)
|
||||
|
||||
def OnStyleNeeded(self, std, extra):
|
||||
notify = self.scintilla.SCIUnpackNotifyMessage(extra)
|
||||
endStyledChar = self.scintilla.SendScintilla(scintillacon.SCI_GETENDSTYLED)
|
||||
lineEndStyled = self.scintilla.LineFromChar(endStyledChar)
|
||||
endStyled = self.scintilla.LineIndex(lineEndStyled)
|
||||
#print "enPosPaint %d endStyledChar %d lineEndStyled %d endStyled %d" % (endPosPaint, endStyledChar, lineEndStyled, endStyled)
|
||||
self.Colorize(endStyled, notify.position)
|
||||
|
||||
def ColorSeg(self, start, end, styleName):
|
||||
end = end+1
|
||||
# assert end-start>=0, "Can't have negative styling"
|
||||
stylenum = self.styles[styleName].stylenum
|
||||
while start<end:
|
||||
self.style_buffer[start]=stylenum
|
||||
start = start+1
|
||||
#self.scintilla.SCISetStyling(end - start + 1, stylenum)
|
||||
|
||||
def RegisterStyle(self, style, stylenum = None):
|
||||
if stylenum is None:
|
||||
stylenum = self.nextstylenum
|
||||
self.nextstylenum = self.nextstylenum + 1
|
||||
FormatterBase.RegisterStyle(self, style, stylenum)
|
||||
|
||||
def ColorizeString(self, str, charStart, styleStart):
|
||||
raise RuntimeError("You must override this method")
|
||||
|
||||
def Colorize(self, start=0, end=-1):
|
||||
scintilla = self.scintilla
|
||||
# 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 = scintilla.GetTextRange(start, end, decode=False)
|
||||
if start > 0:
|
||||
stylenum = scintilla.SCIGetStyleAt(start - 1)
|
||||
styleStart = self.GetStyleByNum(stylenum).name
|
||||
else:
|
||||
styleStart = None
|
||||
# trace("Coloring", start, end, end-start, len(stringVal), styleStart, self.scintilla.SCIGetCharAt(start))
|
||||
scintilla.SCIStartStyling(start, 31)
|
||||
self.style_buffer = array.array("b", (0,)*len(stringVal))
|
||||
self.ColorizeString(stringVal, styleStart)
|
||||
scintilla.SCISetStylingEx(self.style_buffer)
|
||||
self.style_buffer = None
|
||||
# trace("After styling, end styled is", self.scintilla.SCIGetEndStyled())
|
||||
if self.bCompleteWhileIdle and not self.bHaveIdleHandler and end!=-1 and end < scintilla.GetTextLength():
|
||||
self.bHaveIdleHandler = 1
|
||||
win32ui.GetApp().AddIdleHandler(self.DoMoreColoring)
|
||||
# Kicking idle makes the app seem slower when initially repainting!
|
||||
# win32ui.GetMainFrame().PostMessage(WM_KICKIDLE, 0, 0)
|
||||
|
||||
def DoMoreColoring(self, handler, count):
|
||||
try:
|
||||
scintilla = self.scintilla
|
||||
endStyled = scintilla.SCIGetEndStyled()
|
||||
lineStartStyled = scintilla.LineFromChar(endStyled)
|
||||
start = scintilla.LineIndex(lineStartStyled)
|
||||
end = scintilla.LineIndex(lineStartStyled+1)
|
||||
textlen = scintilla.GetTextLength()
|
||||
if end < 0: end = textlen
|
||||
|
||||
finished = end >= textlen
|
||||
self.Colorize(start, end)
|
||||
except (win32ui.error, AttributeError):
|
||||
# Window may have closed before we finished - no big deal!
|
||||
finished = 1
|
||||
|
||||
if finished:
|
||||
self.bHaveIdleHandler = 0
|
||||
win32ui.GetApp().DeleteIdleHandler(handler)
|
||||
return not finished
|
||||
|
||||
# A Formatter that knows how to format Python source
|
||||
from keyword import iskeyword, kwlist
|
||||
|
||||
wordstarts = '_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||
wordchars = '._0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||
operators = '%^&*()-+=|{}[]:;<>,/?!.~'
|
||||
|
||||
STYLE_DEFAULT = "Whitespace"
|
||||
STYLE_COMMENT = "Comment"
|
||||
STYLE_COMMENT_BLOCK = "Comment Blocks"
|
||||
STYLE_NUMBER = "Number"
|
||||
STYLE_STRING = "String"
|
||||
STYLE_SQSTRING = "SQ String"
|
||||
STYLE_TQSSTRING = "TQS String"
|
||||
STYLE_TQDSTRING = "TQD String"
|
||||
STYLE_KEYWORD = "Keyword"
|
||||
STYLE_CLASS = "Class"
|
||||
STYLE_METHOD = "Method"
|
||||
STYLE_OPERATOR = "Operator"
|
||||
STYLE_IDENTIFIER = "Identifier"
|
||||
STYLE_BRACE = "Brace/Paren - matching"
|
||||
STYLE_BRACEBAD = "Brace/Paren - unmatched"
|
||||
STYLE_STRINGEOL = "String with no terminator"
|
||||
STYLE_LINENUMBER = "Line numbers"
|
||||
STYLE_INDENTGUIDE = "Indent guide"
|
||||
STYLE_SELECTION = "Selection"
|
||||
|
||||
STRING_STYLES = [STYLE_STRING, STYLE_SQSTRING, STYLE_TQSSTRING, STYLE_TQDSTRING, STYLE_STRINGEOL]
|
||||
|
||||
# These styles can have any ID - they are not special to scintilla itself.
|
||||
# However, if we use the built-in lexer, then we must use its style numbers
|
||||
# so in that case, they _are_ special.
|
||||
# (name, format, background, scintilla id)
|
||||
PYTHON_STYLES = [
|
||||
(STYLE_DEFAULT, (0, 0, 200, 0, 0x808080), CLR_INVALID, scintillacon.SCE_P_DEFAULT ),
|
||||
(STYLE_COMMENT, (0, 2, 200, 0, 0x008000), CLR_INVALID, scintillacon.SCE_P_COMMENTLINE ),
|
||||
(STYLE_COMMENT_BLOCK,(0, 2, 200, 0, 0x808080), CLR_INVALID, scintillacon.SCE_P_COMMENTBLOCK ),
|
||||
(STYLE_NUMBER, (0, 0, 200, 0, 0x808000), CLR_INVALID, scintillacon.SCE_P_NUMBER ),
|
||||
(STYLE_STRING, (0, 0, 200, 0, 0x008080), CLR_INVALID, scintillacon.SCE_P_STRING ),
|
||||
(STYLE_SQSTRING, STYLE_STRING, CLR_INVALID, scintillacon.SCE_P_CHARACTER ),
|
||||
(STYLE_TQSSTRING, STYLE_STRING, CLR_INVALID, scintillacon.SCE_P_TRIPLE ),
|
||||
(STYLE_TQDSTRING, STYLE_STRING, CLR_INVALID, scintillacon.SCE_P_TRIPLEDOUBLE),
|
||||
(STYLE_STRINGEOL, (0, 0, 200, 0, 0x000000), 0x008080, scintillacon.SCE_P_STRINGEOL),
|
||||
(STYLE_KEYWORD, (0, 1, 200, 0, 0x800000), CLR_INVALID, scintillacon.SCE_P_WORD),
|
||||
(STYLE_CLASS, (0, 1, 200, 0, 0xFF0000), CLR_INVALID, scintillacon.SCE_P_CLASSNAME ),
|
||||
(STYLE_METHOD, (0, 1, 200, 0, 0x808000), CLR_INVALID, scintillacon.SCE_P_DEFNAME),
|
||||
(STYLE_OPERATOR, (0, 0, 200, 0, 0x000000), CLR_INVALID, scintillacon.SCE_P_OPERATOR),
|
||||
(STYLE_IDENTIFIER, (0, 0, 200, 0, 0x000000), CLR_INVALID, scintillacon.SCE_P_IDENTIFIER ),
|
||||
]
|
||||
|
||||
# These styles _always_ have this specific style number, regardless of
|
||||
# internal or external formatter.
|
||||
SPECIAL_STYLES = [
|
||||
(STYLE_BRACE, (0, 0, 200, 0, 0x000000), 0xffff80, scintillacon.STYLE_BRACELIGHT),
|
||||
(STYLE_BRACEBAD, (0, 0, 200, 0, 0x000000), 0x8ea5f2, scintillacon.STYLE_BRACEBAD),
|
||||
(STYLE_LINENUMBER, (0, 0, 200, 0, 0x000000), win32api.GetSysColor(win32con.COLOR_3DFACE), scintillacon.STYLE_LINENUMBER),
|
||||
(STYLE_INDENTGUIDE, (0, 0, 200, 0, 0x000000), CLR_INVALID, scintillacon.STYLE_INDENTGUIDE),
|
||||
## Not actually a style; requires special handling to send appropriate messages to scintilla
|
||||
(STYLE_SELECTION, (0, 0, 200, 0, CLR_INVALID), win32api.RGB(0xc0, 0xc0, 0xc0), 999999),
|
||||
]
|
||||
|
||||
PythonSampleCode = """\
|
||||
# Some Python
|
||||
class Sample(Super):
|
||||
def Fn(self):
|
||||
\tself.v = 1024
|
||||
dest = 'dest.html'
|
||||
x = func(a + 1)|)
|
||||
s = "I forget...
|
||||
## A large
|
||||
## comment block"""
|
||||
|
||||
class PythonSourceFormatter(Formatter):
|
||||
string_style_names = STRING_STYLES
|
||||
def GetSampleText(self):
|
||||
return PythonSampleCode
|
||||
|
||||
def LoadStyles(self):
|
||||
pass
|
||||
|
||||
def SetStyles(self):
|
||||
for name, format, bg, ignore in PYTHON_STYLES:
|
||||
self.RegisterStyle( Style(name, format, bg) )
|
||||
for name, format, bg, sc_id in SPECIAL_STYLES:
|
||||
self.RegisterStyle( Style(name, format, bg), sc_id )
|
||||
|
||||
def ClassifyWord(self, cdoc, start, end, prevWord):
|
||||
word = cdoc[start:end+1].decode('latin-1')
|
||||
attr = STYLE_IDENTIFIER
|
||||
if prevWord == "class":
|
||||
attr = STYLE_CLASS
|
||||
elif prevWord == "def":
|
||||
attr = STYLE_METHOD
|
||||
elif word[0] in string.digits:
|
||||
attr = STYLE_NUMBER
|
||||
elif iskeyword(word):
|
||||
attr = STYLE_KEYWORD
|
||||
self.ColorSeg(start, end, attr)
|
||||
return word
|
||||
|
||||
def ColorizeString(self, str, styleStart):
|
||||
if styleStart is None: styleStart = STYLE_DEFAULT
|
||||
return self.ColorizePythonCode(str, 0, styleStart)
|
||||
|
||||
def ColorizePythonCode(self, cdoc, charStart, styleStart):
|
||||
# Straight translation of C++, should do better
|
||||
lengthDoc = len(cdoc)
|
||||
if lengthDoc <= charStart: return
|
||||
prevWord = ""
|
||||
state = styleStart
|
||||
chPrev = chPrev2 = chPrev3 = ' '
|
||||
chNext2 = chNext = cdoc[charStart:charStart+1].decode('latin-1')
|
||||
startSeg = i = charStart
|
||||
while i < lengthDoc:
|
||||
ch = chNext
|
||||
chNext = ' '
|
||||
if i+1 < lengthDoc: chNext = cdoc[i+1:i+2].decode('latin-1')
|
||||
chNext2 = ' '
|
||||
if i+2 < lengthDoc: chNext2 = cdoc[i+2:i+3].decode('latin-1')
|
||||
if state == STYLE_DEFAULT:
|
||||
if ch in wordstarts:
|
||||
self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
|
||||
state = STYLE_KEYWORD
|
||||
startSeg = i
|
||||
elif ch == '#':
|
||||
self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
|
||||
if chNext == '#':
|
||||
state = STYLE_COMMENT_BLOCK
|
||||
else:
|
||||
state = STYLE_COMMENT
|
||||
startSeg = i
|
||||
elif ch == '\"':
|
||||
self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
|
||||
startSeg = i
|
||||
state = STYLE_COMMENT
|
||||
if chNext == '\"' and chNext2 == '\"':
|
||||
i = i + 2
|
||||
state = STYLE_TQDSTRING
|
||||
ch = ' '
|
||||
chPrev = ' '
|
||||
chNext = ' '
|
||||
if i+1 < lengthDoc: chNext = cdoc[i+1]
|
||||
else:
|
||||
state = STYLE_STRING
|
||||
elif ch == '\'':
|
||||
self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
|
||||
startSeg = i
|
||||
state = STYLE_COMMENT
|
||||
if chNext == '\'' and chNext2 == '\'':
|
||||
i = i + 2
|
||||
state = STYLE_TQSSTRING
|
||||
ch = ' '
|
||||
chPrev = ' '
|
||||
chNext = ' '
|
||||
if i+1 < lengthDoc: chNext = cdoc[i+1]
|
||||
else:
|
||||
state = STYLE_SQSTRING
|
||||
elif ch in operators:
|
||||
self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
|
||||
self.ColorSeg(i, i, STYLE_OPERATOR)
|
||||
startSeg = i+1
|
||||
elif state == STYLE_KEYWORD:
|
||||
if ch not in wordchars:
|
||||
prevWord = self.ClassifyWord(cdoc, startSeg, i-1, prevWord)
|
||||
state = STYLE_DEFAULT
|
||||
startSeg = i
|
||||
if ch == '#':
|
||||
if chNext == '#':
|
||||
state = STYLE_COMMENT_BLOCK
|
||||
else:
|
||||
state = STYLE_COMMENT
|
||||
elif ch == '\"':
|
||||
if chNext == '\"' and chNext2 == '\"':
|
||||
i = i + 2
|
||||
state = STYLE_TQDSTRING
|
||||
ch = ' '
|
||||
chPrev = ' '
|
||||
chNext = ' '
|
||||
if i+1 < lengthDoc: chNext = cdoc[i+1]
|
||||
else:
|
||||
state = STYLE_STRING
|
||||
elif ch == '\'':
|
||||
if chNext == '\'' and chNext2 == '\'':
|
||||
i = i + 2
|
||||
state = STYLE_TQSSTRING
|
||||
ch = ' '
|
||||
chPrev = ' '
|
||||
chNext = ' '
|
||||
if i+1 < lengthDoc: chNext = cdoc[i+1]
|
||||
else:
|
||||
state = STYLE_SQSTRING
|
||||
elif ch in operators:
|
||||
self.ColorSeg(startSeg, i, STYLE_OPERATOR)
|
||||
startSeg = i+1
|
||||
elif state == STYLE_COMMENT or state == STYLE_COMMENT_BLOCK:
|
||||
if ch == '\r' or ch == '\n':
|
||||
self.ColorSeg(startSeg, i-1, state)
|
||||
state = STYLE_DEFAULT
|
||||
startSeg = i
|
||||
elif state == STYLE_STRING:
|
||||
if ch == '\\':
|
||||
if chNext == '\"' or chNext == '\'' or chNext == '\\':
|
||||
i = i + 1
|
||||
ch = chNext
|
||||
chNext = ' '
|
||||
if i+1 < lengthDoc: chNext = cdoc[i+1]
|
||||
elif ch == '\"':
|
||||
self.ColorSeg(startSeg, i, STYLE_STRING)
|
||||
state = STYLE_DEFAULT
|
||||
startSeg = i+1
|
||||
elif state == STYLE_SQSTRING:
|
||||
if ch == '\\':
|
||||
if chNext == '\"' or chNext == '\'' or chNext == '\\':
|
||||
i = i+1
|
||||
ch = chNext
|
||||
chNext = ' '
|
||||
if i+1 < lengthDoc: chNext = cdoc[i+1]
|
||||
elif ch == '\'':
|
||||
self.ColorSeg(startSeg, i, STYLE_SQSTRING)
|
||||
state = STYLE_DEFAULT
|
||||
startSeg = i+1
|
||||
elif state == STYLE_TQSSTRING:
|
||||
if ch == '\'' and chPrev == '\'' and chPrev2 == '\'' and chPrev3 != '\\':
|
||||
self.ColorSeg(startSeg, i, STYLE_TQSSTRING)
|
||||
state = STYLE_DEFAULT
|
||||
startSeg = i+1
|
||||
elif state == STYLE_TQDSTRING and ch == '\"' and chPrev == '\"' and chPrev2 == '\"' and chPrev3 != '\\':
|
||||
self.ColorSeg(startSeg, i, STYLE_TQDSTRING)
|
||||
state = STYLE_DEFAULT
|
||||
startSeg = i+1
|
||||
chPrev3 = chPrev2
|
||||
chPrev2 = chPrev
|
||||
chPrev = ch
|
||||
i = i + 1
|
||||
if startSeg < lengthDoc:
|
||||
if state == STYLE_KEYWORD:
|
||||
self.ClassifyWord(cdoc, startSeg, lengthDoc-1, prevWord)
|
||||
else:
|
||||
self.ColorSeg(startSeg, lengthDoc-1, state)
|
||||
|
||||
|
||||
# These taken from the SciTE properties file.
|
||||
source_formatter_extensions = [
|
||||
( ".py .pys .pyw".split(), scintillacon.SCLEX_PYTHON ),
|
||||
( ".html .htm .asp .shtml".split(), scintillacon.SCLEX_HTML ),
|
||||
( "c .cc .cpp .cxx .h .hh .hpp .hxx .idl .odl .php3 .phtml .inc .js".split(), scintillacon.SCLEX_CPP ),
|
||||
( ".vbs .frm .ctl .cls".split(), scintillacon.SCLEX_VB ),
|
||||
( ".pl .pm .cgi .pod".split(), scintillacon.SCLEX_PERL ),
|
||||
( ".sql .spec .body .sps .spb .sf .sp".split(), scintillacon.SCLEX_SQL ),
|
||||
( ".tex .sty".split(), scintillacon.SCLEX_LATEX ),
|
||||
( ".xml .xul".split(), scintillacon.SCLEX_XML ),
|
||||
( ".err".split(), scintillacon.SCLEX_ERRORLIST ),
|
||||
( ".mak".split(), scintillacon.SCLEX_MAKEFILE ),
|
||||
( ".bat .cmd".split(), scintillacon.SCLEX_BATCH ),
|
||||
]
|
||||
|
||||
class BuiltinSourceFormatter(FormatterBase):
|
||||
# A class that represents a formatter built-in to Scintilla
|
||||
def __init__(self, scintilla, ext):
|
||||
self.ext = ext
|
||||
FormatterBase.__init__(self, scintilla)
|
||||
|
||||
def Colorize(self, start=0, end=-1):
|
||||
self.scintilla.SendScintilla(scintillacon.SCI_COLOURISE, start, end)
|
||||
def RegisterStyle(self, style, stylenum = None):
|
||||
assert style.stylenum is None, "Style has already been registered"
|
||||
if stylenum is None:
|
||||
stylenum = self.nextstylenum
|
||||
self.nextstylenum = self.nextstylenum + 1
|
||||
assert self.styles.get(stylenum) is None, "We are reusing a style number!"
|
||||
style.stylenum = stylenum
|
||||
self.styles[style.name] = style
|
||||
self.styles_by_id[stylenum] = style
|
||||
|
||||
def HookFormatter(self, parent = None):
|
||||
sc = self.scintilla
|
||||
for exts, formatter in source_formatter_extensions:
|
||||
if self.ext in exts:
|
||||
formatter_use = formatter
|
||||
break
|
||||
else:
|
||||
formatter_use = scintillacon.SCLEX_PYTHON
|
||||
sc.SendScintilla(scintillacon.SCI_SETLEXER, formatter_use)
|
||||
keywords = ' '.join(kwlist)
|
||||
sc.SCISetKeywords(keywords)
|
||||
|
||||
class BuiltinPythonSourceFormatter(BuiltinSourceFormatter):
|
||||
sci_lexer_name = scintillacon.SCLEX_PYTHON
|
||||
string_style_names = STRING_STYLES
|
||||
def __init__(self, sc, ext = ".py"):
|
||||
BuiltinSourceFormatter.__init__(self, sc, ext)
|
||||
def SetStyles(self):
|
||||
for name, format, bg, sc_id in PYTHON_STYLES:
|
||||
self.RegisterStyle( Style(name, format, bg), sc_id )
|
||||
for name, format, bg, sc_id in SPECIAL_STYLES:
|
||||
self.RegisterStyle( Style(name, format, bg), sc_id )
|
||||
def GetSampleText(self):
|
||||
return PythonSampleCode
|
179
venv/Lib/site-packages/pythonwin/pywin/scintilla/keycodes.py
Normal file
179
venv/Lib/site-packages/pythonwin/pywin/scintilla/keycodes.py
Normal file
|
@ -0,0 +1,179 @@
|
|||
import string
|
||||
import win32con
|
||||
import win32api
|
||||
import win32ui
|
||||
|
||||
MAPVK_VK_TO_CHAR = 2
|
||||
|
||||
key_name_to_vk = {}
|
||||
key_code_to_name = {}
|
||||
|
||||
_better_names = {
|
||||
"escape": "esc",
|
||||
"return": "enter",
|
||||
"back": "pgup",
|
||||
"next": "pgdn",
|
||||
}
|
||||
|
||||
def _fillvkmap():
|
||||
# Pull the VK_names from win32con
|
||||
names = [entry for entry in win32con.__dict__ if entry.startswith("VK_")]
|
||||
for name in names:
|
||||
code = getattr(win32con, name)
|
||||
n = name[3:].lower()
|
||||
key_name_to_vk[n] = code
|
||||
if n in _better_names:
|
||||
n = _better_names[n]
|
||||
key_name_to_vk[n] = code
|
||||
key_code_to_name[code] = n
|
||||
|
||||
|
||||
_fillvkmap()
|
||||
|
||||
def get_vk(chardesc):
|
||||
if len(chardesc)==1:
|
||||
# it is a character.
|
||||
info = win32api.VkKeyScan(chardesc)
|
||||
if info==-1:
|
||||
# Note: returning None, None causes an error when keyboard layout is non-English, see the report below
|
||||
# https://stackoverflow.com/questions/45138084/pythonwin-occasionally-gives-an-error-on-opening
|
||||
return 0, 0
|
||||
vk = win32api.LOBYTE(info)
|
||||
state = win32api.HIBYTE(info)
|
||||
modifiers = 0
|
||||
if state & 0x1:
|
||||
modifiers |= win32con.SHIFT_PRESSED
|
||||
if state & 0x2:
|
||||
modifiers |= win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED
|
||||
if state & 0x4:
|
||||
modifiers |= win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED
|
||||
return vk, modifiers
|
||||
# must be a 'key name'
|
||||
return key_name_to_vk.get(chardesc.lower()), 0
|
||||
|
||||
modifiers = {
|
||||
"alt" : win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED,
|
||||
"lalt" : win32con.LEFT_ALT_PRESSED,
|
||||
"ralt" : win32con.RIGHT_ALT_PRESSED,
|
||||
"ctrl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
|
||||
"ctl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
|
||||
"control" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
|
||||
"lctrl" : win32con.LEFT_CTRL_PRESSED,
|
||||
"lctl" : win32con.LEFT_CTRL_PRESSED,
|
||||
"rctrl" : win32con.RIGHT_CTRL_PRESSED,
|
||||
"rctl" : win32con.RIGHT_CTRL_PRESSED,
|
||||
"shift" : win32con.SHIFT_PRESSED,
|
||||
"key" : 0, # ignore key tag.
|
||||
}
|
||||
|
||||
def parse_key_name(name):
|
||||
name = name + "-" # Add a sentinal
|
||||
start = pos = 0
|
||||
max = len(name)
|
||||
toks = []
|
||||
while pos<max:
|
||||
if name[pos] in "+-":
|
||||
tok = name[start:pos]
|
||||
# use the ascii lower() version of tok, so ascii chars require
|
||||
# an explicit shift modifier - ie 'Ctrl+G' should be treated as
|
||||
# 'ctrl+g' - 'ctrl+shift+g' would be needed if desired.
|
||||
# This is mainly to avoid changing all the old keystroke defs
|
||||
toks.append(tok.lower())
|
||||
pos += 1 # skip the sep
|
||||
start = pos
|
||||
pos += 1
|
||||
flags = 0
|
||||
# do the modifiers
|
||||
for tok in toks[:-1]:
|
||||
mod = modifiers.get(tok.lower())
|
||||
if mod is not None:
|
||||
flags |= mod
|
||||
# the key name
|
||||
vk, this_flags = get_vk(toks[-1])
|
||||
return vk, flags | this_flags
|
||||
|
||||
_checks = [
|
||||
[ # Shift
|
||||
("Shift", win32con.SHIFT_PRESSED),
|
||||
],
|
||||
[ # Ctrl key
|
||||
("Ctrl", win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED),
|
||||
("LCtrl", win32con.LEFT_CTRL_PRESSED),
|
||||
("RCtrl", win32con.RIGHT_CTRL_PRESSED),
|
||||
],
|
||||
[ # Alt key
|
||||
("Alt", win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED),
|
||||
("LAlt", win32con.LEFT_ALT_PRESSED),
|
||||
("RAlt", win32con.RIGHT_ALT_PRESSED),
|
||||
],
|
||||
]
|
||||
|
||||
def make_key_name(vk, flags):
|
||||
# Check alt keys.
|
||||
flags_done = 0
|
||||
parts = []
|
||||
for moddata in _checks:
|
||||
for name, checkflag in moddata:
|
||||
if flags & checkflag:
|
||||
parts.append(name)
|
||||
flags_done = flags_done & checkflag
|
||||
break
|
||||
if flags_done & flags:
|
||||
parts.append(hex( flags & ~flags_done ) )
|
||||
# Now the key name.
|
||||
if vk is None:
|
||||
parts.append("<Unknown scan code>")
|
||||
else:
|
||||
try:
|
||||
parts.append(key_code_to_name[vk])
|
||||
except KeyError:
|
||||
# Not in our virtual key map - ask Windows what character this
|
||||
# key corresponds to.
|
||||
scancode = win32api.MapVirtualKey(vk, MAPVK_VK_TO_CHAR)
|
||||
parts.append(chr(scancode))
|
||||
sep = "+"
|
||||
if sep in parts: sep = "-"
|
||||
return sep.join([p.capitalize() for p in parts])
|
||||
|
||||
def _psc(char):
|
||||
sc, mods = get_vk(char)
|
||||
print("Char %s -> %d -> %s" % (repr(char), sc, key_code_to_name.get(sc)))
|
||||
|
||||
def test1():
|
||||
for ch in """aA0/?[{}];:'"`~_-+=\\|,<.>/?""":
|
||||
_psc(ch)
|
||||
for code in ["Home", "End", "Left", "Right", "Up", "Down", "Menu", "Next"]:
|
||||
_psc(code)
|
||||
|
||||
def _pkn(n):
|
||||
vk, flags = parse_key_name(n)
|
||||
print("%s -> %s,%s -> %s" % (n, vk, flags, make_key_name(vk, flags)))
|
||||
|
||||
def test2():
|
||||
_pkn("ctrl+alt-shift+x")
|
||||
_pkn("ctrl-home")
|
||||
_pkn("Shift-+")
|
||||
_pkn("Shift--")
|
||||
_pkn("Shift+-")
|
||||
_pkn("Shift++")
|
||||
_pkn("LShift-+")
|
||||
_pkn("ctl+home")
|
||||
_pkn("ctl+enter")
|
||||
_pkn("alt+return")
|
||||
_pkn("Alt+/")
|
||||
_pkn("Alt+BadKeyName")
|
||||
_pkn("A") # an ascii char - should be seen as 'a'
|
||||
_pkn("a")
|
||||
_pkn("Shift-A")
|
||||
_pkn("Shift-a")
|
||||
_pkn("a")
|
||||
_pkn("(")
|
||||
_pkn("Ctrl+(")
|
||||
_pkn("Ctrl+Shift-8")
|
||||
_pkn("Ctrl+*")
|
||||
_pkn("{")
|
||||
_pkn("!")
|
||||
_pkn(".")
|
||||
|
||||
if __name__=='__main__':
|
||||
test2()
|
1992
venv/Lib/site-packages/pythonwin/pywin/scintilla/scintillacon.py
Normal file
1992
venv/Lib/site-packages/pythonwin/pywin/scintilla/scintillacon.py
Normal file
File diff suppressed because it is too large
Load diff
722
venv/Lib/site-packages/pythonwin/pywin/scintilla/view.py
Normal file
722
venv/Lib/site-packages/pythonwin/pywin/scintilla/view.py
Normal file
|
@ -0,0 +1,722 @@
|
|||
# A general purpose MFC CCtrlView view that uses Scintilla.
|
||||
|
||||
from . import control
|
||||
from . import IDLEenvironment # IDLE emulation.
|
||||
from pywin.mfc import docview
|
||||
from pywin.mfc import dialog
|
||||
from . import scintillacon
|
||||
import win32con
|
||||
import win32ui
|
||||
import afxres
|
||||
import string
|
||||
import array
|
||||
import sys
|
||||
import types
|
||||
import __main__ # for attribute lookup
|
||||
from . import bindings
|
||||
from . import keycodes
|
||||
import struct
|
||||
import re
|
||||
import os
|
||||
|
||||
PRINTDLGORD = 1538
|
||||
IDC_PRINT_MAG_EDIT = 1010
|
||||
EM_FORMATRANGE = win32con.WM_USER+57
|
||||
|
||||
wordbreaks = "._" + string.ascii_uppercase + string.ascii_lowercase + string.digits
|
||||
|
||||
patImport=re.compile('import (?P<name>.*)')
|
||||
|
||||
_event_commands = [
|
||||
# File menu
|
||||
"win32ui.ID_FILE_LOCATE", "win32ui.ID_FILE_CHECK", "afxres.ID_FILE_CLOSE",
|
||||
"afxres.ID_FILE_NEW", "afxres.ID_FILE_OPEN", "afxres.ID_FILE_SAVE",
|
||||
"afxres.ID_FILE_SAVE_AS", "win32ui.ID_FILE_SAVE_ALL",
|
||||
# Edit menu
|
||||
"afxres.ID_EDIT_UNDO", "afxres.ID_EDIT_REDO", "afxres.ID_EDIT_CUT",
|
||||
"afxres.ID_EDIT_COPY", "afxres.ID_EDIT_PASTE", "afxres.ID_EDIT_SELECT_ALL",
|
||||
"afxres.ID_EDIT_FIND", "afxres.ID_EDIT_REPEAT", "afxres.ID_EDIT_REPLACE",
|
||||
# View menu
|
||||
"win32ui.ID_VIEW_WHITESPACE", "win32ui.ID_VIEW_FIXED_FONT",
|
||||
"win32ui.ID_VIEW_BROWSE", "win32ui.ID_VIEW_INTERACTIVE",
|
||||
# Window menu
|
||||
"afxres.ID_WINDOW_ARRANGE", "afxres.ID_WINDOW_CASCADE",
|
||||
"afxres.ID_WINDOW_NEW", "afxres.ID_WINDOW_SPLIT",
|
||||
"afxres.ID_WINDOW_TILE_HORZ", "afxres.ID_WINDOW_TILE_VERT",
|
||||
# Others
|
||||
"afxres.ID_APP_EXIT", "afxres.ID_APP_ABOUT",
|
||||
]
|
||||
|
||||
_extra_event_commands = [
|
||||
("EditDelete", afxres.ID_EDIT_CLEAR),
|
||||
("LocateModule", win32ui.ID_FILE_LOCATE),
|
||||
("GotoLine", win32ui.ID_EDIT_GOTO_LINE),
|
||||
("DbgBreakpointToggle", win32ui.IDC_DBG_ADD),
|
||||
("DbgGo", win32ui.IDC_DBG_GO),
|
||||
("DbgStepOver", win32ui.IDC_DBG_STEPOVER),
|
||||
("DbgStep", win32ui.IDC_DBG_STEP),
|
||||
("DbgStepOut", win32ui.IDC_DBG_STEPOUT),
|
||||
("DbgBreakpointClearAll", win32ui.IDC_DBG_CLEAR),
|
||||
("DbgClose", win32ui.IDC_DBG_CLOSE),
|
||||
]
|
||||
|
||||
event_commands = []
|
||||
def _CreateEvents():
|
||||
for name in _event_commands:
|
||||
val = eval(name)
|
||||
name_parts = name.split("_")[1:]
|
||||
name_parts = [p.capitalize() for p in name_parts]
|
||||
event = ''.join(name_parts)
|
||||
event_commands.append((event, val))
|
||||
for name, id in _extra_event_commands:
|
||||
event_commands.append((name, id))
|
||||
|
||||
_CreateEvents()
|
||||
del _event_commands; del _extra_event_commands
|
||||
|
||||
command_reflectors = [
|
||||
(win32ui.ID_EDIT_UNDO, win32con.WM_UNDO),
|
||||
(win32ui.ID_EDIT_REDO, scintillacon.SCI_REDO),
|
||||
(win32ui.ID_EDIT_CUT, win32con.WM_CUT),
|
||||
(win32ui.ID_EDIT_COPY, win32con.WM_COPY),
|
||||
(win32ui.ID_EDIT_PASTE, win32con.WM_PASTE),
|
||||
(win32ui.ID_EDIT_CLEAR, win32con.WM_CLEAR),
|
||||
(win32ui.ID_EDIT_SELECT_ALL, scintillacon.SCI_SELECTALL),
|
||||
]
|
||||
|
||||
def DoBraceMatch(control):
|
||||
curPos = control.SCIGetCurrentPos()
|
||||
charBefore = ' '
|
||||
if curPos: charBefore = control.SCIGetCharAt(curPos-1)
|
||||
charAt = control.SCIGetCharAt(curPos)
|
||||
braceAtPos = braceOpposite = -1
|
||||
if charBefore in "[](){}": braceAtPos = curPos-1
|
||||
if braceAtPos==-1:
|
||||
if charAt in "[](){}": braceAtPos = curPos
|
||||
if braceAtPos != -1:
|
||||
braceOpposite = control.SCIBraceMatch(braceAtPos, 0)
|
||||
if braceAtPos != -1 and braceOpposite==-1:
|
||||
control.SCIBraceBadHighlight(braceAtPos)
|
||||
else:
|
||||
# either clear them both or set them both.
|
||||
control.SCIBraceHighlight(braceAtPos, braceOpposite)
|
||||
|
||||
def _get_class_attributes(ob):
|
||||
# Recurse into base classes looking for attributes
|
||||
items = []
|
||||
try:
|
||||
items = items + dir(ob)
|
||||
for i in ob.__bases__:
|
||||
for item in _get_class_attributes(i):
|
||||
if item not in items:
|
||||
items.append(item)
|
||||
except AttributeError:
|
||||
pass
|
||||
return items
|
||||
|
||||
# Supposed to look like an MFC CEditView, but
|
||||
# also supports IDLE extensions and other source code generic features.
|
||||
class CScintillaView(docview.CtrlView, control.CScintillaColorEditInterface):
|
||||
def __init__(self, doc):
|
||||
docview.CtrlView.__init__(self, doc, "Scintilla", win32con.WS_CHILD | win32con.WS_VSCROLL | win32con.WS_HSCROLL | win32con.WS_CLIPCHILDREN | win32con.WS_VISIBLE)
|
||||
self._tabWidth = 8 # Mirror of what we send to Scintilla - never change this directly
|
||||
self.bAutoCompleteAttributes = 1
|
||||
self.bShowCallTips = 1
|
||||
self.bMatchBraces = 0 # Editor option will default this to true later!
|
||||
self.bindings = bindings.BindingsManager(self)
|
||||
|
||||
self.idle = IDLEenvironment.IDLEEditorWindow(self)
|
||||
self.idle.IDLEExtension("AutoExpand")
|
||||
# SendScintilla is called so frequently it is worth optimizing.
|
||||
self.SendScintilla = self._obj_.SendMessage
|
||||
|
||||
def OnDestroy(self, msg):
|
||||
self.SendScintilla = None
|
||||
return docview.CtrlView.OnDestroy(self, msg)
|
||||
|
||||
def _MakeColorizer(self):
|
||||
ext = os.path.splitext(self.GetDocument().GetPathName())[1]
|
||||
from . import formatter
|
||||
return formatter.BuiltinPythonSourceFormatter(self, ext)
|
||||
|
||||
|
||||
# def SendScintilla(self, msg, w=0, l=0):
|
||||
# return self._obj_.SendMessage(msg, w, l)
|
||||
|
||||
def SCISetTabWidth(self, width):
|
||||
# I need to remember the tab-width for the AutoIndent extension. This may go.
|
||||
self._tabWidth = width
|
||||
control.CScintillaEditInterface.SCISetTabWidth(self, width)
|
||||
|
||||
def GetTabWidth(self):
|
||||
return self._tabWidth
|
||||
|
||||
def HookHandlers(self):
|
||||
# Create events for all the menu names.
|
||||
for name, val in event_commands:
|
||||
# handler = lambda id, code, tosend=val, parent=parent: parent.OnCommand(tosend, 0) and 0
|
||||
self.bindings.bind(name, None, cid=val)
|
||||
|
||||
# Hook commands that do nothing other than send Scintilla messages.
|
||||
for command, reflection in command_reflectors:
|
||||
handler = lambda id, code, ss=self.SendScintilla, tosend=reflection: ss(tosend) and 0
|
||||
self.HookCommand(handler, command)
|
||||
|
||||
self.HookCommand(self.OnCmdViewWS, win32ui.ID_VIEW_WHITESPACE)
|
||||
self.HookCommandUpdate(self.OnUpdateViewWS, win32ui.ID_VIEW_WHITESPACE)
|
||||
self.HookCommand(self.OnCmdViewIndentationGuides, win32ui.ID_VIEW_INDENTATIONGUIDES)
|
||||
self.HookCommandUpdate(self.OnUpdateViewIndentationGuides, win32ui.ID_VIEW_INDENTATIONGUIDES)
|
||||
self.HookCommand(self.OnCmdViewRightEdge, win32ui.ID_VIEW_RIGHT_EDGE)
|
||||
self.HookCommandUpdate(self.OnUpdateViewRightEdge, win32ui.ID_VIEW_RIGHT_EDGE)
|
||||
self.HookCommand(self.OnCmdViewEOL, win32ui.ID_VIEW_EOL)
|
||||
self.HookCommandUpdate(self.OnUpdateViewEOL, win32ui.ID_VIEW_EOL)
|
||||
self.HookCommand(self.OnCmdViewFixedFont, win32ui.ID_VIEW_FIXED_FONT)
|
||||
self.HookCommandUpdate(self.OnUpdateViewFixedFont, win32ui.ID_VIEW_FIXED_FONT)
|
||||
self.HookCommand(self.OnCmdFileLocate, win32ui.ID_FILE_LOCATE)
|
||||
self.HookCommand(self.OnCmdEditFind, win32ui.ID_EDIT_FIND)
|
||||
self.HookCommand(self.OnCmdEditRepeat, win32ui.ID_EDIT_REPEAT)
|
||||
self.HookCommand(self.OnCmdEditReplace, win32ui.ID_EDIT_REPLACE)
|
||||
self.HookCommand(self.OnCmdGotoLine, win32ui.ID_EDIT_GOTO_LINE)
|
||||
self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT)
|
||||
self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT_DIRECT)
|
||||
self.HookCommand(self.OnFilePrintPreview,
|
||||
win32ui.ID_FILE_PRINT_PREVIEW)
|
||||
# Key bindings.
|
||||
self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
|
||||
self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN)
|
||||
# Hook wheeley mouse events
|
||||
# self.HookMessage(self.OnMouseWheel, win32con.WM_MOUSEWHEEL)
|
||||
self.HookFormatter()
|
||||
|
||||
def OnInitialUpdate(self):
|
||||
doc = self.GetDocument()
|
||||
|
||||
# Enable Unicode
|
||||
self.SendScintilla(scintillacon.SCI_SETCODEPAGE, scintillacon.SC_CP_UTF8, 0)
|
||||
self.SendScintilla(scintillacon.SCI_SETKEYSUNICODE, 1, 0)
|
||||
|
||||
# Create margins
|
||||
self.SendScintilla(scintillacon.SCI_SETMARGINTYPEN, 1, scintillacon.SC_MARGIN_SYMBOL);
|
||||
self.SendScintilla(scintillacon.SCI_SETMARGINMASKN, 1, 0xF);
|
||||
self.SendScintilla(scintillacon.SCI_SETMARGINTYPEN, 2, scintillacon.SC_MARGIN_SYMBOL);
|
||||
self.SendScintilla(scintillacon.SCI_SETMARGINMASKN, 2, scintillacon.SC_MASK_FOLDERS);
|
||||
self.SendScintilla(scintillacon.SCI_SETMARGINSENSITIVEN, 2, 1);
|
||||
|
||||
self.GetDocument().HookViewNotifications(self) # is there an MFC way to grab this?
|
||||
self.HookHandlers()
|
||||
|
||||
# Load the configuration information.
|
||||
self.OnWinIniChange(None)
|
||||
|
||||
self.SetSel()
|
||||
|
||||
self.GetDocument().FinalizeViewCreation(self) # is there an MFC way to grab this?
|
||||
|
||||
|
||||
def _GetSubConfigNames(self):
|
||||
return None # By default we use only sections without sub-sections.
|
||||
|
||||
def OnWinIniChange(self, section = None):
|
||||
self.bindings.prepare_configure()
|
||||
try:
|
||||
self.DoConfigChange()
|
||||
finally:
|
||||
self.bindings.complete_configure()
|
||||
|
||||
def DoConfigChange(self):
|
||||
# Bit of a hack I dont kow what to do about - these should be "editor options"
|
||||
from pywin.framework.editor import GetEditorOption
|
||||
self.bAutoCompleteAttributes = GetEditorOption("Autocomplete Attributes", 1)
|
||||
self.bShowCallTips = GetEditorOption("Show Call Tips", 1)
|
||||
# Update the key map and extension data.
|
||||
configManager.configure(self, self._GetSubConfigNames())
|
||||
if configManager.last_error:
|
||||
win32ui.MessageBox(configManager.last_error, "Configuration Error")
|
||||
self.bMatchBraces = GetEditorOption("Match Braces", 1)
|
||||
self.ApplyFormattingStyles(1)
|
||||
|
||||
def OnDestroy(self, msg):
|
||||
self.bindings.close()
|
||||
self.bindings = None
|
||||
self.idle.close()
|
||||
self.idle = None
|
||||
control.CScintillaColorEditInterface.close(self)
|
||||
return docview.CtrlView.OnDestroy(self, msg)
|
||||
|
||||
def OnMouseWheel(self, msg):
|
||||
zDelta = msg[2] >> 16
|
||||
vpos = self.GetScrollPos(win32con.SB_VERT)
|
||||
vpos = vpos - zDelta/40 # 3 lines per notch
|
||||
self.SetScrollPos(win32con.SB_VERT, vpos)
|
||||
self.SendScintilla(win32con.WM_VSCROLL,
|
||||
(vpos<<16) | win32con.SB_THUMBPOSITION,
|
||||
0)
|
||||
|
||||
def OnBraceMatch(self, std, extra):
|
||||
if not self.bMatchBraces: return
|
||||
DoBraceMatch(self)
|
||||
|
||||
def OnNeedShown(self, std, extra):
|
||||
notify = self.SCIUnpackNotifyMessage(extra)
|
||||
# OnNeedShown is called before an edit operation when
|
||||
# text is folded (as it is possible the text insertion will happen
|
||||
# in a folded region.) As this happens _before_ the insert,
|
||||
# we ignore the length (if we are at EOF, pos + length may
|
||||
# actually be beyond the end of buffer)
|
||||
self.EnsureCharsVisible(notify.position)
|
||||
|
||||
def EnsureCharsVisible(self, start, end = None):
|
||||
if end is None: end = start
|
||||
lineStart = self.LineFromChar(min(start, end))
|
||||
lineEnd = self.LineFromChar(max(start, end))
|
||||
while lineStart <= lineEnd:
|
||||
self.SCIEnsureVisible(lineStart)
|
||||
lineStart = lineStart + 1
|
||||
|
||||
# Helper to add an event to a menu.
|
||||
def AppendMenu(self, menu, text="", event=None, flags = None, checked=0):
|
||||
if event is None:
|
||||
assert flags is not None, "No event or custom flags!"
|
||||
cmdid = 0
|
||||
else:
|
||||
cmdid = self.bindings.get_command_id(event)
|
||||
if cmdid is None:
|
||||
# No event of that name - no point displaying it.
|
||||
print('View.AppendMenu(): Unknown event "%s" specified for menu text "%s" - ignored' % (event, text))
|
||||
return
|
||||
keyname = configManager.get_key_binding( event, self._GetSubConfigNames() )
|
||||
if keyname is not None:
|
||||
text = text + "\t" + keyname
|
||||
if flags is None: flags = win32con.MF_STRING|win32con.MF_ENABLED
|
||||
if checked: flags = flags | win32con.MF_CHECKED
|
||||
menu.AppendMenu(flags, cmdid, text)
|
||||
|
||||
def OnKeyDown(self, msg):
|
||||
return self.bindings.fire_key_event( msg )
|
||||
|
||||
def GotoEndOfFileEvent(self, event):
|
||||
self.SetSel(-1)
|
||||
|
||||
def KeyDotEvent(self, event):
|
||||
## Don't trigger autocomplete if any text is selected
|
||||
s,e = self.GetSel()
|
||||
if s!=e:
|
||||
return 1
|
||||
self.SCIAddText(".")
|
||||
if self.bAutoCompleteAttributes:
|
||||
self._AutoComplete()
|
||||
|
||||
# View Whitespace/EOL/Indentation UI.
|
||||
|
||||
def OnCmdViewWS(self, cmd, code): # Handle the menu command
|
||||
viewWS = self.SCIGetViewWS()
|
||||
self.SCISetViewWS(not viewWS)
|
||||
def OnUpdateViewWS(self, cmdui): # Update the tick on the UI.
|
||||
cmdui.SetCheck(self.SCIGetViewWS())
|
||||
cmdui.Enable()
|
||||
def OnCmdViewIndentationGuides(self, cmd, code): # Handle the menu command
|
||||
viewIG = self.SCIGetIndentationGuides()
|
||||
self.SCISetIndentationGuides(not viewIG)
|
||||
def OnUpdateViewIndentationGuides(self, cmdui): # Update the tick on the UI.
|
||||
cmdui.SetCheck(self.SCIGetIndentationGuides())
|
||||
cmdui.Enable()
|
||||
def OnCmdViewRightEdge(self, cmd, code): # Handle the menu command
|
||||
if self.SCIGetEdgeMode() == scintillacon.EDGE_NONE:
|
||||
mode = scintillacon.EDGE_BACKGROUND
|
||||
else:
|
||||
mode = scintillacon.EDGE_NONE
|
||||
self.SCISetEdgeMode(mode)
|
||||
def OnUpdateViewRightEdge(self, cmdui): # Update the tick on the UI.
|
||||
cmdui.SetCheck(self.SCIGetEdgeMode() != scintillacon.EDGE_NONE)
|
||||
cmdui.Enable()
|
||||
def OnCmdViewEOL(self, cmd, code): # Handle the menu command
|
||||
viewEOL = self.SCIGetViewEOL()
|
||||
self.SCISetViewEOL(not viewEOL)
|
||||
def OnUpdateViewEOL(self, cmdui): # Update the tick on the UI.
|
||||
cmdui.SetCheck(self.SCIGetViewEOL())
|
||||
cmdui.Enable()
|
||||
|
||||
def OnCmdViewFixedFont(self, cmd, code): # Handle the menu command
|
||||
self._GetColorizer().bUseFixed = not self._GetColorizer().bUseFixed
|
||||
self.ApplyFormattingStyles(0)
|
||||
# Ensure the selection is visible!
|
||||
self.ScrollCaret()
|
||||
|
||||
def OnUpdateViewFixedFont(self, cmdui): # Update the tick on the UI.
|
||||
c = self._GetColorizer()
|
||||
if c is not None: cmdui.SetCheck(c.bUseFixed)
|
||||
cmdui.Enable(c is not None)
|
||||
|
||||
def OnCmdEditFind(self, cmd, code):
|
||||
from . import find
|
||||
find.ShowFindDialog()
|
||||
def OnCmdEditRepeat(self, cmd, code):
|
||||
from . import find
|
||||
find.FindNext()
|
||||
def OnCmdEditReplace(self, cmd, code):
|
||||
from . import find
|
||||
find.ShowReplaceDialog()
|
||||
|
||||
def OnCmdFileLocate(self, cmd, id):
|
||||
line = self.GetLine().strip()
|
||||
import pywin.framework.scriptutils
|
||||
m = patImport.match(line)
|
||||
if m:
|
||||
# Module name on this line - locate that!
|
||||
modName = m.group('name')
|
||||
fileName = pywin.framework.scriptutils.LocatePythonFile(modName)
|
||||
if fileName is None:
|
||||
win32ui.SetStatusText("Can't locate module %s" % modName)
|
||||
return 1 # Let the default get it.
|
||||
else:
|
||||
win32ui.GetApp().OpenDocumentFile(fileName)
|
||||
else:
|
||||
# Just to a "normal" locate - let the default handler get it.
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def OnCmdGotoLine(self, cmd, id):
|
||||
try:
|
||||
lineNo = int(input("Enter Line Number"))-1
|
||||
except (ValueError, KeyboardInterrupt):
|
||||
return 0
|
||||
self.SCIEnsureVisible(lineNo)
|
||||
self.SCIGotoLine(lineNo)
|
||||
return 0
|
||||
|
||||
def SaveTextFile(self, filename, encoding=None):
|
||||
doc = self.GetDocument()
|
||||
doc._SaveTextToFile(self, filename, encoding=encoding)
|
||||
doc.SetModifiedFlag(0)
|
||||
return 1
|
||||
|
||||
def _AutoComplete(self):
|
||||
def list2dict(l):
|
||||
ret={}
|
||||
for i in l:
|
||||
ret[i] = None
|
||||
return ret
|
||||
|
||||
self.SCIAutoCCancel() # Cancel old auto-complete lists.
|
||||
# First try and get an object without evaluating calls
|
||||
ob = self._GetObjectAtPos(bAllowCalls = 0)
|
||||
# If that failed, try and process call or indexing to get the object.
|
||||
if ob is None:
|
||||
ob = self._GetObjectAtPos(bAllowCalls = 1)
|
||||
items_dict = {}
|
||||
if ob is not None:
|
||||
try: # Catch unexpected errors when fetching attribute names from the object
|
||||
# extra attributes of win32ui objects
|
||||
if hasattr(ob, "_obj_"):
|
||||
try:
|
||||
items_dict.update(list2dict(dir(ob._obj_)))
|
||||
except AttributeError:
|
||||
pass # object has no __dict__
|
||||
|
||||
# normal attributes
|
||||
try:
|
||||
items_dict.update(list2dict(dir(ob)))
|
||||
except AttributeError:
|
||||
pass # object has no __dict__
|
||||
if hasattr(ob, "__class__"):
|
||||
items_dict.update(list2dict(_get_class_attributes(ob.__class__)))
|
||||
# The object may be a COM object with typelib support - lets see if we can get its props.
|
||||
# (contributed by Stefan Migowsky)
|
||||
try:
|
||||
# Get the automation attributes
|
||||
items_dict.update(ob.__class__._prop_map_get_)
|
||||
# See if there is an write only property
|
||||
# could be optimized
|
||||
items_dict.update(ob.__class__._prop_map_put_)
|
||||
# append to the already evaluated list
|
||||
except AttributeError:
|
||||
pass
|
||||
# The object might be a pure COM dynamic dispatch with typelib support - lets see if we can get its props.
|
||||
if hasattr(ob, "_oleobj_"):
|
||||
try:
|
||||
for iTI in range(0,ob._oleobj_.GetTypeInfoCount()):
|
||||
typeInfo = ob._oleobj_.GetTypeInfo(iTI)
|
||||
self._UpdateWithITypeInfo (items_dict, typeInfo)
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
win32ui.SetStatusText("Error attempting to get object attributes - %s" % (repr(sys.exc_info()[0]),))
|
||||
|
||||
# ensure all keys are strings.
|
||||
items = [str(k) for k in items_dict.keys()]
|
||||
# All names that start with "_" go!
|
||||
items = [k for k in items if not k.startswith('_')]
|
||||
|
||||
if not items:
|
||||
# Heuristics a-la AutoExpand
|
||||
# The idea is to find other usages of the current binding
|
||||
# and assume, that it refers to the same object (or at least,
|
||||
# to an object of the same type)
|
||||
# Contributed by Vadim Chugunov [vadimch@yahoo.com]
|
||||
left, right = self._GetWordSplit()
|
||||
if left=="": # Ignore standalone dots
|
||||
return None
|
||||
# We limit our search to the current class, if that
|
||||
# information is available
|
||||
minline, maxline, curclass = self._GetClassInfoFromBrowser()
|
||||
endpos = self.LineIndex(maxline)
|
||||
text = self.GetTextRange(self.LineIndex(minline),endpos)
|
||||
try:
|
||||
l = re.findall(r"\b"+left+"\.\w+",text)
|
||||
except re.error:
|
||||
# parens etc may make an invalid RE, but this code wouldnt
|
||||
# benefit even if the RE did work :-)
|
||||
l = []
|
||||
prefix = len(left)+1
|
||||
unique = {}
|
||||
for li in l:
|
||||
unique[li[prefix:]] = 1
|
||||
# Assuming traditional usage of self...
|
||||
if curclass and left=="self":
|
||||
self._UpdateWithClassMethods(unique,curclass)
|
||||
|
||||
items = [word for word in unique.keys() if word[:2]!='__' or word[-2:]!='__']
|
||||
# Ignore the word currently to the right of the dot - probably a red-herring.
|
||||
try:
|
||||
items.remove(right[1:])
|
||||
except ValueError:
|
||||
pass
|
||||
if items:
|
||||
items.sort()
|
||||
self.SCIAutoCSetAutoHide(0)
|
||||
self.SCIAutoCShow(items)
|
||||
|
||||
def _UpdateWithITypeInfo (self, items_dict, typeInfo):
|
||||
import pythoncom
|
||||
typeInfos = [typeInfo]
|
||||
# suppress IDispatch and IUnknown methods
|
||||
inspectedIIDs = {pythoncom.IID_IDispatch:None}
|
||||
|
||||
while len(typeInfos)>0:
|
||||
typeInfo = typeInfos.pop()
|
||||
typeAttr = typeInfo.GetTypeAttr()
|
||||
|
||||
if typeAttr.iid not in inspectedIIDs:
|
||||
inspectedIIDs[typeAttr.iid] = None
|
||||
for iFun in range(0,typeAttr.cFuncs):
|
||||
funDesc = typeInfo.GetFuncDesc(iFun)
|
||||
funName = typeInfo.GetNames(funDesc.memid)[0]
|
||||
if funName not in items_dict:
|
||||
items_dict[funName] = None
|
||||
|
||||
# Inspect the type info of all implemented types
|
||||
# E.g. IShellDispatch5 implements IShellDispatch4 which implements IShellDispatch3 ...
|
||||
for iImplType in range(0,typeAttr.cImplTypes):
|
||||
iRefType = typeInfo.GetRefTypeOfImplType(iImplType)
|
||||
refTypeInfo = typeInfo.GetRefTypeInfo(iRefType)
|
||||
typeInfos.append(refTypeInfo)
|
||||
|
||||
# TODO: This is kinda slow. Probably need some kind of cache
|
||||
# here that is flushed upon file save
|
||||
# Or maybe we don't need the superclass methods at all ?
|
||||
def _UpdateWithClassMethods(self,dict,classinfo):
|
||||
if not hasattr(classinfo,"methods"):
|
||||
# No 'methods' - probably not what we think it is.
|
||||
return
|
||||
dict.update(classinfo.methods)
|
||||
for super in classinfo.super:
|
||||
if hasattr(super,"methods"):
|
||||
self._UpdateWithClassMethods(dict,super)
|
||||
|
||||
# Find which class definition caret is currently in and return
|
||||
# indexes of the the first and the last lines of that class definition
|
||||
# Data is obtained from module browser (if enabled)
|
||||
def _GetClassInfoFromBrowser(self,pos=-1):
|
||||
minline = 0
|
||||
maxline = self.GetLineCount()-1
|
||||
doc = self.GetParentFrame().GetActiveDocument()
|
||||
browser = None
|
||||
try:
|
||||
if doc is not None:
|
||||
browser = doc.GetAllViews()[1]
|
||||
except IndexError:
|
||||
pass
|
||||
if browser is None:
|
||||
return (minline,maxline,None) # Current window has no browser
|
||||
if not browser.list: return (minline,maxline,None) # Not initialized
|
||||
path = self.GetDocument().GetPathName()
|
||||
if not path: return (minline,maxline,None) # No current path
|
||||
|
||||
import pywin.framework.scriptutils
|
||||
curmodule, path = pywin.framework.scriptutils.GetPackageModuleName(path)
|
||||
try:
|
||||
clbrdata = browser.list.root.clbrdata
|
||||
except AttributeError:
|
||||
return (minline,maxline,None) # No class data for this module.
|
||||
curline = self.LineFromChar(pos)
|
||||
curclass = None
|
||||
# Find out which class we are in
|
||||
for item in clbrdata.values():
|
||||
if item.module==curmodule:
|
||||
item_lineno = item.lineno - 1 # Scintilla counts lines from 0, whereas pyclbr - from 1
|
||||
if minline < item_lineno <= curline:
|
||||
minline = item_lineno
|
||||
curclass = item
|
||||
if curline < item_lineno < maxline:
|
||||
maxline = item_lineno
|
||||
return (minline,maxline,curclass)
|
||||
|
||||
|
||||
def _GetObjectAtPos(self, pos = -1, bAllowCalls = 0):
|
||||
left, right = self._GetWordSplit(pos, bAllowCalls)
|
||||
if left: # It is an attribute lookup
|
||||
# How is this for a hack!
|
||||
namespace = sys.modules.copy()
|
||||
namespace.update(__main__.__dict__)
|
||||
# Get the debugger's context.
|
||||
try:
|
||||
from pywin.framework import interact
|
||||
if interact.edit is not None and interact.edit.currentView is not None:
|
||||
globs, locs = interact.edit.currentView.GetContext()[:2]
|
||||
if globs: namespace.update(globs)
|
||||
if locs: namespace.update(locs)
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
return eval(left, namespace)
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
def _GetWordSplit(self, pos = -1, bAllowCalls = 0):
|
||||
if pos==-1: pos = self.GetSel()[0]-1 # Character before current one
|
||||
limit = self.GetTextLength()
|
||||
before = []
|
||||
after = []
|
||||
index = pos-1
|
||||
wordbreaks_use = wordbreaks
|
||||
if bAllowCalls: wordbreaks_use = wordbreaks_use + "()[]"
|
||||
while index>=0:
|
||||
char = self.SCIGetCharAt(index)
|
||||
if char not in wordbreaks_use: break
|
||||
before.insert(0, char)
|
||||
index = index-1
|
||||
index = pos
|
||||
while index<=limit:
|
||||
char = self.SCIGetCharAt(index)
|
||||
if char not in wordbreaks_use: break
|
||||
after.append(char)
|
||||
index=index+1
|
||||
return ''.join(before), ''.join(after)
|
||||
|
||||
def OnPrepareDC (self, dc, pInfo):
|
||||
# print "OnPrepareDC for page", pInfo.GetCurPage(), "of", pInfo.GetFromPage(), "to", pInfo.GetToPage(), ", starts=", self.starts
|
||||
if dc.IsPrinting():
|
||||
# Check if we are beyond the end.
|
||||
# (only do this when actually printing, else messes up print preview!)
|
||||
if not pInfo.GetPreview() and self.starts is not None:
|
||||
prevPage = pInfo.GetCurPage() - 1
|
||||
if prevPage > 0 and self.starts[prevPage] >= self.GetTextLength():
|
||||
# All finished.
|
||||
pInfo.SetContinuePrinting(0)
|
||||
return
|
||||
dc.SetMapMode(win32con.MM_TEXT);
|
||||
|
||||
def OnPreparePrinting(self, pInfo):
|
||||
flags = win32ui.PD_USEDEVMODECOPIES | \
|
||||
win32ui.PD_ALLPAGES | \
|
||||
win32ui.PD_NOSELECTION # Dont support printing just a selection.
|
||||
# NOTE: Custom print dialogs are stopping the user's values from coming back :-(
|
||||
# self.prtDlg = PrintDialog(pInfo, PRINTDLGORD, flags)
|
||||
# pInfo.SetPrintDialog(self.prtDlg)
|
||||
pInfo.SetMinPage(1)
|
||||
# max page remains undefined for now.
|
||||
pInfo.SetFromPage(1)
|
||||
pInfo.SetToPage(1)
|
||||
ret = self.DoPreparePrinting(pInfo)
|
||||
return ret
|
||||
|
||||
def OnBeginPrinting(self, dc, pInfo):
|
||||
self.starts = None
|
||||
return self._obj_.OnBeginPrinting(dc, pInfo)
|
||||
|
||||
def CalculatePageRanges(self, dc, pInfo):
|
||||
# Calculate page ranges and max page
|
||||
self.starts = {0:0}
|
||||
metrics = dc.GetTextMetrics()
|
||||
left, top, right, bottom = pInfo.GetDraw()
|
||||
# Leave space at the top for the header.
|
||||
rc = (left, top + int((9*metrics['tmHeight'])/2), right, bottom)
|
||||
pageStart = 0
|
||||
maxPage = 0
|
||||
textLen = self.GetTextLength()
|
||||
while pageStart < textLen:
|
||||
pageStart = self.FormatRange(dc, pageStart, textLen, rc, 0)
|
||||
maxPage = maxPage + 1
|
||||
self.starts[maxPage] = pageStart
|
||||
# And a sentinal for one page past the end
|
||||
self.starts[maxPage+1] = textLen
|
||||
# When actually printing, maxPage doesnt have any effect at this late state.
|
||||
# but is needed to make the Print Preview work correctly.
|
||||
pInfo.SetMaxPage(maxPage)
|
||||
|
||||
def OnFilePrintPreview(self, *arg):
|
||||
self._obj_.OnFilePrintPreview()
|
||||
|
||||
def OnFilePrint(self, *arg):
|
||||
self._obj_.OnFilePrint()
|
||||
|
||||
def FormatRange(self, dc, pageStart, lengthDoc, rc, draw):
|
||||
"""
|
||||
typedef struct _formatrange {
|
||||
HDC hdc;
|
||||
HDC hdcTarget;
|
||||
RECT rc;
|
||||
RECT rcPage;
|
||||
CHARRANGE chrg;} FORMATRANGE;
|
||||
"""
|
||||
fmt='PPIIIIIIIIll'
|
||||
hdcRender = dc.GetHandleOutput()
|
||||
hdcFormat = dc.GetHandleAttrib()
|
||||
fr = struct.pack(fmt, hdcRender, hdcFormat, rc[0], rc[1], rc[2], rc[3], rc[0], rc[1], rc[2], rc[3], pageStart, lengthDoc)
|
||||
nextPageStart = self.SendScintilla(EM_FORMATRANGE, draw, fr)
|
||||
return nextPageStart
|
||||
|
||||
def OnPrint(self, dc, pInfo):
|
||||
metrics = dc.GetTextMetrics()
|
||||
# print "dev", w, h, l, metrics['tmAscent'], metrics['tmDescent']
|
||||
if self.starts is None:
|
||||
self.CalculatePageRanges(dc, pInfo)
|
||||
pageNum = pInfo.GetCurPage() - 1
|
||||
# Setup the header of the page - docname on left, pagenum on right.
|
||||
doc = self.GetDocument()
|
||||
cxChar = metrics['tmAveCharWidth']
|
||||
cyChar = metrics['tmHeight']
|
||||
left, top, right, bottom = pInfo.GetDraw()
|
||||
dc.TextOut(0, 2*cyChar, doc.GetTitle())
|
||||
pagenum_str = win32ui.LoadString(afxres.AFX_IDS_PRINTPAGENUM) % (pageNum+1,)
|
||||
dc.SetTextAlign(win32con.TA_RIGHT)
|
||||
dc.TextOut(right, 2*cyChar, pagenum_str)
|
||||
dc.SetTextAlign(win32con.TA_LEFT)
|
||||
top = top + int((7*cyChar)/2)
|
||||
dc.MoveTo(left, top)
|
||||
dc.LineTo(right, top)
|
||||
top = top + cyChar
|
||||
rc = (left, top, right, bottom)
|
||||
nextPageStart = self.FormatRange(dc, self.starts[pageNum], self.starts[pageNum+1], rc, 1)
|
||||
|
||||
def LoadConfiguration():
|
||||
global configManager
|
||||
# Bit of a hack I dont kow what to do about?
|
||||
from .config import ConfigManager
|
||||
configName = rc = win32ui.GetProfileVal("Editor", "Keyboard Config", "default")
|
||||
configManager = ConfigManager(configName)
|
||||
if configManager.last_error:
|
||||
bTryDefault = 0
|
||||
msg = "Error loading configuration '%s'\n\n%s" % (configName, configManager.last_error)
|
||||
if configName != "default":
|
||||
msg = msg + "\n\nThe default configuration will be loaded."
|
||||
bTryDefault = 1
|
||||
win32ui.MessageBox(msg)
|
||||
if bTryDefault:
|
||||
configManager = ConfigManager("default")
|
||||
if configManager.last_error:
|
||||
win32ui.MessageBox("Error loading configuration 'default'\n\n%s" % (configManager.last_error))
|
||||
|
||||
configManager = None
|
||||
LoadConfiguration()
|
Loading…
Add table
Add a link
Reference in a new issue