blob: 1f9842e425766207887367b69957c550b229e269 [file] [log] [blame]
Jack Jansen9ad27522001-02-21 13:54:31 +00001import re
Just van Rossum40f9b7b1999-01-30 22:39:17 +00002import W
3import macfs
4import os
5import MacPrefs
6import MacOS
7import string
Just van Rossumcee949f2001-12-03 18:11:36 +00008import webbrowser
Just van Rossum40f9b7b1999-01-30 22:39:17 +00009
Just van Rossum40f9b7b1999-01-30 22:39:17 +000010
11app = W.getapplication()
12
Jack Jansen9ad27522001-02-21 13:54:31 +000013_titlepat = re.compile('<title>\([^<]*\)</title>')
Just van Rossum40f9b7b1999-01-30 22:39:17 +000014
15def sucktitle(path):
16 f = open(path)
17 text = f.read(1024) # assume the title is in the first 1024 bytes
18 f.close()
Just van Rossumcee949f2001-12-03 18:11:36 +000019 lowertext = text.lower()
Jack Jansen9ad27522001-02-21 13:54:31 +000020 matcher = _titlepat.search(lowertext)
21 if matcher:
22 return matcher.group(1)
Just van Rossum40f9b7b1999-01-30 22:39:17 +000023 return path
24
25def 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
36
37
38class TwoLineList(W.List):
39
40 LDEF_ID = 468
41
42 def createlist(self):
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000043 from Carbon import List
Just van Rossum40f9b7b1999-01-30 22:39:17 +000044 self._calcbounds()
45 self.SetPort()
46 rect = self._bounds
47 rect = rect[0]+1, rect[1]+1, rect[2]-16, rect[3]-1
48 self._list = List.LNew(rect, (0, 0, 1, 0), (0, 28), self.LDEF_ID, self._parentwindow.wid,
49 0, 1, 0, 1)
50 self.set(self.items)
51
52
53_resultscounter = 1
54
55class Results:
56
57 def __init__(self, hits):
58 global _resultscounter
59 hits = map(lambda (path, hits): (sucktitle(path), path, hits), hits)
60 hits.sort()
61 self.hits = hits
62 nicehits = map(
63 lambda (title, path, hits):
64 title + '\r' + string.join(
65 map(lambda (c, p): "%s (%d)" % (p, c), hits), ', '), hits)
66 nicehits.sort()
67 self.w = W.Window((440, 300), "Search results %d" % _resultscounter, minsize = (200, 100))
Just van Rossumcee949f2001-12-03 18:11:36 +000068 self.w.results = W.TwoLineList((-1, -1, 1, -14), nicehits, self.listhit)
Just van Rossum40f9b7b1999-01-30 22:39:17 +000069 self.w.open()
70 self.w.bind('return', self.listhit)
71 self.w.bind('enter', self.listhit)
72 _resultscounter = _resultscounter + 1
Just van Rossum40f9b7b1999-01-30 22:39:17 +000073
74 def listhit(self, isdbl = 1):
75 if isdbl:
76 for i in self.w.results.getselection():
Just van Rossumcee949f2001-12-03 18:11:36 +000077 path = self.hits[i][1]
78 url = "file://" + "/".join(path.split(":"))
79 webbrowser.open(url)
80
Just van Rossum40f9b7b1999-01-30 22:39:17 +000081
82class Status:
83
84 def __init__(self):
Just van Rossumdc3c6172001-06-19 21:37:33 +000085 self.w = W.Dialog((440, 64), "Searching\xc9")
Just van Rossumcee949f2001-12-03 18:11:36 +000086 self.w.searching = W.TextBox((4, 4, -4, 16), "")
Just van Rossum40f9b7b1999-01-30 22:39:17 +000087 self.w.hits = W.TextBox((4, 24, -4, 16), "Hits: 0")
88 self.w.canceltip = W.TextBox((4, 44, -4, 16), "Type cmd-period (.) to cancel.")
89 self.w.open()
90
91 def set(self, path, hits):
92 self.w.searching.set(path)
93 self.w.hits.set('Hits: ' + `hits`)
94 app.breathe()
95
96 def close(self):
97 self.w.close()
98
99
100def match(text, patterns, all):
101 hits = []
102 hitsappend = hits.append
103 stringcount = string.count
104 for pat in patterns:
105 c = stringcount(text, pat)
106 if c > 0:
107 hitsappend((c, pat))
108 elif all:
109 hits[:] = []
110 break
111 hits.sort()
112 hits.reverse()
113 return hits
114
Just van Rossumcee949f2001-12-03 18:11:36 +0000115
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000116def dosearch(docpath, searchstring, settings):
117 (docpath, kind, case, word, tut, lib, ref, ext, api) = settings
118 books = [(tut, 'tut'), (lib, 'lib'), (ref, 'ref'), (ext, 'ext'), (api, 'api')]
119 if not case:
120 searchstring = string.lower(searchstring)
121
122 if kind == 1:
123 patterns = string.split(searchstring)
124 all = 1
125 elif kind == 2:
126 patterns = string.split(searchstring)
127 all = 0
128 else:
129 patterns = [searchstring]
130 all = 0 # not relevant
131
132 ospathjoin = os.path.join
133 stringlower = string.lower
134 status = Status()
135 statusset = status.set
136 _match = match
137 _open = open
138 hits = {}
139 try:
140 MacOS.EnableAppswitch(0)
141 try:
142 for do, name in books:
143 if not do:
144 continue
145 bookpath = ospathjoin(docpath, name)
146 if not os.path.exists(bookpath):
147 continue
148 files = os.listdir(bookpath)
149 for file in files:
150 fullpath = ospathjoin(bookpath, file)
151 if fullpath[-5:] <> '.html':
152 continue
153 statusset(fullpath, len(hits))
154 f = _open(fullpath)
155 text = f.read()
156 if not case:
157 text = stringlower(text)
158 f.close()
159 filehits = _match(text, patterns, all)
160 if filehits:
161 hits[fullpath] = filehits
162 finally:
163 MacOS.EnableAppswitch(-1)
164 status.close()
165 except KeyboardInterrupt:
166 pass
167 hits = hits.items()
168 hits.sort()
169 return hits
170
171
172class PyDocSearch:
173
174 def __init__(self):
175 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
176 try:
177 (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine
178 except:
179 (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine = \
180 ("", 0, 0, 0, 1, 1, 0, 0, 0)
181
182 if docpath and not verifydocpath(docpath):
183 docpath = ""
184
185 self.w = W.Window((400, 200), "Search the Python Documentation")
186 self.w.searchtext = W.EditText((10, 10, -100, 20), callback = self.checkbuttons)
187 self.w.searchbutton = W.Button((-90, 12, 80, 16), "Search", self.search)
188 buttons = []
189
190 gutter = 10
191 width = 130
192 bookstart = width + 2 * gutter
193 self.w.phraseradio = W.RadioButton((10, 38, width, 16), "As a phrase", buttons)
194 self.w.allwordsradio = W.RadioButton((10, 58, width, 16), "All words", buttons)
195 self.w.anywordsradio = W.RadioButton((10, 78, width, 16), "Any word", buttons)
196 self.w.casesens = W.CheckBox((10, 98, width, 16), "Case sensitive")
197 self.w.wholewords = W.CheckBox((10, 118, width, 16), "Whole words")
198 self.w.tutorial = W.CheckBox((bookstart, 38, -10, 16), "Tutorial")
199 self.w.library = W.CheckBox((bookstart, 58, -10, 16), "Library reference")
200 self.w.langueref = W.CheckBox((bookstart, 78, -10, 16), "Lanuage reference manual")
201 self.w.extending = W.CheckBox((bookstart, 98, -10, 16), "Extending & embedding")
202 self.w.api = W.CheckBox((bookstart, 118, -10, 16), "C/C++ API")
203
Just van Rossumcee949f2001-12-03 18:11:36 +0000204 self.w.setdocfolderbutton = W.Button((10, -30, 100, 16), "Set doc folder", self.setdocpath)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000205
206 if docpath:
207 self.w.setdefaultbutton(self.w.searchbutton)
208 else:
209 self.w.setdefaultbutton(self.w.setdocfolderbutton)
210
211 self.docpath = docpath
212 if not docpath:
213 docpath = "(please select the Python html documentation folder)"
Just van Rossumcee949f2001-12-03 18:11:36 +0000214 self.w.docfolder = W.TextBox((120, -28, -10, 16), docpath)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000215
216 [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio][kind].set(1)
217
218 self.w.casesens.set(case)
219 self.w.wholewords.set(word)
220 self.w.tutorial.set(tut)
221 self.w.library.set(lib)
222 self.w.langueref.set(ref)
223 self.w.extending.set(ext)
224 self.w.api.set(api)
225
226 self.w.open()
227 self.w.wholewords.enable(0)
228 self.w.bind('<close>', self.close)
229 self.w.searchbutton.enable(0)
230
231 def search(self):
232 hits = dosearch(self.docpath, self.w.searchtext.get(), self.getsettings())
233 if hits:
234 Results(hits)
235 elif hasattr(MacOS, 'SysBeep'):
236 MacOS.SysBeep(0)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000237
238 def setdocpath(self):
239 fss, ok = macfs.GetDirectory()
240 if ok:
241 docpath = fss.as_pathname()
242 if not verifydocpath(docpath):
243 W.Message("This does not seem to be a Python documentation folder...")
244 else:
245 self.docpath = docpath
246 self.w.docfolder.set(docpath)
247 self.w.setdefaultbutton(self.w.searchbutton)
248
249 def close(self):
250 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
251 prefs.docsearchengine = self.getsettings()
252
253 def getsettings(self):
254 radiobuttons = [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio]
255 for i in range(3):
256 if radiobuttons[i].get():
257 kind = i
258 break
259 docpath = self.docpath
260 case = self.w.casesens.get()
261 word = self.w.wholewords.get()
262 tut = self.w.tutorial.get()
263 lib = self.w.library.get()
264 ref = self.w.langueref.get()
265 ext = self.w.extending.get()
266 api = self.w.api.get()
267 return (docpath, kind, case, word, tut, lib, ref, ext, api)
268
269 def checkbuttons(self):
270 self.w.searchbutton.enable(not not self.w.searchtext.get())