blob: fc8a8f1e4f19888c3106615099a0ec90513e0a7c [file] [log] [blame]
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +00001import sys
2import os
3import string
Guido van Rossumb3418881998-10-13 03:45:15 +00004import imp
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +00005from Tkinter import *
Guido van Rossumb3418881998-10-13 03:45:15 +00006import tkSimpleDialog
Guido van Rossum2aeeb551998-10-12 21:01:37 +00007import tkMessageBox
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +00008
Guido van Rossum2aeeb551998-10-12 21:01:37 +00009about_title = "About IDLE"
10about_text = """\
11IDLE 0.1
12
13A not totally unintegrated development environment for Python
14
15by Guido van Rossum
16"""
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000017
18class EditorWindow:
19
20 from Percolator import Percolator
21 from ColorDelegator import ColorDelegator
22 from UndoDelegator import UndoDelegator
23 from IOBinding import IOBinding
24 from SearchBinding import SearchBinding
25 from AutoIndent import AutoIndent
26 from AutoExpand import AutoExpand
27 import Bindings
Guido van Rossum2aeeb551998-10-12 21:01:37 +000028
29 about_title = about_title
30 about_text = about_text
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000031
32 def __init__(self, root, filename=None):
Guido van Rossum2aeeb551998-10-12 21:01:37 +000033 self.root = root
34 self.menubar = Menu(root)
35 self.top = top = Toplevel(root, menu=self.menubar)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000036 self.vbar = vbar = Scrollbar(top, name='vbar')
37 self.text = text = Text(top, name='text')
38
Guido van Rossum2aeeb551998-10-12 21:01:37 +000039 self.createmenubar()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000040 self.Bindings.apply_bindings(text)
41
42 self.top.protocol("WM_DELETE_WINDOW", self.close)
43 self.top.bind("<<close-window>>", self.close_event)
44 self.text.bind("<<center-insert>>", self.center_insert_event)
Guido van Rossume7b2e651998-10-12 23:56:08 +000045 self.text.bind("<<help>>", self.help_dialog)
46 self.text.bind("<<about-idle>>", self.about_dialog)
Guido van Rossumb3418881998-10-13 03:45:15 +000047 self.text.bind("<<open-module>>", self.open_module)
Guido van Rossumbaf53b41998-10-16 20:08:34 +000048 self.text.bind("<<do-nothing>>", lambda event: "break")
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000049
50 vbar['command'] = text.yview
51 vbar.pack(side=RIGHT, fill=Y)
52
53 text['yscrollcommand'] = vbar.set
54 text['background'] = 'white'
55 if sys.platform[:3] == 'win':
56 text['font'] = ("lucida console", 8)
57 text.pack(side=LEFT, fill=BOTH, expand=1)
58 text.focus_set()
59
60 self.auto = auto = self.AutoIndent(text)
61 self.autoex = self.AutoExpand(text)
62 self.per = per = self.Percolator(text)
63 if self.ispythonsource(filename):
64 self.color = color = self.ColorDelegator(); per.insertfilter(color)
65 ##print "Initial colorizer"
66 else:
67 ##print "No initial colorizer"
68 self.color = None
69 self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
70 self.search = search = self.SearchBinding(undo)
71 self.io = io = self.IOBinding(undo)
72
73 undo.set_saved_change_hook(self.saved_change_hook)
74 io.set_filename_change_hook(self.filename_change_hook)
75
76 if filename:
77 if os.path.exists(filename):
78 io.loadfile(filename)
79 else:
80 io.set_filename(filename)
81
82 self.saved_change_hook()
83
Guido van Rossume7b2e651998-10-12 23:56:08 +000084 menu_specs = [
85 ("file", "File"),
86 ("edit", "Edit"),
87 ("help", "Help"),
88 ]
89
Guido van Rossum2aeeb551998-10-12 21:01:37 +000090 def createmenubar(self):
91 mbar = self.menubar
Guido van Rossume7b2e651998-10-12 23:56:08 +000092 self.menudict = mdict = {}
93 for name, label in self.menu_specs:
94 mdict[name] = menu = Menu(mbar, name=name)
95 mbar.add_cascade(label=label, menu=menu)
96 self.Bindings.fill_menus(self.text, mdict)
Guido van Rossum2aeeb551998-10-12 21:01:37 +000097
Guido van Rossume7b2e651998-10-12 23:56:08 +000098 def about_dialog(self, event=None):
Guido van Rossum2aeeb551998-10-12 21:01:37 +000099 tkMessageBox.showinfo(self.about_title, self.about_text,
100 master=self.text)
101
Guido van Rossume7b2e651998-10-12 23:56:08 +0000102 def help_dialog(self, event=None):
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000103 from HelpWindow import HelpWindow
104 HelpWindow(root=self.root)
Guido van Rossumb3418881998-10-13 03:45:15 +0000105
106 def open_module(self, event=None):
107 try:
108 name = self.text.get("sel.first", "sel.last")
109 except TclError:
110 name = ""
111 else:
112 name = string.strip(name)
113 if not name:
114 name = tkSimpleDialog.askstring("Module",
Guido van Rossume1dedc01998-10-16 16:09:57 +0000115 "Enter the name of a Python module\n"
116 "to search on sys.path and open:",
Guido van Rossumb3418881998-10-13 03:45:15 +0000117 parent=self.text)
118 if name:
119 name = string.strip(name)
120 if not name:
121 return
122 try:
123 (f, file, (suffix, mode, type)) = imp.find_module(name)
124 except ImportError, msg:
125 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
126 return
127 if type != imp.PY_SOURCE:
128 tkMessageBox.showerror("Unsupported type",
129 "%s is not a source module" % name, parent=self.text)
130 return
131 if f:
132 f.close()
133 self.flist.open(file, self)
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000134
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000135 def gotoline(self, lineno):
136 if lineno is not None and lineno > 0:
137 self.text.mark_set("insert", "%d.0" % lineno)
138 self.text.tag_remove("sel", "1.0", "end")
139 self.text.tag_add("sel", "insert", "insert +1l")
140 self.center()
141
142 def ispythonsource(self, filename):
143 if not filename:
144 return 1
145 if os.path.normcase(filename[-3:]) == ".py":
146 return 1
147 try:
148 f = open(filename)
149 line = f.readline()
150 f.close()
151 except IOError:
152 return 0
153 return line[:2] == '#!' and string.find(line, 'python') >= 0
154
155 close_hook = None
156
157 def set_close_hook(self, close_hook):
158 self.close_hook = close_hook
159
160 def filename_change_hook(self):
161 self.saved_change_hook()
162 if self.ispythonsource(self.io.filename):
163 self.addcolorizer()
164 else:
165 self.rmcolorizer()
166
167 def addcolorizer(self):
168 if self.color:
169 return
170 ##print "Add colorizer"
171 self.per.removefilter(self.undo)
172 self.color = self.ColorDelegator()
173 self.per.insertfilter(self.color)
174 self.per.insertfilter(self.undo)
175
176 def rmcolorizer(self):
177 if not self.color:
178 return
179 ##print "Remove colorizer"
180 self.per.removefilter(self.undo)
181 self.per.removefilter(self.color)
182 self.color = None
183 self.per.insertfilter(self.undo)
184
185 def saved_change_hook(self):
186 if self.io.filename:
187 title = self.io.filename
188 else:
189 title = "(Untitled)"
190 if not self.undo.get_saved():
191 title = title + " *"
192 self.top.wm_title(title)
193
194 def center_insert_event(self, event):
195 self.center()
196
197 def center(self, mark="insert"):
198 insert = float(self.text.index(mark + " linestart"))
199 end = float(self.text.index("end"))
200 if insert > end-insert:
201 self.text.see("1.0")
202 else:
203 self.text.see("end")
204 self.text.see(mark)
205
206 def close_event(self, event):
207 self.close()
208
209 def close(self):
210 self.top.wm_deiconify()
211 self.top.tkraise()
212 reply = self.io.maybesave()
213 if reply != "cancel":
214 if self.color and self.color.colorizing:
215 self.color.close()
216 self.top.bell()
217 return "cancel"
218 if self.close_hook:
219 self.close_hook()
220 if self.color:
221 self.color.close() # Cancel colorization
222 self.top.destroy()
223 return reply
224
225
226def fixwordbreaks(root):
227 tk = root.tk
228 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
229 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
230 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
231
232
233def test():
234 root = Tk()
235 fixwordbreaks(root)
236 root.withdraw()
237 if sys.argv[1:]:
238 filename = sys.argv[1]
239 else:
240 filename = None
241 edit = EditorWindow(root, filename)
242 edit.set_close_hook(root.quit)
243 root.mainloop()
244 root.destroy()
245
246if __name__ == '__main__':
247 test()