blob: 9a11c2da11e1f88bb9c596d59bc01feddcecdc76 [file] [log] [blame]
Guido van Rossumf06ee5f1996-11-27 19:52:01 +00001#! /usr/bin/env python
Guido van Rossum9cf8f331992-03-30 10:54:51 +00002
3# A miniature multi-window editor using STDWIN's text objects.
4#
5# Usage: miniedit [file] ...
6#
7# The user interface is similar to that of the miniedit demo application
8# in C that comes with STDWIN.
9#
10# XXX need to comment the functions
11# XXX Not yet implemented:
12# disabling menu entries for inapplicable actions
13# Find operations
14
15
16import sys
17import stdwin
18from stdwinevents import *
19
20
21# Constant: list of WE_COMMAND events that (may) change the text buffer
22# so we can decide whether to set the 'changed' flag.
23# Note that it is possible for such a command to fail (a backspace
24# at the beginning of the buffer) but we'll set the changed flag anyway
25# -- it's too complicated to check this condition right now.
26#
27changing = [WC_RETURN, WC_TAB, WC_BACKSPACE]
28
29
30# The list of currently open windows;
31# this is maintained so we can stop when there are no windows left
32#
33windows = []
34
35
36# A note on window data attributes (set by open_window):
37#
38# w.textobject the window's text object
39# w.changed true when the window's text is changed
40# w.filename filename connected to the window; '' if none
41
42
43# Main program
44#
45def main():
46 #
47 # Set a reasonable default window size.
48 # If we are using a fixed-width font this will open a 80x24 window;
49 # for variable-width fonts we approximate this based on an average
50 #
51 stdwin.setdefwinsize(40*stdwin.textwidth('in'), 24*stdwin.lineheight())
52 #
53 # Create global menus (as local variables)
54 #
55 filemenu = make_file_menu(stdwin)
56 editmenu = make_edit_menu(stdwin)
57 findmenu = make_find_menu(stdwin)
58 #
59 # Get the list of files from the command line (maybe none)
60 #
61 files = sys.argv[1:]
62 #
63 # Open any files -- errors will be reported but do won't stop us
64 #
65 for filename in files:
66 open_file(filename)
67 #
68 # If there were no files, or none of them could be opened,
69 # put up a dialog asking for a filename
70 #
71 if not windows:
72 try:
73 open_dialog(None)
74 except KeyboardInterrupt:
75 pass # User cancelled
76 #
77 # If the dialog was cancelled, create an empty new window
78 #
79 if not windows:
80 new_window(None)
81 #
82 # Main event loop -- stop when we have no open windows left
83 #
84 while windows:
85 #
86 # Get the next event -- ignore interrupts
87 #
88 try:
89 type, window, detail = event = stdwin.getevent()
90 except KeyboardInterrupt:
91 type, window, detail = event = WE_NONE, None, None
92 #
93 # Event decoding switch
94 #
95 if not window:
96 pass # Ignore such events
97 elif type == WE_MENU:
98 #
99 # Execute menu operation
100 #
101 menu, item = detail
102 try:
103 menu.actions[item](window)
104 except KeyboardInterrupt:
105 pass # User cancelled
106 elif type == WE_CLOSE:
107 #
108 # Close a window
109 #
110 try:
111 close_dialog(window)
112 except KeyboardInterrupt:
113 pass # User cancelled
114 elif type == WE_SIZE:
115 #
116 # A window was resized --
117 # let the text object recompute the line breaks
118 # and change the document size accordingly,
119 # so scroll bars will work
120 #
121 fix_textsize(window)
122 elif window.textobject.event(event):
123 #
124 # The event was eaten by the text object --
125 # set the changed flag if not already set
126 #
127 if type == WE_CHAR or \
128 type == WE_COMMAND and detail in changing:
129 window.changed = 1
130 fix_docsize(window)
131 #
132 # Delete all objects that may still reference the window
133 # in the event -- this is needed otherwise the window
134 # won't actually be closed and may receive further
135 # events, which will confuse the event decoder
136 #
137 del type, window, detail, event
138
139
140def make_file_menu(object):
141 menu = object.menucreate('File')
142 menu.actions = []
143 additem(menu, 'New', 'N', new_window)
144 additem(menu, 'Open..', 'O', open_dialog)
145 additem(menu, '', '', None)
146 additem(menu, 'Save', 'S', save_dialog)
147 additem(menu, 'Save As..', '', save_as_dialog)
148 additem(menu, 'Save a Copy..', '', save_copy_dialog)
149 additem(menu, 'Revert', 'R', revert_dialog)
150 additem(menu, 'Quit', 'Q', quit_dialog)
151 return menu
152
153
154def make_edit_menu(object):
155 menu = object.menucreate('Edit')
156 menu.actions = []
157 additem(menu, 'Cut', 'X', do_cut)
158 additem(menu, 'Copy', 'C', do_copy)
159 additem(menu, 'Paste', 'V', do_paste)
160 additem(menu, 'Clear', 'B', do_clear)
161 additem(menu, 'Select All', 'A', do_select_all)
162 return menu
163
164
165def make_find_menu(object):
166 menu = object.menucreate('Find')
167 menu.actions = []
168 # XXX
169 return menu
170
171
172def additem(menu, text, shortcut, function):
173 if shortcut:
174 menu.additem(text, shortcut)
175 else:
176 menu.additem(text)
177 menu.actions.append(function)
178
179
180def open_dialog(current_ignored):
181 filename = stdwin.askfile('Open file:', '', 0)
182 open_file(filename)
183
184
185def open_file(filename):
186 try:
187 fp = open(filename, 'r')
188 except RuntimeError:
189 stdwin.message(filename + ': cannot open')
190 return # Error, forget it
191 try:
192 contents = fp.read()
193 except RuntimeError:
194 stdwin.message(filename + ': read error')
195 return # Error, forget it
196 del fp # Close the file
197 open_window(filename, filename, contents)
198
199
200def new_window(current_ignored):
201 open_window('', 'Untitled', '')
202
203
204def open_window(filename, title, contents):
205 try:
206 window = stdwin.open(title)
207 except RuntimeError:
208 stdwin.message('cannot open new window')
209 return # Error, forget it
210 window.textobject = window.textcreate((0, 0), window.getwinsize())
211 window.textobject.settext(contents)
212 window.changed = 0
213 window.filename = filename
214 fix_textsize(window)
215 windows.append(window)
216
217
218def quit_dialog(window):
219 for window in windows[:]:
220 close_dialog(window)
221
222
223def close_dialog(window):
224 if window.changed:
225 prompt = 'Save changes to ' + window.gettitle() + ' ?'
226 if stdwin.askync(prompt, 1):
227 save_dialog(window)
228 if window.changed:
229 return # Save failed (not) cancelled
230 windows.remove(window)
231 del window.textobject
232
233
234def save_dialog(window):
235 if not window.filename:
236 save_as_dialog(window)
237 return
238 if save_file(window, window.filename):
239 window.changed = 0
240
241
242def save_as_dialog(window):
243 prompt = 'Save ' + window.gettitle() + ' as:'
244 filename = stdwin.askfile(prompt, window.filename, 1)
245 if save_file(window, filename):
246 window.filename = filename
247 window.settitle(filename)
248 window.changed = 0
249
250
251def save_copy_dialog(window):
252 prompt = 'Save a copy of ' + window.gettitle() + ' as:'
253 filename = stdwin.askfile(prompt, window.filename, 1)
254 void = save_file(window, filename)
255
256
257def save_file(window, filename):
258 try:
259 fp = open(filename, 'w')
260 except RuntimeError:
261 stdwin.message(filename + ': cannot create')
262 return 0
263 contents = window.textobject.gettext()
264 try:
265 fp.write(contents)
266 except RuntimeError:
267 stdwin.message(filename + ': write error')
268 return 0
269 return 1
270
271
272def revert_dialog(window):
273 if not window.filename:
274 stdwin.message('This window has no file to revert from')
275 return
276 if window.changed:
277 prompt = 'Really read ' + window.filename + ' back from file?'
278 if not stdwin.askync(prompt, 1):
279 return
280 try:
281 fp = open(window.filename, 'r')
282 except RuntimeError:
283 stdwin.message(filename + ': cannot open')
284 return
285 contents = fp.read()
286 del fp # Close the file
287 window.textobject.settext(contents)
288 window.changed = 0
289 fix_docsize(window)
290
291
292def fix_textsize(window):
293 corner = window.getwinsize()
294 area = (0, 0), (corner)
295 window.textobject.move(area)
296 fix_docsize(window)
297
298
299def fix_docsize(window):
300 area = window.textobject.getrect()
301 origin, corner = area
302 width, height = corner
303 window.setdocsize(0, height)
304
305
306def do_cut(window):
307 selection = window.textobject.getfocustext()
308 if not selection:
309 stdwin.fleep() # Nothing to cut
310 elif not window.setselection(WS_PRIMARY, selection):
311 stdwin.fleep() # Window manager glitch...
312 else:
313 stdwin.rotatecutbuffers(1)
314 stdwin.setcutbuffer(0, selection)
315 window.textobject.replace('')
316 window.changed = 1
317 fix_docsize(window)
318
319
320def do_copy(window):
321 selection = window.textobject.getfocustext()
322 if not selection:
323 stdwin.fleep() # Nothing to cut
324 elif not window.setselection(WS_PRIMARY, selection):
325 stdwin.fleep() # Window manager glitch...
326 else:
327 stdwin.rotatecutbuffers(1)
328 stdwin.setcutbuffer(0, selection)
329
330
331def do_paste(window):
332 selection = stdwin.getselection(WS_PRIMARY)
333 if not selection:
334 selection = stdwin.getcutbuffer(0)
335 if not selection:
336 stdwin.fleep() # Nothing to paste
337 else:
338 window.textobject.replace(selection)
339 window.changed = 1
340 fix_docsize(window)
341
342def do_clear(window):
343 first, last = window.textobject.getfocus()
344 if first == last:
345 stdwin.fleep() # Nothing to clear
346 else:
347 window.textobject.replace('')
348 window.changed = 1
349 fix_docsize(window)
350
351
352def do_select_all(window):
353 window.textobject.setfocus(0, 0x7fffffff) # XXX Smaller on the Mac!
354
355
356main()