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.

find.py 16KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. # find.py - Find and Replace
  2. import afxres
  3. import win32api
  4. import win32con
  5. import win32ui
  6. from pywin.framework import scriptutils
  7. from pywin.mfc import dialog
  8. FOUND_NOTHING = 0
  9. FOUND_NORMAL = 1
  10. FOUND_LOOPED_BACK = 2
  11. FOUND_NEXT_FILE = 3
  12. class SearchParams:
  13. def __init__(self, other=None):
  14. if other is None:
  15. self.__dict__["findText"] = ""
  16. self.__dict__["replaceText"] = ""
  17. self.__dict__["matchCase"] = 0
  18. self.__dict__["matchWords"] = 0
  19. self.__dict__["acrossFiles"] = 0
  20. self.__dict__["remember"] = 1
  21. self.__dict__["sel"] = (-1, -1)
  22. self.__dict__["keepDialogOpen"] = 0
  23. else:
  24. self.__dict__.update(other.__dict__)
  25. # Helper so we cant misspell attributes :-)
  26. def __setattr__(self, attr, val):
  27. if not hasattr(self, attr):
  28. raise AttributeError(attr)
  29. self.__dict__[attr] = val
  30. curDialog = None
  31. lastSearch = defaultSearch = SearchParams()
  32. searchHistory = []
  33. def ShowFindDialog():
  34. _ShowDialog(FindDialog)
  35. def ShowReplaceDialog():
  36. _ShowDialog(ReplaceDialog)
  37. def _ShowDialog(dlgClass):
  38. global curDialog
  39. if curDialog is not None:
  40. if curDialog.__class__ != dlgClass:
  41. curDialog.DestroyWindow()
  42. curDialog = None
  43. else:
  44. curDialog.SetFocus()
  45. if curDialog is None:
  46. curDialog = dlgClass()
  47. curDialog.CreateWindow()
  48. def FindNext():
  49. params = SearchParams(lastSearch)
  50. params.sel = (-1, -1)
  51. if not params.findText:
  52. ShowFindDialog()
  53. else:
  54. return _FindIt(None, params)
  55. def _GetControl(control=None):
  56. if control is None:
  57. control = scriptutils.GetActiveEditControl()
  58. return control
  59. def _FindIt(control, searchParams):
  60. global lastSearch, defaultSearch
  61. control = _GetControl(control)
  62. if control is None:
  63. return FOUND_NOTHING
  64. # Move to the next char, so we find the next one.
  65. flags = 0
  66. if searchParams.matchWords:
  67. flags = flags | win32con.FR_WHOLEWORD
  68. if searchParams.matchCase:
  69. flags = flags | win32con.FR_MATCHCASE
  70. if searchParams.sel == (-1, -1):
  71. sel = control.GetSel()
  72. # If the position is the same as we found last time,
  73. # then we assume it is a "FindNext"
  74. if sel == lastSearch.sel:
  75. sel = sel[0] + 1, sel[0] + 1
  76. else:
  77. sel = searchParams.sel
  78. if sel[0] == sel[1]:
  79. sel = sel[0], control.GetTextLength()
  80. rc = FOUND_NOTHING
  81. # (Old edit control will fail here!)
  82. posFind, foundSel = control.FindText(flags, sel, searchParams.findText)
  83. lastSearch = SearchParams(searchParams)
  84. if posFind >= 0:
  85. rc = FOUND_NORMAL
  86. lineno = control.LineFromChar(posFind)
  87. control.SCIEnsureVisible(lineno)
  88. control.SetSel(foundSel)
  89. control.SetFocus()
  90. win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
  91. if rc == FOUND_NOTHING and lastSearch.acrossFiles:
  92. # Loop around all documents. First find this document.
  93. try:
  94. try:
  95. doc = control.GetDocument()
  96. except AttributeError:
  97. try:
  98. doc = control.GetParent().GetDocument()
  99. except AttributeError:
  100. print("Cant find a document for the control!")
  101. doc = None
  102. if doc is not None:
  103. template = doc.GetDocTemplate()
  104. alldocs = template.GetDocumentList()
  105. mypos = lookpos = alldocs.index(doc)
  106. while 1:
  107. lookpos = (lookpos + 1) % len(alldocs)
  108. if lookpos == mypos:
  109. break
  110. view = alldocs[lookpos].GetFirstView()
  111. posFind, foundSel = view.FindText(
  112. flags, (0, view.GetTextLength()), searchParams.findText
  113. )
  114. if posFind >= 0:
  115. nChars = foundSel[1] - foundSel[0]
  116. lineNo = view.LineFromChar(posFind) # zero based.
  117. lineStart = view.LineIndex(lineNo)
  118. colNo = posFind - lineStart # zero based.
  119. scriptutils.JumpToDocument(
  120. alldocs[lookpos].GetPathName(),
  121. lineNo + 1,
  122. colNo + 1,
  123. nChars,
  124. )
  125. rc = FOUND_NEXT_FILE
  126. break
  127. except win32ui.error:
  128. pass
  129. if rc == FOUND_NOTHING:
  130. # Loop around this control - attempt to find from the start of the control.
  131. posFind, foundSel = control.FindText(
  132. flags, (0, sel[0] - 1), searchParams.findText
  133. )
  134. if posFind >= 0:
  135. control.SCIEnsureVisible(control.LineFromChar(foundSel[0]))
  136. control.SetSel(foundSel)
  137. control.SetFocus()
  138. win32ui.SetStatusText("Not found! Searching from the top of the file.")
  139. rc = FOUND_LOOPED_BACK
  140. else:
  141. lastSearch.sel = -1, -1
  142. win32ui.SetStatusText("Can not find '%s'" % searchParams.findText)
  143. if rc != FOUND_NOTHING:
  144. lastSearch.sel = foundSel
  145. if lastSearch.remember:
  146. defaultSearch = lastSearch
  147. # track search history
  148. try:
  149. ix = searchHistory.index(searchParams.findText)
  150. except ValueError:
  151. if len(searchHistory) > 50:
  152. searchHistory[50:] = []
  153. else:
  154. del searchHistory[ix]
  155. searchHistory.insert(0, searchParams.findText)
  156. return rc
  157. def _ReplaceIt(control):
  158. control = _GetControl(control)
  159. statusText = "Can not find '%s'." % lastSearch.findText
  160. rc = FOUND_NOTHING
  161. if control is not None and lastSearch.sel != (-1, -1):
  162. control.ReplaceSel(lastSearch.replaceText)
  163. rc = FindNext()
  164. if rc != FOUND_NOTHING:
  165. statusText = win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)
  166. win32ui.SetStatusText(statusText)
  167. return rc
  168. class FindReplaceDialog(dialog.Dialog):
  169. def __init__(self):
  170. dialog.Dialog.__init__(self, self._GetDialogTemplate())
  171. self.HookCommand(self.OnFindNext, 109)
  172. def OnInitDialog(self):
  173. self.editFindText = self.GetDlgItem(102)
  174. self.butMatchWords = self.GetDlgItem(105)
  175. self.butMatchCase = self.GetDlgItem(107)
  176. self.butKeepDialogOpen = self.GetDlgItem(115)
  177. self.butAcrossFiles = self.GetDlgItem(116)
  178. self.butRemember = self.GetDlgItem(117)
  179. self.editFindText.SetWindowText(defaultSearch.findText)
  180. control = _GetControl()
  181. # _GetControl only gets normal MDI windows; if the interactive
  182. # window is docked and no document open, we get None.
  183. if control:
  184. # If we have a selection, default to that.
  185. sel = control.GetSelText()
  186. if len(sel) != 0:
  187. self.editFindText.SetWindowText(sel)
  188. if defaultSearch.remember:
  189. defaultSearch.findText = sel
  190. for hist in searchHistory:
  191. self.editFindText.AddString(hist)
  192. if hasattr(self.editFindText, "SetEditSel"):
  193. self.editFindText.SetEditSel(0, -1)
  194. else:
  195. self.editFindText.SetSel(0, -1)
  196. self.butMatchWords.SetCheck(defaultSearch.matchWords)
  197. self.butMatchCase.SetCheck(defaultSearch.matchCase)
  198. self.butKeepDialogOpen.SetCheck(defaultSearch.keepDialogOpen)
  199. self.butAcrossFiles.SetCheck(defaultSearch.acrossFiles)
  200. self.butRemember.SetCheck(defaultSearch.remember)
  201. return dialog.Dialog.OnInitDialog(self)
  202. def OnDestroy(self, msg):
  203. global curDialog
  204. curDialog = None
  205. return dialog.Dialog.OnDestroy(self, msg)
  206. def DoFindNext(self):
  207. params = SearchParams()
  208. params.findText = self.editFindText.GetWindowText()
  209. params.matchCase = self.butMatchCase.GetCheck()
  210. params.matchWords = self.butMatchWords.GetCheck()
  211. params.acrossFiles = self.butAcrossFiles.GetCheck()
  212. params.remember = self.butRemember.GetCheck()
  213. return _FindIt(None, params)
  214. def OnFindNext(self, id, code):
  215. if code != 0: # BN_CLICKED
  216. # 3d controls (python.exe + start_pythonwin.pyw) send
  217. # other notification codes
  218. return 1 #
  219. if not self.editFindText.GetWindowText():
  220. win32api.MessageBeep()
  221. return 1
  222. if self.DoFindNext() != FOUND_NOTHING:
  223. if not self.butKeepDialogOpen.GetCheck():
  224. self.DestroyWindow()
  225. class FindDialog(FindReplaceDialog):
  226. def _GetDialogTemplate(self):
  227. style = (
  228. win32con.DS_MODALFRAME
  229. | win32con.WS_POPUP
  230. | win32con.WS_VISIBLE
  231. | win32con.WS_CAPTION
  232. | win32con.WS_SYSMENU
  233. | win32con.DS_SETFONT
  234. )
  235. visible = win32con.WS_CHILD | win32con.WS_VISIBLE
  236. dt = [
  237. ["Find", (0, 2, 240, 75), style, None, (8, "MS Sans Serif")],
  238. ["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible],
  239. [
  240. "ComboBox",
  241. "",
  242. 102,
  243. (50, 7, 120, 120),
  244. visible
  245. | win32con.WS_BORDER
  246. | win32con.WS_TABSTOP
  247. | win32con.WS_VSCROLL
  248. | win32con.CBS_DROPDOWN
  249. | win32con.CBS_AUTOHSCROLL,
  250. ],
  251. [
  252. "Button",
  253. "Match &whole word only",
  254. 105,
  255. (5, 23, 100, 10),
  256. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  257. ],
  258. [
  259. "Button",
  260. "Match &case",
  261. 107,
  262. (5, 33, 100, 10),
  263. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  264. ],
  265. [
  266. "Button",
  267. "Keep &dialog open",
  268. 115,
  269. (5, 43, 100, 10),
  270. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  271. ],
  272. [
  273. "Button",
  274. "Across &open files",
  275. 116,
  276. (5, 52, 100, 10),
  277. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  278. ],
  279. [
  280. "Button",
  281. "&Remember as default search",
  282. 117,
  283. (5, 61, 150, 10),
  284. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  285. ],
  286. [
  287. "Button",
  288. "&Find Next",
  289. 109,
  290. (185, 5, 50, 14),
  291. visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,
  292. ],
  293. [
  294. "Button",
  295. "Cancel",
  296. win32con.IDCANCEL,
  297. (185, 23, 50, 14),
  298. visible | win32con.WS_TABSTOP,
  299. ],
  300. ]
  301. return dt
  302. class ReplaceDialog(FindReplaceDialog):
  303. def _GetDialogTemplate(self):
  304. style = (
  305. win32con.DS_MODALFRAME
  306. | win32con.WS_POPUP
  307. | win32con.WS_VISIBLE
  308. | win32con.WS_CAPTION
  309. | win32con.WS_SYSMENU
  310. | win32con.DS_SETFONT
  311. )
  312. visible = win32con.WS_CHILD | win32con.WS_VISIBLE
  313. dt = [
  314. ["Replace", (0, 2, 240, 95), style, 0, (8, "MS Sans Serif")],
  315. ["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible],
  316. [
  317. "ComboBox",
  318. "",
  319. 102,
  320. (60, 7, 110, 120),
  321. visible
  322. | win32con.WS_BORDER
  323. | win32con.WS_TABSTOP
  324. | win32con.WS_VSCROLL
  325. | win32con.CBS_DROPDOWN
  326. | win32con.CBS_AUTOHSCROLL,
  327. ],
  328. ["Static", "Re&place with:", 103, (5, 25, 50, 10), visible],
  329. [
  330. "ComboBox",
  331. "",
  332. 104,
  333. (60, 24, 110, 120),
  334. visible
  335. | win32con.WS_BORDER
  336. | win32con.WS_TABSTOP
  337. | win32con.WS_VSCROLL
  338. | win32con.CBS_DROPDOWN
  339. | win32con.CBS_AUTOHSCROLL,
  340. ],
  341. [
  342. "Button",
  343. "Match &whole word only",
  344. 105,
  345. (5, 42, 100, 10),
  346. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  347. ],
  348. [
  349. "Button",
  350. "Match &case",
  351. 107,
  352. (5, 52, 100, 10),
  353. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  354. ],
  355. [
  356. "Button",
  357. "Keep &dialog open",
  358. 115,
  359. (5, 62, 100, 10),
  360. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  361. ],
  362. [
  363. "Button",
  364. "Across &open files",
  365. 116,
  366. (5, 72, 100, 10),
  367. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  368. ],
  369. [
  370. "Button",
  371. "&Remember as default search",
  372. 117,
  373. (5, 81, 150, 10),
  374. visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP,
  375. ],
  376. [
  377. "Button",
  378. "&Find Next",
  379. 109,
  380. (185, 5, 50, 14),
  381. visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,
  382. ],
  383. [
  384. "Button",
  385. "&Replace",
  386. 110,
  387. (185, 23, 50, 14),
  388. visible | win32con.WS_TABSTOP,
  389. ],
  390. [
  391. "Button",
  392. "Replace &All",
  393. 111,
  394. (185, 41, 50, 14),
  395. visible | win32con.WS_TABSTOP,
  396. ],
  397. [
  398. "Button",
  399. "Cancel",
  400. win32con.IDCANCEL,
  401. (185, 59, 50, 14),
  402. visible | win32con.WS_TABSTOP,
  403. ],
  404. ]
  405. return dt
  406. def OnInitDialog(self):
  407. rc = FindReplaceDialog.OnInitDialog(self)
  408. self.HookCommand(self.OnReplace, 110)
  409. self.HookCommand(self.OnReplaceAll, 111)
  410. self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE)
  411. self.editReplaceText = self.GetDlgItem(104)
  412. self.editReplaceText.SetWindowText(lastSearch.replaceText)
  413. if hasattr(self.editReplaceText, "SetEditSel"):
  414. self.editReplaceText.SetEditSel(0, -1)
  415. else:
  416. self.editReplaceText.SetSel(0, -1)
  417. self.butReplace = self.GetDlgItem(110)
  418. self.butReplaceAll = self.GetDlgItem(111)
  419. self.CheckButtonStates()
  420. return rc # 0 when focus set
  421. def CheckButtonStates(self):
  422. # We can do a "Replace" or "Replace All" if the current selection
  423. # is the same as the search text.
  424. ft = self.editFindText.GetWindowText()
  425. control = _GetControl()
  426. # bCanReplace = len(ft)>0 and control.GetSelText() == ft
  427. bCanReplace = control is not None and lastSearch.sel == control.GetSel()
  428. self.butReplace.EnableWindow(bCanReplace)
  429. # self.butReplaceAll.EnableWindow(bCanReplace)
  430. def OnActivate(self, msg):
  431. wparam = msg[2]
  432. fActive = win32api.LOWORD(wparam)
  433. if fActive != win32con.WA_INACTIVE:
  434. self.CheckButtonStates()
  435. def OnFindNext(self, id, code):
  436. if code != 0:
  437. return 1
  438. self.DoFindNext()
  439. self.CheckButtonStates()
  440. def OnReplace(self, id, code):
  441. if code != 0:
  442. return 1
  443. lastSearch.replaceText = self.editReplaceText.GetWindowText()
  444. _ReplaceIt(None)
  445. def OnReplaceAll(self, id, code):
  446. if code != 0:
  447. return 1
  448. control = _GetControl(None)
  449. if control is not None:
  450. control.SetSel(0)
  451. num = 0
  452. if self.DoFindNext() == FOUND_NORMAL:
  453. num = 1
  454. lastSearch.replaceText = self.editReplaceText.GetWindowText()
  455. while _ReplaceIt(control) == FOUND_NORMAL:
  456. num = num + 1
  457. win32ui.SetStatusText("Replaced %d occurrences" % num)
  458. if num > 0 and not self.butKeepDialogOpen.GetCheck():
  459. self.DestroyWindow()
  460. if __name__ == "__main__":
  461. ShowFindDialog()