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
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
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
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)
63 # Open any files -- errors will be reported but do won't stop us
65 for filename
in files
:
68 # If there were no files, or none of them could be opened,
69 # put up a dialog asking for a filename
74 except KeyboardInterrupt:
77 # If the dialog was cancelled, create an empty new window
82 # Main event loop -- stop when we have no open windows left
86 # Get the next event -- ignore interrupts
89 type, window
, detail
= event
= stdwin
.getevent()
90 except KeyboardInterrupt:
91 type, window
, detail
= event
= WE_NONE
, None, None
93 # Event decoding switch
96 pass # Ignore such events
99 # Execute menu operation
103 menu
.actions
[item
](window
)
104 except KeyboardInterrupt:
105 pass # User cancelled
106 elif type == WE_CLOSE
:
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
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
:
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')
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
)
154 def make_edit_menu(object):
155 menu
= object.menucreate('Edit')
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
)
165 def make_find_menu(object):
166 menu
= object.menucreate('Find')
172 def additem(menu
, text
, shortcut
, function
):
174 menu
.additem(text
, shortcut
)
177 menu
.actions
.append(function
)
180 def open_dialog(current_ignored
):
181 filename
= stdwin
.askfile('Open file:', '', 0)
185 def open_file(filename
):
187 fp
= open(filename
, 'r')
189 stdwin
.message(filename
+ ': cannot open')
190 return # Error, forget it
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
):
206 window
= stdwin
.open(title
)
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
)
213 window
.filename
= filename
215 windows
.append(window
)
218 def quit_dialog(window
):
219 for window
in windows
[:]:
223 def close_dialog(window
):
225 prompt
= 'Save changes to ' + window
.gettitle() + ' ?'
226 if stdwin
.askync(prompt
, 1):
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
)
238 if save_file(window
, window
.filename
):
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
)
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
):
259 fp
= open(filename
, 'w')
261 stdwin
.message(filename
+ ': cannot create')
263 contents
= window
.textobject
.gettext()
267 stdwin
.message(filename
+ ': write error')
272 def revert_dialog(window
):
273 if not window
.filename
:
274 stdwin
.message('This window has no file to revert from')
277 prompt
= 'Really read ' + window
.filename
+ ' back from file?'
278 if not stdwin
.askync(prompt
, 1):
281 fp
= open(window
.filename
, 'r')
283 stdwin
.message(filename
+ ': cannot open')
286 del fp
# Close the file
287 window
.textobject
.settext(contents
)
292 def fix_textsize(window
):
293 corner
= window
.getwinsize()
294 area
= (0, 0), (corner
)
295 window
.textobject
.move(area
)
299 def fix_docsize(window
):
300 area
= window
.textobject
.getrect()
301 origin
, corner
= area
302 width
, height
= corner
303 window
.setdocsize(0, height
)
307 selection
= window
.textobject
.getfocustext()
309 stdwin
.fleep() # Nothing to cut
310 elif not window
.setselection(WS_PRIMARY
, selection
):
311 stdwin
.fleep() # Window manager glitch...
313 stdwin
.rotatecutbuffers(1)
314 stdwin
.setcutbuffer(0, selection
)
315 window
.textobject
.replace('')
321 selection
= window
.textobject
.getfocustext()
323 stdwin
.fleep() # Nothing to cut
324 elif not window
.setselection(WS_PRIMARY
, selection
):
325 stdwin
.fleep() # Window manager glitch...
327 stdwin
.rotatecutbuffers(1)
328 stdwin
.setcutbuffer(0, selection
)
331 def do_paste(window
):
332 selection
= stdwin
.getselection(WS_PRIMARY
)
334 selection
= stdwin
.getcutbuffer(0)
336 stdwin
.fleep() # Nothing to paste
338 window
.textobject
.replace(selection
)
342 def do_clear(window
):
343 first
, last
= window
.textobject
.getfocus()
345 stdwin
.fleep() # Nothing to clear
347 window
.textobject
.replace('')
352 def do_select_all(window
):
353 window
.textobject
.setfocus(0, 0x7fffffff) # XXX Smaller on the Mac!