blob: 0c1fb33caa3aba942ecda7033840c032b2bbae77 [file] [log] [blame]
Jack Jansen17720092001-07-14 14:02:21 +00001# A minimal text editor using MLTE. Based on wed.py.
2#
3# To be done:
4# - Functionality: find, etc.
5
6from Menu import DrawMenuBar
7from FrameWork import *
8import Win
9import Qd
10import Res
11import Scrap
12import os
13import macfs
14import MacTextEditor
15import Mlte
16
17UNDOLABELS = [ # Indexed by MLTECanUndo() value
18 "Typing", "Cut", "Paste", "Clear", "Font Change", "Color Change", "Size Change",
19 "Style Change", "Align Left", "Align Center", "Align Right", "Drop", "Move"]
20
21class WasteWindow(Window):
22 def open(self, path, name, data):
23 self.path = path
24 self.name = name
25 r = windowbounds(400, 400)
26 w = Win.NewWindow(r, name, 1, 0, -1, 1, 0x55555555)
27 self.wid = w
28## vr = 0, 0, r[2]-r[0]-15, r[3]-r[1]-15
29## dr = (0, 0, 10240, 0)
30## Qd.SetPort(w)
31## Qd.TextFont(4)
32## Qd.TextSize(9)
33## self.ted = waste.WENew(dr, vr, flags)
34 flags = MacTextEditor.kTXNDrawGrowIconMask|MacTextEditor.kTXNShowWindowMask|MacTextEditor.kTXNWantHScrollBarMask| \
35 MacTextEditor.kTXNWantVScrollBarMask
36 self.ted, self.frameid = Mlte.TXNNewObject(None, w, None, flags, MacTextEditor.kTXNTextEditStyleFrameType,
37 MacTextEditor.kTXNTextFile, MacTextEditor.kTXNMacOSEncoding)
38## self.tedtexthandle = Res.Resource(data)
39## self.ted.WEUseText(self.tedtexthandle)
40 self.ted.TXNSetData(MacTextEditor.kTXNTextData, data, 0, 0x7fffffff)
41## self.ted.WECalText()
42## w.DrawGrowIcon()
43## self.scrollbars()
44 self.changed = 0
45 self.do_postopen()
46 self.do_activate(1, None)
47
48 def do_idle(self, event):
49## (what, message, when, where, modifiers) = event
50## Qd.SetPort(self.wid)
51 self.ted.TXNIdle()
52 self.ted.TXNAdjustCursor(None)
53
54## def getscrollbarvalues(self):
55## dr = self.ted.WEGetDestRect()
56## vr = self.ted.WEGetViewRect()
57## vx = self.scalebarvalue(dr[0], dr[2], vr[0], vr[2])
58## vy = self.scalebarvalue(dr[1], dr[3], vr[1], vr[3])
59#### print dr, vr, vx, vy
60## return vx, vy
61##
62## def scrollbar_callback(self, which, what, value):
63## if which == 'y':
64## if what == 'set':
65## height = self.ted.WEGetHeight(0, 0x3fffffff)
66## cur = self.getscrollbarvalues()[1]
67## delta = (cur-value)*height/32767
68## if what == '-':
69## topline_off,dummy = self.ted.WEGetOffset((1,1))
70## topline_num = self.ted.WEOffsetToLine(topline_off)
71## delta = self.ted.WEGetHeight(topline_num, topline_num+1)
72## elif what == '--':
73## delta = (self.ted.WEGetViewRect()[3]-10)
74## if delta <= 0:
75## delta = 10 # Random value
76## elif what == '+':
77## # XXXX Wrong: should be bottom line size
78## topline_off,dummy = self.ted.WEGetOffset((1,1))
79## topline_num = self.ted.WEOffsetToLine(topline_off)
80## delta = -self.ted.WEGetHeight(topline_num, topline_num+1)
81## elif what == '++':
82## delta = -(self.ted.WEGetViewRect()[3]-10)
83## if delta >= 0:
84## delta = -10
85## self.ted.WEScroll(0, delta)
86#### print 'SCROLL Y', delta
87## else:
88## if what == 'set':
89## return # XXXX
90## vr = self.ted.WEGetViewRect()
91## winwidth = vr[2]-vr[0]
92## if what == '-':
93## delta = winwidth/10
94## elif what == '--':
95## delta = winwidth/2
96## elif what == '+':
97## delta = -winwidth/10
98## elif what == '++':
99## delta = -winwidth/2
100## self.ted.WEScroll(delta, 0)
101## # Pin the scroll
102## l, t, r, b = self.ted.WEGetDestRect()
103## vl, vt, vr, vb = self.ted.WEGetViewRect()
104## if t > 0 or l > 0:
105## dx = dy = 0
106## if t > 0: dy = -t
107## if l > 0: dx = -l
108#### print 'Extra scroll', dx, dy
109## self.ted.WEScroll(dx, dy)
110## elif b < vb:
111#### print 'Extra downscroll', b-vb
112## self.ted.WEScroll(0, b-vb)
113
114
115 def do_activate(self, onoff, evt):
116## print "ACTIVATE", onoff
117 Qd.SetPort(self.wid)
118 Window.do_activate(self, onoff, evt)
119 if onoff:
120 self.ted.TXNFocus(1)
121 self.parent.active = self
122 self.parent.updatemenubar()
123 else:
124 self.ted.TXNFocus(0)
125
126 def do_update(self, wid, event):
127 Qd.SetPort(self.wid)
128## region = wid.GetWindowPort().visRgn
129## if Qd.EmptyRgn(region):
130## return
131## Qd.EraseRgn(region)
132 self.ted.TXNUpdate()
133## self.updatescrollbars()
134
135## def do_postresize(self, width, height, window):
136## l, t, r, b = self.ted.WEGetViewRect()
137## vr = (l, t, l+width-15, t+height-15)
138## self.ted.WESetViewRect(vr)
139## self.wid.InvalWindowRect(vr)
140## ScrolledWindow.do_postresize(self, width, height, window)
141
142 def do_contentclick(self, local, modifiers, evt):
143## (what, message, when, where, modifiers) = evt
144 self.ted.TXNClick(evt)
145## self.updatescrollbars()
146 self.parent.updatemenubar()
147
148 def do_char(self, ch, event):
149 self.ted.TXNKeyDown(event)
150## self.ted.WESelView()
151## (what, message, when, where, modifiers) = event
152## self.ted.WEKey(ord(ch), modifiers)
153## self.changed = 1
154## self.updatescrollbars()
155## self.parent.updatemenubar()
156
157 def close(self):
158 if self.changed:
159 save = EasyDialogs.AskYesNoCancel('Save window "%s" before closing?'%self.name, 1)
160 if save > 0:
161 self.menu_save()
162 elif save < 0:
163 return
164 if self.parent.active == self:
165 self.parent.active = None
166## self.parent.updatemenubar()
167 del self.ted
168## del self.tedtexthandle
169 self.do_postclose()
170
171 def menu_save(self):
172 if not self.path:
173 self.menu_save_as()
174 return # Will call us recursively
175 print 'Saving to ', self.path
176 dhandle = self.ted.TXNGetData(0, 0x7fffffff)
177 data = dhandle.data
178 fp = open(self.path, 'wb') # NOTE: wb, because data has CR for end-of-line
179 fp.write(data)
180 if data[-1] <> '\r': fp.write('\r')
181 fp.close()
182 self.changed = 0
183
184 def menu_save_as(self):
185 fss, ok = macfs.StandardPutFile('Save as:')
186 if not ok: return
187 self.path = fss.as_pathname()
188 self.name = os.path.split(self.path)[-1]
189 self.wid.SetWTitle(self.name)
190 self.menu_save()
191
192 def menu_cut(self):
193## self.ted.WESelView()
194 self.ted.TXNCut()
195### Mlte.ConvertToPublicScrap()
196## Scrap.ZeroScrap()
197## self.ted.WECut()
198## self.updatescrollbars()
199 self.parent.updatemenubar()
200 self.changed = 1
201
202 def menu_copy(self):
203## Scrap.ZeroScrap()
204 self.ted.TXNCopy()
205### Mlte.ConvertToPublicScrap()
206## self.updatescrollbars()
207 self.parent.updatemenubar()
208
209 def menu_paste(self):
210### Mlte.ConvertFromPublicScrap()
211 self.ted.TXNPaste()
212## self.updatescrollbars()
213 self.parent.updatemenubar()
214 self.changed = 1
215
216 def menu_clear(self):
217## self.ted.WESelView()
218 self.ted.TXNClear()
219## self.updatescrollbars()
220 self.parent.updatemenubar()
221 self.changed = 1
222
223 def menu_undo(self):
224 self.ted.TXNUndo()
225## self.updatescrollbars()
226 self.parent.updatemenubar()
227
228 def menu_redo(self):
229 self.ted.TXNRedo()
230## self.updatescrollbars()
231 self.parent.updatemenubar()
232
233 def have_selection(self):
234 start, stop = self.ted.TXNGetSelection()
235 return start < stop
236
237 def can_paste(self):
238 return Mlte.TXNIsScrapPastable()
239
240 def can_undo(self):
241 can, which = self.ted.TXNCanUndo()
242 if not can:
243 return None
244 if which >= len(UNDOLABELS):
245 # Unspecified undo
246 return "Undo"
247 which = UNDOLABELS[which]
248
249 return "Undo "+which
250
251 def can_redo(self):
252 can, which = self.ted.TXNCanRedo()
253 if not can:
254 return None
255 if which >= len(UNDOLABELS):
256 # Unspecified undo
257 return "Redo"
258 which = UNDOLABELS[which]
259
260 return "Redo "+which
261
262class Mlted(Application):
263 def __init__(self):
264 Application.__init__(self)
265 self.num = 0
266 self.active = None
267 self.updatemenubar()
268
269 def makeusermenus(self):
270 self.filemenu = m = Menu(self.menubar, "File")
271 self.newitem = MenuItem(m, "New window", "N", self.open)
272 self.openitem = MenuItem(m, "Open...", "O", self.openfile)
273 self.closeitem = MenuItem(m, "Close", "W", self.closewin)
274 m.addseparator()
275 self.saveitem = MenuItem(m, "Save", "S", self.save)
276 self.saveasitem = MenuItem(m, "Save as...", "", self.saveas)
277 m.addseparator()
278 self.quititem = MenuItem(m, "Quit", "Q", self.quit)
279
280 self.editmenu = m = Menu(self.menubar, "Edit")
281 self.undoitem = MenuItem(m, "Undo", "Z", self.undo)
282 self.redoitem = MenuItem(m, "Undo", None, self.redo)
283 self.cutitem = MenuItem(m, "Cut", "X", self.cut)
284 self.copyitem = MenuItem(m, "Copy", "C", self.copy)
285 self.pasteitem = MenuItem(m, "Paste", "V", self.paste)
286 self.clearitem = MenuItem(m, "Clear", "", self.clear)
287
288 # Groups of items enabled together:
289 self.windowgroup = [self.closeitem, self.saveitem, self.saveasitem, self.editmenu]
290 self.focusgroup = [self.cutitem, self.copyitem, self.clearitem]
291 self.windowgroup_on = -1
292 self.focusgroup_on = -1
293 self.pastegroup_on = -1
294 self.undo_label = "never"
295 self.redo_label = "never"
296
297 def updatemenubar(self):
298 changed = 0
299 on = (self.active <> None)
300 if on <> self.windowgroup_on:
301 for m in self.windowgroup:
302 m.enable(on)
303 self.windowgroup_on = on
304 changed = 1
305 if on:
306 # only if we have an edit menu
307 on = self.active.have_selection()
308 if on <> self.focusgroup_on:
309 for m in self.focusgroup:
310 m.enable(on)
311 self.focusgroup_on = on
312 changed = 1
313 on = self.active.can_paste()
314 if on <> self.pastegroup_on:
315 self.pasteitem.enable(on)
316 self.pastegroup_on = on
317 changed = 1
318 on = self.active.can_undo()
319 if on <> self.undo_label:
320 if on:
321 self.undoitem.enable(1)
322 self.undoitem.settext(on)
323 self.undo_label = on
324 else:
325 self.undoitem.settext("Nothing to undo")
326 self.undoitem.enable(0)
327 changed = 1
328 on = self.active.can_redo()
329 if on <> self.redo_label:
330 if on:
331 self.redoitem.enable(1)
332 self.redoitem.settext(on)
333 self.redo_label = on
334 else:
335 self.redoitem.settext("Nothing to redo")
336 self.redoitem.enable(0)
337 changed = 1
338 if changed:
339 DrawMenuBar()
340
341 #
342 # Apple menu
343 #
344
345 def do_about(self, id, item, window, event):
346 EasyDialogs.Message("A simple single-font text editor based on MacTextEditor")
347
348 #
349 # File menu
350 #
351
352 def open(self, *args):
353 self._open(0)
354
355 def openfile(self, *args):
356 self._open(1)
357
358 def _open(self, askfile):
359 if askfile:
360 fss, ok = macfs.StandardGetFile('TEXT')
361 if not ok:
362 return
363 path = fss.as_pathname()
364 name = os.path.split(path)[-1]
365 try:
366 fp = open(path, 'rb') # NOTE binary, we need cr as end-of-line
367 data = fp.read()
368 fp.close()
369 except IOError, arg:
370 EasyDialogs.Message("IOERROR: "+`arg`)
371 return
372 else:
373 path = None
374 name = "Untitled %d"%self.num
375 data = ''
376 w = WasteWindow(self)
377 w.open(path, name, data)
378 self.num = self.num + 1
379
380 def closewin(self, *args):
381 if self.active:
382 self.active.close()
383 else:
384 EasyDialogs.Message("No active window?")
385
386 def save(self, *args):
387 if self.active:
388 self.active.menu_save()
389 else:
390 EasyDialogs.Message("No active window?")
391
392 def saveas(self, *args):
393 if self.active:
394 self.active.menu_save_as()
395 else:
396 EasyDialogs.Message("No active window?")
397
398
399 def quit(self, *args):
400 for w in self._windows.values():
401 w.close()
402 if self._windows:
403 return
404 self._quit()
405
406 #
407 # Edit menu
408 #
409
410 def undo(self, *args):
411 if self.active:
412 self.active.menu_undo()
413 else:
414 EasyDialogs.Message("No active window?")
415
416 def redo(self, *args):
417 if self.active:
418 self.active.menu_redo()
419 else:
420 EasyDialogs.Message("No active window?")
421
422 def cut(self, *args):
423 if self.active:
424 self.active.menu_cut()
425 else:
426 EasyDialogs.Message("No active window?")
427
428 def copy(self, *args):
429 if self.active:
430 self.active.menu_copy()
431 else:
432 EasyDialogs.Message("No active window?")
433
434 def paste(self, *args):
435 if self.active:
436 self.active.menu_paste()
437 else:
438 EasyDialogs.Message("No active window?")
439
440 def clear(self, *args):
441 if self.active:
442 self.active.menu_clear()
443 else:
444 EasyDialogs.Message("No active window?")
445
446 #
447 # Other stuff
448 #
449
450 def idle(self, event):
451 if self.active:
452 self.active.do_idle(event)
453
454def main():
455 Mlte.TXNInitTextension(0)
456 try:
457 App = Mlted()
458 App.mainloop()
459 finally:
460 Mlte.TXNTerminateTextension()
461
462if __name__ == '__main__':
463 main()
464