blob: 61d3959f6cd25aebb6a4693dbe9b68c3a661ec5a [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
Jack Jansenfd0b00e2003-01-26 22:15:48 +00009import EasyDialogs
Just van Rossum40f9b7b1999-01-30 22:39:17 +000010
Just van Rossum40f9b7b1999-01-30 22:39:17 +000011
12app = W.getapplication()
13
Jack Jansen9ad27522001-02-21 13:54:31 +000014_titlepat = re.compile('<title>\([^<]*\)</title>')
Just van Rossum40f9b7b1999-01-30 22:39:17 +000015
16def sucktitle(path):
17 f = open(path)
18 text = f.read(1024) # assume the title is in the first 1024 bytes
19 f.close()
Just van Rossumcee949f2001-12-03 18:11:36 +000020 lowertext = text.lower()
Jack Jansen9ad27522001-02-21 13:54:31 +000021 matcher = _titlepat.search(lowertext)
22 if matcher:
23 return matcher.group(1)
Just van Rossum40f9b7b1999-01-30 22:39:17 +000024 return path
25
26def verifydocpath(docpath):
27 try:
28 tut = os.path.join(docpath, "tut")
29 lib = os.path.join(docpath, "lib")
30 ref = os.path.join(docpath, "ref")
31 for path in [tut, lib, ref]:
32 if not os.path.exists(path):
33 return 0
34 except:
35 return 0
36 return 1
37
38
Just van Rossum40f9b7b1999-01-30 22:39:17 +000039_resultscounter = 1
40
41class Results:
42
43 def __init__(self, hits):
44 global _resultscounter
45 hits = map(lambda (path, hits): (sucktitle(path), path, hits), hits)
46 hits.sort()
47 self.hits = hits
48 nicehits = map(
49 lambda (title, path, hits):
50 title + '\r' + string.join(
51 map(lambda (c, p): "%s (%d)" % (p, c), hits), ', '), hits)
52 nicehits.sort()
53 self.w = W.Window((440, 300), "Search results %d" % _resultscounter, minsize = (200, 100))
Just van Rossumcee949f2001-12-03 18:11:36 +000054 self.w.results = W.TwoLineList((-1, -1, 1, -14), nicehits, self.listhit)
Just van Rossum40f9b7b1999-01-30 22:39:17 +000055 self.w.open()
56 self.w.bind('return', self.listhit)
57 self.w.bind('enter', self.listhit)
58 _resultscounter = _resultscounter + 1
Just van Rossum40f9b7b1999-01-30 22:39:17 +000059
60 def listhit(self, isdbl = 1):
61 if isdbl:
62 for i in self.w.results.getselection():
Just van Rossumcee949f2001-12-03 18:11:36 +000063 path = self.hits[i][1]
64 url = "file://" + "/".join(path.split(":"))
65 webbrowser.open(url)
66
Just van Rossum40f9b7b1999-01-30 22:39:17 +000067
68class Status:
69
70 def __init__(self):
Just van Rossumdc3c6172001-06-19 21:37:33 +000071 self.w = W.Dialog((440, 64), "Searching\xc9")
Just van Rossumcee949f2001-12-03 18:11:36 +000072 self.w.searching = W.TextBox((4, 4, -4, 16), "")
Just van Rossum40f9b7b1999-01-30 22:39:17 +000073 self.w.hits = W.TextBox((4, 24, -4, 16), "Hits: 0")
74 self.w.canceltip = W.TextBox((4, 44, -4, 16), "Type cmd-period (.) to cancel.")
75 self.w.open()
76
77 def set(self, path, hits):
78 self.w.searching.set(path)
79 self.w.hits.set('Hits: ' + `hits`)
80 app.breathe()
81
82 def close(self):
83 self.w.close()
84
85
86def match(text, patterns, all):
87 hits = []
88 hitsappend = hits.append
89 stringcount = string.count
90 for pat in patterns:
91 c = stringcount(text, pat)
92 if c > 0:
93 hitsappend((c, pat))
94 elif all:
95 hits[:] = []
96 break
97 hits.sort()
98 hits.reverse()
99 return hits
100
Just van Rossumcee949f2001-12-03 18:11:36 +0000101
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000102def dosearch(docpath, searchstring, settings):
103 (docpath, kind, case, word, tut, lib, ref, ext, api) = settings
104 books = [(tut, 'tut'), (lib, 'lib'), (ref, 'ref'), (ext, 'ext'), (api, 'api')]
105 if not case:
106 searchstring = string.lower(searchstring)
107
108 if kind == 1:
109 patterns = string.split(searchstring)
110 all = 1
111 elif kind == 2:
112 patterns = string.split(searchstring)
113 all = 0
114 else:
115 patterns = [searchstring]
116 all = 0 # not relevant
117
118 ospathjoin = os.path.join
119 stringlower = string.lower
120 status = Status()
121 statusset = status.set
122 _match = match
123 _open = open
124 hits = {}
125 try:
Jack Jansen815d2bf2002-01-21 23:00:52 +0000126 if hasattr(MacOS, 'EnableAppswitch'):
127 MacOS.EnableAppswitch(0)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000128 try:
129 for do, name in books:
130 if not do:
131 continue
132 bookpath = ospathjoin(docpath, name)
133 if not os.path.exists(bookpath):
134 continue
135 files = os.listdir(bookpath)
136 for file in files:
137 fullpath = ospathjoin(bookpath, file)
138 if fullpath[-5:] <> '.html':
139 continue
140 statusset(fullpath, len(hits))
141 f = _open(fullpath)
142 text = f.read()
143 if not case:
144 text = stringlower(text)
145 f.close()
146 filehits = _match(text, patterns, all)
147 if filehits:
148 hits[fullpath] = filehits
149 finally:
Jack Jansen815d2bf2002-01-21 23:00:52 +0000150 if hasattr(MacOS, 'EnableAppswitch'):
151 MacOS.EnableAppswitch(-1)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000152 status.close()
153 except KeyboardInterrupt:
154 pass
155 hits = hits.items()
156 hits.sort()
157 return hits
158
159
160class PyDocSearch:
161
162 def __init__(self):
163 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
164 try:
165 (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine
166 except:
167 (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine = \
168 ("", 0, 0, 0, 1, 1, 0, 0, 0)
169
170 if docpath and not verifydocpath(docpath):
171 docpath = ""
172
173 self.w = W.Window((400, 200), "Search the Python Documentation")
174 self.w.searchtext = W.EditText((10, 10, -100, 20), callback = self.checkbuttons)
175 self.w.searchbutton = W.Button((-90, 12, 80, 16), "Search", self.search)
176 buttons = []
177
178 gutter = 10
179 width = 130
180 bookstart = width + 2 * gutter
181 self.w.phraseradio = W.RadioButton((10, 38, width, 16), "As a phrase", buttons)
182 self.w.allwordsradio = W.RadioButton((10, 58, width, 16), "All words", buttons)
183 self.w.anywordsradio = W.RadioButton((10, 78, width, 16), "Any word", buttons)
184 self.w.casesens = W.CheckBox((10, 98, width, 16), "Case sensitive")
185 self.w.wholewords = W.CheckBox((10, 118, width, 16), "Whole words")
186 self.w.tutorial = W.CheckBox((bookstart, 38, -10, 16), "Tutorial")
187 self.w.library = W.CheckBox((bookstart, 58, -10, 16), "Library reference")
188 self.w.langueref = W.CheckBox((bookstart, 78, -10, 16), "Lanuage reference manual")
189 self.w.extending = W.CheckBox((bookstart, 98, -10, 16), "Extending & embedding")
190 self.w.api = W.CheckBox((bookstart, 118, -10, 16), "C/C++ API")
191
Just van Rossumcee949f2001-12-03 18:11:36 +0000192 self.w.setdocfolderbutton = W.Button((10, -30, 100, 16), "Set doc folder", self.setdocpath)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000193
194 if docpath:
195 self.w.setdefaultbutton(self.w.searchbutton)
196 else:
197 self.w.setdefaultbutton(self.w.setdocfolderbutton)
198
199 self.docpath = docpath
200 if not docpath:
201 docpath = "(please select the Python html documentation folder)"
Just van Rossumcee949f2001-12-03 18:11:36 +0000202 self.w.docfolder = W.TextBox((120, -28, -10, 16), docpath)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000203
204 [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio][kind].set(1)
205
206 self.w.casesens.set(case)
207 self.w.wholewords.set(word)
208 self.w.tutorial.set(tut)
209 self.w.library.set(lib)
210 self.w.langueref.set(ref)
211 self.w.extending.set(ext)
212 self.w.api.set(api)
213
214 self.w.open()
215 self.w.wholewords.enable(0)
216 self.w.bind('<close>', self.close)
217 self.w.searchbutton.enable(0)
218
219 def search(self):
220 hits = dosearch(self.docpath, self.w.searchtext.get(), self.getsettings())
221 if hits:
222 Results(hits)
223 elif hasattr(MacOS, 'SysBeep'):
224 MacOS.SysBeep(0)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000225
226 def setdocpath(self):
Jack Jansenfd0b00e2003-01-26 22:15:48 +0000227 docpath = EasyDialogs.AskFolder()
228 if docpath:
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000229 if not verifydocpath(docpath):
230 W.Message("This does not seem to be a Python documentation folder...")
231 else:
232 self.docpath = docpath
233 self.w.docfolder.set(docpath)
234 self.w.setdefaultbutton(self.w.searchbutton)
235
236 def close(self):
237 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
238 prefs.docsearchengine = self.getsettings()
239
240 def getsettings(self):
241 radiobuttons = [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio]
242 for i in range(3):
243 if radiobuttons[i].get():
244 kind = i
245 break
246 docpath = self.docpath
247 case = self.w.casesens.get()
248 word = self.w.wholewords.get()
249 tut = self.w.tutorial.get()
250 lib = self.w.library.get()
251 ref = self.w.langueref.get()
252 ext = self.w.extending.get()
253 api = self.w.api.get()
254 return (docpath, kind, case, word, tut, lib, ref, ext, api)
255
256 def checkbuttons(self):
257 self.w.searchbutton.enable(not not self.w.searchtext.get())