Spaces:
Paused
Paused
# find.py - Find and Replace | |
import afxres | |
import win32api | |
import win32con | |
import win32ui | |
from pywin.framework import scriptutils | |
from pywin.mfc import dialog | |
FOUND_NOTHING = 0 | |
FOUND_NORMAL = 1 | |
FOUND_LOOPED_BACK = 2 | |
FOUND_NEXT_FILE = 3 | |
class SearchParams: | |
def __init__(self, other=None): | |
if other is None: | |
self.__dict__["findText"] = "" | |
self.__dict__["replaceText"] = "" | |
self.__dict__["matchCase"] = 0 | |
self.__dict__["matchWords"] = 0 | |
self.__dict__["acrossFiles"] = 0 | |
self.__dict__["remember"] = 1 | |
self.__dict__["sel"] = (-1, -1) | |
self.__dict__["keepDialogOpen"] = 0 | |
else: | |
self.__dict__.update(other.__dict__) | |
# Helper so we cant misspell attributes :-) | |
def __setattr__(self, attr, val): | |
if not hasattr(self, attr): | |
raise AttributeError(attr) | |
self.__dict__[attr] = val | |
curDialog = None | |
lastSearch = defaultSearch = SearchParams() | |
searchHistory = [] | |
def ShowFindDialog(): | |
_ShowDialog(FindDialog) | |
def ShowReplaceDialog(): | |
_ShowDialog(ReplaceDialog) | |
def _ShowDialog(dlgClass): | |
global curDialog | |
if curDialog is not None: | |
if curDialog.__class__ != dlgClass: | |
curDialog.DestroyWindow() | |
curDialog = None | |
else: | |
curDialog.SetFocus() | |
if curDialog is None: | |
curDialog = dlgClass() | |
curDialog.CreateWindow() | |
def FindNext(): | |
params = SearchParams(lastSearch) | |
params.sel = (-1, -1) | |
if not params.findText: | |
ShowFindDialog() | |
else: | |
return _FindIt(None, params) | |
def _GetControl(control=None): | |
if control is None: | |
control = scriptutils.GetActiveEditControl() | |
return control | |
def _FindIt(control, searchParams): | |
global lastSearch, defaultSearch | |
control = _GetControl(control) | |
if control is None: | |
return FOUND_NOTHING | |
# Move to the next char, so we find the next one. | |
flags = 0 | |
if searchParams.matchWords: | |
flags = flags | win32con.FR_WHOLEWORD | |
if searchParams.matchCase: | |
flags = flags | win32con.FR_MATCHCASE | |
if searchParams.sel == (-1, -1): | |
sel = control.GetSel() | |
# If the position is the same as we found last time, | |
# then we assume it is a "FindNext" | |
if sel == lastSearch.sel: | |
sel = sel[0] + 1, sel[0] + 1 | |
else: | |
sel = searchParams.sel | |
if sel[0] == sel[1]: | |
sel = sel[0], control.GetTextLength() | |
rc = FOUND_NOTHING | |
# (Old edit control will fail here!) | |
posFind, foundSel = control.FindText(flags, sel, searchParams.findText) | |
lastSearch = SearchParams(searchParams) | |
if posFind >= 0: | |
rc = FOUND_NORMAL | |
lineno = control.LineFromChar(posFind) | |
control.SCIEnsureVisible(lineno) | |
control.SetSel(foundSel) | |
control.SetFocus() | |
win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)) | |
if rc == FOUND_NOTHING and lastSearch.acrossFiles: | |
# Loop around all documents. First find this document. | |
try: | |
try: | |
doc = control.GetDocument() | |
except AttributeError: | |
try: | |
doc = control.GetParent().GetDocument() | |
except AttributeError: | |
print("Cant find a document for the control!") | |
doc = None | |
if doc is not None: | |
template = doc.GetDocTemplate() | |
alldocs = template.GetDocumentList() | |
mypos = lookpos = alldocs.index(doc) | |
while 1: | |
lookpos = (lookpos + 1) % len(alldocs) | |
if lookpos == mypos: | |
break | |
view = alldocs[lookpos].GetFirstView() | |
posFind, foundSel = view.FindText( | |
flags, (0, view.GetTextLength()), searchParams.findText | |
) | |
if posFind >= 0: | |
nChars = foundSel[1] - foundSel[0] | |
lineNo = view.LineFromChar(posFind) # zero based. | |
lineStart = view.LineIndex(lineNo) | |
colNo = posFind - lineStart # zero based. | |
scriptutils.JumpToDocument( | |
alldocs[lookpos].GetPathName(), | |
lineNo + 1, | |
colNo + 1, | |
nChars, | |
) | |
rc = FOUND_NEXT_FILE | |
break | |
except win32ui.error: | |
pass | |
if rc == FOUND_NOTHING: | |
# Loop around this control - attempt to find from the start of the control. | |
posFind, foundSel = control.FindText( | |
flags, (0, sel[0] - 1), searchParams.findText | |
) | |
if posFind >= 0: | |
control.SCIEnsureVisible(control.LineFromChar(foundSel[0])) | |
control.SetSel(foundSel) | |
control.SetFocus() | |
win32ui.SetStatusText("Not found! Searching from the top of the file.") | |
rc = FOUND_LOOPED_BACK | |
else: | |
lastSearch.sel = -1, -1 | |
win32ui.SetStatusText("Can not find '%s'" % searchParams.findText) | |
if rc != FOUND_NOTHING: | |
lastSearch.sel = foundSel | |
if lastSearch.remember: | |
defaultSearch = lastSearch | |
# track search history | |
try: | |
ix = searchHistory.index(searchParams.findText) | |
except ValueError: | |
if len(searchHistory) > 50: | |
searchHistory[50:] = [] | |
else: | |
del searchHistory[ix] | |
searchHistory.insert(0, searchParams.findText) | |
return rc | |
def _ReplaceIt(control): | |
control = _GetControl(control) | |
statusText = "Can not find '%s'." % lastSearch.findText | |
rc = FOUND_NOTHING | |
if control is not None and lastSearch.sel != (-1, -1): | |
control.ReplaceSel(lastSearch.replaceText) | |
rc = FindNext() | |
if rc != FOUND_NOTHING: | |
statusText = win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE) | |
win32ui.SetStatusText(statusText) | |
return rc | |
class FindReplaceDialog(dialog.Dialog): | |
def __init__(self): | |
dialog.Dialog.__init__(self, self._GetDialogTemplate()) | |
self.HookCommand(self.OnFindNext, 109) | |
def OnInitDialog(self): | |
self.editFindText = self.GetDlgItem(102) | |
self.butMatchWords = self.GetDlgItem(105) | |
self.butMatchCase = self.GetDlgItem(107) | |
self.butKeepDialogOpen = self.GetDlgItem(115) | |
self.butAcrossFiles = self.GetDlgItem(116) | |
self.butRemember = self.GetDlgItem(117) | |
self.editFindText.SetWindowText(defaultSearch.findText) | |
control = _GetControl() | |
# _GetControl only gets normal MDI windows; if the interactive | |
# window is docked and no document open, we get None. | |
if control: | |
# If we have a selection, default to that. | |
sel = control.GetSelText() | |
if len(sel) != 0: | |
self.editFindText.SetWindowText(sel) | |
if defaultSearch.remember: | |
defaultSearch.findText = sel | |
for hist in searchHistory: | |
self.editFindText.AddString(hist) | |
if hasattr(self.editFindText, "SetEditSel"): | |
self.editFindText.SetEditSel(0, -1) | |
else: | |
self.editFindText.SetSel(0, -1) | |
self.butMatchWords.SetCheck(defaultSearch.matchWords) | |
self.butMatchCase.SetCheck(defaultSearch.matchCase) | |
self.butKeepDialogOpen.SetCheck(defaultSearch.keepDialogOpen) | |
self.butAcrossFiles.SetCheck(defaultSearch.acrossFiles) | |
self.butRemember.SetCheck(defaultSearch.remember) | |
return dialog.Dialog.OnInitDialog(self) | |
def OnDestroy(self, msg): | |
global curDialog | |
curDialog = None | |
return dialog.Dialog.OnDestroy(self, msg) | |
def DoFindNext(self): | |
params = SearchParams() | |
params.findText = self.editFindText.GetWindowText() | |
params.matchCase = self.butMatchCase.GetCheck() | |
params.matchWords = self.butMatchWords.GetCheck() | |
params.acrossFiles = self.butAcrossFiles.GetCheck() | |
params.remember = self.butRemember.GetCheck() | |
return _FindIt(None, params) | |
def OnFindNext(self, id, code): | |
if code != 0: # BN_CLICKED | |
# 3d controls (python.exe + start_pythonwin.pyw) send | |
# other notification codes | |
return 1 # | |
if not self.editFindText.GetWindowText(): | |
win32api.MessageBeep() | |
return 1 | |
if self.DoFindNext() != FOUND_NOTHING: | |
if not self.butKeepDialogOpen.GetCheck(): | |
self.DestroyWindow() | |
class FindDialog(FindReplaceDialog): | |
def _GetDialogTemplate(self): | |
style = ( | |
win32con.DS_MODALFRAME | |
| win32con.WS_POPUP | |
| win32con.WS_VISIBLE | |
| win32con.WS_CAPTION | |
| win32con.WS_SYSMENU | |
| win32con.DS_SETFONT | |
) | |
visible = win32con.WS_CHILD | win32con.WS_VISIBLE | |
dt = [ | |
["Find", (0, 2, 240, 75), style, None, (8, "MS Sans Serif")], | |
["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible], | |
[ | |
"ComboBox", | |
"", | |
102, | |
(50, 7, 120, 120), | |
visible | |
| win32con.WS_BORDER | |
| win32con.WS_TABSTOP | |
| win32con.WS_VSCROLL | |
| win32con.CBS_DROPDOWN | |
| win32con.CBS_AUTOHSCROLL, | |
], | |
[ | |
"Button", | |
"Match &whole word only", | |
105, | |
(5, 23, 100, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"Match &case", | |
107, | |
(5, 33, 100, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"Keep &dialog open", | |
115, | |
(5, 43, 100, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"Across &open files", | |
116, | |
(5, 52, 100, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"&Remember as default search", | |
117, | |
(5, 61, 150, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"&Find Next", | |
109, | |
(185, 5, 50, 14), | |
visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"Cancel", | |
win32con.IDCANCEL, | |
(185, 23, 50, 14), | |
visible | win32con.WS_TABSTOP, | |
], | |
] | |
return dt | |
class ReplaceDialog(FindReplaceDialog): | |
def _GetDialogTemplate(self): | |
style = ( | |
win32con.DS_MODALFRAME | |
| win32con.WS_POPUP | |
| win32con.WS_VISIBLE | |
| win32con.WS_CAPTION | |
| win32con.WS_SYSMENU | |
| win32con.DS_SETFONT | |
) | |
visible = win32con.WS_CHILD | win32con.WS_VISIBLE | |
dt = [ | |
["Replace", (0, 2, 240, 95), style, 0, (8, "MS Sans Serif")], | |
["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible], | |
[ | |
"ComboBox", | |
"", | |
102, | |
(60, 7, 110, 120), | |
visible | |
| win32con.WS_BORDER | |
| win32con.WS_TABSTOP | |
| win32con.WS_VSCROLL | |
| win32con.CBS_DROPDOWN | |
| win32con.CBS_AUTOHSCROLL, | |
], | |
["Static", "Re&place with:", 103, (5, 25, 50, 10), visible], | |
[ | |
"ComboBox", | |
"", | |
104, | |
(60, 24, 110, 120), | |
visible | |
| win32con.WS_BORDER | |
| win32con.WS_TABSTOP | |
| win32con.WS_VSCROLL | |
| win32con.CBS_DROPDOWN | |
| win32con.CBS_AUTOHSCROLL, | |
], | |
[ | |
"Button", | |
"Match &whole word only", | |
105, | |
(5, 42, 100, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"Match &case", | |
107, | |
(5, 52, 100, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"Keep &dialog open", | |
115, | |
(5, 62, 100, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"Across &open files", | |
116, | |
(5, 72, 100, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"&Remember as default search", | |
117, | |
(5, 81, 150, 10), | |
visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"&Find Next", | |
109, | |
(185, 5, 50, 14), | |
visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"&Replace", | |
110, | |
(185, 23, 50, 14), | |
visible | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"Replace &All", | |
111, | |
(185, 41, 50, 14), | |
visible | win32con.WS_TABSTOP, | |
], | |
[ | |
"Button", | |
"Cancel", | |
win32con.IDCANCEL, | |
(185, 59, 50, 14), | |
visible | win32con.WS_TABSTOP, | |
], | |
] | |
return dt | |
def OnInitDialog(self): | |
rc = FindReplaceDialog.OnInitDialog(self) | |
self.HookCommand(self.OnReplace, 110) | |
self.HookCommand(self.OnReplaceAll, 111) | |
self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE) | |
self.editReplaceText = self.GetDlgItem(104) | |
self.editReplaceText.SetWindowText(lastSearch.replaceText) | |
if hasattr(self.editReplaceText, "SetEditSel"): | |
self.editReplaceText.SetEditSel(0, -1) | |
else: | |
self.editReplaceText.SetSel(0, -1) | |
self.butReplace = self.GetDlgItem(110) | |
self.butReplaceAll = self.GetDlgItem(111) | |
self.CheckButtonStates() | |
return rc # 0 when focus set | |
def CheckButtonStates(self): | |
# We can do a "Replace" or "Replace All" if the current selection | |
# is the same as the search text. | |
ft = self.editFindText.GetWindowText() | |
control = _GetControl() | |
# bCanReplace = len(ft)>0 and control.GetSelText() == ft | |
bCanReplace = control is not None and lastSearch.sel == control.GetSel() | |
self.butReplace.EnableWindow(bCanReplace) | |
# self.butReplaceAll.EnableWindow(bCanReplace) | |
def OnActivate(self, msg): | |
wparam = msg[2] | |
fActive = win32api.LOWORD(wparam) | |
if fActive != win32con.WA_INACTIVE: | |
self.CheckButtonStates() | |
def OnFindNext(self, id, code): | |
if code != 0: | |
return 1 | |
self.DoFindNext() | |
self.CheckButtonStates() | |
def OnReplace(self, id, code): | |
if code != 0: | |
return 1 | |
lastSearch.replaceText = self.editReplaceText.GetWindowText() | |
_ReplaceIt(None) | |
def OnReplaceAll(self, id, code): | |
if code != 0: | |
return 1 | |
control = _GetControl(None) | |
if control is not None: | |
control.SetSel(0) | |
num = 0 | |
if self.DoFindNext() == FOUND_NORMAL: | |
num = 1 | |
lastSearch.replaceText = self.editReplaceText.GetWindowText() | |
while _ReplaceIt(control) == FOUND_NORMAL: | |
num = num + 1 | |
win32ui.SetStatusText("Replaced %d occurrences" % num) | |
if num > 0 and not self.butKeepDialogOpen.GetCheck(): | |
self.DestroyWindow() | |
if __name__ == "__main__": | |
ShowFindDialog() | |