1 # A minimal text editor.
4 # - Functionality: find, etc.
6 from Menu
import DrawMenuBar
7 from FrameWork
import *
19 UNDOLABELS
= [ # Indexed by WEGetUndoInfo() value
20 None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style"]
22 # Style and size menu. Note that style order is important (tied to bit values)
24 ("Bold", "B"), ("Italic", "I"), ("Underline", "U"), ("Outline", "O"),
25 ("Shadow", ""), ("Condensed", ""), ("Extended", "")
27 SIZES
= [ 9, 10, 12, 14, 18, 24]
30 Qd
.SetRectRgn(BIGREGION
, -16000, -16000, 16000, 16000)
32 class WasteWindow(ScrolledWindow
):
33 def open(self
, path
, name
, data
):
36 r
= windowbounds(400, 400)
37 w
= Win
.NewWindow(r
, name
, 1, 0, -1, 1, 0x55555555)
39 vr
= 0, 0, r
[2]-r
[0]-15, r
[3]-r
[1]-15
44 flags
= WASTEconst
.weDoAutoScroll | WASTEconst
.weDoOutlineHilite | \
46 self
.ted
= waste
.WENew(dr
, vr
, flags
)
47 self
.ted
.WEInstallTabHooks()
48 style
, soup
= self
.getstylesoup()
49 self
.ted
.WEInsert(data
, style
, soup
)
50 self
.ted
.WESetSelection(0,0)
52 self
.ted
.WEResetModCount()
56 self
.do_activate(1, None)
58 def getstylesoup(self
):
61 oldrf
= Res
.CurResFile()
63 rf
= Res
.FSpOpenResFile(self
.path
, 1)
67 hstyle
= Res
.Get1Resource('styl', 128)
68 hstyle
.DetachResource()
72 hsoup
= Res
.Get1Resource('SOUP', 128)
73 hsoup
.DetachResource()
80 def do_idle(self
, event
):
81 (what
, message
, when
, where
, modifiers
) = event
84 if self
.ted
.WEAdjustCursor(where
, BIGREGION
):
86 Qd
.SetCursor(Qd
.qd
.arrow
)
88 def getscrollbarvalues(self
):
89 dr
= self
.ted
.WEGetDestRect()
90 vr
= self
.ted
.WEGetViewRect()
91 vx
= self
.scalebarvalue(dr
[0], dr
[2], vr
[0], vr
[2])
92 vy
= self
.scalebarvalue(dr
[1], dr
[3], vr
[1], vr
[3])
95 def scrollbar_callback(self
, which
, what
, value
):
98 height
= self
.ted
.WEGetHeight(0, 0x3fffffff)
99 cur
= self
.getscrollbarvalues()[1]
100 delta
= (cur
-value
)*height
/32767
102 topline_off
,dummy
= self
.ted
.WEGetOffset((1,1))
103 topline_num
= self
.ted
.WEOffsetToLine(topline_off
)
104 delta
= self
.ted
.WEGetHeight(topline_num
, topline_num
+1)
106 delta
= (self
.ted
.WEGetViewRect()[3]-10)
108 delta
= 10 # Random value
110 # XXXX Wrong: should be bottom line size
111 topline_off
,dummy
= self
.ted
.WEGetOffset((1,1))
112 topline_num
= self
.ted
.WEOffsetToLine(topline_off
)
113 delta
= -self
.ted
.WEGetHeight(topline_num
, topline_num
+1)
115 delta
= -(self
.ted
.WEGetViewRect()[3]-10)
118 self
.ted
.WEScroll(0, delta
)
122 vr
= self
.ted
.WEGetViewRect()
123 winwidth
= vr
[2]-vr
[0]
132 self
.ted
.WEScroll(delta
, 0)
134 l
, t
, r
, b
= self
.ted
.WEGetDestRect()
135 vl
, vt
, vr
, vb
= self
.ted
.WEGetViewRect()
140 self
.ted
.WEScroll(dx
, dy
)
142 self
.ted
.WEScroll(0, b
-vb
)
145 def do_activate(self
, onoff
, evt
):
147 ScrolledWindow
.do_activate(self
, onoff
, evt
)
149 self
.ted
.WEActivate()
150 self
.parent
.active
= self
151 self
.parent
.updatemenubar()
153 self
.ted
.WEDeactivate()
155 def do_update(self
, wid
, event
):
156 region
= wid
.GetWindowPort().visRgn
157 if Qd
.EmptyRgn(region
):
160 self
.ted
.WEUpdate(region
)
161 self
.updatescrollbars()
163 def do_postresize(self
, width
, height
, window
):
164 l
, t
, r
, b
= self
.ted
.WEGetViewRect()
165 vr
= (l
, t
, l
+width
-15, t
+height
-15)
166 self
.ted
.WESetViewRect(vr
)
167 self
.wid
.InvalWindowRect(vr
)
168 ScrolledWindow
.do_postresize(self
, width
, height
, window
)
170 def do_contentclick(self
, local
, modifiers
, evt
):
171 (what
, message
, when
, where
, modifiers
) = evt
172 self
.ted
.WEClick(local
, modifiers
, when
)
173 self
.updatescrollbars()
174 self
.parent
.updatemenubar()
176 def do_char(self
, ch
, event
):
178 (what
, message
, when
, where
, modifiers
) = event
179 self
.ted
.WEKey(ord(ch
), modifiers
)
180 self
.updatescrollbars()
181 self
.parent
.updatemenubar()
184 if self
.ted
.WEGetModCount():
185 save
= EasyDialogs
.AskYesNoCancel('Save window "%s" before closing?'%self
.name
, 1)
190 if self
.parent
.active
== self
:
191 self
.parent
.active
= None
192 self
.parent
.updatemenubar()
199 return # Will call us recursively
203 dhandle
= self
.ted
.WEGetText()
205 fp
= open(self
.path
, 'wb') # NOTE: wb, because data has CR for end-of-line
207 if data
[-1] <> '\r': fp
.write('\r')
210 # Now save style and soup
212 oldresfile
= Res
.CurResFile()
214 rf
= Res
.FSpOpenResFile(self
.path
, 3)
216 Res
.FSpCreateResFile(self
.path
, '????', 'TEXT', MACFS
.smAllScripts
)
217 rf
= Res
.FSpOpenResFile(self
.path
, 3)
218 styles
= Res
.Resource('')
219 soup
= Res
.Resource('')
220 self
.ted
.WECopyRange(0, 0x3fffffff, None, styles
, soup
)
221 styles
.AddResource('styl', 128, '')
222 soup
.AddResource('SOUP', 128, '')
224 Res
.UseResFile(oldresfile
)
226 self
.ted
.WEResetModCount()
228 def menu_save_as(self
):
229 fss
, ok
= macfs
.StandardPutFile('Save as:')
231 self
.path
= fss
.as_pathname()
232 self
.name
= os
.path
.split(self
.path
)[-1]
233 self
.wid
.SetWTitle(self
.name
)
241 self
.updatescrollbars()
242 self
.parent
.updatemenubar()
247 self
.updatescrollbars()
248 self
.parent
.updatemenubar()
250 def menu_paste(self
):
253 self
.updatescrollbars()
254 self
.parent
.updatemenubar()
256 def menu_clear(self
):
259 self
.updatescrollbars()
260 self
.parent
.updatemenubar()
264 self
.updatescrollbars()
265 self
.parent
.updatemenubar()
267 def menu_setfont(self
, font
):
268 font
= Fm
.GetFNum(font
)
269 self
.mysetstyle(WASTEconst
.weDoFont
, (font
, 0, 0, (0,0,0)))
270 self
.parent
.updatemenubar()
272 def menu_modface(self
, face
):
273 self
.mysetstyle(WASTEconst
.weDoFace|WASTEconst
.weDoToggleFace
,
274 (0, face
, 0, (0,0,0)))
276 def menu_setface(self
, face
):
277 self
.mysetstyle(WASTEconst
.weDoFace|WASTEconst
.weDoReplaceFace
,
278 (0, face
, 0, (0,0,0)))
280 def menu_setsize(self
, size
):
281 self
.mysetstyle(WASTEconst
.weDoSize
, (0, 0, size
, (0,0,0)))
283 def menu_incsize(self
, size
):
284 self
.mysetstyle(WASTEconst
.weDoAddSize
, (0, 0, size
, (0,0,0)))
286 def mysetstyle(self
, which
, how
):
288 self
.ted
.WESetStyle(which
, how
)
289 self
.parent
.updatemenubar()
291 def have_selection(self
):
292 start
, stop
= self
.ted
.WEGetSelection()
296 return self
.ted
.WECanPaste()
299 which
, redo
= self
.ted
.WEGetUndoInfo()
300 which
= UNDOLABELS
[which
]
301 if which
== None: return None
307 def getruninfo(self
):
308 all
= (WASTEconst
.weDoFont | WASTEconst
.weDoFace | WASTEconst
.weDoSize
)
309 dummy
, mode
, (font
, face
, size
, color
) = self
.ted
.WEContinuousStyle(all
)
310 if not (mode
& WASTEconst
.weDoFont
):
313 font
= Fm
.GetFontName(font
)
314 if not (mode
& WASTEconst
.weDoFace
): fact
= None
315 if not (mode
& WASTEconst
.weDoSize
): size
= None
316 return font
, face
, size
318 class Wed(Application
):
320 Application
.__init
__(self
)
324 waste
.STDObjectHandlers()
326 def makeusermenus(self
):
327 self
.filemenu
= m
= Menu(self
.menubar
, "File")
328 self
.newitem
= MenuItem(m
, "New window", "N", self
.open)
329 self
.openitem
= MenuItem(m
, "Open...", "O", self
.openfile
)
330 self
.closeitem
= MenuItem(m
, "Close", "W", self
.closewin
)
332 self
.saveitem
= MenuItem(m
, "Save", "S", self
.save
)
333 self
.saveasitem
= MenuItem(m
, "Save as...", "", self
.saveas
)
335 self
.quititem
= MenuItem(m
, "Quit", "Q", self
.quit
)
337 self
.editmenu
= m
= Menu(self
.menubar
, "Edit")
338 self
.undoitem
= MenuItem(m
, "Undo", "Z", self
.undo
)
339 self
.cutitem
= MenuItem(m
, "Cut", "X", self
.cut
)
340 self
.copyitem
= MenuItem(m
, "Copy", "C", self
.copy
)
341 self
.pasteitem
= MenuItem(m
, "Paste", "V", self
.paste
)
342 self
.clearitem
= MenuItem(m
, "Clear", "", self
.clear
)
346 # Groups of items enabled together:
347 self
.windowgroup
= [self
.closeitem
, self
.saveitem
, self
.saveasitem
,
348 self
.editmenu
, self
.fontmenu
, self
.facemenu
, self
.sizemenu
]
349 self
.focusgroup
= [self
.cutitem
, self
.copyitem
, self
.clearitem
]
350 self
.windowgroup_on
= -1
351 self
.focusgroup_on
= -1
352 self
.pastegroup_on
= -1
353 self
.undo_label
= "never"
356 def makefontmenu(self
):
357 self
.fontmenu
= Menu(self
.menubar
, "Font")
358 self
.fontnames
= getfontnames()
360 for n
in self
.fontnames
:
361 m
= MenuItem(self
.fontmenu
, n
, "", self
.selfont
)
362 self
.fontitems
.append(m
)
363 self
.facemenu
= Menu(self
.menubar
, "Style")
365 for n
, shortcut
in STYLES
:
366 m
= MenuItem(self
.facemenu
, n
, shortcut
, self
.selface
)
367 self
.faceitems
.append(m
)
368 self
.facemenu
.addseparator()
369 self
.faceitem_normal
= MenuItem(self
.facemenu
, "Normal", "N",
371 self
.sizemenu
= Menu(self
.menubar
, "Size")
374 m
= MenuItem(self
.sizemenu
, `n`
, "", self
.selsize
)
375 self
.sizeitems
.append(m
)
376 self
.sizemenu
.addseparator()
377 self
.sizeitem_bigger
= MenuItem(self
.sizemenu
, "Bigger", "+",
379 self
.sizeitem_smaller
= MenuItem(self
.sizemenu
, "Smaller", "-",
382 def selfont(self
, id, item
, *rest
):
384 font
= self
.fontnames
[item
-1]
385 self
.active
.menu_setfont(font
)
387 EasyDialogs
.Message("No active window?")
389 def selface(self
, id, item
, *rest
):
392 self
.active
.menu_modface(face
)
394 EasyDialogs
.Message("No active window?")
396 def selfacenormal(self
, *rest
):
398 self
.active
.menu_setface(0)
400 EasyDialogs
.Message("No active window?")
402 def selsize(self
, id, item
, *rest
):
405 self
.active
.menu_setsize(size
)
407 EasyDialogs
.Message("No active window?")
409 def selsizebigger(self
, *rest
):
411 self
.active
.menu_incsize(2)
413 EasyDialogs
.Message("No active window?")
415 def selsizesmaller(self
, *rest
):
417 self
.active
.menu_incsize(-2)
419 EasyDialogs
.Message("No active window?")
421 def updatemenubar(self
):
423 on
= (self
.active
<> None)
424 if on
<> self
.windowgroup_on
:
425 for m
in self
.windowgroup
:
427 self
.windowgroup_on
= on
430 # only if we have an edit menu
431 on
= self
.active
.have_selection()
432 if on
<> self
.focusgroup_on
:
433 for m
in self
.focusgroup
:
435 self
.focusgroup_on
= on
437 on
= self
.active
.can_paste()
438 if on
<> self
.pastegroup_on
:
439 self
.pasteitem
.enable(on
)
440 self
.pastegroup_on
= on
442 on
= self
.active
.can_undo()
443 if on
<> self
.undo_label
:
445 self
.undoitem
.enable(1)
446 self
.undoitem
.settext(on
)
449 self
.undoitem
.settext("Nothing to undo")
450 self
.undoitem
.enable(0)
452 if self
.updatefontmenus():
457 def updatefontmenus(self
):
458 info
= self
.active
.getruninfo()
459 if info
== self
.ffs_values
:
461 # Remove old checkmarks
462 if self
.ffs_values
== ():
463 self
.ffs_values
= (None, None, None)
464 font
, face
, size
= self
.ffs_values
466 fnum
= self
.fontnames
.index(font
)
467 self
.fontitems
[fnum
].check(0)
469 for i
in range(len(self
.faceitems
)):
471 self
.faceitems
[i
].check(0)
473 for i
in range(len(self
.sizeitems
)):
475 self
.sizeitems
[i
].check(0)
477 self
.ffs_values
= info
479 font
, face
, size
= self
.ffs_values
481 fnum
= self
.fontnames
.index(font
)
482 self
.fontitems
[fnum
].check(1)
484 for i
in range(len(self
.faceitems
)):
486 self
.faceitems
[i
].check(1)
488 for i
in range(len(self
.sizeitems
)):
490 self
.sizeitems
[i
].check(1)
491 # Set outline/normal for sizes
493 exists
= getfontsizes(font
, SIZES
)
494 for i
in range(len(self
.sizeitems
)):
496 self
.sizeitems
[i
].setstyle(0)
498 self
.sizeitems
[i
].setstyle(8)
504 def do_about(self
, id, item
, window
, event
):
505 EasyDialogs
.Message("A simple single-font text editor based on WASTE")
511 def open(self
, *args
):
514 def openfile(self
, *args
):
517 def _open(self
, askfile
):
519 fss
, ok
= macfs
.StandardGetFile('TEXT')
522 path
= fss
.as_pathname()
523 name
= os
.path
.split(path
)[-1]
525 fp
= open(path
, 'rb') # NOTE binary, we need cr as end-of-line
529 EasyDialogs
.Message("IOERROR: "+`arg`
)
533 name
= "Untitled %d"%self
.num
535 w
= WasteWindow(self
)
536 w
.open(path
, name
, data
)
537 self
.num
= self
.num
+ 1
539 def closewin(self
, *args
):
543 EasyDialogs
.Message("No active window?")
545 def save(self
, *args
):
547 self
.active
.menu_save()
549 EasyDialogs
.Message("No active window?")
551 def saveas(self
, *args
):
553 self
.active
.menu_save_as()
555 EasyDialogs
.Message("No active window?")
558 def quit(self
, *args
):
559 for w
in self
._windows
.values():
569 def undo(self
, *args
):
571 self
.active
.menu_undo()
573 EasyDialogs
.Message("No active window?")
575 def cut(self
, *args
):
577 self
.active
.menu_cut()
579 EasyDialogs
.Message("No active window?")
581 def copy(self
, *args
):
583 self
.active
.menu_copy()
585 EasyDialogs
.Message("No active window?")
587 def paste(self
, *args
):
589 self
.active
.menu_paste()
591 EasyDialogs
.Message("No active window?")
593 def clear(self
, *args
):
595 self
.active
.menu_clear()
597 EasyDialogs
.Message("No active window?")
603 def idle(self
, event
):
605 self
.active
.do_idle(event
)
610 n
= Fm
.GetFontName(i
)
611 if n
: names
.append(n
)
614 def getfontsizes(name
, sizes
):
616 num
= Fm
.GetFNum(name
)
618 if Fm
.RealFont(num
, sz
):
628 if __name__
== '__main__':