mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-03 14:49:07 +00:00
first commit
This commit is contained in:
@ -0,0 +1,596 @@
|
||||
# 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.
|
179
.venv/Lib/site-packages/pythonwin/pywin/scintilla/bindings.py
Normal file
179
.venv/Lib/site-packages/pythonwin/pywin/scintilla/bindings.py
Normal file
@ -0,0 +1,179 @@
|
||||
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)
|
361
.venv/Lib/site-packages/pythonwin/pywin/scintilla/config.py
Normal file
361
.venv/Lib/site-packages/pythonwin/pywin/scintilla/config.py
Normal file
@ -0,0 +1,361 @@
|
||||
# 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 importlib.util
|
||||
|
||||
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 == importlib.util.MAGIC_NUMBER
|
||||
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(importlib.util.MAGIC_NUMBER, 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 + "".join(lines), # produces correct tracebacks
|
||||
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()
|
293
.venv/Lib/site-packages/pythonwin/pywin/scintilla/configui.py
Normal file
293
.venv/Lib/site-packages/pythonwin/pywin/scintilla/configui.py
Normal file
@ -0,0 +1,293 @@
|
||||
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()
|
569
.venv/Lib/site-packages/pythonwin/pywin/scintilla/control.py
Normal file
569
.venv/Lib/site-packages/pythonwin/pywin/scintilla/control.py
Normal file
@ -0,0 +1,569 @@
|
||||
# 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.tobytes()[:-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.tobytes()[: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)
|
311
.venv/Lib/site-packages/pythonwin/pywin/scintilla/document.py
Normal file
311
.venv/Lib/site-packages/pythonwin/pywin/scintilla/document.py
Normal file
@ -0,0 +1,311 @@
|
||||
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, try utf-8 - if that fails we will
|
||||
# fallback to latin-1 to treat it as bytes...
|
||||
if source_encoding is None:
|
||||
source_encoding = "utf-8"
|
||||
# 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 = "utf-8"
|
||||
|
||||
## 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))
|
500
.venv/Lib/site-packages/pythonwin/pywin/scintilla/find.py
Normal file
500
.venv/Lib/site-packages/pythonwin/pywin/scintilla/find.py
Normal file
@ -0,0 +1,500 @@
|
||||
# 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()
|
692
.venv/Lib/site-packages/pythonwin/pywin/scintilla/formatter.py
Normal file
692
.venv/Lib/site-packages/pythonwin/pywin/scintilla/formatter.py
Normal file
@ -0,0 +1,692 @@
|
||||
# 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
|
191
.venv/Lib/site-packages/pythonwin/pywin/scintilla/keycodes.py
Normal file
191
.venv/Lib/site-packages/pythonwin/pywin/scintilla/keycodes.py
Normal file
@ -0,0 +1,191 @@
|
||||
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()
|
2000
.venv/Lib/site-packages/pythonwin/pywin/scintilla/scintillacon.py
Normal file
2000
.venv/Lib/site-packages/pythonwin/pywin/scintilla/scintillacon.py
Normal file
File diff suppressed because it is too large
Load Diff
839
.venv/Lib/site-packages/pythonwin/pywin/scintilla/view.py
Normal file
839
.venv/Lib/site-packages/pythonwin/pywin/scintilla/view.py
Normal file
@ -0,0 +1,839 @@
|
||||
# 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()
|
Reference in New Issue
Block a user