view.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. # A general purpose MFC CCtrlView view that uses Scintilla.
  2. import control
  3. import IDLEenvironment # IDLE emulation.
  4. from pywin.mfc import docview
  5. from pywin.mfc import dialog
  6. 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. import bindings
  16. 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. 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. import find
  305. find.ShowFindDialog()
  306. def OnCmdEditRepeat(self, cmd, code):
  307. import find
  308. find.FindNext()
  309. def OnCmdEditReplace(self, cmd, code):
  310. 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(raw_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 xrange(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.iterkeys()]
  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.iterkeys() 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 xrange(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 xrange(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.itervalues():
  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()