view.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. # A general purpose MFC CCtrlView view that uses Scintilla.
  2. from . import control
  3. from . import IDLEenvironment # IDLE emulation.
  4. from pywin.mfc import docview
  5. from pywin.mfc import dialog
  6. from . import scintillacon
  7. import win32con
  8. import win32ui
  9. import afxres
  10. import string
  11. import array
  12. import sys
  13. import types
  14. import __main__ # for attribute lookup
  15. from . import bindings
  16. from . import keycodes
  17. import struct
  18. import re
  19. import os
  20. PRINTDLGORD = 1538
  21. IDC_PRINT_MAG_EDIT = 1010
  22. EM_FORMATRANGE = win32con.WM_USER+57
  23. wordbreaks = "._" + string.ascii_uppercase + string.ascii_lowercase + string.digits
  24. patImport=re.compile('import (?P<name>.*)')
  25. _event_commands = [
  26. # File menu
  27. "win32ui.ID_FILE_LOCATE", "win32ui.ID_FILE_CHECK", "afxres.ID_FILE_CLOSE",
  28. "afxres.ID_FILE_NEW", "afxres.ID_FILE_OPEN", "afxres.ID_FILE_SAVE",
  29. "afxres.ID_FILE_SAVE_AS", "win32ui.ID_FILE_SAVE_ALL",
  30. # Edit menu
  31. "afxres.ID_EDIT_UNDO", "afxres.ID_EDIT_REDO", "afxres.ID_EDIT_CUT",
  32. "afxres.ID_EDIT_COPY", "afxres.ID_EDIT_PASTE", "afxres.ID_EDIT_SELECT_ALL",
  33. "afxres.ID_EDIT_FIND", "afxres.ID_EDIT_REPEAT", "afxres.ID_EDIT_REPLACE",
  34. # View menu
  35. "win32ui.ID_VIEW_WHITESPACE", "win32ui.ID_VIEW_FIXED_FONT",
  36. "win32ui.ID_VIEW_BROWSE", "win32ui.ID_VIEW_INTERACTIVE",
  37. # Window menu
  38. "afxres.ID_WINDOW_ARRANGE", "afxres.ID_WINDOW_CASCADE",
  39. "afxres.ID_WINDOW_NEW", "afxres.ID_WINDOW_SPLIT",
  40. "afxres.ID_WINDOW_TILE_HORZ", "afxres.ID_WINDOW_TILE_VERT",
  41. # Others
  42. "afxres.ID_APP_EXIT", "afxres.ID_APP_ABOUT",
  43. ]
  44. _extra_event_commands = [
  45. ("EditDelete", afxres.ID_EDIT_CLEAR),
  46. ("LocateModule", win32ui.ID_FILE_LOCATE),
  47. ("GotoLine", win32ui.ID_EDIT_GOTO_LINE),
  48. ("DbgBreakpointToggle", win32ui.IDC_DBG_ADD),
  49. ("DbgGo", win32ui.IDC_DBG_GO),
  50. ("DbgStepOver", win32ui.IDC_DBG_STEPOVER),
  51. ("DbgStep", win32ui.IDC_DBG_STEP),
  52. ("DbgStepOut", win32ui.IDC_DBG_STEPOUT),
  53. ("DbgBreakpointClearAll", win32ui.IDC_DBG_CLEAR),
  54. ("DbgClose", win32ui.IDC_DBG_CLOSE),
  55. ]
  56. event_commands = []
  57. def _CreateEvents():
  58. for name in _event_commands:
  59. val = eval(name)
  60. name_parts = name.split("_")[1:]
  61. name_parts = [p.capitalize() for p in name_parts]
  62. event = ''.join(name_parts)
  63. event_commands.append((event, val))
  64. for name, id in _extra_event_commands:
  65. event_commands.append((name, id))
  66. _CreateEvents()
  67. del _event_commands; del _extra_event_commands
  68. command_reflectors = [
  69. (win32ui.ID_EDIT_UNDO, win32con.WM_UNDO),
  70. (win32ui.ID_EDIT_REDO, scintillacon.SCI_REDO),
  71. (win32ui.ID_EDIT_CUT, win32con.WM_CUT),
  72. (win32ui.ID_EDIT_COPY, win32con.WM_COPY),
  73. (win32ui.ID_EDIT_PASTE, win32con.WM_PASTE),
  74. (win32ui.ID_EDIT_CLEAR, win32con.WM_CLEAR),
  75. (win32ui.ID_EDIT_SELECT_ALL, scintillacon.SCI_SELECTALL),
  76. ]
  77. def DoBraceMatch(control):
  78. curPos = control.SCIGetCurrentPos()
  79. charBefore = ' '
  80. if curPos: charBefore = control.SCIGetCharAt(curPos-1)
  81. charAt = control.SCIGetCharAt(curPos)
  82. braceAtPos = braceOpposite = -1
  83. if charBefore in "[](){}": braceAtPos = curPos-1
  84. if braceAtPos==-1:
  85. if charAt in "[](){}": braceAtPos = curPos
  86. if braceAtPos != -1:
  87. braceOpposite = control.SCIBraceMatch(braceAtPos, 0)
  88. if braceAtPos != -1 and braceOpposite==-1:
  89. control.SCIBraceBadHighlight(braceAtPos)
  90. else:
  91. # either clear them both or set them both.
  92. control.SCIBraceHighlight(braceAtPos, braceOpposite)
  93. def _get_class_attributes(ob):
  94. # Recurse into base classes looking for attributes
  95. items = []
  96. try:
  97. items = items + dir(ob)
  98. for i in ob.__bases__:
  99. for item in _get_class_attributes(i):
  100. if item not in items:
  101. items.append(item)
  102. except AttributeError:
  103. pass
  104. return items
  105. # Supposed to look like an MFC CEditView, but
  106. # also supports IDLE extensions and other source code generic features.
  107. class CScintillaView(docview.CtrlView, control.CScintillaColorEditInterface):
  108. def __init__(self, doc):
  109. docview.CtrlView.__init__(self, doc, "Scintilla", win32con.WS_CHILD | win32con.WS_VSCROLL | win32con.WS_HSCROLL | win32con.WS_CLIPCHILDREN | win32con.WS_VISIBLE)
  110. self._tabWidth = 8 # Mirror of what we send to Scintilla - never change this directly
  111. self.bAutoCompleteAttributes = 1
  112. self.bShowCallTips = 1
  113. self.bMatchBraces = 0 # Editor option will default this to true later!
  114. self.bindings = bindings.BindingsManager(self)
  115. self.idle = IDLEenvironment.IDLEEditorWindow(self)
  116. self.idle.IDLEExtension("AutoExpand")
  117. # SendScintilla is called so frequently it is worth optimizing.
  118. self.SendScintilla = self._obj_.SendMessage
  119. def OnDestroy(self, msg):
  120. self.SendScintilla = None
  121. return docview.CtrlView.OnDestroy(self, msg)
  122. def _MakeColorizer(self):
  123. ext = os.path.splitext(self.GetDocument().GetPathName())[1]
  124. from . import formatter
  125. return formatter.BuiltinPythonSourceFormatter(self, ext)
  126. # def SendScintilla(self, msg, w=0, l=0):
  127. # return self._obj_.SendMessage(msg, w, l)
  128. def SCISetTabWidth(self, width):
  129. # I need to remember the tab-width for the AutoIndent extension. This may go.
  130. self._tabWidth = width
  131. control.CScintillaEditInterface.SCISetTabWidth(self, width)
  132. def GetTabWidth(self):
  133. return self._tabWidth
  134. def HookHandlers(self):
  135. # Create events for all the menu names.
  136. for name, val in event_commands:
  137. # handler = lambda id, code, tosend=val, parent=parent: parent.OnCommand(tosend, 0) and 0
  138. self.bindings.bind(name, None, cid=val)
  139. # Hook commands that do nothing other than send Scintilla messages.
  140. for command, reflection in command_reflectors:
  141. handler = lambda id, code, ss=self.SendScintilla, tosend=reflection: ss(tosend) and 0
  142. self.HookCommand(handler, command)
  143. self.HookCommand(self.OnCmdViewWS, win32ui.ID_VIEW_WHITESPACE)
  144. self.HookCommandUpdate(self.OnUpdateViewWS, win32ui.ID_VIEW_WHITESPACE)
  145. self.HookCommand(self.OnCmdViewIndentationGuides, win32ui.ID_VIEW_INDENTATIONGUIDES)
  146. self.HookCommandUpdate(self.OnUpdateViewIndentationGuides, win32ui.ID_VIEW_INDENTATIONGUIDES)
  147. self.HookCommand(self.OnCmdViewRightEdge, win32ui.ID_VIEW_RIGHT_EDGE)
  148. self.HookCommandUpdate(self.OnUpdateViewRightEdge, win32ui.ID_VIEW_RIGHT_EDGE)
  149. self.HookCommand(self.OnCmdViewEOL, win32ui.ID_VIEW_EOL)
  150. self.HookCommandUpdate(self.OnUpdateViewEOL, win32ui.ID_VIEW_EOL)
  151. self.HookCommand(self.OnCmdViewFixedFont, win32ui.ID_VIEW_FIXED_FONT)
  152. self.HookCommandUpdate(self.OnUpdateViewFixedFont, win32ui.ID_VIEW_FIXED_FONT)
  153. self.HookCommand(self.OnCmdFileLocate, win32ui.ID_FILE_LOCATE)
  154. self.HookCommand(self.OnCmdEditFind, win32ui.ID_EDIT_FIND)
  155. self.HookCommand(self.OnCmdEditRepeat, win32ui.ID_EDIT_REPEAT)
  156. self.HookCommand(self.OnCmdEditReplace, win32ui.ID_EDIT_REPLACE)
  157. self.HookCommand(self.OnCmdGotoLine, win32ui.ID_EDIT_GOTO_LINE)
  158. self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT)
  159. self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT_DIRECT)
  160. self.HookCommand(self.OnFilePrintPreview,
  161. win32ui.ID_FILE_PRINT_PREVIEW)
  162. # Key bindings.
  163. self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
  164. self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN)
  165. # Hook wheeley mouse events
  166. # self.HookMessage(self.OnMouseWheel, win32con.WM_MOUSEWHEEL)
  167. self.HookFormatter()
  168. def OnInitialUpdate(self):
  169. doc = self.GetDocument()
  170. # Enable Unicode
  171. self.SendScintilla(scintillacon.SCI_SETCODEPAGE, scintillacon.SC_CP_UTF8, 0)
  172. self.SendScintilla(scintillacon.SCI_SETKEYSUNICODE, 1, 0)
  173. # Create margins
  174. self.SendScintilla(scintillacon.SCI_SETMARGINTYPEN, 1, scintillacon.SC_MARGIN_SYMBOL);
  175. self.SendScintilla(scintillacon.SCI_SETMARGINMASKN, 1, 0xF);
  176. self.SendScintilla(scintillacon.SCI_SETMARGINTYPEN, 2, scintillacon.SC_MARGIN_SYMBOL);
  177. self.SendScintilla(scintillacon.SCI_SETMARGINMASKN, 2, scintillacon.SC_MASK_FOLDERS);
  178. self.SendScintilla(scintillacon.SCI_SETMARGINSENSITIVEN, 2, 1);
  179. self.GetDocument().HookViewNotifications(self) # is there an MFC way to grab this?
  180. self.HookHandlers()
  181. # Load the configuration information.
  182. self.OnWinIniChange(None)
  183. self.SetSel()
  184. self.GetDocument().FinalizeViewCreation(self) # is there an MFC way to grab this?
  185. def _GetSubConfigNames(self):
  186. return None # By default we use only sections without sub-sections.
  187. def OnWinIniChange(self, section = None):
  188. self.bindings.prepare_configure()
  189. try:
  190. self.DoConfigChange()
  191. finally:
  192. self.bindings.complete_configure()
  193. def DoConfigChange(self):
  194. # Bit of a hack I dont kow what to do about - these should be "editor options"
  195. from pywin.framework.editor import GetEditorOption
  196. self.bAutoCompleteAttributes = GetEditorOption("Autocomplete Attributes", 1)
  197. self.bShowCallTips = GetEditorOption("Show Call Tips", 1)
  198. # Update the key map and extension data.
  199. configManager.configure(self, self._GetSubConfigNames())
  200. if configManager.last_error:
  201. win32ui.MessageBox(configManager.last_error, "Configuration Error")
  202. self.bMatchBraces = GetEditorOption("Match Braces", 1)
  203. self.ApplyFormattingStyles(1)
  204. def OnDestroy(self, msg):
  205. self.bindings.close()
  206. self.bindings = None
  207. self.idle.close()
  208. self.idle = None
  209. control.CScintillaColorEditInterface.close(self)
  210. return docview.CtrlView.OnDestroy(self, msg)
  211. def OnMouseWheel(self, msg):
  212. zDelta = msg[2] >> 16
  213. vpos = self.GetScrollPos(win32con.SB_VERT)
  214. vpos = vpos - zDelta/40 # 3 lines per notch
  215. self.SetScrollPos(win32con.SB_VERT, vpos)
  216. self.SendScintilla(win32con.WM_VSCROLL,
  217. (vpos<<16) | win32con.SB_THUMBPOSITION,
  218. 0)
  219. def OnBraceMatch(self, std, extra):
  220. if not self.bMatchBraces: return
  221. DoBraceMatch(self)
  222. def OnNeedShown(self, std, extra):
  223. notify = self.SCIUnpackNotifyMessage(extra)
  224. # OnNeedShown is called before an edit operation when
  225. # text is folded (as it is possible the text insertion will happen
  226. # in a folded region.) As this happens _before_ the insert,
  227. # we ignore the length (if we are at EOF, pos + length may
  228. # actually be beyond the end of buffer)
  229. self.EnsureCharsVisible(notify.position)
  230. def EnsureCharsVisible(self, start, end = None):
  231. if end is None: end = start
  232. lineStart = self.LineFromChar(min(start, end))
  233. lineEnd = self.LineFromChar(max(start, end))
  234. while lineStart <= lineEnd:
  235. self.SCIEnsureVisible(lineStart)
  236. lineStart = lineStart + 1
  237. # Helper to add an event to a menu.
  238. def AppendMenu(self, menu, text="", event=None, flags = None, checked=0):
  239. if event is None:
  240. assert flags is not None, "No event or custom flags!"
  241. cmdid = 0
  242. else:
  243. cmdid = self.bindings.get_command_id(event)
  244. if cmdid is None:
  245. # No event of that name - no point displaying it.
  246. print('View.AppendMenu(): Unknown event "%s" specified for menu text "%s" - ignored' % (event, text))
  247. return
  248. keyname = configManager.get_key_binding( event, self._GetSubConfigNames() )
  249. if keyname is not None:
  250. text = text + "\t" + keyname
  251. if flags is None: flags = win32con.MF_STRING|win32con.MF_ENABLED
  252. if checked: flags = flags | win32con.MF_CHECKED
  253. menu.AppendMenu(flags, cmdid, text)
  254. def OnKeyDown(self, msg):
  255. return self.bindings.fire_key_event( msg )
  256. def GotoEndOfFileEvent(self, event):
  257. self.SetSel(-1)
  258. def KeyDotEvent(self, event):
  259. ## Don't trigger autocomplete if any text is selected
  260. s,e = self.GetSel()
  261. if s!=e:
  262. return 1
  263. self.SCIAddText(".")
  264. if self.bAutoCompleteAttributes:
  265. self._AutoComplete()
  266. # View Whitespace/EOL/Indentation UI.
  267. def OnCmdViewWS(self, cmd, code): # Handle the menu command
  268. viewWS = self.SCIGetViewWS()
  269. self.SCISetViewWS(not viewWS)
  270. def OnUpdateViewWS(self, cmdui): # Update the tick on the UI.
  271. cmdui.SetCheck(self.SCIGetViewWS())
  272. cmdui.Enable()
  273. def OnCmdViewIndentationGuides(self, cmd, code): # Handle the menu command
  274. viewIG = self.SCIGetIndentationGuides()
  275. self.SCISetIndentationGuides(not viewIG)
  276. def OnUpdateViewIndentationGuides(self, cmdui): # Update the tick on the UI.
  277. cmdui.SetCheck(self.SCIGetIndentationGuides())
  278. cmdui.Enable()
  279. def OnCmdViewRightEdge(self, cmd, code): # Handle the menu command
  280. if self.SCIGetEdgeMode() == scintillacon.EDGE_NONE:
  281. mode = scintillacon.EDGE_BACKGROUND
  282. else:
  283. mode = scintillacon.EDGE_NONE
  284. self.SCISetEdgeMode(mode)
  285. def OnUpdateViewRightEdge(self, cmdui): # Update the tick on the UI.
  286. cmdui.SetCheck(self.SCIGetEdgeMode() != scintillacon.EDGE_NONE)
  287. cmdui.Enable()
  288. def OnCmdViewEOL(self, cmd, code): # Handle the menu command
  289. viewEOL = self.SCIGetViewEOL()
  290. self.SCISetViewEOL(not viewEOL)
  291. def OnUpdateViewEOL(self, cmdui): # Update the tick on the UI.
  292. cmdui.SetCheck(self.SCIGetViewEOL())
  293. cmdui.Enable()
  294. def OnCmdViewFixedFont(self, cmd, code): # Handle the menu command
  295. self._GetColorizer().bUseFixed = not self._GetColorizer().bUseFixed
  296. self.ApplyFormattingStyles(0)
  297. # Ensure the selection is visible!
  298. self.ScrollCaret()
  299. def OnUpdateViewFixedFont(self, cmdui): # Update the tick on the UI.
  300. c = self._GetColorizer()
  301. if c is not None: cmdui.SetCheck(c.bUseFixed)
  302. cmdui.Enable(c is not None)
  303. def OnCmdEditFind(self, cmd, code):
  304. from . import find
  305. find.ShowFindDialog()
  306. def OnCmdEditRepeat(self, cmd, code):
  307. from . import find
  308. find.FindNext()
  309. def OnCmdEditReplace(self, cmd, code):
  310. from . import find
  311. find.ShowReplaceDialog()
  312. def OnCmdFileLocate(self, cmd, id):
  313. line = self.GetLine().strip()
  314. import pywin.framework.scriptutils
  315. m = patImport.match(line)
  316. if m:
  317. # Module name on this line - locate that!
  318. modName = m.group('name')
  319. fileName = pywin.framework.scriptutils.LocatePythonFile(modName)
  320. if fileName is None:
  321. win32ui.SetStatusText("Can't locate module %s" % modName)
  322. return 1 # Let the default get it.
  323. else:
  324. win32ui.GetApp().OpenDocumentFile(fileName)
  325. else:
  326. # Just to a "normal" locate - let the default handler get it.
  327. return 1
  328. return 0
  329. def OnCmdGotoLine(self, cmd, id):
  330. try:
  331. lineNo = int(input("Enter Line Number"))-1
  332. except (ValueError, KeyboardInterrupt):
  333. return 0
  334. self.SCIEnsureVisible(lineNo)
  335. self.SCIGotoLine(lineNo)
  336. return 0
  337. def SaveTextFile(self, filename, encoding=None):
  338. doc = self.GetDocument()
  339. doc._SaveTextToFile(self, filename, encoding=encoding)
  340. doc.SetModifiedFlag(0)
  341. return 1
  342. def _AutoComplete(self):
  343. def list2dict(l):
  344. ret={}
  345. for i in l:
  346. ret[i] = None
  347. return ret
  348. self.SCIAutoCCancel() # Cancel old auto-complete lists.
  349. # First try and get an object without evaluating calls
  350. ob = self._GetObjectAtPos(bAllowCalls = 0)
  351. # If that failed, try and process call or indexing to get the object.
  352. if ob is None:
  353. ob = self._GetObjectAtPos(bAllowCalls = 1)
  354. items_dict = {}
  355. if ob is not None:
  356. try: # Catch unexpected errors when fetching attribute names from the object
  357. # extra attributes of win32ui objects
  358. if hasattr(ob, "_obj_"):
  359. try:
  360. items_dict.update(list2dict(dir(ob._obj_)))
  361. except AttributeError:
  362. pass # object has no __dict__
  363. # normal attributes
  364. try:
  365. items_dict.update(list2dict(dir(ob)))
  366. except AttributeError:
  367. pass # object has no __dict__
  368. if hasattr(ob, "__class__"):
  369. items_dict.update(list2dict(_get_class_attributes(ob.__class__)))
  370. # The object may be a COM object with typelib support - lets see if we can get its props.
  371. # (contributed by Stefan Migowsky)
  372. try:
  373. # Get the automation attributes
  374. items_dict.update(ob.__class__._prop_map_get_)
  375. # See if there is an write only property
  376. # could be optimized
  377. items_dict.update(ob.__class__._prop_map_put_)
  378. # append to the already evaluated list
  379. except AttributeError:
  380. pass
  381. # The object might be a pure COM dynamic dispatch with typelib support - lets see if we can get its props.
  382. if hasattr(ob, "_oleobj_"):
  383. try:
  384. for iTI in range(0,ob._oleobj_.GetTypeInfoCount()):
  385. typeInfo = ob._oleobj_.GetTypeInfo(iTI)
  386. self._UpdateWithITypeInfo (items_dict, typeInfo)
  387. except:
  388. pass
  389. except:
  390. win32ui.SetStatusText("Error attempting to get object attributes - %s" % (repr(sys.exc_info()[0]),))
  391. # ensure all keys are strings.
  392. items = [str(k) for k in items_dict.keys()]
  393. # All names that start with "_" go!
  394. items = [k for k in items if not k.startswith('_')]
  395. if not items:
  396. # Heuristics a-la AutoExpand
  397. # The idea is to find other usages of the current binding
  398. # and assume, that it refers to the same object (or at least,
  399. # to an object of the same type)
  400. # Contributed by Vadim Chugunov [vadimch@yahoo.com]
  401. left, right = self._GetWordSplit()
  402. if left=="": # Ignore standalone dots
  403. return None
  404. # We limit our search to the current class, if that
  405. # information is available
  406. minline, maxline, curclass = self._GetClassInfoFromBrowser()
  407. endpos = self.LineIndex(maxline)
  408. text = self.GetTextRange(self.LineIndex(minline),endpos)
  409. try:
  410. l = re.findall(r"\b"+left+"\.\w+",text)
  411. except re.error:
  412. # parens etc may make an invalid RE, but this code wouldnt
  413. # benefit even if the RE did work :-)
  414. l = []
  415. prefix = len(left)+1
  416. unique = {}
  417. for li in l:
  418. unique[li[prefix:]] = 1
  419. # Assuming traditional usage of self...
  420. if curclass and left=="self":
  421. self._UpdateWithClassMethods(unique,curclass)
  422. items = [word for word in unique.keys() if word[:2]!='__' or word[-2:]!='__']
  423. # Ignore the word currently to the right of the dot - probably a red-herring.
  424. try:
  425. items.remove(right[1:])
  426. except ValueError:
  427. pass
  428. if items:
  429. items.sort()
  430. self.SCIAutoCSetAutoHide(0)
  431. self.SCIAutoCShow(items)
  432. def _UpdateWithITypeInfo (self, items_dict, typeInfo):
  433. import pythoncom
  434. typeInfos = [typeInfo]
  435. # suppress IDispatch and IUnknown methods
  436. inspectedIIDs = {pythoncom.IID_IDispatch:None}
  437. while len(typeInfos)>0:
  438. typeInfo = typeInfos.pop()
  439. typeAttr = typeInfo.GetTypeAttr()
  440. if typeAttr.iid not in inspectedIIDs:
  441. inspectedIIDs[typeAttr.iid] = None
  442. for iFun in range(0,typeAttr.cFuncs):
  443. funDesc = typeInfo.GetFuncDesc(iFun)
  444. funName = typeInfo.GetNames(funDesc.memid)[0]
  445. if funName not in items_dict:
  446. items_dict[funName] = None
  447. # Inspect the type info of all implemented types
  448. # E.g. IShellDispatch5 implements IShellDispatch4 which implements IShellDispatch3 ...
  449. for iImplType in range(0,typeAttr.cImplTypes):
  450. iRefType = typeInfo.GetRefTypeOfImplType(iImplType)
  451. refTypeInfo = typeInfo.GetRefTypeInfo(iRefType)
  452. typeInfos.append(refTypeInfo)
  453. # TODO: This is kinda slow. Probably need some kind of cache
  454. # here that is flushed upon file save
  455. # Or maybe we don't need the superclass methods at all ?
  456. def _UpdateWithClassMethods(self,dict,classinfo):
  457. if not hasattr(classinfo,"methods"):
  458. # No 'methods' - probably not what we think it is.
  459. return
  460. dict.update(classinfo.methods)
  461. for super in classinfo.super:
  462. if hasattr(super,"methods"):
  463. self._UpdateWithClassMethods(dict,super)
  464. # Find which class definition caret is currently in and return
  465. # indexes of the the first and the last lines of that class definition
  466. # Data is obtained from module browser (if enabled)
  467. def _GetClassInfoFromBrowser(self,pos=-1):
  468. minline = 0
  469. maxline = self.GetLineCount()-1
  470. doc = self.GetParentFrame().GetActiveDocument()
  471. browser = None
  472. try:
  473. if doc is not None:
  474. browser = doc.GetAllViews()[1]
  475. except IndexError:
  476. pass
  477. if browser is None:
  478. return (minline,maxline,None) # Current window has no browser
  479. if not browser.list: return (minline,maxline,None) # Not initialized
  480. path = self.GetDocument().GetPathName()
  481. if not path: return (minline,maxline,None) # No current path
  482. import pywin.framework.scriptutils
  483. curmodule, path = pywin.framework.scriptutils.GetPackageModuleName(path)
  484. try:
  485. clbrdata = browser.list.root.clbrdata
  486. except AttributeError:
  487. return (minline,maxline,None) # No class data for this module.
  488. curline = self.LineFromChar(pos)
  489. curclass = None
  490. # Find out which class we are in
  491. for item in clbrdata.values():
  492. if item.module==curmodule:
  493. item_lineno = item.lineno - 1 # Scintilla counts lines from 0, whereas pyclbr - from 1
  494. if minline < item_lineno <= curline:
  495. minline = item_lineno
  496. curclass = item
  497. if curline < item_lineno < maxline:
  498. maxline = item_lineno
  499. return (minline,maxline,curclass)
  500. def _GetObjectAtPos(self, pos = -1, bAllowCalls = 0):
  501. left, right = self._GetWordSplit(pos, bAllowCalls)
  502. if left: # It is an attribute lookup
  503. # How is this for a hack!
  504. namespace = sys.modules.copy()
  505. namespace.update(__main__.__dict__)
  506. # Get the debugger's context.
  507. try:
  508. from pywin.framework import interact
  509. if interact.edit is not None and interact.edit.currentView is not None:
  510. globs, locs = interact.edit.currentView.GetContext()[:2]
  511. if globs: namespace.update(globs)
  512. if locs: namespace.update(locs)
  513. except ImportError:
  514. pass
  515. try:
  516. return eval(left, namespace)
  517. except:
  518. pass
  519. return None
  520. def _GetWordSplit(self, pos = -1, bAllowCalls = 0):
  521. if pos==-1: pos = self.GetSel()[0]-1 # Character before current one
  522. limit = self.GetTextLength()
  523. before = []
  524. after = []
  525. index = pos-1
  526. wordbreaks_use = wordbreaks
  527. if bAllowCalls: wordbreaks_use = wordbreaks_use + "()[]"
  528. while index>=0:
  529. char = self.SCIGetCharAt(index)
  530. if char not in wordbreaks_use: break
  531. before.insert(0, char)
  532. index = index-1
  533. index = pos
  534. while index<=limit:
  535. char = self.SCIGetCharAt(index)
  536. if char not in wordbreaks_use: break
  537. after.append(char)
  538. index=index+1
  539. return ''.join(before), ''.join(after)
  540. def OnPrepareDC (self, dc, pInfo):
  541. # print "OnPrepareDC for page", pInfo.GetCurPage(), "of", pInfo.GetFromPage(), "to", pInfo.GetToPage(), ", starts=", self.starts
  542. if dc.IsPrinting():
  543. # Check if we are beyond the end.
  544. # (only do this when actually printing, else messes up print preview!)
  545. if not pInfo.GetPreview() and self.starts is not None:
  546. prevPage = pInfo.GetCurPage() - 1
  547. if prevPage > 0 and self.starts[prevPage] >= self.GetTextLength():
  548. # All finished.
  549. pInfo.SetContinuePrinting(0)
  550. return
  551. dc.SetMapMode(win32con.MM_TEXT);
  552. def OnPreparePrinting(self, pInfo):
  553. flags = win32ui.PD_USEDEVMODECOPIES | \
  554. win32ui.PD_ALLPAGES | \
  555. win32ui.PD_NOSELECTION # Dont support printing just a selection.
  556. # NOTE: Custom print dialogs are stopping the user's values from coming back :-(
  557. # self.prtDlg = PrintDialog(pInfo, PRINTDLGORD, flags)
  558. # pInfo.SetPrintDialog(self.prtDlg)
  559. pInfo.SetMinPage(1)
  560. # max page remains undefined for now.
  561. pInfo.SetFromPage(1)
  562. pInfo.SetToPage(1)
  563. ret = self.DoPreparePrinting(pInfo)
  564. return ret
  565. def OnBeginPrinting(self, dc, pInfo):
  566. self.starts = None
  567. return self._obj_.OnBeginPrinting(dc, pInfo)
  568. def CalculatePageRanges(self, dc, pInfo):
  569. # Calculate page ranges and max page
  570. self.starts = {0:0}
  571. metrics = dc.GetTextMetrics()
  572. left, top, right, bottom = pInfo.GetDraw()
  573. # Leave space at the top for the header.
  574. rc = (left, top + int((9*metrics['tmHeight'])/2), right, bottom)
  575. pageStart = 0
  576. maxPage = 0
  577. textLen = self.GetTextLength()
  578. while pageStart < textLen:
  579. pageStart = self.FormatRange(dc, pageStart, textLen, rc, 0)
  580. maxPage = maxPage + 1
  581. self.starts[maxPage] = pageStart
  582. # And a sentinal for one page past the end
  583. self.starts[maxPage+1] = textLen
  584. # When actually printing, maxPage doesnt have any effect at this late state.
  585. # but is needed to make the Print Preview work correctly.
  586. pInfo.SetMaxPage(maxPage)
  587. def OnFilePrintPreview(self, *arg):
  588. self._obj_.OnFilePrintPreview()
  589. def OnFilePrint(self, *arg):
  590. self._obj_.OnFilePrint()
  591. def FormatRange(self, dc, pageStart, lengthDoc, rc, draw):
  592. """
  593. typedef struct _formatrange {
  594. HDC hdc;
  595. HDC hdcTarget;
  596. RECT rc;
  597. RECT rcPage;
  598. CHARRANGE chrg;} FORMATRANGE;
  599. """
  600. fmt='PPIIIIIIIIll'
  601. hdcRender = dc.GetHandleOutput()
  602. hdcFormat = dc.GetHandleAttrib()
  603. fr = struct.pack(fmt, hdcRender, hdcFormat, rc[0], rc[1], rc[2], rc[3], rc[0], rc[1], rc[2], rc[3], pageStart, lengthDoc)
  604. nextPageStart = self.SendScintilla(EM_FORMATRANGE, draw, fr)
  605. return nextPageStart
  606. def OnPrint(self, dc, pInfo):
  607. metrics = dc.GetTextMetrics()
  608. # print "dev", w, h, l, metrics['tmAscent'], metrics['tmDescent']
  609. if self.starts is None:
  610. self.CalculatePageRanges(dc, pInfo)
  611. pageNum = pInfo.GetCurPage() - 1
  612. # Setup the header of the page - docname on left, pagenum on right.
  613. doc = self.GetDocument()
  614. cxChar = metrics['tmAveCharWidth']
  615. cyChar = metrics['tmHeight']
  616. left, top, right, bottom = pInfo.GetDraw()
  617. dc.TextOut(0, 2*cyChar, doc.GetTitle())
  618. pagenum_str = win32ui.LoadString(afxres.AFX_IDS_PRINTPAGENUM) % (pageNum+1,)
  619. dc.SetTextAlign(win32con.TA_RIGHT)
  620. dc.TextOut(right, 2*cyChar, pagenum_str)
  621. dc.SetTextAlign(win32con.TA_LEFT)
  622. top = top + int((7*cyChar)/2)
  623. dc.MoveTo(left, top)
  624. dc.LineTo(right, top)
  625. top = top + cyChar
  626. rc = (left, top, right, bottom)
  627. nextPageStart = self.FormatRange(dc, self.starts[pageNum], self.starts[pageNum+1], rc, 1)
  628. def LoadConfiguration():
  629. global configManager
  630. # Bit of a hack I dont kow what to do about?
  631. from .config import ConfigManager
  632. configName = rc = win32ui.GetProfileVal("Editor", "Keyboard Config", "default")
  633. configManager = ConfigManager(configName)
  634. if configManager.last_error:
  635. bTryDefault = 0
  636. msg = "Error loading configuration '%s'\n\n%s" % (configName, configManager.last_error)
  637. if configName != "default":
  638. msg = msg + "\n\nThe default configuration will be loaded."
  639. bTryDefault = 1
  640. win32ui.MessageBox(msg)
  641. if bTryDefault:
  642. configManager = ConfigManager("default")
  643. if configManager.last_error:
  644. win32ui.MessageBox("Error loading configuration 'default'\n\n%s" % (configManager.last_error))
  645. configManager = None
  646. LoadConfiguration()