123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- # 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)
|