|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- # 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())
-
- import array
- import os
- import struct
-
- import win32api
- import win32con
- import win32ui
- from pywin import default_scintilla_encoding
- from pywin.mfc import window
-
- 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 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)
|