123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- # 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()
|