Class around PixMap objects that allows more python-like access. By Joe Strout.
[python/dscho.git] / Demo / stdwin / miniedit.py
blob9a11c2da11e1f88bb9c596d59bc01feddcecdc76
1 #! /usr/bin/env python
3 # A miniature multi-window editor using STDWIN's text objects.
5 # Usage: miniedit [file] ...
7 # The user interface is similar to that of the miniedit demo application
8 # in C that comes with STDWIN.
10 # XXX need to comment the functions
11 # XXX Not yet implemented:
12 # disabling menu entries for inapplicable actions
13 # Find operations
16 import sys
17 import stdwin
18 from stdwinevents import *
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.
27 changing = [WC_RETURN, WC_TAB, WC_BACKSPACE]
30 # The list of currently open windows;
31 # this is maintained so we can stop when there are no windows left
33 windows = []
36 # A note on window data attributes (set by open_window):
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
43 # Main program
45 def main():
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
51 stdwin.setdefwinsize(40*stdwin.textwidth('in'), 24*stdwin.lineheight())
53 # Create global menus (as local variables)
55 filemenu = make_file_menu(stdwin)
56 editmenu = make_edit_menu(stdwin)
57 findmenu = make_find_menu(stdwin)
59 # Get the list of files from the command line (maybe none)
61 files = sys.argv[1:]
63 # Open any files -- errors will be reported but do won't stop us
65 for filename in files:
66 open_file(filename)
68 # If there were no files, or none of them could be opened,
69 # put up a dialog asking for a filename
71 if not windows:
72 try:
73 open_dialog(None)
74 except KeyboardInterrupt:
75 pass # User cancelled
77 # If the dialog was cancelled, create an empty new window
79 if not windows:
80 new_window(None)
82 # Main event loop -- stop when we have no open windows left
84 while windows:
86 # Get the next event -- ignore interrupts
88 try:
89 type, window, detail = event = stdwin.getevent()
90 except KeyboardInterrupt:
91 type, window, detail = event = WE_NONE, None, None
93 # Event decoding switch
95 if not window:
96 pass # Ignore such events
97 elif type == WE_MENU:
99 # Execute menu operation
101 menu, item = detail
102 try:
103 menu.actions[item](window)
104 except KeyboardInterrupt:
105 pass # User cancelled
106 elif type == WE_CLOSE:
108 # Close a window
110 try:
111 close_dialog(window)
112 except KeyboardInterrupt:
113 pass # User cancelled
114 elif type == WE_SIZE:
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
121 fix_textsize(window)
122 elif window.textobject.event(event):
124 # The event was eaten by the text object --
125 # set the changed flag if not already set
127 if type == WE_CHAR or \
128 type == WE_COMMAND and detail in changing:
129 window.changed = 1
130 fix_docsize(window)
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
137 del type, window, detail, event
140 def 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
154 def 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
165 def make_find_menu(object):
166 menu = object.menucreate('Find')
167 menu.actions = []
168 # XXX
169 return menu
172 def additem(menu, text, shortcut, function):
173 if shortcut:
174 menu.additem(text, shortcut)
175 else:
176 menu.additem(text)
177 menu.actions.append(function)
180 def open_dialog(current_ignored):
181 filename = stdwin.askfile('Open file:', '', 0)
182 open_file(filename)
185 def 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)
200 def new_window(current_ignored):
201 open_window('', 'Untitled', '')
204 def 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)
218 def quit_dialog(window):
219 for window in windows[:]:
220 close_dialog(window)
223 def 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
234 def 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
242 def 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
251 def 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)
257 def 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
272 def 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)
292 def fix_textsize(window):
293 corner = window.getwinsize()
294 area = (0, 0), (corner)
295 window.textobject.move(area)
296 fix_docsize(window)
299 def fix_docsize(window):
300 area = window.textobject.getrect()
301 origin, corner = area
302 width, height = corner
303 window.setdocsize(0, height)
306 def 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)
320 def 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)
331 def 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)
342 def 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)
352 def do_select_all(window):
353 window.textobject.setfocus(0, 0x7fffffff) # XXX Smaller on the Mac!
356 main()