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