2013-08-28 00:44:27 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
import ctypes, os, sys
|
|
|
|
|
2019-05-04 18:14:48 +00:00
|
|
|
from ctypes import c_int, c_ulong, c_char_p, c_wchar_p, c_ushort, c_uint, c_long, c_ssize_t
|
2013-08-28 00:44:27 +00:00
|
|
|
|
2019-07-21 13:26:02 +00:00
|
|
|
def IsEnumeration(t):
|
|
|
|
return t[:1].isupper()
|
|
|
|
|
2022-01-04 23:07:50 +00:00
|
|
|
basicTypes = ["bool", "int", "position", "line", "pointer", "colour", "colouralpha"]
|
|
|
|
|
|
|
|
def BasicTypeOrEnumeration(t):
|
|
|
|
return t in basicTypes or IsEnumeration(t)
|
|
|
|
|
2013-08-28 00:44:27 +00:00
|
|
|
class TEXTRANGE(ctypes.Structure):
|
|
|
|
_fields_= (\
|
|
|
|
('cpMin', c_long),
|
|
|
|
('cpMax', c_long),
|
|
|
|
('lpstrText', ctypes.POINTER(ctypes.c_char)),
|
|
|
|
)
|
|
|
|
|
2022-05-25 20:16:39 +00:00
|
|
|
class TEXTRANGEFULL(ctypes.Structure):
|
|
|
|
_fields_= (\
|
|
|
|
('cpMin', c_ssize_t),
|
|
|
|
('cpMax', c_ssize_t),
|
|
|
|
('lpstrText', ctypes.POINTER(ctypes.c_char)),
|
|
|
|
)
|
|
|
|
|
2013-08-28 00:44:27 +00:00
|
|
|
class FINDTEXT(ctypes.Structure):
|
|
|
|
_fields_= (\
|
|
|
|
('cpMin', c_long),
|
|
|
|
('cpMax', c_long),
|
|
|
|
('lpstrText', c_char_p),
|
|
|
|
('cpMinText', c_long),
|
|
|
|
('cpMaxText', c_long),
|
|
|
|
)
|
|
|
|
|
2022-05-25 20:16:39 +00:00
|
|
|
class FINDTEXTFULL(ctypes.Structure):
|
|
|
|
_fields_= (\
|
|
|
|
('cpMin', c_ssize_t),
|
|
|
|
('cpMax', c_ssize_t),
|
|
|
|
('lpstrText', c_char_p),
|
|
|
|
('cpMinText', c_ssize_t),
|
|
|
|
('cpMaxText', c_ssize_t),
|
|
|
|
)
|
|
|
|
|
2013-08-28 00:44:27 +00:00
|
|
|
class SciCall:
|
|
|
|
def __init__(self, fn, ptr, msg, stringResult=False):
|
|
|
|
self._fn = fn
|
|
|
|
self._ptr = ptr
|
|
|
|
self._msg = msg
|
|
|
|
self._stringResult = stringResult
|
|
|
|
def __call__(self, w=0, l=0):
|
|
|
|
ww = ctypes.cast(w, c_char_p)
|
|
|
|
if self._stringResult:
|
|
|
|
lengthBytes = self._fn(self._ptr, self._msg, ww, None)
|
|
|
|
if lengthBytes == 0:
|
|
|
|
return bytearray()
|
|
|
|
result = (ctypes.c_byte * lengthBytes)(0)
|
|
|
|
lengthBytes2 = self._fn(self._ptr, self._msg, ww, ctypes.cast(result, c_char_p))
|
|
|
|
assert lengthBytes == lengthBytes2
|
|
|
|
return bytearray(result)[:lengthBytes]
|
|
|
|
else:
|
|
|
|
ll = ctypes.cast(l, c_char_p)
|
|
|
|
return self._fn(self._ptr, self._msg, ww, ll)
|
|
|
|
|
2019-05-04 18:14:48 +00:00
|
|
|
sciFX = ctypes.CFUNCTYPE(c_ssize_t, c_char_p, c_int, c_char_p, c_char_p)
|
2013-08-28 00:44:27 +00:00
|
|
|
|
|
|
|
class ScintillaCallable:
|
|
|
|
def __init__(self, face, scifn, sciptr):
|
|
|
|
self.__dict__["face"] = face
|
|
|
|
self.__dict__["used"] = set()
|
|
|
|
self.__dict__["all"] = set()
|
|
|
|
# The k member is for accessing constants as a dictionary
|
|
|
|
self.__dict__["k"] = {}
|
|
|
|
for f in face.features:
|
|
|
|
self.all.add(f)
|
|
|
|
if face.features[f]["FeatureType"] == "val":
|
|
|
|
self.k[f] = int(self.face.features[f]["Value"], 0)
|
|
|
|
elif face.features[f]["FeatureType"] == "evt":
|
|
|
|
self.k["SCN_"+f] = int(self.face.features[f]["Value"], 0)
|
|
|
|
scifn = sciFX(scifn)
|
|
|
|
self.__dict__["_scifn"] = scifn
|
|
|
|
self.__dict__["_sciptr"] = sciptr
|
|
|
|
def __getattr__(self, name):
|
|
|
|
if name in self.face.features:
|
|
|
|
self.used.add(name)
|
|
|
|
feature = self.face.features[name]
|
|
|
|
value = int(feature["Value"], 0)
|
|
|
|
#~ print("Feature", name, feature)
|
|
|
|
if feature["FeatureType"] == "val":
|
|
|
|
self.__dict__[name] = value
|
|
|
|
return value
|
|
|
|
else:
|
|
|
|
if feature["Param2Type"] == "stringresult" and \
|
|
|
|
name not in ["GetText", "GetLine", "GetCurLine"]:
|
|
|
|
return SciCall(self._scifn, self._sciptr, value, True)
|
|
|
|
else:
|
|
|
|
return SciCall(self._scifn, self._sciptr, value)
|
|
|
|
elif ("Get" + name) in self.face.features:
|
|
|
|
self.used.add("Get" + name)
|
|
|
|
feature = self.face.features["Get" + name]
|
|
|
|
value = int(feature["Value"], 0)
|
|
|
|
if feature["FeatureType"] == "get" and \
|
|
|
|
not name.startswith("Get") and \
|
|
|
|
not feature["Param1Type"] and \
|
|
|
|
not feature["Param2Type"] and \
|
2022-01-04 23:07:50 +00:00
|
|
|
BasicTypeOrEnumeration(feature["ReturnType"]):
|
2013-08-28 00:44:27 +00:00
|
|
|
#~ print("property", feature)
|
|
|
|
return self._scifn(self._sciptr, value, None, None)
|
|
|
|
elif name.startswith("SCN_") and name in self.k:
|
|
|
|
self.used.add(name)
|
|
|
|
feature = self.face.features[name[4:]]
|
|
|
|
value = int(feature["Value"], 0)
|
|
|
|
#~ print("Feature", name, feature)
|
|
|
|
if feature["FeatureType"] == "val":
|
|
|
|
return value
|
|
|
|
raise AttributeError(name)
|
|
|
|
def __setattr__(self, name, val):
|
|
|
|
if ("Set" + name) in self.face.features:
|
|
|
|
self.used.add("Set" + name)
|
|
|
|
feature = self.face.features["Set" + name]
|
|
|
|
value = int(feature["Value"], 0)
|
|
|
|
#~ print("setproperty", feature)
|
|
|
|
if feature["FeatureType"] == "set" and not name.startswith("Set"):
|
2022-01-04 23:07:50 +00:00
|
|
|
if BasicTypeOrEnumeration(feature["Param1Type"]):
|
2013-08-28 00:44:27 +00:00
|
|
|
return self._scifn(self._sciptr, value, c_char_p(val), None)
|
|
|
|
elif feature["Param2Type"] in ["string"]:
|
|
|
|
return self._scifn(self._sciptr, value, None, c_char_p(val))
|
|
|
|
raise AttributeError(name)
|
|
|
|
raise AttributeError(name)
|
|
|
|
def getvalue(self, name):
|
|
|
|
if name in self.face.features:
|
|
|
|
feature = self.face.features[name]
|
|
|
|
if feature["FeatureType"] != "evt":
|
|
|
|
try:
|
|
|
|
return int(feature["Value"], 0)
|
|
|
|
except ValueError:
|
|
|
|
return -1
|
|
|
|
return -1
|
|
|
|
|
|
|
|
|
|
|
|
def ByteRange(self, start, end):
|
|
|
|
tr = TEXTRANGE()
|
|
|
|
tr.cpMin = start
|
|
|
|
tr.cpMax = end
|
|
|
|
length = end - start
|
|
|
|
tr.lpstrText = ctypes.create_string_buffer(length + 1)
|
|
|
|
self.GetTextRange(0, ctypes.byref(tr))
|
|
|
|
text = tr.lpstrText[:length]
|
|
|
|
text += b"\0" * (length - len(text))
|
|
|
|
return text
|
2022-05-25 20:16:39 +00:00
|
|
|
def ByteRangeFull(self, start, end):
|
|
|
|
tr = TEXTRANGEFULL()
|
|
|
|
tr.cpMin = start
|
|
|
|
tr.cpMax = end
|
|
|
|
length = end - start
|
|
|
|
tr.lpstrText = ctypes.create_string_buffer(length + 1)
|
|
|
|
self.GetTextRangeFull(0, ctypes.byref(tr))
|
|
|
|
text = tr.lpstrText[:length]
|
|
|
|
text += b"\0" * (length - len(text))
|
|
|
|
return text
|
2013-08-28 00:44:27 +00:00
|
|
|
def StyledTextRange(self, start, end):
|
|
|
|
tr = TEXTRANGE()
|
|
|
|
tr.cpMin = start
|
|
|
|
tr.cpMax = end
|
|
|
|
length = 2 * (end - start)
|
|
|
|
tr.lpstrText = ctypes.create_string_buffer(length + 2)
|
|
|
|
self.GetStyledText(0, ctypes.byref(tr))
|
|
|
|
styledText = tr.lpstrText[:length]
|
|
|
|
styledText += b"\0" * (length - len(styledText))
|
|
|
|
return styledText
|
|
|
|
def FindBytes(self, start, end, s, flags):
|
|
|
|
ft = FINDTEXT()
|
|
|
|
ft.cpMin = start
|
|
|
|
ft.cpMax = end
|
|
|
|
ft.lpstrText = s
|
|
|
|
ft.cpMinText = 0
|
|
|
|
ft.cpMaxText = 0
|
|
|
|
pos = self.FindText(flags, ctypes.byref(ft))
|
|
|
|
#~ print(start, end, ft.cpMinText, ft.cpMaxText)
|
|
|
|
return pos
|
2022-05-25 20:16:39 +00:00
|
|
|
def FindBytesFull(self, start, end, s, flags):
|
|
|
|
ft = FINDTEXTFULL()
|
|
|
|
ft.cpMin = start
|
|
|
|
ft.cpMax = end
|
|
|
|
ft.lpstrText = s
|
|
|
|
ft.cpMinText = 0
|
|
|
|
ft.cpMaxText = 0
|
|
|
|
pos = self.FindTextFull(flags, ctypes.byref(ft))
|
|
|
|
#~ print(start, end, ft.cpMinText, ft.cpMaxText)
|
|
|
|
return pos
|
2013-08-28 00:44:27 +00:00
|
|
|
|
|
|
|
def Contents(self):
|
|
|
|
return self.ByteRange(0, self.Length)
|
2019-05-04 18:14:48 +00:00
|
|
|
|
2013-08-28 00:44:27 +00:00
|
|
|
def SetContents(self, s):
|
|
|
|
self.TargetStart = 0
|
|
|
|
self.TargetEnd = self.Length
|
|
|
|
self.ReplaceTarget(len(s), s)
|
|
|
|
|