codekingpro/portable-devtools
114k
1# An Python interface to the Scintilla control.2#3# Exposes Python classes that allow you to use Scintilla as4# a "standard" MFC edit control (eg, control.GetTextLength(), control.GetSel()5# plus many Scintilla specific features (eg control.SCIAddStyledText())6 7import array8import os9import struct10 11import win32api12import win32con13import win32ui14from pywin import default_scintilla_encoding15from pywin.mfc import window16 17from . import scintillacon18 19# Load Scintilla.dll to get access to the control.20# We expect to find this in the same directory as win32ui.pyd21dllid = None22if win32ui.debug: # If running _d version of Pythonwin...23 try:24 dllid = win32api.LoadLibrary(25 os.path.join(os.path.split(win32ui.__file__)[0], "Scintilla_d.DLL")26 )27 except (28 win32api.error29 ): # Not there - we dont _need_ a debug ver, so ignore this error.30 pass31if dllid is None:32 try:33 dllid = win32api.LoadLibrary(34 os.path.join(os.path.split(win32ui.__file__)[0], "Scintilla.DLL")35 )36 except win32api.error:37 pass38if dllid is None:39 # Still not there - lets see if Windows can find it by searching?40 dllid = win32api.LoadLibrary("Scintilla.DLL")41 42# null_byte is str in py2k, bytes on py3k43null_byte = "\0".encode("ascii")44 45## These are from Richedit.h - need to add to win32con or commctrl46EM_GETTEXTRANGE = 109947EM_EXLINEFROMCHAR = 107848EM_FINDTEXTEX = 110349EM_GETSELTEXT = 108650EM_EXSETSEL = win32con.WM_USER + 5551 52 53class ScintillaNotification:54 def __init__(self, **args):55 self.__dict__.update(args)56 57 58class ScintillaControlInterface:59 def SCIUnpackNotifyMessage(self, msg):60 format = "iiiiPiiiPPiiii"61 bytes = win32ui.GetBytes(msg, struct.calcsize(format))62 (63 position,64 ch,65 modifiers,66 modificationType,67 text_ptr,68 length,69 linesAdded,70 msg,71 wParam,72 lParam,73 line,74 foldLevelNow,75 foldLevelPrev,76 margin,77 ) = struct.unpack(format, bytes)78 return ScintillaNotification(79 position=position,80 ch=ch,81 modifiers=modifiers,82 modificationType=modificationType,83 text_ptr=text_ptr,84 length=length,85 linesAdded=linesAdded,86 msg=msg,87 wParam=wParam,88 lParam=lParam,89 line=line,90 foldLevelNow=foldLevelNow,91 foldLevelPrev=foldLevelPrev,92 margin=margin,93 )94 95 def SCIAddText(self, text):96 self.SendMessage(97 scintillacon.SCI_ADDTEXT, text.encode(default_scintilla_encoding)98 )99 100 def SCIAddStyledText(self, text, style=None):101 # If style is None, text is assumed to be a "native" Scintilla buffer.102 # If style is specified, text is a normal string, and the style is103 # assumed to apply to the entire string.104 if style is not None:105 text = list(map(lambda char, style=style: char + chr(style), text))106 text = "".join(text)107 self.SendMessage(108 scintillacon.SCI_ADDSTYLEDTEXT, text.encode(default_scintilla_encoding)109 )110 111 def SCIInsertText(self, text, pos=-1):112 # SCIInsertText allows unicode or bytes - but if they are bytes,113 # the caller must ensure it is encoded correctly.114 if isinstance(text, str):115 text = text.encode(default_scintilla_encoding)116 self.SendScintilla(scintillacon.SCI_INSERTTEXT, pos, text + null_byte)117 118 def SCISetSavePoint(self):119 self.SendScintilla(scintillacon.SCI_SETSAVEPOINT)120 121 def SCISetUndoCollection(self, collectFlag):122 self.SendScintilla(scintillacon.SCI_SETUNDOCOLLECTION, collectFlag)123 124 def SCIBeginUndoAction(self):125 self.SendScintilla(scintillacon.SCI_BEGINUNDOACTION)126 127 def SCIEndUndoAction(self):128 self.SendScintilla(scintillacon.SCI_ENDUNDOACTION)129 130 def SCIGetCurrentPos(self):131 return self.SendScintilla(scintillacon.SCI_GETCURRENTPOS)132 133 def SCIGetCharAt(self, pos):134 # Must ensure char is unsigned!135 return chr(self.SendScintilla(scintillacon.SCI_GETCHARAT, pos) & 0xFF)136 137 def SCIGotoLine(self, line):138 self.SendScintilla(scintillacon.SCI_GOTOLINE, line)139 140 def SCIBraceMatch(self, pos, maxReStyle):141 return self.SendScintilla(scintillacon.SCI_BRACEMATCH, pos, maxReStyle)142 143 def SCIBraceHighlight(self, pos, posOpposite):144 return self.SendScintilla(scintillacon.SCI_BRACEHIGHLIGHT, pos, posOpposite)145 146 def SCIBraceBadHighlight(self, pos):147 return self.SendScintilla(scintillacon.SCI_BRACEBADLIGHT, pos)148 149 ####################################150 # Styling151 # def SCIColourise(self, start=0, end=-1):152 # NOTE - dependent on of we use builtin lexer, so handled below.153 def SCIGetEndStyled(self):154 return self.SendScintilla(scintillacon.SCI_GETENDSTYLED)155 156 def SCIStyleSetFore(self, num, v):157 return self.SendScintilla(scintillacon.SCI_STYLESETFORE, num, v)158 159 def SCIStyleSetBack(self, num, v):160 return self.SendScintilla(scintillacon.SCI_STYLESETBACK, num, v)161 162 def SCIStyleSetEOLFilled(self, num, v):163 return self.SendScintilla(scintillacon.SCI_STYLESETEOLFILLED, num, v)164 165 def SCIStyleSetFont(self, num, name, characterset=0):166 buff = (name + "\0").encode(default_scintilla_encoding)167 self.SendScintilla(scintillacon.SCI_STYLESETFONT, num, buff)168 self.SendScintilla(scintillacon.SCI_STYLESETCHARACTERSET, num, characterset)169 170 def SCIStyleSetBold(self, num, bBold):171 self.SendScintilla(scintillacon.SCI_STYLESETBOLD, num, bBold)172 173 def SCIStyleSetItalic(self, num, bItalic):174 self.SendScintilla(scintillacon.SCI_STYLESETITALIC, num, bItalic)175 176 def SCIStyleSetSize(self, num, size):177 self.SendScintilla(scintillacon.SCI_STYLESETSIZE, num, size)178 179 def SCIGetViewWS(self):180 return self.SendScintilla(scintillacon.SCI_GETVIEWWS)181 182 def SCISetViewWS(self, val):183 self.SendScintilla(scintillacon.SCI_SETVIEWWS, not (val == 0))184 self.InvalidateRect()185 186 def SCISetIndentationGuides(self, val):187 self.SendScintilla(scintillacon.SCI_SETINDENTATIONGUIDES, val)188 189 def SCIGetIndentationGuides(self):190 return self.SendScintilla(scintillacon.SCI_GETINDENTATIONGUIDES)191 192 def SCISetIndent(self, val):193 self.SendScintilla(scintillacon.SCI_SETINDENT, val)194 195 def SCIGetIndent(self, val):196 return self.SendScintilla(scintillacon.SCI_GETINDENT)197 198 def SCIGetViewEOL(self):199 return self.SendScintilla(scintillacon.SCI_GETVIEWEOL)200 201 def SCISetViewEOL(self, val):202 self.SendScintilla(scintillacon.SCI_SETVIEWEOL, not (val == 0))203 self.InvalidateRect()204 205 def SCISetTabWidth(self, width):206 self.SendScintilla(scintillacon.SCI_SETTABWIDTH, width, 0)207 208 def SCIStartStyling(self, pos, mask):209 self.SendScintilla(scintillacon.SCI_STARTSTYLING, pos, mask)210 211 def SCISetStyling(self, pos, attr):212 self.SendScintilla(scintillacon.SCI_SETSTYLING, pos, attr)213 214 def SCISetStylingEx(self, ray): # ray is an array.215 address, length = ray.buffer_info()216 self.SendScintilla(scintillacon.SCI_SETSTYLINGEX, length, address)217 218 def SCIGetStyleAt(self, pos):219 return self.SendScintilla(scintillacon.SCI_GETSTYLEAT, pos)220 221 def SCISetMarginWidth(self, width):222 self.SendScintilla(scintillacon.SCI_SETMARGINWIDTHN, 1, width)223 224 def SCISetMarginWidthN(self, n, width):225 self.SendScintilla(scintillacon.SCI_SETMARGINWIDTHN, n, width)226 227 def SCISetFoldFlags(self, flags):228 self.SendScintilla(scintillacon.SCI_SETFOLDFLAGS, flags)229 230 # Markers231 def SCIMarkerDefineAll(self, markerNum, markerType, fore, back):232 self.SCIMarkerDefine(markerNum, markerType)233 self.SCIMarkerSetFore(markerNum, fore)234 self.SCIMarkerSetBack(markerNum, back)235 236 def SCIMarkerDefine(self, markerNum, markerType):237 self.SendScintilla(scintillacon.SCI_MARKERDEFINE, markerNum, markerType)238 239 def SCIMarkerSetFore(self, markerNum, fore):240 self.SendScintilla(scintillacon.SCI_MARKERSETFORE, markerNum, fore)241 242 def SCIMarkerSetBack(self, markerNum, back):243 self.SendScintilla(scintillacon.SCI_MARKERSETBACK, markerNum, back)244 245 def SCIMarkerAdd(self, lineNo, markerNum):246 self.SendScintilla(scintillacon.SCI_MARKERADD, lineNo, markerNum)247 248 def SCIMarkerDelete(self, lineNo, markerNum):249 self.SendScintilla(scintillacon.SCI_MARKERDELETE, lineNo, markerNum)250 251 def SCIMarkerDeleteAll(self, markerNum=-1):252 self.SendScintilla(scintillacon.SCI_MARKERDELETEALL, markerNum)253 254 def SCIMarkerGet(self, lineNo):255 return self.SendScintilla(scintillacon.SCI_MARKERGET, lineNo)256 257 def SCIMarkerNext(self, lineNo, markerNum):258 return self.SendScintilla(scintillacon.SCI_MARKERNEXT, lineNo, markerNum)259 260 def SCICancel(self):261 self.SendScintilla(scintillacon.SCI_CANCEL)262 263 # AutoComplete264 def SCIAutoCShow(self, text):265 if type(text) in [type([]), type(())]:266 text = " ".join(text)267 buff = (text + "\0").encode(default_scintilla_encoding)268 return self.SendScintilla(scintillacon.SCI_AUTOCSHOW, 0, buff)269 270 def SCIAutoCCancel(self):271 self.SendScintilla(scintillacon.SCI_AUTOCCANCEL)272 273 def SCIAutoCActive(self):274 return self.SendScintilla(scintillacon.SCI_AUTOCACTIVE)275 276 def SCIAutoCComplete(self):277 return self.SendScintilla(scintillacon.SCI_AUTOCCOMPLETE)278 279 def SCIAutoCStops(self, stops):280 buff = (stops + "\0").encode(default_scintilla_encoding)281 self.SendScintilla(scintillacon.SCI_AUTOCSTOPS, 0, buff)282 283 def SCIAutoCSetAutoHide(self, hide):284 self.SendScintilla(scintillacon.SCI_AUTOCSETAUTOHIDE, hide)285 286 def SCIAutoCSetFillups(self, fillups):287 self.SendScintilla(scintillacon.SCI_AUTOCSETFILLUPS, fillups)288 289 # Call tips290 def SCICallTipShow(self, text, pos=-1):291 if pos == -1:292 pos = self.GetSel()[0]293 buff = (text + "\0").encode(default_scintilla_encoding)294 self.SendScintilla(scintillacon.SCI_CALLTIPSHOW, pos, buff)295 296 def SCICallTipCancel(self):297 self.SendScintilla(scintillacon.SCI_CALLTIPCANCEL)298 299 def SCICallTipActive(self):300 return self.SendScintilla(scintillacon.SCI_CALLTIPACTIVE)301 302 def SCICallTipPosStart(self):303 return self.SendScintilla(scintillacon.SCI_CALLTIPPOSSTART)304 305 def SCINewline(self):306 self.SendScintilla(scintillacon.SCI_NEWLINE)307 308 # Lexer etc309 def SCISetKeywords(self, keywords, kw_list_no=0):310 buff = (keywords + "\0").encode(default_scintilla_encoding)311 self.SendScintilla(scintillacon.SCI_SETKEYWORDS, kw_list_no, buff)312 313 def SCISetProperty(self, name, value):314 name_buff = array.array("b", (name + "\0").encode(default_scintilla_encoding))315 val_buff = array.array(316 "b", (str(value) + "\0").encode(default_scintilla_encoding)317 )318 address_name_buffer = name_buff.buffer_info()[0]319 address_val_buffer = val_buff.buffer_info()[0]320 self.SendScintilla(321 scintillacon.SCI_SETPROPERTY, address_name_buffer, address_val_buffer322 )323 324 def SCISetStyleBits(self, nbits):325 self.SendScintilla(scintillacon.SCI_SETSTYLEBITS, nbits)326 327 # Folding328 def SCIGetFoldLevel(self, lineno):329 return self.SendScintilla(scintillacon.SCI_GETFOLDLEVEL, lineno)330 331 def SCIToggleFold(self, lineno):332 return self.SendScintilla(scintillacon.SCI_TOGGLEFOLD, lineno)333 334 def SCIEnsureVisible(self, lineno):335 self.SendScintilla(scintillacon.SCI_ENSUREVISIBLE, lineno)336 337 def SCIGetFoldExpanded(self, lineno):338 return self.SendScintilla(scintillacon.SCI_GETFOLDEXPANDED, lineno)339 340 # right edge341 def SCISetEdgeColumn(self, edge):342 self.SendScintilla(scintillacon.SCI_SETEDGECOLUMN, edge)343 344 def SCIGetEdgeColumn(self):345 return self.SendScintilla(scintillacon.SCI_GETEDGECOLUMN)346 347 def SCISetEdgeMode(self, mode):348 self.SendScintilla(scintillacon.SCI_SETEDGEMODE, mode)349 350 def SCIGetEdgeMode(self):351 return self.SendScintilla(scintillacon.SCI_GETEDGEMODE)352 353 def SCISetEdgeColor(self, color):354 self.SendScintilla(scintillacon.SCI_SETEDGECOLOUR, color)355 356 def SCIGetEdgeColor(self):357 return self.SendScintilla(scintillacon.SCI_GETEDGECOLOR)358 359 # Multi-doc360 def SCIGetDocPointer(self):361 return self.SendScintilla(scintillacon.SCI_GETDOCPOINTER)362 363 def SCISetDocPointer(self, p):364 return self.SendScintilla(scintillacon.SCI_SETDOCPOINTER, 0, p)365 366 def SCISetWrapMode(self, mode):367 return self.SendScintilla(scintillacon.SCI_SETWRAPMODE, mode)368 369 def SCIGetWrapMode(self):370 return self.SendScintilla(scintillacon.SCI_GETWRAPMODE)371 372 373class CScintillaEditInterface(ScintillaControlInterface):374 def close(self):375 self.colorizer = None376 377 def Clear(self):378 self.SendScintilla(win32con.WM_CLEAR)379 380 def FindText(self, flags, range, findText):381 """LPARAM for EM_FINDTEXTEX:382 typedef struct _findtextex {383 CHARRANGE chrg;384 LPCTSTR lpstrText;385 CHARRANGE chrgText;} FINDTEXTEX;386 typedef struct _charrange {387 LONG cpMin;388 LONG cpMax;} CHARRANGE;389 """390 findtextex_fmt = "llPll"391 ## Scintilla does not handle unicode in EM_FINDTEXT msg (FINDTEXTEX struct)392 txt_buff = (findText + "\0").encode(default_scintilla_encoding)393 txt_array = array.array("b", txt_buff)394 ft_buff = struct.pack(395 findtextex_fmt, range[0], range[1], txt_array.buffer_info()[0], 0, 0396 )397 ft_array = array.array("b", ft_buff)398 rc = self.SendScintilla(EM_FINDTEXTEX, flags, ft_array.buffer_info()[0])399 ftUnpacked = struct.unpack(findtextex_fmt, ft_array)400 return rc, (ftUnpacked[3], ftUnpacked[4])401 402 def GetSel(self):403 currentPos = self.SendScintilla(scintillacon.SCI_GETCURRENTPOS)404 anchorPos = self.SendScintilla(scintillacon.SCI_GETANCHOR)405 if currentPos < anchorPos:406 return (currentPos, anchorPos)407 else:408 return (anchorPos, currentPos)409 return currentPos410 411 def GetSelText(self):412 start, end = self.GetSel()413 txtBuf = array.array("b", null_byte * (end - start + 1))414 addressTxtBuf = txtBuf.buffer_info()[0]415 # EM_GETSELTEXT is documented as returning the number of chars416 # not including the NULL, but scintilla includes the NULL. A417 # quick glance at the scintilla impl doesn't make this418 # obvious - the NULL is included in the 'selection' object419 # and reflected in the length of that 'selection' object.420 # I expect that is a bug in scintilla and may be fixed by now,421 # but we just blindly assume that the last char is \0 and422 # strip it.423 self.SendScintilla(EM_GETSELTEXT, 0, addressTxtBuf)424 return txtBuf.tobytes()[:-1].decode(default_scintilla_encoding)425 426 def SetSel(self, start=0, end=None):427 if type(start) == type(()):428 assert (429 end is None430 ), "If you pass a point in the first param, the second must be None"431 start, end = start432 elif end is None:433 end = start434 if start < 0:435 start = self.GetTextLength()436 if end < 0:437 end = self.GetTextLength()438 assert start <= self.GetTextLength(), "The start postion is invalid (%d/%d)" % (439 start,440 self.GetTextLength(),441 )442 assert end <= self.GetTextLength(), "The end postion is invalid (%d/%d)" % (443 end,444 self.GetTextLength(),445 )446 cr = struct.pack("ll", start, end)447 crBuff = array.array("b", cr)448 addressCrBuff = crBuff.buffer_info()[0]449 rc = self.SendScintilla(EM_EXSETSEL, 0, addressCrBuff)450 451 def GetLineCount(self):452 return self.SendScintilla(win32con.EM_GETLINECOUNT)453 454 def LineFromChar(self, charPos=-1):455 if charPos == -1:456 charPos = self.GetSel()[0]457 assert (458 charPos >= 0 and charPos <= self.GetTextLength()459 ), "The charPos postion (%s) is invalid (max=%s)" % (460 charPos,461 self.GetTextLength(),462 )463 # return self.SendScintilla(EM_EXLINEFROMCHAR, charPos)464 # EM_EXLINEFROMCHAR puts charPos in lParam, not wParam465 return self.SendScintilla(EM_EXLINEFROMCHAR, 0, charPos)466 467 def LineIndex(self, line):468 return self.SendScintilla(win32con.EM_LINEINDEX, line)469 470 def ScrollCaret(self):471 return self.SendScintilla(win32con.EM_SCROLLCARET)472 473 def GetCurLineNumber(self):474 return self.LineFromChar(self.SCIGetCurrentPos())475 476 def GetTextLength(self):477 return self.SendScintilla(scintillacon.SCI_GETTEXTLENGTH)478 479 def GetTextRange(self, start=0, end=-1, decode=True):480 if end == -1:481 end = self.SendScintilla(scintillacon.SCI_GETTEXTLENGTH)482 assert end >= start, "Negative index requested (%d/%d)" % (start, end)483 assert (484 start >= 0 and start <= self.GetTextLength()485 ), "The start postion is invalid"486 assert end >= 0 and end <= self.GetTextLength(), "The end postion is invalid"487 initer = null_byte * (end - start + 1)488 buff = array.array("b", initer)489 addressBuffer = buff.buffer_info()[0]490 tr = struct.pack("llP", start, end, addressBuffer)491 trBuff = array.array("b", tr)492 addressTrBuff = trBuff.buffer_info()[0]493 num_bytes = self.SendScintilla(EM_GETTEXTRANGE, 0, addressTrBuff)494 ret = buff.tobytes()[:num_bytes]495 if decode:496 ret = ret.decode(default_scintilla_encoding)497 return ret498 499 def ReplaceSel(self, str):500 buff = (str + "\0").encode(default_scintilla_encoding)501 self.SendScintilla(scintillacon.SCI_REPLACESEL, 0, buff)502 503 def GetLine(self, line=-1):504 if line == -1:505 line = self.GetCurLineNumber()506 start = self.LineIndex(line)507 end = self.LineIndex(line + 1)508 return self.GetTextRange(start, end)509 510 def SetReadOnly(self, flag=1):511 return self.SendScintilla(win32con.EM_SETREADONLY, flag)512 513 def LineScroll(self, lines, cols=0):514 return self.SendScintilla(win32con.EM_LINESCROLL, cols, lines)515 516 def GetFirstVisibleLine(self):517 return self.SendScintilla(win32con.EM_GETFIRSTVISIBLELINE)518 519 def SetWordWrap(self, mode):520 if mode != win32ui.CRichEditView_WrapNone:521 raise ValueError("We dont support word-wrap (I dont think :-)")522 523 524class CScintillaColorEditInterface(CScintillaEditInterface):525 ################################526 # Plug-in colorizer support527 def _GetColorizer(self):528 if not hasattr(self, "colorizer"):529 self.colorizer = self._MakeColorizer()530 return self.colorizer531 532 def _MakeColorizer(self):533 # Give parent a chance to hook.534 parent_func = getattr(self.GetParentFrame(), "_MakeColorizer", None)535 if parent_func is not None:536 return parent_func()537 from . import formatter538 539 ## return formatter.PythonSourceFormatter(self)540 return formatter.BuiltinPythonSourceFormatter(self)541 542 def Colorize(self, start=0, end=-1):543 c = self._GetColorizer()544 if c is not None:545 c.Colorize(start, end)546 547 def ApplyFormattingStyles(self, bReload=1):548 c = self._GetColorizer()549 if c is not None:550 c.ApplyFormattingStyles(bReload)551 552 # The Parent window will normally hook553 def HookFormatter(self, parent=None):554 c = self._GetColorizer()555 if c is not None: # No need if we have no color!556 c.HookFormatter(parent)557 558 559class CScintillaEdit(window.Wnd, CScintillaColorEditInterface):560 def __init__(self, wnd=None):561 if wnd is None:562 wnd = win32ui.CreateWnd()563 window.Wnd.__init__(self, wnd)564 565 def SendScintilla(self, msg, w=0, l=0):566 return self.SendMessage(msg, w, l)567 568 def CreateWindow(self, style, rect, parent, id):569 self._obj_.CreateWindow("Scintilla", "Scintilla", style, rect, parent, id, None)570 