This commit was manufactured by cvs2svn to create tag 'r23b1-mac'.
[python/dscho.git] / Mac / Tools / IDE / PyDocSearch.py
blobf9d2cb5a466b897e56ecf48ce5918bee6e3869bc
1 import re
2 import W
3 import os
4 import MacPrefs
5 import MacOS
6 import string
7 import webbrowser
8 import EasyDialogs
11 app = W.getapplication()
13 _titlepat = re.compile('<title>\([^<]*\)</title>')
15 def sucktitle(path):
16 f = open(path)
17 text = f.read(1024) # assume the title is in the first 1024 bytes
18 f.close()
19 lowertext = text.lower()
20 matcher = _titlepat.search(lowertext)
21 if matcher:
22 return matcher.group(1)
23 return path
25 def verifydocpath(docpath):
26 try:
27 tut = os.path.join(docpath, "tut")
28 lib = os.path.join(docpath, "lib")
29 ref = os.path.join(docpath, "ref")
30 for path in [tut, lib, ref]:
31 if not os.path.exists(path):
32 return 0
33 except:
34 return 0
35 return 1
38 _resultscounter = 1
40 class Results:
42 def __init__(self, hits):
43 global _resultscounter
44 hits = map(lambda (path, hits): (sucktitle(path), path, hits), hits)
45 hits.sort()
46 self.hits = hits
47 nicehits = map(
48 lambda (title, path, hits):
49 title + '\r' + string.join(
50 map(lambda (c, p): "%s (%d)" % (p, c), hits), ', '), hits)
51 nicehits.sort()
52 self.w = W.Window((440, 300), "Search results %d" % _resultscounter, minsize = (200, 100))
53 self.w.results = W.TwoLineList((-1, -1, 1, -14), nicehits, self.listhit)
54 self.w.open()
55 self.w.bind('return', self.listhit)
56 self.w.bind('enter', self.listhit)
57 _resultscounter = _resultscounter + 1
59 def listhit(self, isdbl = 1):
60 if isdbl:
61 for i in self.w.results.getselection():
62 path = self.hits[i][1]
63 url = "file://" + "/".join(path.split(":"))
64 webbrowser.open(url)
67 class Status:
69 def __init__(self):
70 self.w = W.Dialog((440, 64), "Searching\xc9")
71 self.w.searching = W.TextBox((4, 4, -4, 16), "")
72 self.w.hits = W.TextBox((4, 24, -4, 16), "Hits: 0")
73 self.w.canceltip = W.TextBox((4, 44, -4, 16), "Type cmd-period (.) to cancel.")
74 self.w.open()
76 def set(self, path, hits):
77 self.w.searching.set(path)
78 self.w.hits.set('Hits: ' + `hits`)
79 app.breathe()
81 def close(self):
82 self.w.close()
85 def match(text, patterns, all):
86 hits = []
87 hitsappend = hits.append
88 stringcount = string.count
89 for pat in patterns:
90 c = stringcount(text, pat)
91 if c > 0:
92 hitsappend((c, pat))
93 elif all:
94 hits[:] = []
95 break
96 hits.sort()
97 hits.reverse()
98 return hits
101 def dosearch(docpath, searchstring, settings):
102 (docpath, kind, case, word, tut, lib, ref, ext, api) = settings
103 books = [(tut, 'tut'), (lib, 'lib'), (ref, 'ref'), (ext, 'ext'), (api, 'api')]
104 if not case:
105 searchstring = string.lower(searchstring)
107 if kind == 1:
108 patterns = string.split(searchstring)
109 all = 1
110 elif kind == 2:
111 patterns = string.split(searchstring)
112 all = 0
113 else:
114 patterns = [searchstring]
115 all = 0 # not relevant
117 ospathjoin = os.path.join
118 stringlower = string.lower
119 status = Status()
120 statusset = status.set
121 _match = match
122 _open = open
123 hits = {}
124 try:
125 if hasattr(MacOS, 'EnableAppswitch'):
126 MacOS.EnableAppswitch(0)
127 try:
128 for do, name in books:
129 if not do:
130 continue
131 bookpath = ospathjoin(docpath, name)
132 if not os.path.exists(bookpath):
133 continue
134 files = os.listdir(bookpath)
135 for file in files:
136 fullpath = ospathjoin(bookpath, file)
137 if fullpath[-5:] <> '.html':
138 continue
139 statusset(fullpath, len(hits))
140 f = _open(fullpath)
141 text = f.read()
142 if not case:
143 text = stringlower(text)
144 f.close()
145 filehits = _match(text, patterns, all)
146 if filehits:
147 hits[fullpath] = filehits
148 finally:
149 if hasattr(MacOS, 'EnableAppswitch'):
150 MacOS.EnableAppswitch(-1)
151 status.close()
152 except KeyboardInterrupt:
153 pass
154 hits = hits.items()
155 hits.sort()
156 return hits
159 class PyDocSearch:
161 def __init__(self):
162 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
163 try:
164 (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine
165 except:
166 (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine = \
167 ("", 0, 0, 0, 1, 1, 0, 0, 0)
169 if docpath and not verifydocpath(docpath):
170 docpath = ""
172 self.w = W.Window((400, 200), "Search the Python Documentation")
173 self.w.searchtext = W.EditText((10, 10, -100, 20), callback = self.checkbuttons)
174 self.w.searchbutton = W.Button((-90, 12, 80, 16), "Search", self.search)
175 buttons = []
177 gutter = 10
178 width = 130
179 bookstart = width + 2 * gutter
180 self.w.phraseradio = W.RadioButton((10, 38, width, 16), "As a phrase", buttons)
181 self.w.allwordsradio = W.RadioButton((10, 58, width, 16), "All words", buttons)
182 self.w.anywordsradio = W.RadioButton((10, 78, width, 16), "Any word", buttons)
183 self.w.casesens = W.CheckBox((10, 98, width, 16), "Case sensitive")
184 self.w.wholewords = W.CheckBox((10, 118, width, 16), "Whole words")
185 self.w.tutorial = W.CheckBox((bookstart, 38, -10, 16), "Tutorial")
186 self.w.library = W.CheckBox((bookstart, 58, -10, 16), "Library reference")
187 self.w.langueref = W.CheckBox((bookstart, 78, -10, 16), "Lanuage reference manual")
188 self.w.extending = W.CheckBox((bookstart, 98, -10, 16), "Extending & embedding")
189 self.w.api = W.CheckBox((bookstart, 118, -10, 16), "C/C++ API")
191 self.w.setdocfolderbutton = W.Button((10, -30, 100, 16), "Set doc folder", self.setdocpath)
193 if docpath:
194 self.w.setdefaultbutton(self.w.searchbutton)
195 else:
196 self.w.setdefaultbutton(self.w.setdocfolderbutton)
198 self.docpath = docpath
199 if not docpath:
200 docpath = "(please select the Python html documentation folder)"
201 self.w.docfolder = W.TextBox((120, -28, -10, 16), docpath)
203 [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio][kind].set(1)
205 self.w.casesens.set(case)
206 self.w.wholewords.set(word)
207 self.w.tutorial.set(tut)
208 self.w.library.set(lib)
209 self.w.langueref.set(ref)
210 self.w.extending.set(ext)
211 self.w.api.set(api)
213 self.w.open()
214 self.w.wholewords.enable(0)
215 self.w.bind('<close>', self.close)
216 self.w.searchbutton.enable(0)
218 def search(self):
219 hits = dosearch(self.docpath, self.w.searchtext.get(), self.getsettings())
220 if hits:
221 Results(hits)
222 elif hasattr(MacOS, 'SysBeep'):
223 MacOS.SysBeep(0)
225 def setdocpath(self):
226 docpath = EasyDialogs.AskFolder()
227 if docpath:
228 if not verifydocpath(docpath):
229 W.Message("This does not seem to be a Python documentation folder...")
230 else:
231 self.docpath = docpath
232 self.w.docfolder.set(docpath)
233 self.w.setdefaultbutton(self.w.searchbutton)
235 def close(self):
236 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
237 prefs.docsearchengine = self.getsettings()
239 def getsettings(self):
240 radiobuttons = [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio]
241 for i in range(3):
242 if radiobuttons[i].get():
243 kind = i
244 break
245 docpath = self.docpath
246 case = self.w.casesens.get()
247 word = self.w.wholewords.get()
248 tut = self.w.tutorial.get()
249 lib = self.w.library.get()
250 ref = self.w.langueref.get()
251 ext = self.w.extending.get()
252 api = self.w.api.get()
253 return (docpath, kind, case, word, tut, lib, ref, ext, api)
255 def checkbuttons(self):
256 self.w.searchbutton.enable(not not self.w.searchtext.get())