Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

view.py 30KB

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