1 # A minimal text editor.
4 # - Functionality: find, etc.
6 from Menu
import DrawMenuBar
7 from FrameWork
import *
18 UNDOLABELS
= [ # Indexed by WEGetUndoInfo() value
19 None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style"]
21 # Style and size menu. Note that style order is important (tied to bit values)
23 ("Bold", "B"), ("Italic", "I"), ("Underline", "U"), ("Outline", "O"),
24 ("Shadow", ""), ("Condensed", ""), ("Extended", "")
26 SIZES
= [ 9, 10, 12, 14, 18, 24]
29 Qd
.SetRectRgn(BIGREGION
, -16000, -16000, 16000, 16000)
31 class WasteWindow(ScrolledWindow
):
32 def open(self
, path
, name
, data
):
35 r
= windowbounds(400, 400)
36 w
= Win
.NewWindow(r
, name
, 1, 0, -1, 1, 0x55555555)
38 vr
= 0, 0, r
[2]-r
[0]-15, r
[3]-r
[1]-15
43 flags
= WASTEconst
.weDoAutoScroll | WASTEconst
.weDoOutlineHilite | \
45 self
.ted
= waste
.WENew(dr
, vr
, flags
)
46 self
.ted
.WEInstallTabHooks()
47 style
, soup
= self
.getstylesoup()
48 self
.ted
.WEInsert(data
, style
, soup
)
49 self
.ted
.WESetSelection(0,0)
51 self
.ted
.WEResetModCount()
55 self
.do_activate(1, None)
57 def getstylesoup(self
):
60 oldrf
= Res
.CurResFile()
62 rf
= Res
.OpenResFile(self
.path
)
66 hstyle
= Res
.Get1Resource('styl', 128)
67 hstyle
.DetachResource()
71 hsoup
= Res
.Get1Resource('SOUP', 128)
72 hsoup
.DetachResource()
79 def do_idle(self
, event
):
80 (what
, message
, when
, where
, modifiers
) = event
83 if self
.ted
.WEAdjustCursor(where
, BIGREGION
):
85 Qd
.SetCursor(Qd
.qd
.arrow
)
87 def getscrollbarvalues(self
):
88 dr
= self
.ted
.WEGetDestRect()
89 vr
= self
.ted
.WEGetViewRect()
90 vx
= self
.scalebarvalue(dr
[0], dr
[2], vr
[0], vr
[2])
91 vy
= self
.scalebarvalue(dr
[1], dr
[3], vr
[1], vr
[3])
94 def scrollbar_callback(self
, which
, what
, value
):
97 height
= self
.ted
.WEGetHeight(0, 0x3fffffff)
98 cur
= self
.getscrollbarvalues()[1]
99 delta
= (cur
-value
)*height
/32767
101 topline_off
,dummy
= self
.ted
.WEGetOffset((1,1))
102 topline_num
= self
.ted
.WEOffsetToLine(topline_off
)
103 delta
= self
.ted
.WEGetHeight(topline_num
, topline_num
+1)
105 delta
= (self
.ted
.WEGetViewRect()[3]-10)
107 delta
= 10 # Random value
109 # XXXX Wrong: should be bottom line size
110 topline_off
,dummy
= self
.ted
.WEGetOffset((1,1))
111 topline_num
= self
.ted
.WEOffsetToLine(topline_off
)
112 delta
= -self
.ted
.WEGetHeight(topline_num
, topline_num
+1)
114 delta
= -(self
.ted
.WEGetViewRect()[3]-10)
117 self
.ted
.WEScroll(0, delta
)
121 vr
= self
.ted
.WEGetViewRect()
122 winwidth
= vr
[2]-vr
[0]
131 self
.ted
.WEScroll(delta
, 0)
133 l
, t
, r
, b
= self
.ted
.WEGetDestRect()
134 vl
, vt
, vr
, vb
= self
.ted
.WEGetViewRect()
139 self
.ted
.WEScroll(dx
, dy
)
141 self
.ted
.WEScroll(0, b
-vb
)
144 def do_activate(self
, onoff
, evt
):
146 ScrolledWindow
.do_activate(self
, onoff
, evt
)
148 self
.ted
.WEActivate()
149 self
.parent
.active
= self
150 self
.parent
.updatemenubar()
152 self
.ted
.WEDeactivate()
154 def do_update(self
, wid
, event
):
155 region
= wid
.GetWindowPort().visRgn
156 if Qd
.EmptyRgn(region
):
159 self
.ted
.WEUpdate(region
)
160 self
.updatescrollbars()
162 def do_postresize(self
, width
, height
, window
):
163 l
, t
, r
, b
= self
.ted
.WEGetViewRect()
164 vr
= (l
, t
, l
+width
-15, t
+height
-15)
165 self
.ted
.WESetViewRect(vr
)
167 ScrolledWindow
.do_postresize(self
, width
, height
, window
)
169 def do_contentclick(self
, local
, modifiers
, evt
):
170 (what
, message
, when
, where
, modifiers
) = evt
171 self
.ted
.WEClick(local
, modifiers
, when
)
172 self
.updatescrollbars()
173 self
.parent
.updatemenubar()
175 def do_char(self
, ch
, event
):
177 (what
, message
, when
, where
, modifiers
) = event
178 self
.ted
.WEKey(ord(ch
), modifiers
)
179 self
.updatescrollbars()
180 self
.parent
.updatemenubar()
183 if self
.ted
.WEGetModCount():
184 save
= EasyDialogs
.AskYesNoCancel('Save window "%s" before closing?'%self
.name
, 1)
189 if self
.parent
.active
== self
:
190 self
.parent
.active
= None
191 self
.parent
.updatemenubar()
198 return # Will call us recursively
202 dhandle
= self
.ted
.WEGetText()
204 fp
= open(self
.path
, 'wb') # NOTE: wb, because data has CR for end-of-line
206 if data
[-1] <> '\r': fp
.write('\r')
209 # Now save style and soup
211 oldresfile
= Res
.CurResFile()
213 rf
= Res
.OpenResFile(self
.path
)
215 Res
.CreateResFile(self
.path
)
216 rf
= Res
.OpenResFile(self
.path
)
217 styles
= Res
.Resource('')
218 soup
= Res
.Resource('')
219 self
.ted
.WECopyRange(0, 0x3fffffff, None, styles
, soup
)
220 styles
.AddResource('styl', 128, '')
221 soup
.AddResource('SOUP', 128, '')
223 Res
.UseResFile(oldresfile
)
225 self
.ted
.WEResetModCount()
227 def menu_save_as(self
):
228 fss
, ok
= macfs
.StandardPutFile('Save as:')
230 self
.path
= fss
.as_pathname()
231 self
.name
= os
.path
.split(self
.path
)[-1]
232 self
.wid
.SetWTitle(self
.name
)
240 self
.updatescrollbars()
241 self
.parent
.updatemenubar()
246 self
.updatescrollbars()
247 self
.parent
.updatemenubar()
249 def menu_paste(self
):
252 self
.updatescrollbars()
253 self
.parent
.updatemenubar()
255 def menu_clear(self
):
258 self
.updatescrollbars()
259 self
.parent
.updatemenubar()
263 self
.updatescrollbars()
264 self
.parent
.updatemenubar()
266 def menu_setfont(self
, font
):
267 font
= Fm
.GetFNum(font
)
268 self
.mysetstyle(WASTEconst
.weDoFont
, (font
, 0, 0, (0,0,0)))
269 self
.parent
.updatemenubar()
271 def menu_modface(self
, face
):
272 self
.mysetstyle(WASTEconst
.weDoFace|WASTEconst
.weDoToggleFace
,
273 (0, face
, 0, (0,0,0)))
275 def menu_setface(self
, face
):
276 self
.mysetstyle(WASTEconst
.weDoFace|WASTEconst
.weDoReplaceFace
,
277 (0, face
, 0, (0,0,0)))
279 def menu_setsize(self
, size
):
280 self
.mysetstyle(WASTEconst
.weDoSize
, (0, 0, size
, (0,0,0)))
282 def menu_incsize(self
, size
):
283 self
.mysetstyle(WASTEconst
.weDoAddSize
, (0, 0, size
, (0,0,0)))
285 def mysetstyle(self
, which
, how
):
287 self
.ted
.WESetStyle(which
, how
)
288 self
.parent
.updatemenubar()
290 def have_selection(self
):
291 start
, stop
= self
.ted
.WEGetSelection()
295 return self
.ted
.WECanPaste()
298 which
, redo
= self
.ted
.WEGetUndoInfo()
299 which
= UNDOLABELS
[which
]
300 if which
== None: return None
306 def getruninfo(self
):
307 all
= (WASTEconst
.weDoFont | WASTEconst
.weDoFace | WASTEconst
.weDoSize
)
308 dummy
, mode
, (font
, face
, size
, color
) = self
.ted
.WEContinuousStyle(all
)
309 if not (mode
& WASTEconst
.weDoFont
):
312 font
= Fm
.GetFontName(font
)
313 if not (mode
& WASTEconst
.weDoFace
): fact
= None
314 if not (mode
& WASTEconst
.weDoSize
): size
= None
315 return font
, face
, size
317 class Wed(Application
):
319 Application
.__init
__(self
)
323 waste
.STDObjectHandlers()
325 def makeusermenus(self
):
326 self
.filemenu
= m
= Menu(self
.menubar
, "File")
327 self
.newitem
= MenuItem(m
, "New window", "N", self
.open)
328 self
.openitem
= MenuItem(m
, "Open...", "O", self
.openfile
)
329 self
.closeitem
= MenuItem(m
, "Close", "W", self
.closewin
)
331 self
.saveitem
= MenuItem(m
, "Save", "S", self
.save
)
332 self
.saveasitem
= MenuItem(m
, "Save as...", "", self
.saveas
)
334 self
.quititem
= MenuItem(m
, "Quit", "Q", self
.quit
)
336 self
.editmenu
= m
= Menu(self
.menubar
, "Edit")
337 self
.undoitem
= MenuItem(m
, "Undo", "Z", self
.undo
)
338 self
.cutitem
= MenuItem(m
, "Cut", "X", self
.cut
)
339 self
.copyitem
= MenuItem(m
, "Copy", "C", self
.copy
)
340 self
.pasteitem
= MenuItem(m
, "Paste", "V", self
.paste
)
341 self
.clearitem
= MenuItem(m
, "Clear", "", self
.clear
)
345 # Groups of items enabled together:
346 self
.windowgroup
= [self
.closeitem
, self
.saveitem
, self
.saveasitem
,
347 self
.editmenu
, self
.fontmenu
, self
.facemenu
, self
.sizemenu
]
348 self
.focusgroup
= [self
.cutitem
, self
.copyitem
, self
.clearitem
]
349 self
.windowgroup_on
= -1
350 self
.focusgroup_on
= -1
351 self
.pastegroup_on
= -1
352 self
.undo_label
= "never"
355 def makefontmenu(self
):
356 self
.fontmenu
= Menu(self
.menubar
, "Font")
357 self
.fontnames
= getfontnames()
359 for n
in self
.fontnames
:
360 m
= MenuItem(self
.fontmenu
, n
, "", self
.selfont
)
361 self
.fontitems
.append(m
)
362 self
.facemenu
= Menu(self
.menubar
, "Style")
364 for n
, shortcut
in STYLES
:
365 m
= MenuItem(self
.facemenu
, n
, shortcut
, self
.selface
)
366 self
.faceitems
.append(m
)
367 self
.facemenu
.addseparator()
368 self
.faceitem_normal
= MenuItem(self
.facemenu
, "Normal", "N",
370 self
.sizemenu
= Menu(self
.menubar
, "Size")
373 m
= MenuItem(self
.sizemenu
, `n`
, "", self
.selsize
)
374 self
.sizeitems
.append(m
)
375 self
.sizemenu
.addseparator()
376 self
.sizeitem_bigger
= MenuItem(self
.sizemenu
, "Bigger", "+",
378 self
.sizeitem_smaller
= MenuItem(self
.sizemenu
, "Smaller", "-",
381 def selfont(self
, id, item
, *rest
):
383 font
= self
.fontnames
[item
-1]
384 self
.active
.menu_setfont(font
)
386 EasyDialogs
.Message("No active window?")
388 def selface(self
, id, item
, *rest
):
391 self
.active
.menu_modface(face
)
393 EasyDialogs
.Message("No active window?")
395 def selfacenormal(self
, *rest
):
397 self
.active
.menu_setface(0)
399 EasyDialogs
.Message("No active window?")
401 def selsize(self
, id, item
, *rest
):
404 self
.active
.menu_setsize(size
)
406 EasyDialogs
.Message("No active window?")
408 def selsizebigger(self
, *rest
):
410 self
.active
.menu_incsize(2)
412 EasyDialogs
.Message("No active window?")
414 def selsizesmaller(self
, *rest
):
416 self
.active
.menu_incsize(-2)
418 EasyDialogs
.Message("No active window?")
420 def updatemenubar(self
):
422 on
= (self
.active
<> None)
423 if on
<> self
.windowgroup_on
:
424 for m
in self
.windowgroup
:
426 self
.windowgroup_on
= on
429 # only if we have an edit menu
430 on
= self
.active
.have_selection()
431 if on
<> self
.focusgroup_on
:
432 for m
in self
.focusgroup
:
434 self
.focusgroup_on
= on
436 on
= self
.active
.can_paste()
437 if on
<> self
.pastegroup_on
:
438 self
.pasteitem
.enable(on
)
439 self
.pastegroup_on
= on
441 on
= self
.active
.can_undo()
442 if on
<> self
.undo_label
:
444 self
.undoitem
.enable(1)
445 self
.undoitem
.settext(on
)
448 self
.undoitem
.settext("Nothing to undo")
449 self
.undoitem
.enable(0)
451 if self
.updatefontmenus():
456 def updatefontmenus(self
):
457 info
= self
.active
.getruninfo()
458 if info
== self
.ffs_values
:
460 # Remove old checkmarks
461 if self
.ffs_values
== ():
462 self
.ffs_values
= (None, None, None)
463 font
, face
, size
= self
.ffs_values
465 fnum
= self
.fontnames
.index(font
)
466 self
.fontitems
[fnum
].check(0)
468 for i
in range(len(self
.faceitems
)):
470 self
.faceitems
[i
].check(0)
472 for i
in range(len(self
.sizeitems
)):
474 self
.sizeitems
[i
].check(0)
476 self
.ffs_values
= info
478 font
, face
, size
= self
.ffs_values
480 fnum
= self
.fontnames
.index(font
)
481 self
.fontitems
[fnum
].check(1)
483 for i
in range(len(self
.faceitems
)):
485 self
.faceitems
[i
].check(1)
487 for i
in range(len(self
.sizeitems
)):
489 self
.sizeitems
[i
].check(1)
490 # Set outline/normal for sizes
492 exists
= getfontsizes(font
, SIZES
)
493 for i
in range(len(self
.sizeitems
)):
495 self
.sizeitems
[i
].setstyle(0)
497 self
.sizeitems
[i
].setstyle(8)
503 def do_about(self
, id, item
, window
, event
):
504 EasyDialogs
.Message("A simple single-font text editor based on WASTE")
510 def open(self
, *args
):
513 def openfile(self
, *args
):
516 def _open(self
, askfile
):
518 fss
, ok
= macfs
.StandardGetFile('TEXT')
521 path
= fss
.as_pathname()
522 name
= os
.path
.split(path
)[-1]
524 fp
= open(path
, 'rb') # NOTE binary, we need cr as end-of-line
528 EasyDialogs
.Message("IOERROR: "+`arg`
)
532 name
= "Untitled %d"%self
.num
534 w
= WasteWindow(self
)
535 w
.open(path
, name
, data
)
536 self
.num
= self
.num
+ 1
538 def closewin(self
, *args
):
542 EasyDialogs
.Message("No active window?")
544 def save(self
, *args
):
546 self
.active
.menu_save()
548 EasyDialogs
.Message("No active window?")
550 def saveas(self
, *args
):
552 self
.active
.menu_save_as()
554 EasyDialogs
.Message("No active window?")
557 def quit(self
, *args
):
558 for w
in self
._windows
.values():
568 def undo(self
, *args
):
570 self
.active
.menu_undo()
572 EasyDialogs
.Message("No active window?")
574 def cut(self
, *args
):
576 self
.active
.menu_cut()
578 EasyDialogs
.Message("No active window?")
580 def copy(self
, *args
):
582 self
.active
.menu_copy()
584 EasyDialogs
.Message("No active window?")
586 def paste(self
, *args
):
588 self
.active
.menu_paste()
590 EasyDialogs
.Message("No active window?")
592 def clear(self
, *args
):
594 self
.active
.menu_clear()
596 EasyDialogs
.Message("No active window?")
602 def idle(self
, event
):
604 self
.active
.do_idle(event
)
609 n
= Fm
.GetFontName(i
)
610 if n
: names
.append(n
)
613 def getfontsizes(name
, sizes
):
615 num
= Fm
.GetFNum(name
)
617 if Fm
.RealFont(num
, sz
):
627 if __name__
== '__main__':